Warning: file_exists(): open_basedir restriction in effect. File(/www/wwwroot/com.xiximiao.oa/com.cmstop/public/www/wp-content/db.php) is not within the allowed path(s): (/www/wwwroot/com.xiximiao.oa/com.cmstop/public/www/:/tmp/:/proc/:/var/log/nginx/:/www/wwwroot/com.xiximiao.oa/com.cmstop/public/:/www/wwwroot/com.xiximiao.oa/com.cmstop/vendor/:/www/wwwroot/com.xiximiao.oa/com.cmstop/ppk/) in /www/wwwroot/com.xiximiao.oa/com.cmstop/public/www/wp-includes/load.php on line 707
2376 ETF基金溢价套利(模拟效果好!)高收益低回测量化策略代码分享 改进版 45648 » 轻知量化 QMT、PTrade、聚宽策略分享交流平台

2376 ETF基金溢价套利(模拟效果好!)高收益低回测量化策略代码分享 改进版 45648

ETF基金溢价套利策略逻辑说明

本策略为 ETF 折溢价动态套利策略,核心逻辑如下:
  1. 套利机会捕捉机制
  • 实时计算 ETF 溢价率:(开盘价 / 单位净值 - 1)*100
  • 设置动态溢价阈值(初始 - 2.5%)
  • 筛选溢价率 <-1.5% 的折价品种
  • 排除当日分红的异常数据
  • 要求日均成交额 > 1000 万元保证流动性
  1. 净值获取系统
  • 历史净值:通过聚宽 get_extras 接口获取
  • 实时净值:爬取东方财富网基金详情页数据
  • 净值异常过滤(溢价绝对值 > 20% 剔除)
  1. 交易执行规则
  • 每日 09:30 执行买卖操作
  • 持有最大 2 只折价最深的 ETF
  • 等分可用现金买入(单笔最低 2 万元限制)
  • 设置限价单(开盘价 * 1.02 防暴涨)
  • 自动卖出溢价收窄的持仓
  1. 风险控制体系
  • 动态调整溢价阈值(未成交日降低 0.15%)
  • 剔除成交量不足品种
  • 限制最大持仓数量
  • 设置 20% 异常溢价熔断
  • 严格处理分红除权数据

 策略代码

# 回测资金 1000000

from jqdata import *
import datetime
import talib
from jqlib.technical_analysis  import *
import sys
import requests     #爬基金的VALUE和IOPV 
##
## 初始化函数,设定基准等等
def initialize(context):
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启异步报单
    #set_option('async_order', True)
    # 根据实际行情限制每个订单的成交量
    # 0.05:对限价单,即每分钟成交量的5%    set_option('order_volume_ratio', 0.05)
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 是否未来函数
    set_option("avoid_future_data", True)
    # 设置费率
    set_order_cost(OrderCost(
        open_tax=0, close_tax=0, open_commission=0.00025, 
        close_commission=0.00025, close_today_commission=0, min_commission=5), 
        type='fund')
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    # 至少的溢价百分数,默认为2.5%
    g.least_premium=2.5
    # 没有买入时,则g.least_premium=max(g.least_premium-0.1,1.5)
    # 一旦买入,则g.no_buy_days=0, g.least_premium=2.5
    # 至少达到的成交量,单位为元,默认为1000万元
    g.least_money=1.0e7
    # 交易费的费率g.trade_fee_ratio,暂定为万2.5,用于计算最少买入金额=5/g.trade_fee_ratio
    g.trade_fee_ratio=0.00025
    # 最大的持有ETF数量,默认为2只
    g.ETFNum_hold = 2
    # 临时存放基金的全局表列
    g.etf_df=[]
    # 执行所有定时运行
    do_schedule(context)
##
def after_code_changed(context):
    # 取消所有定时运行
    unschedule_all()
    do_schedule(context)
##    
def do_schedule(context):   
    run_daily(pre_process, '09:15', reference_security='000300.XSHG')
    # 买卖操作
    # 卖出时间:在回测时9:30,在模拟或实盘时应在9:27
    run_daily(exe_sell, '09:30', reference_security='000300.XSHG')
    run_daily(exe_buy, '09:30', reference_security='000300.XSHG')
##    
## pre_process预处理函数:得到符合要求的溢价基金

关键函数解锁后查看:

##
## 卖出
def exe_sell(context):
    df = g.etf_df
    current = get_current_data()
    #获得基金开盘价【最早在9:27读取】,并计算溢价'premium'
    df['day_open'] = [current[c].day_open for c in df.index.tolist()]
    df['premium'] = (df.day_open / df.unit_net_value - 1) * 100
    # 因510210.XSHG在2023-06-19分红,导致计算premium错误而在2023-06-20买入,完善如下:
    df['factor'] = [attribute_history(c, 1, '1d', fields=['factor'])['factor'][-1] for c in df.index.tolist()]
    # 为简单处理:对于昨日分红的基金,则不考虑,直接删除
    df=df[df['factor']==1]    
    # 溢价必须在±20%之内,否则净值不合理而删除
    df=df[abs(df['premium'])<20]
    # df的第一列是代码,第二列是成交金额,第三列净值,第四列是最新价,第五列为溢价的百分数
    ## 根据溢价大小排序
    if hasattr(df, 'sort'):
        df = df.sort(['premium'], ascending = True)
    else:
        df = df.sort_values(['premium'], ascending = True)
    print("df=\n%s" % df.head(5))
    df = df[(df.premium < -1.0*g.least_premium)] 
    order_etf = df[:g.ETFNum_hold].index.tolist()
    # 显示需要卖出的etf列表
    etf_to_sell=list(set(context.portfolio.positions.keys())-set(order_etf))
    if len( etf_to_sell)>0:
        log.info(" ## In exe_sell, 需要卖出的ETF列表 ## %s" % etf_to_sell)
        message="需卖:"
        N=len( etf_to_sell)
        for i in range(N):
            message=message + etf_to_sell[i][:6]
            if i 0:
            if order_sell != None:
                log.info("--恭喜,已成功卖出基金%s【%s】--" %  (get_security_info(etf).display_name,etf))
            else:
                log.info("--遗憾,未成功卖出基金%s【%s】--" %  (get_security_info(etf).display_name,etf))
## 买入
def exe_buy(context):
    least_money_to_buy=5.00/g.trade_fee_ratio
    # 如果现金不足且没有货币基金,则直接退出
    if context.portfolio.available_cash < least_money_to_buy: 
        return
    current = get_current_data()
    df = g.etf_df
    #获得基金开盘价【最早在9:27读取】,并计算溢价'premium'
    df['day_open'] = [current[c].day_open for c in df.index.tolist()]
    df['premium'] = (df.day_open / df.unit_net_value - 1) * 100
    # 因510210.XSHG在2023-06-19分红,导致计算premium错误而在2023-06-20买入,完善如下:
    df['factor'] = [attribute_history(c, 1, '1d', fields=['factor'])['factor'][-1] for c in df.index.tolist()]
    # 为简单处理:对于昨日分红的基金,则不考虑,直接删除
    df=df[df['factor']==1] 
    # 溢价必须在±20%之内,否则净值不合理而删除
    df=df[abs(df['premium'])<20]
    ## 根据溢价大小排序
    if hasattr(df, 'sort'):
        df = df.sort(['premium'], ascending = True)
    else:
        df = df.sort_values(['premium'], ascending = True)
    # 买入的净值在一定差值范围内(现价与净值差小于g.least_premium%)
    df = df[(df.premium < -1.0*g.least_premium)]
    if len(df)>0:
        log.info(" --- 买入:已有符合溢价<-%.2f条件的ETF, 溢价最小的前%s个的ETF ---" % (g.least_premium,min(len(df),5)) )
        log.info("符合条件的基金:\n%s" % df.head(5) )
        g.least_premium=2.5
    else:
        log.info(" --- 买入:没有符合溢价<-%.2f条件的ETF ---" % g.least_premium)    
        g.least_premium=max(g.least_premium-0.15,1.5)
    order_etf = df[:g.ETFNum_hold].index.tolist()
    hold_set=set(context.portfolio.positions.keys())    #已持有的ETF
    to_buy_set=set(order_etf) | hold_set                #买入后持有的ETF(并集)
    to_buy_list=list(to_buy_set)
    # 显示需要买入的etf列表
    if len(to_buy_list)>0:
        log.info(" ## In exe_buy, 需要买入的ETF列表 %s ##" % to_buy_list)
    message="待买:"
    N=len( to_buy_list)
    for i in range(N):
        message=message + to_buy_list[i][:6]
        if i 0:
        cash = context.portfolio.available_cash / to_buy_num
        # 金额太小,手续费的比重过大而不合算
        if cash>least_money_to_buy:
            for etf in to_buy_list:
                # 限价单:为确保买入,限价为当前价上浮2%。
                data_close = attribute_history(etf, 1, '1d', ['close'])['close']
                # 昨日昨日收盘价
                last_close=data_close[-1]
                limit_price=last_close*1.02
                # at 2023-10-10
                limit_price=current[etf].day_open*1.02
                # 下面买入操作,可能导致资金不足!!
                num_to_buy=int(cash/(limit_price*1.005*1.00025)/100)*100
                order_buy=order_target(etf,num_to_buy,LimitOrderStyle(limit_price))
                if order_buy != None and order_buy.filled > 0:
                    log.info("--恭喜,已成功买入基金%s【%s】:%s股--" %  (get_security_info(etf).display_name,etf,order_buy.filled))
                else:
                    log.info("--遗憾,未成功买入基金%s【%s】--" %  (get_security_info(etf).display_name,etf))
##
def after_trading_end(context):
    log.info('---- 股市在满天晚霞中落幕!----')
    '''
    trades = get_trades()
    for _trade in trades.values(): 
        print('成交记录:'+str(_trade))
    '''
    print('—————————————————分割线—————————————')
##
## 根据ETF基金代码在东方财富网(etf.eastmoney.com)获取ETF的昨日净值
def get_etf_value(stockcode):
    #取基金代码的前6位
    stockcode=stockcode[:6]
    url = "http://etf.eastmoney.com/" + stockcode + ".html"
    response = requests.get(url)
    etfDataInfo = response.text
    tmp_str="fix_dwjz  bold ui-color-green"
    init_position=etfDataInfo.find(tmp_str)
    if init_position==-1:
        return -1   
    else:
        init_position=etfDataInfo.find(tmp_str)+len(tmp_str)
        etf_value=float(etfDataInfo[init_position+2:init_position+8])
        return etf_value   
2025-02-23
⚠️
本站资源大多来自网络,仅供网友学习交流,未经作者或上传书面授权,请勿作他用。
站长 vx: xiangyin615 或者 留言反馈 ,我们将尽快处理。
Notice: When you of the legal rights be violate, please stir to vx: xiangyin615
个人中心
购物车
优惠劵
搜索