股票量化回测分析

        在金融市场中,股票价格的波动受到多种因素的影响,包括经济数据、公司业绩和政策变化等。为了分析和预测股票价格的走势,投资者可以借助各种技术指标和分析方法,如:金叉死叉、相对强弱指标(RSI)和移动平均线(MACD)等技术指标。这些指标可以辅助投资者识别股票价格的趋势和反转点,从而指导投资者进行买卖决策。

        然而,需要注意的是,技术指标和分析方法并不能完全预测股票价格的走势,它们只是提供了一种辅助工具。技术指标也可能存在滞后性和误导性的问题,因此投资者应谨慎使用,并结合基本面分析和市场情绪等因素进行综合判断。投资买卖股票存在一定的风险,可能导致投资者遭受巨大损失。尽管买卖股票有亏有赚是常见的现象,但通过科学的投资方法和风险管理策略,投资者仍然可以提高投资的成功率和盈利能力。

温馨提醒:本文分析仅供参考,谨慎根据指标进行买卖股票,还需结合其余指标分析。

目录

1、导入基本库

2、导入数据

3、交易信号设置

 4、交易买卖位可视化

 5、股票买卖操作--回测

 6、策略累积收益

 7、回测评估


1、导入基本库

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns            
import warnings
plt.rcParams['font.sans-serif']='SimHei'   
plt.rcParams['axes.unicode_minus'] = False
warnings.filterwarnings('ignore')
pd.set_option('display.unicode.east_asian_width',True)
# 需要打开掘金后运行
import talib as ta
from __future__ import print_function, absolute_import, unicode_literals
from gm.api import *
## 终端开启,设置token
set_token('5ba568ac759242c0471cb0fe330cd04214fb0b5b')

天泽掘金量化平台下载:

链接:https://pan.baidu.com/s/1Nul-x4gEe6GhrMF_gWjyUQ 
提取码:1901

2、导入数据

        本文选取了长安汽车(SZSE.000625)股票,时间数据为2020年1月1日到2023年6月7日,数据来源于“天泽掘金量化平台”中导入,导入前需要打开平台才可导入。

# 获取股票数据
def get_stock_data(symbol, fre , start_date , end_date):
    # 掘金获取
    data=history(symbol, frequency = fre, start_time = start_date, end_time = end_date , df = True)
    return data

# 获取数据函数调用
symbol = 'SZSE.000625'  
fre = '1d'
start_date = '2020-01-01'  
end_date = '2023-06-07'  
data = get_stock_data(symbol, fre , start_date , end_date)
data.head(2)

输出结果为:

 数据简单处理

# 数据简单处理
data = data[['bob','close']]
data['bob'] = data['bob'].dt.date
data.head(1)

输出结果:

3、交易信号设置

        设置短期为5日均线,长期为20日均线

# 金叉死叉交易信号
def golden_death_cross_signal(data, short_window, long_window):
    '''
    data:DateFrame数据集,有close收盘价指标
    short_window :短期移动窗口数
    long_window  :长期移动窗口数
    
    (若  短期窗口平均值  >  长期窗口平均值  --- 金叉买入,买卖信号为 1 ;
      若  短期窗口平均值  <  长期窗口平均值  --- 死叉卖出,买卖信号为 -1 )
    '''
    # 移动平均线
    data['MA_short'] = data['close'].rolling(window=short_window).mean()   
    data['MA_long'] = data['close'].rolling(window=long_window).mean()
    
    # 交易信号生成
    data['Signal'] = 0
    data.loc[data['MA_short'] > data['MA_long'], 'Signal'] = 1   # 金叉信号
    data.loc[data['MA_short'] < data['MA_long'], 'Signal'] = -1  # 死叉信号
    
    return data

# 交易信号函数调用
short_window = 5  
long_window = 20 
data = golden_death_cross_signal(data, short_window, long_window)
df = data.copy()
df.tail()

输出结果:

 4、交易买卖位可视化

# 策略交易判断
def golden_death_cross_judge(data):
    '''
    data:DataFrame数据集,要求有Signal买卖信号指标
    
    (该函数是确定买入和卖出的时间点,方便可视化进行分析)
    '''
    buy_signals = []
    sell_signals = []
    for i in range(1, len(data)):
        if data['Signal'][i] == 1 and data['Signal'][i-1] == -1:
            buy_signals.append(data.index[i])
        elif data['Signal'][i] == -1 and data['Signal'][i-1] == 1:
            sell_signals.append(data.index[i])
    return buy_signals, sell_signals

# 交易判断函数调用
buy_signals, sell_signals = golden_death_cross_judge(data)


# 买信号
buy = pd.DataFrame(buy_signals,columns=['buy'])
buy['buy_close'] = 0
buy = buy.set_index('buy')
for i in buy.index:
    buy.loc[i,'buy_close'] = df.loc[i,'close']
# 卖信号
sell = pd.DataFrame(sell_signals,columns=['sell'])
sell['sell_close'] = 0
sell = sell.set_index('sell')
for i in sell.index:
    sell.loc[i,'sell_close'] = df.loc[i,'close']
plt.figure(figsize=(18, 14),dpi=200)
plt.subplot(211)
# 双均线折线图
plt.plot(data['bob'],data['MA_short'],label='MA5')
plt.plot(data['bob'],data['MA_long'],label='MA20')
plt.legend(fontsize='larger')
plt.title('双均线 [5日均线 和 20日均线] 折线图')

plt.subplot(212)
plt.plot(df.index, df['close'], label='close',c='orange')
# 买卖信号绘图
plt.plot(buy.index,buy['buy_close'], '^', markersize=8, color='r', label='Buy')
plt.plot(sell.index,sell['sell_close'], 'v', markersize=8, color='green', label='Sell')
plt.title('买入和卖出信号图')
plt.legend()
plt.show()

输出结果:

 5、股票买卖操作--回测

        假设投资人的初始资金为100000元,每次买卖都以全仓进行买和卖。在买卖操作中涉及到了手续费,即持股资金除以10000(注:不足5元按5元收费)。

# 依据信号 进行 买卖股票
def buy_sell_stock(df,initial_capitals):
    a = 0 # 统计买入次数
    b = 0 # 统计卖出次数
    '''
    df:DateFrame数据集,需要有close、Signal、return三列指标。
        close是收盘价,Signal是买卖信号指标
        return是收益率 【计算方法:(当日收盘价-昨日收盘价)/昨日收盘价】
    initial_capitals:初始资金
    '''
    # 定义资金
    initial_capital = initial_capitals  # 初始资金
    position = 0                        # 股票持仓
    df['Position'] = np.nan             # 持有数量
    df['Return'] = np.nan               # 策略回报率
    df['Holdings Funds'] = np.nan       # 持有的资金(股票仓位)
    df['Balance Funds']  = np.nan       # 余额资金
    df['Total Funds'] = np.nan          # 总资金
    
    for i, row in enumerate(df.iterrows()):
        index, data = row
        '''
        row是元组(a,b)的形式
            a是列索引,赋值给index
            b是该索引对应的行数据,赋值给data
        '''
        
        # 金叉,买入股票
        if data['Signal'] == 1:  
            # 当前没有持仓
            if position == 0:  
                # 买入的股数 
                position = int(initial_capital // (data['close']*100)*100)
                # 指标计算
                '''df.at[]:准确定位一个单元格'''
                df.at[index, 'Position'] = position                         
                df.at[index, 'Return'] = 0
                df.at[index, 'Holdings Funds'] = position * data['close']
                Balance_Funds = initial_capital - df.at[index, 'Holdings Funds'] 
                df.at[index, 'Balance Funds'] = Balance_Funds
                # 手续费计算 --- 买入资金的万分之一(5元起步)
                if df.at[index, 'Holdings Funds']/10000 < 5:
                    service_cost = 5
                else:
                    service_cost = round(df.at[index, 'Holdings Funds']/10000,2)
                # 总资金计算
                df.at[index, 'Total Funds'] = df.at[index, 'Holdings Funds'] + Balance_Funds - service_cost
                
                # 买入信息 输出
                time = df.at[index,'bob']
                buying = df.at[index,'close']
                balance =round(df.at[index, 'Total Funds'],2)
                print(f'\n买入时间:{time} , 买入{position}股 , 买入价{buying}元 , 手续费{service_cost}元 , 剩余{balance}元')
                a = a+1
            
            # 已有持仓,无需操作
            else:  
                df.at[index, 'Position'] = df.at[index-1, 'Position']
                df.at[index, 'Return'] = df.at[index, 'return']
                df.at[index, 'Holdings Funds'] = df.at[index, 'Position'] * df.at[index, 'close']
                df.at[index, 'Balance Funds'] = df.at[index-1, 'Balance Funds']
                df.at[index, 'Total Funds'] = df.at[index, 'Holdings Funds'] + Balance_Funds
                
        # 死叉,卖出股票        
        elif data['Signal'] == -1: 
            # 当前有持仓
            if position > 0:  
                # 指标计算
                df.at[index, 'Position'] = 0
                df.at[index, 'Return'] = df.at[index, 'return']
                df.at[index, 'Holdings Funds'] = 0
                df.at[index, 'Balance Funds'] = 0
                # 清仓获得的资金
                Clearing_Funds = df.at[index, 'close']* df.at[index-1, 'Position']
                # 手续费计算 --- 买入资金的万分之一(5元起步)
                if Clearing_Funds/10000 < 5:
                    service_cost = 5
                else:
                    service_cost = round(Clearing_Funds/10000,2)
                # 总资金计算 
                df.at[index, 'Total Funds'] = Clearing_Funds + df.at[index-1, 'Balance Funds'] - service_cost
                initial_capital = df.at[index, 'Total Funds']
                
                # 卖出输出
                time = df.at[index,'bob']
                selling = df.at[index,'close']
                total = round(df.at[index, 'Total Funds'],2)
                print(f'卖出时间:{time} , 卖出{position}股 , 卖出价{selling}元 , 手续费{service_cost}元 , 总额{total}元')
                # 股数归零
                position = 0
                b = b+1
 
            else:  # 没有持仓,无需操作
                df.at[index, 'Position'] = 0
                df.at[index, 'Return'] = 0
                df.at[index, 'Holdings Funds'] = 0
                df.at[index, 'Balance Funds'] = 0
                df.at[index, 'Total Funds'] = df.at[index - 1, 'Total Funds']
                
    #买卖次数统计
    if a > b:
        print(f'\033[1;31m\n买入次数为{a}次,卖出次数为{b}次,当前为持仓状态。\033[0m')
    else:
        print(f'\033[1;31m\n买入次数为{a}次,卖出次数为{b}次,当前为空仓状态。\033[0m')
    
    return df

# 调用买卖函数策略
initial_capitals =100000
df = buy_sell_stock(df,initial_capitals)

输出结果:

        可以发现共交易了21次,最后赚取了86495.9元。

 6、策略累积收益

        假设另外一策略为买入后不进行操作,为了更好的与机器学习模型的策略作出对比。现在通过计算“一直持有”策略和“指标分析”策略的累积收益率,通过折线图直观分析他们之间的对比,

# 策略累积收益
df['Strategy_return'] = (df['Total Funds']-initial_capitals)/initial_capitals
# 持仓累积收益 --- (不操作)
begin_buy = df[df['Total Funds'].notnull()]['close'].values[0]   # 策略开始买时的价格
df['Cumulative_return'] = (df['close']-begin_buy)/begin_buy      # 假设此处不考虑手续费
# 两者累积收益对比
plt.figure(figsize=(16,8),dpi=150)
plt.plot(df['bob'],df['Cumulative_return'],label = '始终持有策略收益率',c='green')
plt.plot(df['bob'],df['Strategy_return'],  label = '金叉死叉策略收益率',c='orange')
plt.title('不同策略的累积收益率')
plt.legend()
plt.show()

# 导出df文件f
#df.to_excel(f'金死叉策略 - {symbol}.xlsx') 
#df

输出结果:

 7、回测评估

        可以明显发现两者策略到最后都是盈利的,但机器学习的策略更优与一直持有的策略。在机器学习模型中,不凡看到有些累积收益是直线,那些是属于空仓时期,那时没有持有股票,自然也没有收益的累积变化。为了更好的评估机器学习策略的效果,通过夏普比率和最大回撤的计算。其中夏普比率越大越好,最大回撤越小越好!

# 夏普比率
def sharpe_ratio(data):
    data_new = data.dropna()
    return_mean = np.mean(data_new['Strategy_return'])  # 回报率均值
    r = 0.05                                            # 无风险利率
    return_std = np.std(data_new['Return'])             # 回报波动率
    # 夏普比率
    XiaPu_rate = (return_mean - r)/return_std
    print('夏普比率为:',XiaPu_rate)
sharpe_ratio(df)

输出结果:

# 最大回撤
def Max_Drawdown(data):
    '''data:数组、列表格式'''
    Max_Fund = data[0]
    Max_drawdown = 0
    for i in range(1, len(data)):
        if data[i] > Max_Fund:
            Max_Fund = data[i]                              # 最大的值
        else:
            drawdown = (Max_Fund - data[i]) / Max_Fund      # 回撤计算
            if drawdown > Max_drawdown:
                Max_drawdown = drawdown
                Max_index = np.argmax(data[:i] == Max_Fund) # 最大的值对应的索引
                Min_index = i                               # 最小的值对应的索引
                Min_Fund =data[i]                           # 最小的值
    return Max_drawdown, Max_Fund,Min_Fund,Max_index,Min_index

#最大回撤函数调用
df_new = df['Total Funds'].dropna().reset_index(drop=True)
df_new = df_new.tolist()
Max_drawdown, Max_Fund,Min_Fund,Max_index,Min_index = Max_Drawdown(df_new)

print(f'最大值是:{Max_Fund} ; 最小值是:{Min_Fund}')
print(f'最大回撤是:{Max_drawdown:.2%}')

输出结果:

本文结束,若存在错误或者存在不足,欢迎指正!

本文仅供参考,投资有风险,切勿将该分析方法盲目用到实际之中!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值