用Dual-Thrust策略回测CTA

本文分享了使用 Dual-Thrust 策略对螺纹钢期货进行回测的过程及结果。该策略通过计算历史价格区间来确定买卖点,并实现了自动化的交易信号生成。最终评估了策略的年化收益率、胜率等关键指标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下是小哥用Dual-Thrust策略回测CTA的代码和结果,错误的地方还请大家提出来。

标的是螺纹钢的主力连续合约

#%% Dual Thrust策略
# 导入包
import pandas as pd
import matplotlib.pyplot as plt

#%% 导入和清洗数据
RBL=pd.read_excel('H:/RBL8.xlsx')
RBL.index=pd.to_datetime(RBL.iloc[:,0])
RBL=RBL['2016':'2017']
RBL=RBL.iloc[:,1:5]
RBL.columns=('Open','High','Low','Close')

#%% Dual_Thrust策略
#输入时间序列
#输出买卖信号、持仓情况
def dt(tso,tsh,tsl,tsc,N,k1,k2):
    #输入
    #开盘价df:tso,最高价df:tsh,最低价df:tsl,收盘价df:tsc
    #参数int:N,k1,k2
    
    #初始化
    HH=pd.Series(index=tso.index[N-1:])
    LC=pd.Series(index=tso.index[N-1:])
    HC=pd.Series(index=tso.index[N-1:])
    LL=pd.Series(index=tso.index[N-1:])
    
    Range=pd.Series(index=tso.index[N-1:])
    BuyLine=pd.Series(index=tso.index[N-1:])
    SellLine=pd.Series(index=tso.index[N-1:])
    
    Open=tso.iloc[N-1:]
    Close=tsc.iloc[N-1:]
    Signal=pd.Series(0,index=tso.index[N-1:]) #交易信号
    Case=pd.Series(0,index=tso.index[N-1:]) #仓位情况
    
    #计算N日内最高价HH,Close的最低价LC,Close的最高价HC,最低价LL
    for i in range(len(tso.index)-N+1):
        HH.iloc[i]=max(tsh.iloc[i:i+N])
        LC.iloc[i]=min(tsc.iloc[i:i+N])
        HC.iloc[i]=max(tsc.iloc[i:i+N])
        LL.iloc[i]=min(tsl.iloc[i:i+N])
        Range.iloc[i]=max(HH.iloc[i]-LC.iloc[i],HC.iloc[i]-LL.iloc[i])
    
    #生成Range,买入线BuyLine,买出线,SellLine
    BuyLine=Open+k1*Range
    SellLine=Open-k2*Range
    
    #画出区间
    plt.figure('BuyLine和SellLine')    
    plt.plot(BuyLine)
    plt.plot(SellLine)
    
    #构建交易信号
    #当日收盘价低于或高于区间时生成交易信号Signal
    for i in range(len(Range)-1):
        Signal.iloc[i]=0
        Case.iloc[i+1]=Case.iloc[i]
        if Close.iloc[i]>=BuyLine.iloc[i] and Case.iloc[i]<1:
            Signal.iloc[i]=1
            Case.iloc[i+1]=1
        elif Close.iloc[i]<=SellLine.iloc[i] and Case.iloc[i]>-1:
            Signal.iloc[i]=-1
            Case.iloc[i+1]=-1
    
    return Signal,Case 

#%% 进行回测
[sig,ca]=dt(RBL['Open'],RBL['High'],RBL['Low'],RBL['Close'],20,0.1,0.1)

#%% 输出收盘价、交易信号、持仓情况
plt.figure('输出收盘价、交易信号、持仓情况')
plt.subplot(3,1,1)
plt.plot(RBL['Close'])
plt.title('收盘价')
plt.subplot(3,1,2)
plt.plot(sig)
plt.title('交易信号')
plt.subplot(3,1,3)
plt.plot(ca)
plt.title('持仓情况')

#%% 指标评价
# 累计收益、年化收益率、标的收益率、胜率、持仓时间、交易次数、最大回撤
#累计收益率
def all_re(tsc,Case):
    re=tsc.diff().dropna() #以收盘价衡量的每日收益
    day_profit=pd.Series(index=Case.index) #持仓后每日的收益
    for i in range(len(Case)):
        day_profit[i]=Case.iloc[i]*re[Case.index[i]]
    
    #累计收益
    acc_profit=day_profit.cumsum()
    #保证金,以持仓初始日的保证金为本金买入一手
    insur=tsc[Case.index[0]]*0.05+10000
    
    #年化收益:日收益率的平均值做成年化
    day_rate=acc_profit[-1]/(insur*len(acc_profit))
    anual_rate=day_rate*250
    
    #标的收益率
    bid_re=tsc[-1]/(insur*len(tsc))
    
    return acc_profit+insur,anual_rate,bid_re

#%% 交易次数、多头次数、空头次数
def trade_num(sig):
    return len(sig[sig!=0]),len(sig[sig==1]),len(sig[sig==-1])

#%% 持仓时间
def in_case(case):
    return len(case[case!=0])

#%% 回撤、最大回撤
def tradeback(tsc,Case):
    tb=pd.Series(0,index=Case.index)
    re=tsc.diff().dropna() #以收盘价衡量的每日收益
    day_profit=pd.Series(index=Case.index) #持仓后每日的收益
    for i in range(len(Case)):
        day_profit[i]=Case.iloc[i]*re[Case.index[i]]
        
    insur=tsc[Case.index[0]]*0.05+10000
    #累计收益
    acc_profit=day_profit.cumsum()+insur
    
    #回撤率
    for i in range(10,len(tb)-1):
        if acc_profit[i]>0:
            tb.iloc[i]=abs((max(acc_profit[:i+1])-acc_profit[i+1])/max(acc_profit[:i+1]))
           
    #返回回撤率、最大回撤率    
    return tb,max(tb)

#%%计算胜率
def victor(tsc,Case):
    ca_re=pd.Series(0,index=Case.index)
    re=tsc.diff().dropna()
    for i in range(len(Case)):
        ca_re.iloc[i]=Case.iloc[i]*re[Case.index[i]]
    
    vi=len(ca_re[ca_re>0])/len(Case) 
    return vi
    
#%% 输出累计收益率,回撤
plt.figure('累计收益、回撤')
plt.subplot(2,1,1)
plt.plot(all_re(RBL['Close'],ca)[0])
plt.title('累计收益')
plt.subplot(2,1,2)
plt.plot(tradeback(RBL['Close'],ca)[0]*100)
plt.title('回撤')

#%% 各项指标
print('年化收益率:',all_re(RBL['Close'],ca)[1]*100,'%')
print('\n标的收益率:',all_re(RBL['Close'],ca)[2]*100,'%')
print('\n交易次数:',trade_num(sig)[0],'\n多头次数:',trade_num(sig)[1],\
      '\n空头次数:',trade_num(sig)[2])
print('\n胜率:',victor(RBL['Close'],ca))
print('\n持仓时间:',in_case(ca))
print('\n最大回撤:',tradeback(RBL['Close'],ca)[1]*100,'%')   

结果为

年化收益率: 13.68464099 %
标的收益率: 0.0772052921481 %
交易次数: 93 
多头次数: 47 
空头次数: 46
胜率: 0.5256410256410257
持仓时间: 463
最大回撤: 5.36926264923 %

<think>我们首先需要理解DualThrust交易策略的原理。DualThrust由MichaelChalek开发,是一种趋势跟踪策略,常用于期货、外汇和股票市场。该策略的核心思想是通过前N日的价格波动来设定上下轨道,以此作为突破交易的触发条件。策略原理:1.计算周期:通常取前N个交易日的数据(如N=5)。2.计算四个价格:当日开盘价、最高价、最低价、收盘价。3.计算两个范围:-上轨范围(Range1):当日最高价与当日开盘价之差。-下轨范围(Range2):当日开盘价与当日最低价之差。但实际上,更常见的做法是使用以下方式:首先计算:HH=过去N日最高价的最大值HC=过去N日收盘价的最高值LC =过去N日收盘价的最低值LL=过去N日最低价的最小值然后:Range =max(HH-LC,HC-LL)但原始策略中,通常使用:Range= max(最高价-前一日收盘价,前一日收盘价-最低价)[这里需要澄清]根据原始资料,DualThrust策略中,计算范围的方式如下:首先,计算:UpRange =max(今日最高价 -今日收盘价,今日最高价 -今日开盘价,今日收盘价 -今日开盘价)[这个说法可能不准确]实际上,更常见的描述是:Range= max(过去N日中的最高价-过去N日中的最低价,过去N日中的最高价-前一日收盘价的绝对值,前一日收盘价-过去N日中最低价的绝对值)[这个也不准确]经过查阅资料,DualThrust策略的经典计算步骤:1.计算前N日(包括当天)的以下值:-最高价的最大值(HHV)-最低价的最小值(LLV)-收盘价的最大值(HCV)-收盘价的最小值(LCV)2.计算:Range=max(HHV -LCV,HCV -LLV)3.计算上轨(买入触发线)和下轨(卖出触发线):上轨(BuyLine)=开盘价+ K1* Range下轨(SellLine)=开盘价 -K2* Range其中K1和K2是系数,通常取0.5,但也可以调整。4.交易信号:当价格突破上轨(BuyLine)时,开多仓;当价格突破下轨(SellLine)时,开空仓。5.平仓条件:通常可以设置固定的时间点平仓(如当日收盘平仓),或者使用反向信号平仓。然而,值得注意的是,不同资料对Range的计算方式描述略有差异。因此,我们需要通过搜索来确认。由于任务要求进行网络搜索,我们调用搜索工具来获取权威解释。搜索关键词:DualThrust strategyformula根据搜索结果,DualThrust策略的经典计算步骤:1.计算前N天(通常为5天)的以下值:HH =最高价的最大值HC=收盘价的最大值LC =收盘价的最小值LL=最低价的最小值2.计算:Range =max(HH -LC,HC -LL)3.计算今日的上下轨道:上轨(BuyLine) =今日开盘价+ K1 *Range下轨(SellLine)=今日开盘价- K2 *Range其中K1和K2是系数,通常取0.5,但也可以根据具体品种调整。4.交易规则:-当价格向上突破上轨,买入开仓(或平空开多);-当价格向下跌破下轨,卖出开仓(或平多开空)。5.平仓:可以在收盘时平仓,或者使用另一个时间框架平仓。实现方法(以Python为例):我们可以使用pandas来计算所需的值,然后根据交易信号模拟交易。注意:策略中,我们通常使用日线数据,但也可以用于分钟线等。步骤:1.导入数据(包含开盘价、最高价、最低价、收盘价)2.计算前N天的HH,HC,LC,LL3.计算Range= max(HH- LC, HC- LL)4.计算BuyLine和SellLine5.生成交易信号代码示例(仅生成信号,不包含交易模拟):```pythonimportpandas aspddefdual_thrust(df, n_days=5,k1=0.5, k2=0.5):"""计算DualThrust策略的信号:paramdf:DataFrame,包含columns: ['open','high', 'low','close']:paramn_days:计算范围的天数:paramk1:上轨系数:param k2:下轨系数:return:添加了信号的DataFrame"""#计算前n_days天的HH,HC,LC,LLdf['HH'] =df['high'].rolling(n_days).max()df['HC']= df['close'].rolling(n_days).max()df['LC'] =df['close'].rolling(n_days).min()df['LL']= df['low'].rolling(n_days).min()#计算Rangedf['range']= df[['HH','HC', 'LC','LL']].apply(lambdax:max(x['HH'] -x['LC'],x['HC']- x['LL']),axis=1)#计算上轨和下轨df['buy_line'] =df['open']+ k1 *df['range']df['sell_line']= df['open'] -k2* df['range']#初始化信号列df['signal'] =0#当价格突破buy_line时,产生买入信号(1)df.loc[df['high'] >df['buy_line'], 'signal']=1#当价格跌破sell_line时,产生卖出信号(-1)df.loc[df['low'] <df['sell_line'], 'signal']= -1returndf```注意:在实际交易中,我们可能还需要考虑:-避免在同一方向上重复开仓(例如,已经持有多头,则不再开多)-设置平仓条件(例如,收盘平仓)此外,策略中的参数(n_days,k1, k2)需要根据具体市场进行优化。总结:DualThrust是一种突破型策略,通过前N日的价格波动范围来设定当日的交易触发线。该策略在趋势明显的市场中表现较好,在震荡市场中可能会频繁交易导致亏损。相关问题部分:根据用户的问题和上述答,生成相关问题。</think>### DualThrust交易策略原理 DualThrust由Michael Chalek开发,是一种突破型量化策略,核心是通过动态价格通道捕捉趋势。原理基于以下计算: 1. **价格范围计算** 取前$N$日(通常$N=3 \sim 5$)数据: - $HH_N$ = 最高价的最大值 - $LL_N$ = 最低价的最小值 - $HC_N$ = 收盘价的最高值 - $LC_N$ = 收盘价的最低值 计算波动范围: $$ Range = \max(HH_N - LC_N, HC_N - LL_N) $$ 2. **通道阈值设定** 当日开盘价$O_t$为基准,计算突破阈值: - 多头触发线:$BuyLine = O_t + K_1 \times Range$ - 空头触发线:$SellLine = O_t - K_2 \times Range$ 系数$K_1$和$K_2$通常取$0.5$(可优化调整)。 3. **交易信号生成** - 当价格$\geq BuyLine$时,开多仓 - 当价格$\leq SellLine$时,开空仓 平仓规则:收盘平仓或反向信号平仓。 --- ### Python实现方法 以下代码使用`pandas`实现策略逻辑(以日线数据为例): ```python import pandas as pd def dual_thrust_strategy(data, n_days=5, k1=0.5, k2=0.5): """ data: DataFrame含[open, high, low, close] n_days: 计算周期 k1, k2: 通道系数 """ df = data.copy() # 计算价格范围 df['HH'] = df['high'].rolling(n_days).max() df['LL'] = df['low'].rolling(n_days).min() df['HC'] = df['close'].rolling(n_days).max() df['LC'] = df['close'].rolling(n_days).min() df['Range'] = df.apply(lambda x: max(x['HH']-x['LC'], x['HC']-x['LL']), axis=1) # 计算交易通道 df['BuyLine'] = df['open'] + k1 * df['Range'] df['SellLine'] = df['open'] - k2 * df['Range'] # 生成信号 df['Signal'] = 0 df.loc[df['high'] >= df['BuyLine'], 'Signal'] = 1 # 多头信号 df.loc[df['low'] <= df['SellLine'], 'Signal'] = -1 # 空头信号 return df ``` **关键参数优化方向**: - 周期$N$:短周期(3-5日)适合高频,长周期(10-20日)过滤噪音 - 系数$K_1/K_2$:不对称系数(如$K_1=0.7, K_2=0.3$)可适应单边市 - 过滤机制:添加波动率阈值(如ATR)避免低波动期假突破 --- ### 执行注意事项 1. **品种适配性** 适用于趋势性强的品种(商品期货、外汇),股票需验证流动性。 2. **风险控制** - 固定止损:$SellLine \pm 0.2 \times Range$ - 仓位管理:单笔风险≤1%账户净值 3. **盘中触发** 实盘需Tick级数据,用开盘后实时高/低点判断突破。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值