同花顺选股平台最牛收益策略分析
由xd123456创建,最终由xd123456 被浏览 521 用户
同花顺选股平台最牛收益策略分析
近年来,量化交易被越来越多的投资者认可,国内也出现了很多支持量化策略和量化交易的平台。其中同花顺旗下的量化策略平台BackTest(http://backt est.10jqka.com.cn/)因为支持自然语言创建策略而受到很多投资者的欢迎。在BackTest平台上有大量用户自编的量化交易策略,其中很多策略收益都非常可观,年化收益率甚至长期超过100%。
这些策略是否可信呢?经过我们的分析,其中有一类包含“中证1000指数成份股"条件的高收益策略使用了错误的数据,相当于包含了未来函数,该类策略的高收益是不可能实现的。
下面我们对平台中策略得分排名第一的策略进行证伪。
一、策略分析
1.1 策略内容
BackTest支持自然语言策略回测,因此将策略的内容输入后会出现回测结果,具体的策略语句如下
中证1000指数,市值从小到大排列
- 条件一:中证1000指数
中证1000指数是由中证指数有限公司编制,其成分是选择中证800指数样本股之外规模偏小且流动性好的1000
只股票组成,与沪深300和中证500等指数形成互补。
中证1000指数成份股的平均市值及市值中位数较中小板指、创业板指、中证500指数相比都更小,更能综合反映沪深证券市场内小市值公司的整体状况。因此很多小市值的策略都使用中证1000指数成份股作为股票池。
- 条件二:市值从小到大排列
小市值策略。小市值策略:本质上说就是市值小的股票,在将来上涨的概率越大。
同时平台支持设置一定的止盈止损,以及持股时期的设置,该策略的具体设置如下图
1.2 回测结果
在回测此语句后,得到以下的收益曲线
从上面的的曲线可以看出,在2019-12-31到2021-12-31这段时间内收益达到1428.18%,年化收益为306.58%,收益回撤也很小,该策略整体得分81分,在策略广场所有策略中得分排名第1,但是这个策略真的可行吗?
Joinquant([https://www.Joinquant.com/](https://www.joinquant.com/))也是国内知名的量化平台,我们将该策略在Joinquant上进行回测,以下为回测结果:
从上面的的曲线可以看出,在2019-12-31到2021-12-31这段时间内收益为-34.01%,年化收益为-19.25%,回测期间收益几乎一直为负。
那么究竟是什么原因导致了两个平台上同一个策略的收益率有如此大的差别呢?究竟哪一个平台的结果是正确的呢?
1.3 结果分析
通过对比国内三大量化交易平台(Joinquant、Bigquant以及MindGo)的中证1000指数成份股历史数据,三大量化交易平台获取的结果是一致的。因此我们有理由相信Joinquant平台的选股和回测结果有较高的可信度(下图为2021年6月2日三大量化交易平台的中证1000指数成份股对比)。
我们以2021年6月2日BackTest平台策略选出的股票和Joinquant平台条件选股的结果进行比较:
- BackTest平台2020年5月19日的策略选股
股票代码 | 股票简称 | 市值 | |
---|---|---|---|
0 | 603595 | 东尼电子 | 36.3889 |
1 | 300011 | 任子行 | 36.7802 |
2 | 300343 | 联创股份 | 36.8112 |
3 | 603105 | 芯能科技 | 38.0500 |
4 | 002432 | 九安医疗 | 38.5275 |
- Joinquant平台2021年6月2日的策略选股
股票代码 | 股票简称 | 市值 | |
---|---|---|---|
0 | 603555 | 贵人鸟 | 11.6920 |
1 | 600069 | 退市银鸽 | 13.3154 |
2 | 300178 | 腾邦退 | 13.8098 |
3 | 002256 | 兆新股份 | 15.2475 |
4 | 002447 | 晨鑫退 | 15.9846 |
- 我们发现使用Joinquant平台回测策略所选出的股票与BackTest平台所选出的股票是完全不同的,这说明BackTest平台所用中证1000指数成分股数据可能存在问题。
1.4问题猜测
- BackTest平台的中证1000指数成份股数据使用的可能是运行回测程序当天(2022年8月29日)的中证1000指数成份股
二、猜测验证
2.1 验证结果
- 我们首先获取到运行回测程序当天中证1000指数成份股
- 根据其2021年6月2日的市值从小到大进行排列
股票代码 | 股票简称 | 市值 | |
---|---|---|---|
0 | 603595 | 东尼电子 | 36.3889 |
1 | 300011 | 任子行 | 36.7802 |
2 | 300343 | 联创股份 | 36.8112 |
3 | 603105 | 芯能科技 | 38.0500 |
4 | 002432 | 九安医疗 | 38.5275 |
5 | 300827 | 上能电气 | 40.7661 |
6 | 002487 | 大金重工 | 41.4010 |
7 | 300065 | 海兰信 | 41.4101 |
8 | 300205 | 天喻信息 | 41.7584 |
9 | 300007 | 汉威科技 | 42.1074 |
10 | 603626 | 科森科技 | 42.1671 |
- 我们查看同花顺平台策略2021年6月2日的持仓信息
股票代码 | 股票简称 | 市值 | |
---|---|---|---|
0 | 603595 | 东尼电子 | 36.3889 |
1 | 300011 | 任子行 | 36.7802 |
2 | 300343 | 联创股份 | 36.8112 |
3 | 603105 | 芯能科技 | 38.0500 |
4 | 002432 | 九安医疗 | 38.5275 |
- 可以看到持仓标的与市值从小到大排列前5的股票标的一致,因此可以说明正如我们猜测的一样,股票池选用的是2022年8月29日的中证1000指数成分股。
三、高收益原因分析
- 为什么BackTest平台回测的收益高呢?我们来分析交割单中收益最高的股票联创股份(300343)的市值走势图。
上图中折线为该股票市值的变化走势,橙色点为策略的买入点。从图我们可以看出:回测过程中,该股票被多次买入,但该股票当时并不是中证1000指数成份股,而是市值不断成长后才成为中证1000指数成份股,相当于我们在知道未来数据的情况下选出了成长空间最大的股票。
四、结论
虽然同花顺BackTest平台不乏优秀的策略,但是其中也有不少引用了错误的数据或者未来函数,由此产生惊人的年化收益率带有很大的欺骗性,其中包含”中证1000指数成份股,xxx...”的策略都存在错误引用中证1000指数的问题。
附录:代码
最后附上joinquant的回测代码
# 导入函数库
from jqdata import *
# 初始化函数,设定基准等等
def initialize(context):
g.stop_loss=-99
g.close_profit=99
g.max_loss=0.1
g.limit_day=30
g.top=5
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 输出内容到日志 log.info()
log.info('初始函数开始运行且全局只运行一次')
# 过滤掉order系列API产生的比error级别低的log
# log.set_level('order', 'error')
### 股票相关设定 ###
# 股票类每笔交易时的手续费是:买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
## 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入'000300.XSHG'或'510300.XSHG'是一样的)
# 开盘前运行
run_daily(before_market_open, time='before_open', reference_security='000300.XSHG')
# 开盘时运行
run_monthly(market_open, monthday=1, time='14:55', reference_security='000300.XSHG', force=False)
# 收盘后运行
run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')
## 开盘前运行函数
def before_market_open(context):
# 输出运行时间
log.info('函数运行时间(before_market_open):'+str(context.current_dt.time()))
# 设定查询条件
# 给微信发送消息(添加模拟交易,并绑定微信生效)
# send_message('美好的一天~')
# 要操作的股票:平安银行(g.为全局变量)
## 开盘时运行函数
def market_open(context):
g.security = get_index_stocks(index_symbol='000852.XSHG')
today = context.current_dt.date()
q = query(
valuation.code,
valuation.market_cap
).filter(valuation.code.in_(g.security)).order_by(
valuation.market_cap.asc()
)
df=get_fundamentals(q)
g.today_stocks_list=list(df[0:g.top]['code'])
print(g.today_stocks_list)
log.info('函数运行时间(market_open):'+str(context.current_dt.time()))
security = g.security
today=str(context.current_dt)
g.stocks_dict_mssage={}
g.stock_list_copy=g.today_stocks_list
holdings = context.portfolio.positions.keys()
for stock in holdings:
#判断老的组合中股票是否在新组合中,不在的话则卖出
#T+N
if stock not in g.today_stocks_list:
if hold_days(context,stock)>g.limit_day:
order_target_value(stock, 0)
log.info("T+N卖出")
else:
g.stock_list_copy.append(stock)
close_function(context)
N=len(g.stock_list_copy)
#记录每个股票应该买入的数目
if(N!=0):
every_stock= context.portfolio.total_value/N
else:
every_stock=0
#将新组合中的所有股票的仓位调到everyStock
for stock in g.stock_list_copy:
order_target_value(stock,every_stock)
## 收盘后运行函数
def after_market_close(context):
log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time())))
#得到当天所有成交记录
trades = get_trades()
for _trade in trades.values():
log.info('成交记录:'+str(_trade))
log.info('一天结束')
log.info('##############################################################')
def hold_days(context, stocks):
start_date = context.portfolio.positions[stocks].init_time
today = context.current_dt
trade_days =get_trade_days(start_date, today)
return len(trade_days)
def close_function(context):
# 获取所有有持仓的股票
positions_stocks = context.portfolio.positions.keys()
if len(context.portfolio.positions) > 0:
for stock in positions_stocks:
now_price = context.portfolio.positions[stock].price # 标的当前价格
init_time = context.portfolio.positions[stock].init_time
cost = context.portfolio.positions[stock].avg_cost # 标的持仓成本
profit_percent = (now_price - cost) / cost # 标的浮盈比
print(profit_percent)
print('########################################')
today = context.current_dt.date()
close_data = get_price(stock, start_date = init_time, end_date = today,\
frequency = 'daily', fields = ['high'])
condition01 = context.portfolio.positions[stock].closeable_amount > 0 # 可平仓数量>0
condition02 = profit_percent < g.stop_loss or profit_percent > g.close_profit or (now_price < max(close_data['high']) * (1 - g.max_loss) ) # 止损或止盈条件满足其一
if condition01 and condition02: # 符合止盈或止损条件的
order_target(stock, 0)
if(stock in g.stock_list_copy):
g.stock_list_copy.remove(stock)
# 清仓卖出
log.info("触发止盈或止损,执行清仓,Selling %s" % stock) # 记录这次卖出
\