请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3364731 新帖:18

【策略研发】三进兵策略研究报告

好的名字都没了发表于:5 月 10 日 06:49回复(1)

说明:

  • 所谓的三进兵,是指三条EMA均线组合的策略

交易原则:

  • 系统由三条EMA均线组合而成,分别为小均线、中均线、大均线

  • 当小均线金叉大均线、并且中均线位于大均线下方时,买入

  • 当小均线死叉中均线,并且中均线位于大均线上方时,卖出

  • 止损:当买入后,如果收盘价跌破中均线,止损

  • 选股:对个股进行一段时间的回测,得出最优组合,并判断是否可达到正收益,从而判断是否适合本策略

  • 各位宽友如果有想法,可在此基础上做迭代,并分享出来,展示你的才华


说明:¶

  • 所谓的三进兵,是指三条EMA均线组合的策略

交易原则:¶

  • 系统由三条EMA均线组合而成,分别为小均线、中均线、大均线

  • 当小均线金叉大均线、并且中均线位于大均线下方时,买入

  • 当小均线死叉中均线,并且中均线位于大均线上方时,卖出

  • 止损:当买入后,如果收盘价跌破中均线,止损

  • 选股:对个股进行一段时间的回测,得出最优组合,并判断是否可达到正收益,从而判断是否适合本策略

  • 各位宽友如果有想法,可在此基础上做迭代,并分享出来,展示你的才华

导入引用模块¶

import numpy as npimport pandas as pdimport datetimefrom jqdata import *from jqlib.technical_analysis import *

设置全局变量¶

# 记录成交历史trade_history = {}# 指定股票池,这里默认选择了创业板stocks_pool = get_index_stocks('399006.XSHE')

编写公用函数¶

# 获取ma_min值def get_ma(stock, ma_value, end_dt):price = get_price(security=stock, 
                      end_date=end_dt, 
                      frequency='daily', 
                      fields=['close'], 
                      skip_paused=False, 
                      fq='pre', 
                      count=ma_value+10)['close']ma = price[-ma_value:].mean()return ma# 获取ema值def get_ema(stock, ma_value, end_dt):ema = EMA(stock, check_date=end_dt, timeperiod=ma_value)return ema[stock]# 判断是否出现买入信息def is_buy(stock, yesterday, before_yesterday, *ema):ma_min = ema[0]ma_med = ema[1]ma_max = ema[2]# 求出上一个交易日的ma_min,ma_med,ma_max的值y_ma_min_value = get_ema(stock, ma_min, yesterday)y_ma_med_value = get_ema(stock, ma_med, yesterday)y_ma_max_value = get_ema(stock, ma_max, yesterday)# 求出上上个交易日的ma_min,ma_med,ma_max的值by_ma_min_value = get_ema(trade_stock, ma_min, before_yesterday)by_ma_med_value = get_ema(trade_stock, ma_med, before_yesterday)by_ma_max_value = get_ema(trade_stock, ma_max, before_yesterday)if (y_ma_min_value > y_ma_max_value) and (by_ma_min_value < by_ma_max_value) and (y_ma_med_value < y_ma_max_value):return Trueelse:return False
    # 判断是否有卖出信息def is_sell(stock, yesterday, before_yesterday, *ema):ma_min = ema[0]ma_med = ema[1]ma_max = ema[2]# 求出上一个交易日的ma_min,ma_med,ma_max的值y_ma_min_value = get_ema(stock, ma_min, yesterday)y_ma_med_value = get_ema(stock, ma_med, yesterday)y_ma_max_value = get_ema(stock, ma_max, yesterday)# 求出上上个交易日的ma_min,ma_med,ma_max的值by_ma_min_value = get_ema(stock, ma_min, before_yesterday)by_ma_med_value = get_ema(stock, ma_med, before_yesterday)by_ma_max_value = get_ema(stock, ma_max, before_yesterday)if (y_ma_min_value > y_ma_med_value) and (by_ma_min_value < by_ma_med_value) and (y_ma_med_value > y_ma_max_value):return Trueelse:return False
    # 判断是否有卖出信息def is_loss(stock, yesterday, *ema):ma_min = ema[0]ma_med = ema[1]ma_max = ema[2]# 昨日收盘价close = get_price(security=stock, 
                          end_date=yesterday,  frequency='daily', 
                          fields=['open','close'], 
                          skip_paused=False, 
                          fq='pre', 
                          count=10)['close'][-1] 
    
    # 求出上一个交易日的ma_min,ma_med,ma_max的值y_ma_min_value = get_ema(stock, ma_min, yesterday)y_ma_med_value = get_ema(stock, ma_med, yesterday)y_ma_max_value = get_ema(stock, ma_max, yesterday)if (close < y_ma_med_value) and (y_ma_med_value < y_ma_max_value):return Trueelse:return False# 过滤掉有止影线和小实体阳线的时刻def is_high_line(stock, end_dt):price = get_price(security=stock, 
                          end_date=end_dt, 
                          frequency='daily', 
                          fields=['open', 'close', 'high', 'low', 'volume', 'money'], 
                          skip_paused=False, 
                          fq='pre', 
                          count=1)open = price['open'][0]close = price['close'][0]high = price['high'][0]low = price['low'][0]o_c_ratio = (open-close)/closeh_c_ratio = (high-close)/closeif o_c_ratio > 0 and h_c_ratio < 0.02:return Trueelse:return False

交易函数¶

def trade(stock,ma_min,ma_med,ma_max,trade_days):# 记录盈利wine_loss_history = []# 持仓hold_list = {}# 权重weight = 0# 总权重last_weihgt = len(trade_days)# 因为回测的是历史for trade_day in trade_days:# 权重累加weight += 1# 将要被使用的日期集合the_days = get_trade_days(end_date=trade_day, count=5)# 回测当天today = the_days[-1]# 上一个交易日yesterday = the_days[-2]# 上上个交易日before_yesterday = the_days[-4]# ========================卖出操作========================sell_list = []for stock,info in hold_list.items():trade_day = info[0]buy_price = info[1]# 判断是否有卖出信号 re_value = is_sell(stock, yesterday, before_yesterday, ma_min,ma_med,ma_max)# 判断是否触发止损信号loss = is_loss(stock, yesterday, ma_min,ma_med,ma_max)# 进行卖出操作if re_value or loss:sell_list.append(stock)price = get_price(security=stock, 
                          end_date=today,  # 现实中成交按当天的开盘价交易  frequency='daily', 
                          fields=['open','close'], 
                          skip_paused=False, 
                          fq='pre', 
                          count=10)['open'][-1] 
                # 进行记录trade_dic = {'stock':stock, 'buy_date':trade_day, 'buy_price':buy_price, 'sell_date':today, 'sell_price':price, 'ratio':(price-buy_price)/buy_price, 'MA':(ma_min,ma_med,ma_max), 'weight':weight, 'count':last_weihgt}wine_loss_history.append(trade_dic)# 从持仓中删除已经卖出的股票for stock in sell_list:del hold_list[stock]# ========================卖出操作========================# ========================买入操作========================# 判断是否有买入信号re_value = is_buy(stock, yesterday, before_yesterday, ma_min,ma_med,ma_max)# 如果有买入信号,则买入if re_value:# 在今天的开盘时买入,参考的买入价格是昨天的收盘价price = get_price(security=stock, 
                          end_date=today, # 现实中按当天的开盘价交易   frequency='daily', 
                          fields=['open','close'], 
                          skip_paused=False, 
                          fq='pre', 
                          count=10)['open'][-1]hold_list[stock] = [today, price]# ========================买入操作========================#========================将最后一次未卖出的也记录==========================if stock in hold_list.keys():sell_list.append(stock)price = get_price(security=stock, 
                  end_date=today,  # 现实中成交按当天的开盘价交易  frequency='daily', 
                  fields=['open','close'], 
                  skip_paused=False, 
                  fq='pre', 
                  count=10)['open'][-1] # 进行记录trade_dic = {'stock':stock, 'buy_date':trade_day, 'buy_price':buy_price, 'sell_date':today, 'sell_price':price, 'ratio':(price-buy_price)/buy_price, 'MA':(ma_min,ma_med,ma_max), 'weight':weight, 'count':last_weihgt}wine_loss_history.append(trade_dic)#========================将最后一次未卖出的也记录==========================# 返回交易记录return wine_loss_history

value = trade('300059.XSHE',5,20,60) df = pd.DataFrame(value,columns=['stock', 'buy_date', 'buy_price', 'sell_date', 'sell_price', 'ratio', 'MA','b_ma','y_ma']) df

回测¶

# 获利近三年的交易日期days = get_trade_days(end_date=datetime.datetime.now(), count=250*2+100)train_days = days[:-100]trade_days = days[-100:]s_time = datetime.datetime.now()# 回测股票trade_stock = '600600.XSHG'# 保存回测记录all_list = []# 三进兵的三个值集合ma_min1 = [3, 5, 7]ma_med1 = [10, 20, 30]ma_max1 = [40, 50, 60]# 回测不同的三进兵组合 for ma1 in ma_min1:for ma2 in ma_med1:for ma3 in ma_max1:re_dic = trade(trade_stock,ma1,ma2,ma3,train_days)all_list = all_list + re_dic# 输出各三进兵组合的值df = pd.DataFrame(all_list,columns=['stock', 'buy_date', 'buy_price', 'sell_date', 'sell_price', 'ratio', 'MA', 'weight', 'count'])e_time = datetime.datetime.now()print(e_time-s_time)df
0:02:44.335545

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


stockbuy_datebuy_pricesell_datesell_priceratioMAweightcount
0600600.XSHG2016-11-3030.672016-12-0130.24-0.014020(3, 10, 40)95500
1600600.XSHG2017-01-2430.062017-03-1332.650.086161(3, 10, 40)161500
2600600.XSHG2017-05-2431.632017-05-2531.08-0.017389(3, 10, 40)211500
3600600.XSHG2017-08-0232.942017-08-0332.36-0.017608(3, 10, 40)259500
4600600.XSHG2017-10-1732.232017-11-2830.82-0.043748(3, 10, 40)337500
5600600.XSHG2017-12-1434.232018-02-0936.000.051709(3, 10, 40)389500
6600600.XSHG2018-07-2046.112018-07-3045.02-0.023639(3, 10, 40)500500
7600600.XSHG2016-11-3030.672016-12-0130.24-0.014020(3, 10, 50)95500
8600600.XSHG2017-01-2530.062017-03-1332.650.086161(3, 10, 50)161500
9600600.XSHG2017-05-2431.632017-05-2531.08-0.017389(3, 10, 50)211500
10600600.XSHG2017-08-0232.942017-08-0332.36-0.017608(3, 10, 50)259500
11600600.XSHG2017-10-1732.232017-11-2830.82-0.043748(3, 10, 50)337500
12600600.XSHG2017-12-1434.232018-02-1336.630.070114(3, 10, 50)391500
13600600.XSHG2018-02-2738.522018-02-2837.40-0.029076(3, 10, 50)397500
14600600.XSHG2018-02-2837.402018-03-0137.35-0.001337(3, 10, 50)398500
15600600.XSHG2018-07-2046.112018-07-3045.02-0.023639(3, 10, 50)500500
16600600.XSHG2017-01-2530.062017-03-1332.650.086161(3, 10, 60)161500
17600600.XSHG2017-05-2431.632017-05-2531.08-0.017389(3, 10, 60)211500
18600600.XSHG2017-08-0232.942017-08-0332.36-0.017608(3, 10, 60)259500
19600600.XSHG2017-10-1732.232017-11-2830.82-0.043748(3, 10, 60)337500
20600600.XSHG2017-12-1434.232018-02-2638.320.119486(3, 10, 60)395500
21600600.XSHG2018-03-1239.272018-03-1340.360.027757(3, 10, 60)406500
22600600.XSHG2018-07-1745.462018-07-3045.02-0.009679(3, 10, 60)500500
23600600.XSHG2017-01-2430.062017-03-2232.120.068530(3, 20, 40)168500
24600600.XSHG2017-05-2431.632017-05-2531.08-0.017389(3, 20, 40)211500
25600600.XSHG2017-05-3133.152017-07-1931.72-0.043137(3, 20, 40)248500
26600600.XSHG2017-08-0132.992017-08-0432.23-0.023037(3, 20, 40)260500
27600600.XSHG2017-10-1732.232017-11-3030.56-0.051815(3, 20, 40)339500
28600600.XSHG2017-12-1535.032018-03-1340.360.152155(3, 20, 40)406500
29600600.XSHG2018-07-1745.462018-07-1845.17-0.006379(3, 20, 40)492500
..............................
114600600.XSHG2017-10-1931.752017-11-2830.82-0.029291(7, 10, 50)337500
115600600.XSHG2018-07-2046.112018-07-3045.02-0.023639(7, 10, 50)500500
116600600.XSHG2017-01-2530.062017-03-1332.650.086161(7, 10, 60)161500
117600600.XSHG2017-05-2431.632017-05-2531.08-0.017389(7, 10, 60)211500
118600600.XSHG2018-07-1947.252018-07-3045.02-0.047196(7, 10, 60)500500
119600600.XSHG2017-01-2530.062017-04-2432.840.092482(7, 20, 40)189500
120600600.XSHG2017-05-3133.152017-07-1931.72-0.043137(7, 20, 40)248500
121600600.XSHG2017-10-1831.802017-11-3030.56-0.038994(7, 20, 40)339500
122600600.XSHG2017-12-1535.032018-03-1340.360.152155(7, 20, 40)406500
123600600.XSHG2018-07-1947.252018-07-2046.11-0.024127(7, 20, 40)494500
124600600.XSHG2017-01-2630.342017-04-2432.840.082399(7, 20, 50)189500
125600600.XSHG2017-10-2032.112017-11-3030.56-0.048272(7, 20, 50)339500
126600600.XSHG2017-12-1535.032018-03-1340.360.152155(7, 20, 50)406500
127600600.XSHG2018-07-2046.112018-07-3045.02-0.023639(7, 20, 50)500500
128600600.XSHG2017-01-2630.342017-04-2432.840.082399(7, 20, 60)189500
129600600.XSHG2017-05-2431.632017-05-2531.08-0.017389(7, 20, 60)211500
130600600.XSHG2017-10-2331.772017-11-3030.56-0.038086(7, 20, 60)339500
131600600.XSHG2017-12-1535.032018-03-1340.360.152155(7, 20, 60)406500
132600600.XSHG2017-01-2530.062017-04-2432.840.092482(7, 30, 40)189500
133600600.XSHG2017-05-3133.152017-07-1931.72-0.043137(7, 30, 40)248500
134600600.XSHG2017-10-1831.802017-12-0130.38-0.044654(7, 30, 40)340500
135600600.XSHG2017-12-1835.302018-02-2237.960.075354(7, 30, 40)393500
136600600.XSHG2018-07-1947.252018-07-2046.11-0.024127(7, 30, 40)494500
137600600.XSHG2017-01-2630.342017-04-2432.840.082399(7, 30, 50)189500
138600600.XSHG2017-10-2032.112017-12-0130.38-0.053877(7, 30, 50)340500
139600600.XSHG2017-12-1835.302018-03-1340.360.143343(7, 30, 50)406500
140600600.XSHG2018-07-2046.112018-07-3045.02-0.023639(7, 30, 50)500500
141600600.XSHG2017-01-2630.342017-04-2432.840.082399(7, 30, 60)189500
142600600.XSHG2017-10-2331.772017-12-0130.38-0.043752(7, 30, 60)340500
143600600.XSHG2017-12-1835.302018-03-1340.360.143343(7, 30, 60)406500

144 rows × 9 columns

df.to_csv('2018-12-23.csv')

计算最最优组合值¶

group = df.groupby(by=['MA'])
print('组合收益之和')max_sum = group.sum()max_sum = max_sum.sort_values(by=['ratio'],ascending=False).loc[:,['ratio']].head()value = {'stock':trade_stock, 'MA':max_sum.index[0]}print(value)max_sum
组合收益之和
{'MA': (7, 30, 60), 'stock': '600600.XSHG'}

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


ratio
MA
(7, 30, 60)0.181990
(7, 20, 60)0.179080
(3, 30, 60)0.171834
(5, 30, 60)0.168070
(5, 20, 60)0.164303
print('各组合总收益对比图')import matplotlib.pylab as pltmax_sum.T.plot(kind='bar')plt.show()
各组合总收益对比图
print('组合收益标准差')min_std = group.std()min_std = min_std.loc[max_sum.index,['ratio']]min_std = min_std.sort_values(by=['ratio'])value = {'stock':trade_stock, 'MA':min_std.index[0]}print(value)min_std
组合收益标准差
{'MA': (3, 30, 60), 'stock': '600600.XSHG'}

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


ratio
MA
(3, 30, 60)0.050805
(5, 20, 60)0.087410
(7, 20, 60)0.088838
(5, 30, 60)0.090379
(7, 30, 60)0.095422
print('各组合收益拆线图')for name in max_sum.index:table = df[df['MA']==name][['ratio']]x = range(len(table.index))plt.plot(x,table)plt.legend(max_sum.index)plt.show()
各组合收益拆线图
print('各组合收益对比图')series = []for name in max_sum.index:table = df[df['MA']==name]['ratio'].valuesseries.append(table) bar_df = pd.DataFrame(series,max_sum.index).Tbar_df.plot(kind='bar')plt.show()print('交易次数表')bar_df.T
各组合收益对比图
交易次数表

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


01234
MA




(7, 30, 60)0.082399-0.0437520.143343NaNNaN
(7, 20, 60)0.082399-0.017389-0.0380860.152155NaN
(3, 30, 60)0.073852-0.0176080.0223390.099629-0.006379
(5, 30, 60)0.078177-0.017608-0.0446540.152155NaN
(5, 20, 60)0.068530-0.017389-0.0389940.152155NaN
print('求加权后的表现-平均到每一次交易')dic = []for name, g in group:weight_df = df[df['MA']==name]w = weight_df['weight']r = weight_df['ratio']c = weight_df['weight'].sum()v = w*r/cdic.append({'ma':name, 'ratio':sum(v)})w_df = pd.DataFrame(dic)w_df = w_df.sort_values(by=['ratio'], ascending=False)w_df.head()
求加权后的表现-平均到每一次交易

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


maratio
26(7, 30, 60)0.062989
23(7, 20, 60)0.053073
14(5, 20, 60)0.050178
17(5, 30, 60)0.047552
22(7, 20, 50)0.034285
print('求加权后的表现-平均到每天')dic = []count = int(df['count'].mean())l = list(range(1,count))for name, g in group:weight_df = df[df['MA']==name]w = weight_df['weight']r = weight_df['ratio']v = w*r/sum(l)dic.append({'ma':name, 'ratio':sum(v)})w_*ge_df = pd.DataFrame(dic)w_*ge_df = w_*ge_df.sort_values(by=['ratio'], ascending=False)w_*ge_df.head()
求加权后的表现-平均到每天

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


maratio
23(7, 20, 60)0.000487
26(7, 30, 60)0.000472
17(5, 30, 60)0.000455
14(5, 20, 60)0.000452
8(3, 30, 60)0.000424

print('输出各组合的统计一览表\n') for name in min_std.index:    x = df[df['MA']==name]    print(name)    print(x.describe())    print('='*50)

结果比较¶

result_dic = []
print('最大收益组合回测本年',max_sum.index[0])ma1,ma2,ma3 = max_sum.index[0]re_dic = trade(trade_stock,ma1,ma2,ma3,trade_days)re_dic = pd.DataFrame(re_dic)print('总盈利', re_dic.sum()['ratio'])result_dic.append({'收益':re_dic.sum()['ratio']})re_dic
最大收益组合回测本年 (7, 30, 60)
总盈利 0.004999999999999992

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


MAbuy_datebuy_pricecountratiosell_datesell_pricestockweight
0(7, 30, 60)2018-12-1736.01000.0052018-12-2536.18600600.XSHG100
print('最小标准差组合回测本年',min_std.index[0])ma1,ma2,ma3 = min_std.index[0]re_dic = trade(trade_stock,ma1,ma2,ma3,trade_days)re_dic = pd.DataFrame(re_dic)print('总盈利', re_dic.sum()['ratio'])result_dic.append({'收益':re_dic.sum()['ratio']})re_dic
最小标准差组合回测本年 (3, 30, 60)
总盈利 0.019154929577464782

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


MAbuy_datebuy_pricecountratiosell_datesell_pricestockweight
0(3, 30, 60)2018-12-1435.51000.0191552018-12-2536.18600600.XSHG100
print('最优加权组合回测本年',w_df['ma'].iloc[0])ma1,ma2,ma3 = w_df['ma'].iloc[0]re_dic = trade(trade_stock,ma1,ma2,ma3,trade_days)re_dic = pd.DataFrame(re_dic)print('总盈利', re_dic.sum()['ratio'])result_dic.append({'收益':re_dic.sum()['ratio']})re_dic
最优加权组合回测本年 (7, 30, 60)
总盈利 0.004999999999999992

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


MAbuy_datebuy_pricecountratiosell_datesell_pricestockweight
0(7, 30, 60)2018-12-1736.01000.0052018-12-2536.18600600.XSHG100
df = pd.DataFrame(result_dic,index=['最大收益组合'+str(max_sum.index[0]),'最小标准差组合'+str(min_std.index[0]),'最优加权组合'+str(w_df['ma'].iloc[0])])df.T.plot(kind='bar')plt.show()df

.dataframe thead tr:only-child th {        text-align: right;    }    .dataframe thead th {        text-align: left;    }    .dataframe tbody tr th {        vertical-align: top;    }


收益
最大收益组合(7, 30, 60)0.005000
最小标准差组合(3, 30, 60)0.019155
最优加权组合(7, 30, 60)0.005000

改进方案¶

  • ma1[3,5,9] ma2[10,20,30] ma3[40,50,60]

  • 不同的股票,将拥有不同的ma1,ma2,ma3值

  • 求出收益波动最小的方案

  • 添加选股函数

  • 增加更换代码的函数,要求可以空仓,可以更新ma三个值

  • 在选股票或测试的时候,不添加止损

  • 在收盘的时候买进或卖出,而不是在次日开盘交易

  • 将可变的股票池变为外部可读取文件

  • 从市场里筛选,有收盘价上穿ma60的,进行提醒

  • 尝试结合macd等其他指标

  • 提醒可以通过邮件的方式发送

  • 如果能在平台运行,自动发送信息,那是再好不过

  • 在卖出的时候,可以使用分钟回测,一旦卖出条件成熟,就下达卖出指令,避免大阴线下穿

  • 对于不同时期成交的历史,做出加权,越靠近当前的,权值越重,这样才会越符合当前的模型参数值

  • 为回测的数据增加窗口宽度

  • 止损位的设置,如果中线不大于长线,但收盘价跌破了中线,则止损(一来可以停止亏损,二来在二次进攻时还可进入)

 
 
 

全部回复

0/140

达人推荐

量化课程

    移动端课程