神奇的网格交易策略

前段时间在复现各种技术因子的类的择时,选股操作时开发过了网格策略,最终结果并不是非常好,当然震荡市不错,但是单边牛市和熊市表现都非常糟糕.(由于各个倾向于将各个量化步骤/角色分离开,所有以下策略回测均未加止 损,也均是sh50)
震荡市

如果拉长区间

信号学习_BOLL带

源码,uqer博客,信号学习_BOLL带[博]:https://uqer.datayes.com/v3/community/share/5eb81cb3aa644d014598c968

01S_正确性验证:

策略尝试_10日高低点突破_策略化

源码,uqer博客,策略尝试_10日高低点突破_策略化[博]:https://uqer.datayes.com/v3/community/share/5eb81a79aa644d014598c962

20160306_01_code:指数测试

箱体突破04选股尝试

源码,uqer博客,箱体突破04选股尝试[博]:https://uqer.datayes.com/v3/community/share/5eb8132108332701237ad418

突发奇想:凡有效的动量,均为箱体突破

统计分析_放量上涨次日和后几日表现

源码,uqer博客,统计分析_放量上涨次日和后几日表现[博]:https://uqer.datayes.com/v3/community/share/5eb81602083327869c7ad848

20160225_block1_code:统计分析_放量上涨后第2,3,5日价格分布情况

趋势_动量_02相关系数

源码,uqer博客,趋势_动量_02相关系数[博]:https://uqer.datayes.com/v3/community/share/5eb81145083327014e7ada54

HS300,20日周期(调仓,动量周期)
优化6:相关系数尝试

趋势_动量_波动率优化,线性回归系数

源码,uqer博客,趋势_动量_波动率优化,线性回归系数[博]:https://uqer.datayes.com/v3/community/share/5eb8103f08332701627ad7e9

自己思路开发
基础动量策略
20天涨幅最高的入选(前10%),20天一次调仓
结论:
15年01月-17年01月:HS300不忍直视了,反向做空吧^_^
15年01月-17年01月:CYB基本无效

选股_多因子低价低流通

源码,uqer博客,选股_多因子低价低流通[博]:https://uqer.datayes.com/v3/community/share/5eb8121f08332701677adfe9

思路1先价格在流通市值

选股_小市值_HS300消生存偏差改止损

源码,uqer博客,选股_小市值_HS300消生存偏差改止损[博]:https://uqer.datayes.com/v3/community/share/5eb80a8908332701557ad571

原帖:https://uqer.io/community/share/57d7ac22228e5b049bfb98e4
原策略由于未消除生存者偏差,所以实际回测效果是错误的,同时,原策略的止损部分没看明白, 又做了二次调整

期货网格择时

原版

说明:期货,网格,择时,日策略
代码:automoel/tuture/grid_time_day.py
基于:automodel/single/grid_time.py版本git12
修改明细:
原始代码验证:ok和图片一致

股票网格交易策略

什么是网格交易

定义

市盈率PE_第二版(考虑到选择基准问题)

消除生存者误差
universe =set_universe(‘HS300’,start)
后结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2012-01-04 -0.274068249427 -0.246322915 -0.2881532328 -0.3449563217 -0.3286166813 -0.3692663913
2013-01-04 -0.202809304558 -0.1511410341 -0.2007285219 -0.288668968 -0.2802696234 -0.3624716907
2014-01-02 -0.266735509786 -0.2360465751 -0.3015942766 -0.3152534359 -0.2489169115 -0.3472083942
2015-01-05 0.149976947029 0.4870781302 0.1969628325 0.0439215757 0.2656991023 0.0045149584
2015-12-01 0.134236504538 0.4988043886 0.3367256575 0.2174509923 0.2205559834 0.0893469148
```

index:1,group=0是最好
group=0时收益曲线:
![](/images/20191127145748926_14071.png)

可以注意到,生存者偏差对策略也是有影响的,如果不考虑生存者问题,市盈率越高越好。
分5组时最终结果图:
![](/images/20191127145748793_25628.jpg)

幸存者偏差对策略评价的影响

目前通联平台使用 universe =set_universe(‘HS300’)
取得的实际是当前的股票池, 在回测时使用的也是当前的股票池,但是策略结果评价时,以沪深300指数作为参考标的。 这会引入一定误差,但是到底会带来多少误差?(因为参与回测的股票都是“2015年12月时在沪深300标的中”,等价于使用未来信息)
不妨测试下
下面策略: capital_base = 10000000 #足够大,保证买入300个股票
refresh_rate = 80#80工作日调整一次
Commission(buycost=0.000, sellcost=0.000, unit=’perValue’),只是为了检查和沪深300偏差,不设置手续费
等权买入(会导致真实指数有误差,但不应该太大)
另一个引入误差的就是买入时的整手,忽略了零头
还有停牌无法买入,卖出引入误差 其他应该不会有什么很大的误差因

1
2
3
4
5
SH50:
start = '2011-01-01' # 回测起始时间
end = '2015-12-15' # 回测结束时间
benchmark = 'SH50' # 策略参考标准
universe =set_universe('SH50') # 证券池,支持股票和基金

其他_秩相关序数和卡方检查的区别_20170205

在拜读社区大神 @taotao.li_研究 | 因子分析模版, 谢 call 神 ^_^ 作品时,对秩相关序数的计算有疑虑。
百度百科解释:秩相关系数又称等级相关系数,或顺序相关系数,是将两要素的样本值按数据的大小顺序排列位次,以各要素样本值的位次代替实际数据而求得的一种统计量。
粗略来看,和卡方检验类似,也是用来检查线性关系的,非线性感觉上来讲不大适用
不妨在此做个小测试,测试下秩相关序数和卡方检查在数据上的输出特点

1
2
3
1,构造y=x函数,用秩相关序数检查,用卡方检查  
2,y=x+1,分别秩相关序数,开发检查
3,y=x^2正数部分,负数部分,整体,用两者检查

统计分析_周一涨跌和本周涨跌相关性_策略化

1
2
3
4
5
6
7
8
尝试:是否可以使用周一涨跌信息预测本周的涨跌情况  
结论:虽然周一和本周具有相关性,但是周一和本周后四天无相关性,所以意义不大,
另:使用周五预测下周具有较好实际操作意义,具体效果如下详细表格。

结果整理后table:对应代码20160131
原始含义:fmt.format(tmpResult[0]+tmpResult[3]),00和11的概率就是预测正确率,由于不允许做空所以1x时才是有意义的新增指标如下:
新增:fmt.format(tmpResult[3]/(tmpResult[2]+tmpResult[3])),表示10,11中11占比,更具有实际意义的
原始和新增分别对应"_"的前面和后面的数据,()中的表示区间简单统计的收益率,可以认为是策略化后的涨幅。。

市盈率PE_第一版

参考书籍:量化投资策略-如何实现超额收益alpha
开发平台:通联数据
不妨从最简单的pe指标开始

1
2
3
4
5
6
7
8
思路:PE指标为5-10区间的股票可以取得超额收益  
策略:20151213初始策略
(本策略实际为优化后的,
1,原始策略中初始资金10w,会导致后续很多下单失效,扩大资金为100w
2,原始策略股票单个最大为1w,结果确实比当前结果好,但是是以限制策略执行实现的,等价引入了人工判断,后面会有分析)
存在问题:
1,筛选池无法限制在基准指数中中筛选,会导致筛选池和比较基准来源不同。削弱结果的说服力
2,生存者偏差问题,由于问题1,导致无法评估带来影响大小。持有沪深300标的比指数表现好,等于间接利用未来数据(回测时的股票是未来入选沪深300的)

低价股策略

收益曲线图:

1
2
3
4
5
2012-01-04 -0.274068249427 -0.256378176 -0.3462856624 -0.3389911844 -0.3223600194
2013-01-04 -0.202809304558 -0.1796228337 -0.2996162246 -0.2776074807 -0.2868685471
2014-01-02 -0.266735509786 -0.2331957229 -0.3065919079 -0.2546574608 -0.3548286112
2015-01-05 0.149976947029 0.6233388677 0.2126871679 0.2510075383 0.0080761455
2015-12-01 0.134236504538 0.7334131399 0.4125602397 0.3842914317 0.034062207

指标探测_价格n日稳步上涨(SH47HS32)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
策略说明:
价格连续几日依次增长则买入,持有一定周期后再次筛选
进度:已分析序列

import CAL.PyCAL as pycal
import datetime as dt
import pandas as pd

start = '2013-01-01' # 回测起始时间
end = '2015-08-15' # 回测结束时间
benchmark = 'SH50' # 策略参考标准
universe =set_universe('SH50') # 证券池,支持股票和基金
capital_base = 1000000 # 起始资金
freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
refresh_rate =8 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
commission=Commission(buycost=0.000, sellcost=0.000, unit='perValue')

#默认知识,采用统一调仓频率,定时调仓,非买入信号,卖出信号,每个指标独立执行,认为定频调仓可以体现指标优劣
#所有满足条件股票都进行等权买入,认为满足条件股票等权
mkt_equd_get_max=50#通联接口单次查询的最大股票数量
buy_signal_days=4 #产生买入信号需要天数
fields=['tradeDate','closePrice','turnoverVol','lowestPrice','highestPrice']
buy_list_hist={}#保存每日购买记录
except_msg=[]
def initialize(account): # 初始化虚拟账户状态
pass

def handle_data(account): # 每个交易日的买入卖出指令
#取得当日日期和buy_signal_days前日期
curDate = account.current_date
#获取股票信息,开始日期为当前日期前推buy_signal_days*2天
begin_date = curDate - dt.timedelta(days=buy_signal_days*2)
beginDate = begin_date.strftime("%Y%m%d")
#结束日期为curdate的前一天(昨天)
#end_Date=curDate - dt.timedelta(1)
endDate=curDate.strftime('%Y%m%d')
#当日的股票买入列表
buy_list=[]

for stockSecID in universe:
stock_prices=pd.DataFrame()
try:
stock_prices=DataAPI.MktEqudGet(secID=stockSecID,ticker=u"",tradeDate='',beginDate=beginDate,endDate=endDate,field=fields,pandas="1")
except Exception as e:
#print e
except_msg.append(str(e))
if len(stock_prices)==0:
continue
stock_prices.set_index('tradeDate')
stock_prices.sort(columns=['tradeDate'])#todo 验证是否需要再次左侧赋值
total_line=len (stock_prices.index) -1#不使用今日的价格信息
if total_line<buy_signal_days+1:
break
satify=True #满足价格n日内连续增加

for j in xrange(0,buy_signal_days):
if (stock_prices.iloc[total_line-j-2]['closePrice']>0.01 and float(stock_prices.iloc[total_line-j-1]['closePrice'])/stock_prices.iloc[total_line-j-2]['closePrice']<1.00 ):
satify=False#有一天不满足则不满足条件
break
if(stock_prices.iloc[total_line]['lowestPrice']-stock_prices.iloc[total_line]['highestPrice']<0.01 and stock_prices.iloc[total_line]['closePrice']/stock_prices.iloc[total_line-1]['closePrice']>1.095):
satify=False#封死涨停则不买入(实际可能无法买入)
if satify:
buy_list.append(stockSecID)

#卖出所有股票
for stockSecID in account.valid_secpos:
order_to(stockSecID,0)

for stockSecID in buy_list:
if(account.referencePrice[stockSecID]==0):
continue
order(stockSecID,account.referencePortfolioValue/len(buy_list)/account.referencePrice[stockSecID])
#todo 当日卖出会更新cash字段后再买入么,卖出买入用cash或referencePortfoliovalue哪个好一些

if(len(buy_list)>0):
print curDate,buy_list


原始结果:
refresh_rate =8
buy_signal_days=4

指标探测_动量策略(SH27HS38)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#思路,近5日涨幅最大的近5日涨幅还会很大
import pandas as pd
import numpy as np
start = '2013-01-01' # 回测起始时间
end = '2015-08-15' # 回测结束时间
benchmark = 'SH50' # 策略参考标准
universe = set_universe('SH50') # 证券池,支持股票和基金
capital_base = 10000000 # 起始资金
freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
refresh_rate = 5 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
commission=Commission(buycost=0.000,sellcost=0.000,unit='perValue')

sign_days=5
def initialize(account): # 初始化虚拟账户状态
pass

def handle_data(account): # 每个交易日的买入卖出指令
stocks_prices=account.get_attribute_history('closePrice',sign_days)
stocks_rate=pd.DataFrame(columns=['stockSecID','rate'])

for stockSecID in account.universe:
if stocks_prices[stockSecID][0]<0.01:
continue
stocks_rate=stocks_rate.append(pd.Series([stockSecID,stocks_prices[stockSecID][-1]/stocks_prices[stockSecID][0]],index=['stockSecID','rate']),ignore_index=True)
stocks_rate.set_index('stockSecID')
stocks_rate.sort(columns=['rate'],ascending=False)
stocks_list=stocks_rate['stockSecID'].iloc[0:int(len(stocks_rate.index)*1/5)].values
#print account.current_date.strftime('%Y%m%d'),stocks_list
for stockSecID in account.valid_secpos.keys():
order_to(stockSecID,0)
for stockSecID in stocks_list:
order(stockSecID,account.referencePortfolioValue/len(stocks_list)/account.referencePrice[stockSecID])


基本效果图:
SH50
refresh_rate = 5
sign_days=5

指标探测_均线K线相对位置(SH48HS60)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
策略:
start = '2013-01-01' # 回测起始时间
end = '2015-08-15' # 回测结束时间
benchmark = 'SH50' # 策略参考标准
universe = set_universe('SH50') # 证券池,支持股票和基金
capital_base = 10000000 # 起始资金
freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
refresh_rate =3 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
commission=Commission(buycost=0.000, sellcost=0.000, unit='perValue')

signal_days=3
def initialize(account): # 初始化虚拟账户状态
pass

def handle_data(account): # 每个交易日的买入卖出指令
stocks_prices=account.get_history(signal_days)
sell_set=set()
buy_set=set()
for stockSecID in account.universe:
if len(stocks_prices[stockSecID]['closePrice'])<signal_days:
continue
ma5=stocks_prices[stockSecID]['closePrice'].mean()
if stocks_prices[stockSecID]['lowPrice'][-1]<ma5:
sell_set.add(stockSecID)
if stocks_prices[stockSecID]['highPrice'][-1]>ma5:
buy_set.add(stockSecID)

sell_set=set(account.avail_secpos.keys()).difference(buy_set)
buy_set=buy_set.difference(set(account.avail_secpos.keys()))
availCash=account.cash
for stockSecID in sell_set:
order_to(stockSecID,0)
availCash+= account.avail_secpos.get(stockSecID)*account.referencePrice[stockSecID]

for stockSecID in buy_set:
#print account.current_date,availCash,stockSecID,account.referencePrice[stockSecID]
order(stockSecID,availCash/len(buy_set)/account.referencePrice[stockSecID])
原始效果:
[3,3]

指标探测_均线多头排列(SH23HS39)及偏好测试(SH27HS33)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#思路多头排列则买入,非多头排列则卖出
#序列已验证
start = '2013-01-01' # 回测起始时间
end = '2015-08-15' # 回测结束时间
benchmark = 'SH50' # 策略参考标准
universe = set_universe('SH50') # 证券池,支持股票和基金
capital_base = 1000000 # 起始资金
freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
refresh_rate =10 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
commission=Commission(buycost=0.000,sellcost=0.000,unit='perValue')

ma10_days=10
ma20_days=20
ma40_days=40
def initialize(account): # 初始化虚拟账户状态
pass

def handle_data(account): # 每个交易日的买入卖出指令
org_buy_set=set()
org_sell_set=set()
stocks_prices=account.get_attribute_history('closePrice',ma40_days)
for stockSecID in account.universe:
ma10=stocks_prices[stockSecID][-ma10_days:].mean()
ma20=stocks_prices[stockSecID][-ma20_days:].mean()
ma40=stocks_prices[stockSecID].mean()
if account.current_date.strftime('%Y%m%d') in ['20150617','20150625']:
print account.current_date.strftime('%Y%m%d'),ma10,ma20,ma40,stocks_prices[stockSecID]
if(ma10>ma20 and ma20>ma40):
org_buy_set.add(stockSecID)
else:
org_sell_set.add(stockSecID)

for stockSecID in account.valid_secpos:
order_to(stockSecID,0)
for stockSecID in org_buy_set:
order(stockSecID,account.referencePortfolioValue/len(org_buy_set)/account.referencePrice[stockSecID])

if len(org_buy_set|org_sell_set)>0:
print account.current_date.strftime('%Y%m%d'),org_buy_set,org_sell_set

效果图:
SH50,refresh_rate =10

统计分析_放量上涨次日和后几日表现_策略化

1
2
3
4
20160225_block1_code:统计分析_放量上涨后第2,3,5日价格分布情况
upVolRate=1.67
upPriceRate=1.05#达到昨日1.05时认定为:大涨
buyDays=3#一个购买周期

其他_通联群(论坛)琐碎知识

20150901:
通联默认手续费:buycost=0.001, sellcost=0.002, unit=”perValue”,如果交易频率过高会导致收益被手续费蚕食。

20150906:
全仓的每日买进卖出,手续费的影响相当大,回测默认手续费为买千分之一,卖千分之二,所以全仓买卖,每日损失千分之三:
(1-0.003)^250 = 0.47183437515362414
估计一下一年交的手续费都多过一半了

其他_通联数据的买卖交易规则测试

目标:回答下列疑问
1,通联是否会对当日同标的买卖订单做扎差处理
2,是否先执行卖出,在执行买入
3,卖出资金是否当日可用

测试效果:
1,否
2,否,按照下单次序执行,尽可能程序中自己控制,先下sell单,在下buy单
3,是

统计分析_各月份涨跌概率_策略化

分析月份和涨跌关系

从结果看出月份03,04,07,10月结果较好

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×