一个5年收益斩获了131倍的策略

这是一个没有未来函数的策略,回测结果非常的棒。斩获了131倍,同时最大回撤也才33.49%,但是,却有一个隐形的bug,我会写在文章的最后。

低价股策略回测步骤说明,一共分5步走:

Step1:登录聚宽JoinQuant量化平台,若无账号,用手机号可以免费注册。
网址:https://www.joinquant.com

Step2:进入【股票列表】界面,新建一个【股票策略】,并清空新建策略里面的所有内容
具体操作请见:https://www.joinquant.com/help/api/help#faq:%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E5%B9%B6%E8%BF%90%E8%A1%8C%E7%AD%96%E7%95%A5%EF%BC%9F

Step3:打开低价股策略源码文件【低价股策略-JoinQuant源码.txt】,全选复制当中全部源码,然后粘贴进上一步新建的策略当中。

Step4:回测参数设置。起止日期和起始资金根据个人需求设置,但运行频率必须是【分钟】,源码版本必须是【Python3】。
开始时间:2018-01-01
结束时间:2023-05-29
起始资金:100000
运行频率:分钟
源码版本:Python3

Step5:完成上一步参数设置后,点击界面右上方的蓝色回测按钮【运行回测】,等待完成回测即可。由于分钟级别的回测比较耗时,请耐心等待,切记中途不能关闭回测网页或关闭网络。

#导入函数库
from jqdata import *
import numpy as np 
import pandas as pd
from pandas import Series
from datetime import datetime, time

# 初始化函数,设定基准等等
def initialize(context):
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 开启“防未来函数”功能
    set_option('avoid_future_data', True)
    # 输出内容到日志
    log.info('初始函数开始运行且全局只运行一次')
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # ******关闭“强制撮合”功能******
    # set_option("match_by_signal", False)    #原策略当中没有,后期测试加入
    
    # ******设定成交量比例******
    # 根据实际行情限制每个订单的成交量,原策略当中没有,后期测试加入
    # set_option('order_volume_ratio', 0.001) #成交量不超过总成交量的0.1%
    
    # ******为全部交易品种设定固定值滑点******
    # 原策略收益高的原因就是没有设置滑点,设置正常滑点后就亏损了
    # 0.02表示价差是2分钱,买入时买贵1分钱,卖出时卖便宜1分钱
    # set_slippage(FixedSlippage(0.02))   #原策略当中没有,后期测试加入
    
    # 指定股价的阈值,低于阈值的股票会被剔除
    # 若测试高价股,则可以将阈值设为50、100、200等
    g.price_thre = 1.5
    # 选取的股票
    g.sel_stock = None
    # 策略开始买卖时间
    g.strategy_starttime=time(10, 20)
    # 策略尾盘撮合买入时间
    g.strategy_endtime=time(14, 55)
    
    ### 股票相关设定 ###
    # 股票类每笔交易时的手续费是:买入时佣金万分之2.5,卖出时佣金万分之2.5加千分之一印花税, 每笔交易佣金最低扣5块钱
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.00025, close_commission=0.00025, min_commission=5), type='stock')
    run_weekly(weekly, weekday=1, time='09:31', reference_security='000300.XSHG')

    # 开盘前运行
    run_daily(before_market_open, time='before_open', reference_security='000300.XSHG')
    # 开盘时运行
    run_daily(market_open, time='every_bar', reference_security='000300.XSHG')
    # 收盘后运行
    run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')

        
    #调整持仓
def weekly(context):  
    g.sel_stock = None
    current_data = get_current_data()
    # 取得所有股票
    stock_list=get_all_securities().index.tolist()
    # 取得5天的close
    last_5_prices=history(count=5, field='close', security_list=stock_list)
    # 取5天close 平均数
    last_5_prices=pd.DataFrame({'mean_val':last_5_prices.mean()})
    # 均值排序
    last_5_prices_sort=last_5_prices.sort_values(axis=0,ascending=True,by=['mean_val'])

    # 取得均值>=1.5的股票
    q='mean_val>=%s' %str(g.price_thre)
    last_5_prices_query=last_5_prices_sort.query(q)

    last_5_prices_stock=list(last_5_prices_query.index)  

    today_str=context.current_dt.strftime("%Y-%m-%d")
    #log.info(last_5_prices_stock)
    # 去除停牌、ST、退市股,选取股价最低的一个
    for security in last_5_prices_stock:
        if  (current_data[security].paused==False) \
            and not current_data[security].is_st \
            and '*' not in current_data[security].name \
            and '退' not in current_data[security].name:
            g.sel_stock= security
            break
    if g.sel_stock is not None:
        log.info(g.sel_stock)
        log.info(get_security_info(g.sel_stock).display_name)
        # 调仓
        sell_list = set(context.portfolio.positions.keys()) - set([g.sel_stock])
        print('sell:',sell_list)
        for stock in sell_list:
            order_target_value(stock, 0)
    else:
        for stock in context.portfolio.positions.keys():
            order_target_value(stock, 0)

    
def before_market_open(context):
    log.info('函数运行时间(before_market_open):'+context.current_dt.strftime("%Y-%m-%d %H:%M:%S"))
    g.not_buy_flg=1
    g.not_sell_flg=1
    
    g.last_buy_orderid=None
    g.last_sell_orderid=None
    g.last_buy_price = 0
    g.last_sell_price = 0
    g.price_50m={'open':0.000,'close':0.000,'high':0.000,'low':0.000}
    g.price_before_close=0.000      

# 根据当前时间取得购买价格
def get_buy_price(context):
    sel_stock_price=attribute_history(g.sel_stock,1, unit='50m',fields=('open','close','high','low'),skip_paused=True, df=True, fq='pre')
    if context.current_dt.time()>=g.strategy_starttime and context.current_dt.time()< g.strategy_endtime:
        return sel_stock_price['low'][0]
    else:
        return sel_stock_price['close'][0]
        
# 根据当前时间取得卖出价格
def get_sell_price(context):
    sel_stock_price=attribute_history(g.sel_stock,1, unit='50m',fields=('open','close','high','low'),skip_paused=True, df=True, fq='pre')
    if context.current_dt.time()>=g.strategy_starttime and context.current_dt.time()< g.strategy_endtime:
        return sel_stock_price['high'][0]
    else:
        return sel_stock_price['close'][0]
    
def market_open(context):

    current_data = get_current_data()
    if g.sel_stock is None:
        return
    
    # 判断到了交易时间开始交易
    if context.current_dt.time()>=g.strategy_starttime:
        if g.not_buy_flg==1:
            now_buy_price = get_buy_price(context)
            # 取消
            orders=get_open_orders()
            for _order in orders.values():
                if _order.order_id==g.last_buy_orderid:
                    cancel_order(_order)         
            
            g.last_buy_price=now_buy_price
            buy_count=int(context.portfolio.available_cash/now_buy_price/100)*100
            if buy_count>=100:
                new_order=order(g.sel_stock,buy_count,LimitOrderStyle(now_buy_price))
                if new_order is not None:
                    if str(new_order.status) == 'held':
                        g.not_sell_flg = 0
                    else:
                        g.last_buy_orderid = new_order.order_id

        # 卖盘逻辑
        if context.current_dt.time()<g.strategy_endtime and g.not_sell_flg==1:
            now_sell_price = get_sell_price(context)
            
            orders=get_open_orders()
            for _order in orders.values():
                if _order.order_id==g.last_sell_orderid:
                    cancel_order(_order)            
                        
            if g.sel_stock in context.portfolio.positions \
            and context.portfolio.positions[g.sel_stock].closeable_amount>0:
                g.last_sell_price=now_sell_price                
                new_order=order(g.sel_stock,-context.portfolio.positions[g.sel_stock].closeable_amount,LimitOrderStyle(now_sell_price))
                if new_order is not None:
                    if str(new_order.status) == 'held':
                        g.not_sell_flg = 0
                    else:                
                        g.last_sell_orderid = new_order.order_id
            else:
                g.not_sell_flg==0

##收盘后运行函数
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('##############################################################')

如果,我们把回测时间改成今年的5月份呢?结果会如何?

好像对于十来倍的收益,接近一半的回撤也还行,我们来看看他买了啥。

 也就是买之前没戴ST,买之后戴了ST的帽子,五一之后开市,一直跌停到现在,正是由于连续的跌停,导致该策略无法卖出该股票,截止到5月29日,硬生生吃了17个跌停,造成了58%的回撤。

但是,这个策略的bug,依旧不是这里,这里也仅仅是策略独有的缺陷,就是梭哈。那么bug是什么,就是假设我能不废滑点的全部买进

这里是大多数小火鸡都会遇到的问题,假设我能每一只股票都能不费滑点的全部买进,那么是不是就可以做到无损套利,开启逆天模式呢?当然不是,首先A股是撮合制的,如果你要买进,在接近你的挂单价直接就给你撮合了。另外,不是你委托了就能全部买进去的。

比方,我有100万股挂买一15.21,但是市场只有10万股在15.21成交,在策略中,碰到就默认全部买入,这也是大多数策略会遇到的毛病。

不费滑点的全部买进,才是这个策略最大的bug

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

做量化的智明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值