相比普通的k线数据,tick级别的数据量极其庞大,几十TB的硬盘说满就满!对一些机构来说与其花费大量的成本去维护一套tick数据,不如试试直接使用T9软件来开发模型。
T9除了提供基本的每笔成交、五档委托价、委托量数据以外,还提供报价列表中资金流向、开平仓信息、速涨、振幅、内外盘比例等文华特色抬头历史数据。各品种任意交易日的tick数据可直接调用,为高频模型回测提供充分的验证样本。
高频交易策略不再需要真金白银的实盘测试,国内率先支持虚拟撮合的回测平台帮你验证模型好坏!
T9的虚拟撮合机制,既考虑到了委托价等于对手价时,对手盘挂单量不足,部分成交的情况,同时,当委托价等于排队价时,还会计算挂单成交比例,按比例撮合成交,完美虚拟真实交易过程。
可视化的盘口算法模型回测平台,能够同时展示tick数据变化、持仓信息、挂单详情、以及权益曲线变化过程,并提供360度视角的多维度回测报告。
算法交易运行池是高频策略和算法交易策略的运行平台。
可视化的运行平台,提供tick图监控页面,不放过任何一个交易细节。
可比肩C++程序的运行速度,保证每一笔委托能够快速发出,降低成交滑点,提高交易效率。
手动炒单下单慢、滑点大!白盘、夜盘两班倒,精力跟不上?T9高频策略量化来帮忙,计算机代替人工进行炒单,不放过任何一个交易机会!
T9的高频策略量化提供了类C语言的编写平台,自由度高,运行速度快。不仅可以引用盘口的成交数据,五档数据,还能引用报价列表中速涨、振幅、内外盘比例、资金流向等特色抬头数据,能够把你的各种盘感写成盘口算法模型。
期权市场合约众多,各种波动套利影响因素多、算法复杂,即使出现了套利机会,人工也很难捕捉到。而且很多期权合约交易不活跃,即使价格满足套利条件,也很难确保成交。
使用期权算法交易,可对全市场的期权合约报价进行实时监控,当出现套利机会时,算法交易还可以根据当前盘口的挂单状态,自动调整下单手数和委托价格,提高交易成功率。
1、什么是期权定价公式
Black-Scholes-Merton期权定价模型(Black-Scholes-Merton Option Pricing Model),即布莱克—斯克尔斯期权定价模型。
B-S-M定价公式:C=S·N(d1)-X·exp(-r·T)·N(d2)
其中:
d1=[ln(S/X)+(r+σ^2/2)T]/(σ√T) d2=d1-σ·√T
C—期权初始合理价格 X—期权执行价格
S—所交易金融资产现价T—期权有效期
r—连续复利计无风险利率
σ—股票连续复利(对数)回报率的年度波动率(标准差)
N(d1),N(d2)—正态分布变量的累积概率分布函数,在此应当说明两点:
第一,该模型中无风险利率必须是连续复利形式。一个简单的或不连续的无风险利率(设为r0)一般是一年计息一次,而r要求为连续复利利率。r0必须转化为r方能代入上式计算。
两者换算关系为:r=LN(1+r0)或r0=exp(r)-1例如r0=0.06,则r=LN(1+0.06)=0.0583,即100以583%的连续复利投资第二年将获106,该结果与直接用r0=0.06计算的答案一致。
第二,期权有效期T的相对数表示,即期权有效天数与一年365天的比值。如果期权有效期为100天,则T=100/365=0.274。
2、年化波动率及BS公式函数计算
①计算年化波动率:
记录收盘价:CallOptions.PutOptions
计算对数Ln(今日收盘价/昨日收盘价)
计算N天的标准差:如N天标准差=STD()
计算N天的波动率:N天的标准差*SQRT(252)
计算年化的波动率:N天波动率/SQRT(2N)
②推导理论价格计算案例
例如:某股票市价为3.84元,无风险利率为6%,年波动率为15%,求工商银行行权价为3.6元、期限为半年的欧式认购期权和认沽期权价格,其中:期限内不支付红利。
此例中S=3.84,K=3.6,r=0.06,σ=0.15,T=0.5。
计算过程可分为三步:
第一步,先计算出和。
第二步,计算和。由标准正态分布表可查的
则可得
第三步,将上述结果及已知条件代入B-S公式,股票欧式认购期权价格为:
欧式认沽期权价格为:
3、B-S-M模型失效或者可能误差的原因:
①模型对平值期权的估价令人满意,特别是对剩余有效期限超过两月,且不支付红利者效果更好一点。
②对于高度增值或减值的期权,模型的估价有较大偏差,会高估减值期权而低估增值期权。
③对临近到期日的期权的估价存在一定的误差。
④离散度过高或过低的情况下,会低估低离散度的买入期权,高估高离散度的买方期权。
⑤模型基于对市场部分情况的假设条件过于严苛,这与现实情况有所差别,可能会影响到模型的可靠性。
4、根据以上推导过程,编写策略模型计算期权理论价格,形成套利策略如下:
Data CODC0:"m2209-C-4200"; //看涨期权 CODP0:"m2209-P-4200"; //看跌期权 CODC1:"m2209-C-4150"; //看涨期权 CODP1:"m2209-P-4150"; //看跌期权 CODC2:"m2209-C-4100"; //看涨期权 CODP2:"m2209-P-4100"; //看跌期权 CODC3:"m2209-C-4050"; //看涨期权 CODP3:"m2209-P-4050"; //看跌期权 CODC4:"m2209-C-4000"; //看涨期权 CODP4:"m2209-P-4000"; //看跌期权 COD:"m2209"; //标的期货 Vars StringArray CODC; //看涨期权 StringArray CODP; //看跌期权 String CODF; //标的期货 Numeric CDN; //期权合约数量 Numeric N; //下单手数 Numeric X; //循环变量 Numeric R; //无风险收益率 Numeric D; //日期间隔 Numeric P; //条件比例 Numeric TKN; //数据区长度 Numeric TOD; //当前日期 Numeric NEWPF; //F最新价 Numeric HVLF; //F价格波动率 Numeric RLPF,FLPF; //F涨跌停价 Numeric BIDPF,ASKPF; //F买卖一价 Numeric BRPF,SRPF; //F多空头可用持仓 Numeric OPFLG; //模型处理标志 Var_TickData TKD; //数据区 NumericArray STP; //行权价 NumericArray LNP; //自然对数 NumericArray NEWPC; //C最新价 NumericArray NEWPP; //P最新价 NumericArray EPDC; //C行权日期 NumericArray EPDP; //P行权日期 NumericArray HVLC; //C历史波动率 NumericArray HVLP; //P历史波动率 NumericArray THPC; //C理论价格 NumericArray THPP; //P理论价格 NumericArray RTS; //距行权日剩余天数 NumericArray D1,D2; //中间值 NumericArray STRC; //C隐含波动率 NumericArray STRP; //P隐含波动率 NumericArray RLPC,FLPC; //C涨跌停价 NumericArray RLPP,FLPP; //P涨跌停价 NumericArray BIDPC,ASKPC; //C买卖一价 NumericArray BIDPP,ASKPP; //P买卖一价 NumericArray BRPC,SRPC; //C多空头可用持仓 NumericArray BRPP,SRPP; //P多空头可用持仓 NumericArray BKDFLGC,SKDFLGC; //C开仓处理标志 NumericArray BPDFLGC,SPDFLGC; //C平仓处理标志 NumericArray BKDFLGP,SKDFLGP; //P开仓处理标志 NumericArray BPDFLGP,SPDFLGP; //P平仓处理标志 NumericArray BKDFLGF,SKDFLGF; //F开仓处理标志 NumericArray BPDFLGF,SPDFLGF; //F平仓处理标志 Global_NumericArray BCFLG; //多头处理标志 Global_NumericArray SCFLG; //空头处理标志 Global_NumericArray BKIDC,SKIDC; //C开仓委托 Global_NumericArray BPIDC,SPIDC; //C平仓委托 Global_NumericArray BKIDP,SKIDP; //P开仓委托 Global_NumericArray BPIDP,SPIDP; //P平仓委托 Global_NumericArray BKIDF,SKIDF; //F开仓委托 Global_NumericArray BPIDF,SPIDF; //F平仓委托 Global_NumericArray BKFLGC,SKFLGC; //C开仓标志 Global_NumericArray BPFLGC,SPFLGC; //C平仓标志 Global_NumericArray BKFLGP,SKFLGP; //P开仓标志 Global_NumericArray BPFLGP,SPFLGP; //P平仓标志 Global_NumericArray BKFLGF,SKFLGF; //F开仓标志 Global_NumericArray BPFLGF,SPFLGF; //F平仓标志 Global_NumericArray BKMC,SKMC; //C开仓委托手数 Global_NumericArray BPMC,SPMC; //C平仓委托手数 Global_NumericArray BKMP,SKMP; //P开仓委托手数 Global_NumericArray BPMP,SPMP; //P平仓委托手数 Global_NumericArray BKMF,SKMF; //F开仓委托手数 Global_NumericArray BPMF,SPMF; //F平仓委托手数 Global_NumericArray BKPC,SKPC; //C开仓委托价格 Global_NumericArray BPPC,SPPC; //C平仓委托价格 Global_NumericArray BKPP,SKPP; //P开仓委托价格 Global_NumericArray BPPP,SPPP; //P平仓委托价格 Global_NumericArray BKPF,SKPF; //F开仓委托价格 Global_NumericArray BPPF,SPPF; //F平仓委托价格 Begin //------------------------处理开启------------------------// If(1) //处理开启 { CODC[0] = "m2209-C-4200"; //看涨期权 CODP[0] = "m2209-P-4200"; //看跌期权 CODC[1] = "m2209-C-4150"; //看涨期权 CODP[1] = "m2209-P-4150"; //看跌期权 CODC[2] = "m2209-C-4100"; //看涨期权 CODP[2] = "m2209-P-4100"; //看跌期权 CODC[3] = "m2209-C-4050"; //看涨期权 CODP[3] = "m2209-P-4050"; //看跌期权 CODC[4] = "m2209-C-4000"; //看涨期权 CODP[4] = "m2209-P-4000"; //看跌期权 CDN = GetStringArraySize(CODC); //期权合约数量 CODF = "m2209"; //标的期货 OPFLG = 1; //开启模型处理 If(CODF.A_IsExchangeOpen() != 1) //如果非开盘状态 { OPFLG = 2; //关闭模型处理 } For X = 0 To CDN - 1 //遍历期权合约 { If(CODC[X].A_IsExchangeOpen() != 1 || CODP[X].A_IsExchangeOpen() != 1) //如果非开盘状态 { OPFLG = 2; //关闭模型处理 } } } //------------------------变量赋值------------------------// If(OPFLG == 1) //变量赋值 { N = 10; //下单手数 R = 1; //无风险收益率 D = 5; //日期间隔 P = 0.5; //条件比例 TOD = CurrentDate(); //当前日期 NEWPF = CODF.Price("New"); //F最新价 BIDPF = CODF.Price("Bid1"); //F买一价 ASKPF = CODF.Price("Ask1"); //F卖一价 RLPF = CODF.Price("RiseLimit"); //F涨停价 FLPF = CODF.Price("FallLimit"); //F跌停价 BIDPF = IIF(BIDPF == 0 && NEWPF == FLPF,FLPF,BIDPF); //F买一价 ASKPF = IIF(ASKPF == 0 && NEWPF == RLPF,RLPF,ASKPF); //F卖一价 BRPF = CODF.F_BuyRemainPosition(); //F多头可用持仓 SRPF = CODF.F_SellRemainPosition(); //F空头可用持仓 BRPF = Min(BRPF,CODF.A_BuyRemainPosition()); //F多头可用持仓 SRPF = Min(SRPF,CODF.A_SellRemainPosition()); //F空头可用持仓 For X = 0 To CDN - 1 //遍历期权合约 { NEWPC[X] = CODC[X].Price("New"); //C最新价 NEWPP[X] = CODP[X].Price("New"); //P最新价 BIDPC[X] = CODC[X].Price("Bid1"); //C买一价 ASKPC[X] = CODC[X].Price("Ask1"); //C卖一价 BIDPP[X] = CODP[X].Price("Bid1"); //P买一价 ASKPP[X] = CODP[X].Price("Ask1"); //P卖一价 RLPC[X] = CODC[X].Price("RiseLimit"); //C涨停价 FLPC[X] = CODC[X].Price("FallLimit"); //C跌停价 RLPP[X] = CODP[X].Price("RiseLimit"); //P涨停价 FLPP[X] = CODP[X].Price("FallLimit"); //P跌停价 BIDPC[X] = IIF(BIDPC[X] == 0 && NEWPC[X] == FLPC[X],FLPC[X],BIDPC[X]); //C买一价 ASKPC[X] = IIF(ASKPC[X] == 0 && NEWPC[X] == RLPC[X],RLPC[X],ASKPC[X]); //C卖一价 BIDPP[X] = IIF(BIDPP[X] == 0 && NEWPP[X] == FLPP[X],FLPP[X],BIDPP[X]); //P买一价 ASKPP[X] = IIF(ASKPP[X] == 0 && NEWPP[X] == RLPP[X],RLPP[X],ASKPP[X]); //P卖一价 STP[X] = CODC[X].Price("StrikePrice"); //行权价 STRC[X] = CODC[X].Price("Stdderiation"); //C隐含波动率 STRP[X] = CODP[X].Price("Stdderiation"); //P隐含波动率 HVLC[X] = CODC[X].Price("HistoricalVolatility"); //C历史波动率 HVLP[X] = CODP[X].Price("HistoricalVolatility"); //P历史波动率 EPDC[X] = CODC[X].Price("ExpirationDate"); //C行权日期 EPDP[X] = CODP[X].Price("ExpirationDate"); //P行权日期 RTS[X] = DateDiff(TOD,EPDC[X] ); //距行权日剩余天数 BRPC[X] = CODC[X].F_BuyRemainPosition(); //C多头可用持仓 SRPC[X] = CODC[X].F_SellRemainPosition(); //C空头可用持仓 BRPP[X] = CODP[X].F_BuyRemainPosition(); //P多头可用持仓 SRPP[X] = CODP[X].F_SellRemainPosition(); //P空头可用持仓 BRPC[X] = Min(BRPC[X],CODC[X].A_BuyRemainPosition()); //C多头可用持仓 SRPC[X] = Min(SRPC[X],CODC[X].A_SellRemainPosition()); //C空头可用持仓 BRPP[X] = Min(BRPP[X],CODP[X].A_BuyRemainPosition()); //P多头可用持仓 SRPP[X] = Min(SRPP[X],CODP[X].A_SellRemainPosition()); //P空头可用持仓 } } //------------------------数据取值------------------------// If(OPFLG == 1) //数据取值 { TKD = Def_TickData(CODF,1,11); //数据区 If(TKD.State == 1) //如果数据区有效 { TKN = TKD.Num; //数据区长度 For X = 1 To TKN - 1 //遍历数据区 { LNP[X - 1] = Ln(TKD[X].TickPrice / TKD[X - 1].TickPrice); //自然对数 } HVLF = StandardDevArray(LNP,2) * Sqrt(252); //F价格波动率 For X = 0 To CDN - 1 //遍历期权合约 { D1[X] = (Ln(NEWPF / STP[X]) + (R / 10 + 0.5 * Power(HVLF,2)) * RTS[X]) / (HVLF * Power(RTS[X],0.5)); //期权理论价格中间值 D2[X] = D1[X] - HVLF * Power(RTS[X],0.5); //期权理论价格中间值 THPC[X] = NEWPF * NormalSCDensity(D1[X]) - STP[X] * Exp(-1 * (R / 10) * RTS[X]) * NormalSCDensity(D2[X]); //根据B-S-M公式计算看涨期权理论价格 THPP[X] = STP[X] * Exp(-1 * (R / 10) * RTS[X]) * (1 - NormalSCDensity(D2[X])) - NEWPF * (1 - NormalSCDensity(D1[X])); //根据B-S-M公式计算看跌期权理论价格 } } Else //如果数据区无效 { OPFLG = 2; //关闭模型处理 } } //------------------------成交判断------------------------// If(OPFLG == 1) //成交判断 { For X = 0 To CDN - 1 //遍历期权合约 { If(BKFLGF[X] == 1) //如果有F买开委托 { If(F_OrderStatus(BKIDF[X]) == Enum_Filled) //如果F买开委托成交 { Commentary("【多头开仓:F买开委托" + Text(X + 1) + "成交!】"); BKFLGF[X] = 0; //F买开标志归0 } Else If(F_OrderStatus(BKIDF[X]) == Enum_Deleted) //如果F买开委托废单 { Commentary("【多头开仓:F买开委托" + Text(X + 1) + "废单!】"); BKFLGF[X] = 0; //F买开标志归0 } } If(BKFLGP[X] == 1) //如果有P买开委托 { If(F_OrderStatus(BKIDP[X]) == Enum_Filled) //如果P买开委托成交 { Commentary("【多头开仓:P买开委托" + Text(X + 1) + "成交!】"); BKFLGP[X] = 0; //P买开标志归0 } Else If(F_OrderStatus(BKIDP[X]) == Enum_Deleted) //如果P买开委托废单 { Commentary("【多头开仓:P买开委托" + Text(X + 1) + "废单!】"); BKFLGP[X] = 0; //P买开标志归0 } } If(SPFLGF[X] == 1) //如果有F卖平委托 { If(F_OrderStatus(SPIDF[X]) == Enum_Filled) //如果F卖平委托成交 { Commentary("【多头平仓:F卖平委托" + Text(X + 1) + "成交!】"); SPFLGF[X] = 0; //F卖平标志归0 } Else If(F_OrderStatus(SPIDF[X]) == Enum_Deleted) //如果F卖平委托废单 { Commentary("【多头平仓:F卖平委托" + Text(X + 1) + "废单!】"); SPFLGF[X] = 0; //F卖平标志归0 } } If(SPFLGP[X] == 1) //如果有P卖平委托 { If(F_OrderStatus(SPIDP[X]) == Enum_Filled) //如果P卖平委托成交 { Commentary("【多头平仓:P卖平委托" + Text(X + 1) + "成交!】"); SPFLGP[X] = 0; //P卖平标志归0 } Else If(F_OrderStatus(SPIDP[X]) == Enum_Deleted) //如果P卖平委托废单 { Commentary("【多头平仓:P卖平委托" + Text(X + 1) + "废单!】"); SPFLGP[X] = 0; //P卖平标志归0 } } If(SKFLGF[X] == 1) //如果有F卖开委托 { If(F_OrderStatus(SKIDF[X]) == Enum_Filled) //如果F卖开委托成交 { Commentary("【空头开仓:F卖开委托" + Text(X + 1) + "成交!】"); SKFLGF[X] = 0; //F卖开标志归0 } Else If(F_OrderStatus(SKIDF[X]) == Enum_Deleted) //如果F卖开委托废单 { Commentary("【空头开仓:F卖开委托" + Text(X + 1) + "废单!】"); SKFLGF[X] = 0; //F卖开标志归0 } } If(BKFLGC[X] == 1) //如果有C买开委托 { If(F_OrderStatus(BKIDC[X]) == Enum_Filled) //如果C买开委托成交 { Commentary("【空头开仓:C买开委托" + Text(X + 1) + "成交!】"); BKFLGC[X] = 0; //C买开标志归0 } Else If(F_OrderStatus(BKIDC[X]) == Enum_Deleted) //如果C买开委托废单 { Commentary("【空头开仓:C买开委托" + Text(X + 1) + "废单!】"); BKFLGC[X] = 0; //C买开标志归0 } } If(BPFLGF[X] == 1) //如果有F买平委托 { If(F_OrderStatus(BPIDF[X]) == Enum_Filled) //如果F买平委托成交 { Commentary("【空头平仓:F买平委托" + Text(X + 1) + "成交!】"); BPFLGF[X] = 0; //F买平标志归0 } Else If(F_OrderStatus(BPIDF[X]) == Enum_Deleted) //如果F买平委托废单 { Commentary("【空头平仓:F买平委托" + Text(X + 1) + "废单!】"); BPFLGF[X] = 0; //F买平标志归0 } } If(SPFLGC[X] == 1) //如果有C卖平委托 { If(F_OrderStatus(SPIDC[X]) == Enum_Filled) //如果C卖平委托成交 { Commentary("【空头平仓:C卖平委托" + Text(X + 1) + "成交!】"); SPFLGC[X] = 0; //C卖平标志归0 } Else If(F_OrderStatus(SPIDC[X]) == Enum_Deleted) //如果C卖平委托废单 { Commentary("【空头平仓:C卖平委托" + Text(X + 1) + "废单!】"); SPFLGC[X] = 0; //C卖平标志归0 } } } } //------------------------多空处理------------------------// If(OPFLG == 1) //多空处理 { For X = 0 To CDN - 1 //遍历期权合约 { If(BKFLGF[X] == 0 && SPFLGF[X] == 0 && BKFLGP[X] == 0 && SPFLGP[X] == 0 && SKFLGF[X] == 0 && BPFLGF[X] == 0 && BKFLGC[X] == 0 && SPFLGC[X] == 0) //如果没有开平仓委托 { If(DateDiff(TOD,EPDC[X]) > D && DateDiff(TOD,EPDP[X]) > D) //如果满足开仓条件 { If(BCFLG[X] == 0) //如果未执行多头开仓 { If(THPP[X] > P * NEWPP[X] && STRP[X] > HVLP[X]) //如果满足多头开仓条件 { BKDFLGF[X] = 1; //开启F买开处理 BKDFLGP[X] = 1; //开启P买开处理 BCFLG[X] = 1; //已执行多头开仓 } } If(SCFLG[X] == 0) //如果未执行空头开仓 { If(NEWPC[X] < P * THPC[X] && STRC[X] < HVLC[X]) //如果满足空头开仓条件 { SKDFLGF[X] = 1; //开启F卖开处理 BKDFLGC[X] = 1; //开启C买开处理 SCFLG[X] = 1; //已执行空头开仓 } } } Else If(DateDiff(TOD,EPDC[X]) <= D || DateDiff(TOD,EPDP[X]) <= D) //如果满足平仓条件 { If(BCFLG[X] == 1) //如果已执行多头开仓 { SPDFLGF[X] = 1; //开启F卖平处理 SPDFLGP[X] = 1; //开启P卖平处理 BCFLG[X] = 0; //多头处理标志归0 } If(SCFLG[X] == 1) //如果已执行空头开仓 { BPDFLGF[X] = 1; //开启F买平处理 SPDFLGC[X] = 1; //开启C卖平处理 SCFLG[X] = 0; //空头处理标志归0 } } } } } //------------------------委托处理------------------------// If(OPFLG == 1) //委托处理 { For X = 0 To CDN - 1 //遍历期权合约 { If(BKDFLGF[X] == 1) //如果已开启F买开处理 { If(BKFLGF[X] == 0) //如果没有F买开委托 { BKMF[X] = N; //F买开委托手数 BKPF[X] = ASKPF; //F买开委托价格 Commentary("【多头开仓:F买开委托" + Text(X + 1) + "发出!】"); BKIDF[X] = CODF.A_SendOrder(Enum_Buy,Enum_Entry,BKMF[X],BKPF[X]); //发出F买开委托 BKFLGF[X] = 1; //已发出F买开委托 } } If(BKDFLGP[X] == 1) //如果已开启P买开处理 { If(BKFLGP[X] == 0) //如果没有P买开委托 { BKMP[X] = N; //P买开委托手数 BKPP[X] = ASKPP[X]; //P买开委托价格 Commentary("【多头开仓:P买开委托" + Text(X + 1) + "发出!】"); BKIDP[X] = CODP[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMP[X],BKPP[X]); //发出P买开委托 BKFLGP[X] = 1; //已发出P买开委托 } } If(SPDFLGF[X] == 1) //如果已开启F卖平处理 { If(SPFLGF[X] == 0) //如果没有F卖平委托 { If(BRPF >= N) //如果F多头可用持仓达到N手 { SPMF[X] = N; //F卖平委托手数 SPPF[X] = BIDPF; //F卖平委托价格 Commentary("【多头平仓:F卖平委托" + Text(X + 1) + "发出!】"); SPIDF[X] = CODF.A_SendOrder(Enum_Sell,Enum_Exit,SPMF[X],SPPF[X]); //发出F卖平委托 SPFLGF[X] = 1; //已发出F卖平委托 } } } If(SPDFLGP[X] == 1) //如果已开启P卖平处理 { If(SPFLGP[X] == 0) //如果没有P卖平委托 { If(BRPP[X] >= N) //如果有P多头可用持仓达到N手 { SPMP[X] = N; //P卖平委托手数 SPPP[X] = BIDPP[X]; //P卖平委托价格 Commentary("【多头平仓:P卖平委托" + Text(X + 1) + "发出!】"); SPIDP[X] = CODP[X].A_SendOrder(Enum_Sell,Enum_Exit,SPMP[X],SPPP[X]); //发出P卖平委托 SPFLGP[X] = 1; //已发出P卖平委托 } } } If(SKDFLGF[X] == 1) //如果已开启F卖开处理 { If(SKFLGF[X] == 0) //如果没有F卖开委托 { SKMF[X] = N; //F卖开委托手数 SKPF[X] = BIDPF; //F卖开委托价格 Commentary("【空头开仓:F卖开委托" + Text(X + 1) + "发出!】"); SKIDF[X] = CODF.A_SendOrder(Enum_Sell,Enum_Entry,SKMF[X],SKPF[X]); //发出F卖开委托 SKFLGF[X] = 1; //已发出F卖开委托 } } If(BKDFLGC[X] == 1) //如果已开启C买开处理 { If(BKFLGC[X] == 0) //如果没有C买开委托 { BKMC[X] = N; //C买开委托手数 BKPC[X] = ASKPC[X]; //C买开委托价格 Commentary("【空头开仓:C买开委托" + Text(X + 1) + "发出!】"); BKIDC[X] = CODC[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMC[X],BKPC[X]); //发出C买开委托 BKFLGC[X] = 1; //已发出C买开委托 } } If(BPDFLGF[X] == 1) //如果已开启F买平处理 { If(BPFLGF[X] == 0) //如果没有F买平委托 { If(SRPF >= N) //如果F空头可用持仓达到N手 { BPMF[X] = N; //F买平委托手数 BPPF[X] = ASKPF; //F买平委托价格 Commentary("【空头平仓:F买平委托" + Text(X + 1) + "发出!】"); BPIDF[X] = CODF.A_SendOrder(Enum_Buy,Enum_Exit,BPMF[X],BPPF[X]); //发出F买平委托 BPFLGF[X] = 1; //已发出A买平委托 } } } If(SPDFLGC[X] == 1) //如果已开启C卖平处理 { If(SPFLGC[X] == 0) //如果没有C卖平委托 { If(BRPC[X] >= N) //如果C多头可用持仓达到N手 { SPMC[X] = N; //C卖平委托手数 SPPC[X] = BIDPC[X]; //C卖平委托价格 Commentary("【空头平仓:C卖平委托" + Text(X + 1) + "发出!】"); SPIDC[X] = CODC[X].A_SendOrder(Enum_Sell,Enum_Exit,SPMC[X],SPPC[X]); //发出C卖平委托 SPFLGC[X] = 1; //已发出C卖平委托 } } } } } End
①什么是买卖权平价关系?
买卖权平价关系是指具有相同的到期日与执行价格的金融工具,其卖权与买权价格间必然存在的基本关系。如果两者不相同,则存在套利的空间。
欧式期权的平价关系可以表示为:
其中,C为看涨期权的当期的理论价格,P为看跌期权当期的理论价格,1年期无风险利率为r,行权价为K,为某股票现在的价格。
②根据买卖权平价关系推导套利条件
在上述理论的基础上,Tucker根据股指期货和股指期权之间的价格关系阐述了期货与期权的平价关系,并运用这一均衡关系来发现市场的套利机会并验证市场效率。
该理论假设:1)期权为欧式期权。2)税收、手续费等交易成本不计。3)借贷利率相等。
在t时刻,期货和期权的平价关系可表示为:
F为t时刻期货价格,T为期权到期日,C、P分别是看涨看跌期权的价格,r为无风险利率,贴现因子以年化无风险利率计算,通常考虑一个月内(1/12),其数值接近于1,进而上述关系可简化为:
由上式可以推导出套利开仓条件:
策略 | ||
C-P>F-K | 看涨期权价格被高估,看跌期权价格被低估 | 卖出看涨期权,买入看跌期权 |
C-P<F-K | 看涨期权价格被低估,看跌期权价格被高估 | 买入看涨期权,卖出看跌期权 |
③将上述策略编写为策略模型
Data CODC0:"m2209-C-4200"; //看涨期权 CODP0:"m2209-P-4200"; //看跌期权 CODC1:"m2209-C-4150"; //看涨期权 CODP1:"m2209-P-4150"; //看跌期权 CODC2:"m2209-C-4100"; //看涨期权 CODP2:"m2209-P-4100"; //看跌期权 CODC3:"m2209-C-4050"; //看涨期权 CODP3:"m2209-P-4050"; //看跌期权 CODC4:"m2209-C-4000"; //看涨期权 CODP4:"m2209-P-4000"; //看跌期权 COD:"m2209"; //标的期货 Vars StringArray CODC; //看涨期权 StringArray CODP; //看跌期权 String CODF; //标的期货 Numeric CDN; //期权合约数量 Numeric N; //下单手数 Numeric X; //循环变量 Numeric T; //时间间隔 Numeric D; //日期间隔 Numeric TOD; //当前日期 Numeric NOW; //当前时间 Numeric NEWPF; //F最新价 Numeric OPFLG; //模型处理标志 NumericArray STP; //行权价 NumericArray NEWPC; //C最新价 NumericArray NEWPP; //P最新价 NumericArray EPDC; //C行权日期 NumericArray EPDP; //P行权日期 NumericArray RTSC; //C距行权日剩余天数 NumericArray RTSP; //P距行权日剩余天数 NumericArray RLPC,FLPC; //C涨跌停价 NumericArray RLPP,FLPP; //P涨跌停价 NumericArray BIDPC,ASKPC; //C买卖一价 NumericArray BIDPP,ASKPP; //P买卖一价 NumericArray BRPC,SRPC; //C多空头可用持仓 NumericArray BRPP,SRPP; //P多空头可用持仓 NumericArray BKDFLGC,SKDFLGC; //C开仓处理标志 NumericArray BPDFLGC,SPDFLGC; //C开仓处理标志 NumericArray BKDFLGP,SKDFLGP; //P开仓处理标志 NumericArray BPDFLGP,SPDFLGP; //P开仓处理标志 Global_NumericArray KCFLG; //开仓处理标志 Global_NumericArray KPFLG; //开平处理标志 Global_NumericArray BKIDC,SKIDC; //C开仓委托 Global_NumericArray BPIDC,SPIDC; //C平仓委托 Global_NumericArray BKIDP,SKIDP; //P开仓委托 Global_NumericArray BPIDP,SPIDP; //P平仓委托 Global_NumericArray BKFLGC,SKFLGC; //C开仓标志 Global_NumericArray BPFLGC,SPFLGC; //C平仓标志 Global_NumericArray BKFLGP,SKFLGP; //P开仓标志 Global_NumericArray BPFLGP,SPFLGP; //P平仓标志 Global_NumericArray BKDELC,SKDELC; //C开仓撤单标志 Global_NumericArray BKDELP,SKDELP; //P开仓撤单标志 Global_NumericArray BKMC,SKMC; //C开仓委托手数 Global_NumericArray BPMC,SPMC; //C平仓委托手数 Global_NumericArray BKMP,SKMP; //P开仓委托手数 Global_NumericArray BPMP,SPMP; //P平仓委托手数 Global_NumericArray BKPC,SKPC; //C开仓委托价格 Global_NumericArray BPPC,SPPC; //C平仓委托价格 Global_NumericArray BKPP,SKPP; //P开仓委托价格 Global_NumericArray BPPP,SPPP; //P平仓委托价格 Global_NumericArray BKTC,SKTC; //C开仓委托时间 Global_NumericArray BKTP,SKTP; //P开仓委托时间 Begin //------------------------处理开启------------------------// If(1) //处理开启 { CODC[0] = "m2209-C-4200"; //看涨期权 CODP[0] = "m2209-P-4200"; //看跌期权 CODC[1] = "m2209-C-4150"; //看涨期权 CODP[1] = "m2209-P-4150"; //看跌期权 CODC[2] = "m2209-C-4100"; //看涨期权 CODP[2] = "m2209-P-4100"; //看跌期权 CODC[3] = "m2209-C-4050"; //看涨期权 CODP[3] = "m2209-P-4050"; //看跌期权 CODC[4] = "m2209-C-4000"; //看涨期权 CODP[4] = "m2209-P-4000"; //看跌期权 CDN = GetStringArraySize(CODC); //期权合约数量 CODF = "m2209"; //标的期货 OPFLG = 1; //开启模型处理 If(CODF.A_IsExchangeOpen() != 1) //如果非开盘状态 { OPFLG = 2; //关闭模型处理 } For X = 0 To CDN - 1 //遍历期权合约 { If(CODC[X].A_IsExchangeOpen() != 1 || CODP[X].A_IsExchangeOpen() != 1) //如果非开盘状态 { OPFLG = 2; //关闭模型处理 } } } //------------------------变量赋值------------------------// If(OPFLG == 1) //变量赋值 { N = 1; //下单手数 T = 15; //时间间隔 D = 5; //日期间隔 TOD = CurrentDate(); //当前日期 NOW = CurrentTime(); //当前时间 NEWPF = CODF.Price("New"); //F最新价 For X = 0 To CDN - 1 //遍历期权合约 { NEWPC[X] = CODC[X].Price("New"); //C最新价 NEWPP[X] = CODP[X].Price("New"); //P最新价 BIDPC[X] = CODC[X].Price("Bid1"); //C买一价 ASKPC[X] = CODC[X].Price("Ask1"); //C卖一价 BIDPP[X] = CODP[X].Price("Bid1"); //P买一价 ASKPP[X] = CODP[X].Price("Ask1"); //P卖一价 RLPC[X] = CODC[X].Price("RiseLimit"); //C涨停价 FLPC[X] = CODC[X].Price("FallLimit"); //C跌停价 RLPP[X] = CODP[X].Price("RiseLimit"); //P涨停价 FLPP[X] = CODP[X].Price("FallLimit"); //P跌停价 BIDPC[X] = IIF(BIDPC[X] == 0 && NEWPC[X] == FLPC[X],FLPC[X],BIDPC[X]); //C买一价 ASKPC[X] = IIF(ASKPC[X] == 0 && NEWPC[X] == RLPC[X],RLPC[X],ASKPC[X]); //C卖一价 BIDPP[X] = IIF(BIDPP[X] == 0 && NEWPP[X] == FLPP[X],FLPP[X],BIDPP[X]); //P买一价 ASKPP[X] = IIF(ASKPP[X] == 0 && NEWPP[X] == RLPP[X],RLPP[X],ASKPP[X]); //P卖一价 STP[X] = CODC[X].Price("StrikePrice"); //行权价 EPDC[X] = CODC[X].Price("ExpirationDate"); //C行权日期 EPDP[X] = CODP[X].Price("ExpirationDate"); //P行权日期 RTSC[X] = DateDiff(TOD,EPDC[X] ); //C距行权日剩余天数 RTSP[X] = DateDiff(TOD,EPDP[X] ); //P距行权日剩余天数 BRPC[X] = CODC[X].F_BuyRemainPosition(); //C多头可用持仓 SRPC[X] = CODC[X].F_SellRemainPosition(); //C空头可用持仓 BRPP[X] = CODP[X].F_BuyRemainPosition(); //P多头可用持仓 SRPP[X] = CODP[X].F_SellRemainPosition(); //P空头可用持仓 BRPC[X] = Min(BRPC[X],CODC[X].A_BuyRemainPosition()); //C多头可用持仓 SRPC[X] = Min(SRPC[X],CODC[X].A_SellRemainPosition()); //C空头可用持仓 BRPP[X] = Min(BRPP[X],CODP[X].A_BuyRemainPosition()); //P多头可用持仓 SRPP[X] = Min(SRPP[X],CODP[X].A_SellRemainPosition()); //P空头可用持仓 } } //------------------------成交判断------------------------// If(OPFLG == 1) //成交判断 { For X = 0 To CDN - 1 //遍历期权合约 { If(BKFLGC[X] == 1) //如果有C买开委托 { If(F_OrderStatus(BKIDC[X]) == Enum_Filled) //如果C买开委托成交 { Commentary("【多头开仓:C买开委托" + Text(X + 1) + "成交!】"); BKFLGC[X] = 0; //C买开标志归0 BKDELC[X] = 0; //C买开撤单标志归0 } Else If(F_OrderStatus(BKIDC[X]) == Enum_Canceled) //如果C买开委托已撤 { Commentary("【多头开仓:C买开委托" + Text(X + 1) + "已撤!】"); If(F_OrderFilledLot(BKIDC[X]) > 0) //如果C买开委托部分成交 { BKMC[X] = BKMC[X] - F_OrderFilledLot(BKIDC[X]); //C买开委托手数 } If(BKMC[X] > 0) //如果C买开委托手数大于0 { BKPC[X] = RLPC[X]; //C买开委托价格 Commentary("【多头开仓:C买开委托" + Text(X + 1) + "发出!】"); BKIDC[X] = CODC[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMC[X],BKPC[X]); //发出C买开委托 } BKDELC[X] = 0; //C买开撤单标志归0 } Else If(F_OrderStatus(BKIDC[X]) == Enum_Declared || F_OrderStatus(BKIDC[X]) == Enum_FillPart) //如果C买开委托挂单 { If(BKDELC[X] == 0) //如果未撤单 { If(TimeDiff(BKTC[X],NOW) >= T && BKPC[X] != RLPC[X]) //如果时间间隔T秒 { Commentary("【多头开仓:C买开委托" + Text(X + 1) + "撤单!】"); CODC[X].A_DeleteOrder(F_OrderContractNo(BKIDC[X])); //撤掉C买开委托挂单 BKDELC[X] = 1; //已发出撤掉C买开委托挂单 } } } Else If(F_OrderStatus(BKIDC[X]) == Enum_Deleted) //如果C买开委托废单 { Commentary("【多头开仓:C买开委托" + Text(X + 1) + "废单!】"); BKFLGC[X] = 0; //C买开标志归0 } } If(SPFLGC[X] == 1) //如果有C卖平委托 { If(F_OrderStatus(SPIDC[X]) == Enum_Filled) //如果C卖平委托成交 { Commentary("【多头平仓:C卖平委托" + Text(X + 1) + "成交!】"); SPFLGC[X] = 0; //C卖平标志归0 } Else If(F_OrderStatus(SPIDC[X]) == Enum_Deleted) //如果C卖平委托废单 { Commentary("【多头平仓:C卖平委托" + Text(X + 1) + "废单!】"); SPFLGC[X] = 0; //C卖平标志归0 } } If(SKFLGC[X] == 1) //如果有C卖开委托 { If(F_OrderStatus(SKIDC[X]) == Enum_Filled) //如果C卖开委托成交 { Commentary("【空头开仓:C卖开委托" + Text(X + 1) + "成交!】"); SKFLGC[X] = 0; //C卖开标志归0 SKDELC[X] = 0; //C卖开撤单标志归0 } Else If(F_OrderStatus(SKIDC[X]) == Enum_Canceled) //如果C卖开委托已撤 { Commentary("【空头开仓:C卖开委托" + Text(X + 1) + "已撤!】"); If(F_OrderFilledLot(SKIDC[X]) > 0) //如果C卖开委托部分成交 { SKMC[X] = SKMC[X] - F_OrderFilledLot(SKIDC[X]); //C卖开委托手数 } If(SKMC[X] > 0) //如果C卖开委托手数大于0 { SKPC[X] = FLPC[X]; //C卖开委托价格 Commentary("【空头开仓:C卖开委托" + Text(X + 1) + "发出!】"); SKIDC[X] = CODC[X].A_SendOrder(Enum_Sell,Enum_Entry,SKMC[X],SKPC[X]); //发出C卖开委托 } SKDELC[X] = 0; //C卖开撤单标志归0 } Else If(F_OrderStatus(SKIDC[X]) == Enum_Declared || F_OrderStatus(SKIDC[X]) == Enum_FillPart) //如果C卖开委托挂单 { If(SKDELC[X] == 0) //如果未撤单 { If(TimeDiff(SKTC[X],NOW) >= T && SKPC[X] != FLPC[X]) //如果时间间隔T秒 { Commentary("【空头开仓:C卖开委托" + Text(X + 1) + "撤单!】"); CODC[X].A_DeleteOrder(F_OrderContractNo(SKIDC[X])); //撤掉C卖开委托挂单 SKDELC[X] = 1; //已发出撤掉C卖开委托挂单 } } } Else If(F_OrderStatus(SKIDC[X]) == Enum_Deleted) //如果C卖开委托废单 { Commentary("【空头开仓:C卖开委托" + Text(X + 1) + "废单!】"); SKFLGC[X] = 0; //C卖开标志归0 } } If(BPFLGC[X] == 1) //如果有C买平委托 { If(F_OrderStatus(BPIDC[X]) == Enum_Filled) //如果C买平委托成交 { Commentary("【空头平仓:C买平委托" + Text(X + 1) + "成交!】"); BPFLGC[X] = 0; //C买平标志归0 } Else If(F_OrderStatus(BPIDC[X]) == Enum_Deleted) //如果C买平委托废单 { Commentary("【空头平仓:C买平委托" + Text(X + 1) + "废单!】"); BPFLGC[X] = 0; //C买平标志归0 } } If(BKFLGP[X] == 1) //如果有P买开委托 { If(F_OrderStatus(BKIDP[X]) == Enum_Filled) //如果P买开委托成交 { Commentary("【多头开仓:P买开委托" + Text(X + 1) + "成交!】"); BKFLGP[X] = 0; //P买开标志归0 BKDELP[X] = 0; //P买开撤单标志归0 } Else If(F_OrderStatus(BKIDP[X]) == Enum_Canceled) //如果P买开委托已撤 { Commentary("【多头开仓:P买开委托" + Text(X + 1) + "已撤!】"); If(F_OrderFilledLot(BKIDP[X]) > 0) //如果P买开委托部分成交 { BKMP[X] = BKMP[X] - F_OrderFilledLot(BKIDP[X]); //P买开委托手数 } If(BKMP[X] > 0) //如果P买开委托手数大于0 { BKPP[X] = RLPP[X]; //P买开委托价格 Commentary("【多头开仓:P买开委托" + Text(X + 1) + "发出!】"); BKIDP[X] = CODP[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMP[X],BKPP[X]); //发出P买开委托 } BKDELP[X] = 0; //P买开撤单标志归0 } Else If(F_OrderStatus(BKIDP[X]) == Enum_Declared || F_OrderStatus(BKIDP[X]) == Enum_FillPart) //如果P买开委托挂单 { If(BKDELP[X] == 0) //如果未撤单 { If(TimeDiff(BKTP[X],NOW) >= T && BKPP[X] != RLPP[X]) //如果时间间隔T秒 { Commentary("【多头开仓:P买开委托" + Text(X + 1) + "撤单!】"); CODP[X].A_DeleteOrder(F_OrderContractNo(BKIDP[X])); //撤掉P买开委托挂单 BKDELP[X] = 1; //已发出撤掉P买开委托挂单 } } } Else If(F_OrderStatus(BKIDP[X]) == Enum_Deleted) //如果P买开委托废单 { Commentary("【多头开仓:P买开委托" + Text(X + 1) + "废单!】"); BKFLGP[X] = 0; //P买开标志归0 } } If(SPFLGP[X] == 1) //如果有P卖平委托 { If(F_OrderStatus(SPIDP[X]) == Enum_Filled) //如果P卖平委托成交 { Commentary("【多头平仓:P卖平委托" + Text(X + 1) + "成交!】"); SPFLGP[X] = 0; //P卖平标志归0 } Else If(F_OrderStatus(SPIDP[X]) == Enum_Deleted) //如果P卖平委托废单 { Commentary("【多头平仓:P卖平委托" + Text(X + 1) + "废单!】"); SPFLGP[X] = 0; //P卖平标志归0 } } If(SKFLGP[X] == 1) //如果有P卖开委托 { If(F_OrderStatus(SKIDP[X]) == Enum_Filled) //如果P卖开委托成交 { Commentary("【空头开仓:P卖开委托" + Text(X + 1) + "成交!】"); SKFLGP[X] = 0; //P卖开标志归0 SKDELP[X] = 0; //P卖开撤单标志归0 } Else If(F_OrderStatus(SKIDP[X]) == Enum_Canceled) //如果P卖开委托已撤 { Commentary("【空头开仓:P卖开委托" + Text(X + 1) + "已撤!】"); If(F_OrderFilledLot(SKIDP[X]) > 0) //如果P卖开委托部分成交 { SKMP[X] = SKMP[X] - F_OrderFilledLot(SKIDP[X]); //P卖开委托手数 } If(SKMP[X] > 0) //如果P卖开委托手数大于0 { SKPP[X] = FLPP[X]; //P卖开委托价格 Commentary("【空头开仓:P卖开委托" + Text(X + 1) + "发出!】"); SKIDP[X] = CODP[X].A_SendOrder(Enum_Sell,Enum_Entry,SKMP[X],SKPP[X]); //发出P卖开委托 } SKDELP[X] = 0; //P卖开撤单标志归0 } Else If(F_OrderStatus(SKIDP[X]) == Enum_Declared || F_OrderStatus(SKIDP[X]) == Enum_FillPart) //如果P卖开委托挂单 { If(SKDELP[X] == 0) //如果未撤单 { If(TimeDiff(SKTP[X],NOW) >= T && SKPP[X] != FLPP[X]) //如果时间间隔T秒 { Commentary("【空头开仓:P卖开委托" + Text(X + 1) + "撤单!】"); CODP[X].A_DeleteOrder(F_OrderContractNo(SKIDP[X])); //撤掉P卖开委托挂单 SKDELP[X] = 1; //已发出撤掉P卖开委托挂单 } } } Else If(F_OrderStatus(SKIDP[X]) == Enum_Deleted) //如果P卖开委托废单 { Commentary("【空头开仓:P卖开委托" + Text(X + 1) + "废单!】"); SKFLGP[X] = 0; //P卖开标志归0 } } If(BPFLGP[X] == 1) //如果有P买平委托 { If(F_OrderStatus(BPIDP[X]) == Enum_Filled) //如果P买平委托成交 { Commentary("【空头平仓:P买平委托" + Text(X + 1) + "成交!】"); BPFLGP[X] = 0; //P买平标志归0 } Else If(F_OrderStatus(BPIDP[X]) == Enum_Deleted) //如果P买平委托废单 { Commentary("【空头平仓:P买平委托" + Text(X + 1) + "废单!】"); BPFLGP[X] = 0; //P买平标志归0 } } } } //------------------------开平处理------------------------// If(OPFLG == 1) //开平处理 { For X = 0 To CDN - 1 //遍历期权合约 { If(BKFLGC[X] == 0 && SPFLGC[X] == 0 && SKFLGC[X] == 0 && BPFLGC[X] == 0 && BKFLGP[X] == 0 && SPFLGP[X] == 0 && SKFLGP[X] == 0 && BPFLGP[X] == 0) //如果没有开平仓委托 { If(RTSC[X] > D && RTSC[X] > D) //如果满足开平仓条件 { If(KCFLG[X] == 0) //如果未执行平仓处理 { If(KPFLG[X] == 0 || KPFLG[X] == 2) //如果未执行开平处理1 { If(NEWPC[X] - NEWPP[X] > NEWPF - STP[X]) //如果满足平仓条件 { SPDFLGC[X] = 1; //开启C卖平处理 BPDFLGP[X] = 1; //开启P买平处理 KPFLG[X] = 1; //已执行开平处理1 KCFLG[X] = 1; //已执行平仓处理 } } If(KPFLG[X] == 0 || KPFLG[X] == 1) //如果未执行开平处理2 { If(NEWPC[X] - NEWPP[X] < NEWPF - STP[X]) //如果满足平仓条件 { BPDFLGC[X] = 1; //开启C买平处理 SPDFLGP[X] = 1; //开启P卖平处理 KPFLG[X] = 2; //已执行开平处理2 KCFLG[X] = 1; //已执行平仓处理 } } } Else If(KCFLG[X] == 1) //如果已执行平仓处理 { If(KPFLG[X] == 1) //如果已执行开平处理1 { SKDFLGC[X] = 1; //开启C卖开处理 BKDFLGP[X] = 1; //开启P买开处理 } Else If(KPFLG[X] == 2) //如果已执行开平处理2 { BKDFLGC[X] = 1; //开启C买开处理 SKDFLGP[X] = 1; //开启P卖开处理 } KCFLG[X] = 0; //开仓处理标志归0 } } Else If(RTSC[X] <= D || RTSP[X] <= D) //如果满足清仓条件 { If(KPFLG[X] == 1) //如果已执行开平处理1 { BPDFLGC[X] = 1; //开启C买平处理 SPDFLGP[X] = 1; //开启P卖平处理 KCFLG[X] = 0; //开仓处理标志归0 KPFLG[X] = 0; //开平处理标志归0 } Else If(KPFLG[X] == 2) //如果已执行开平处理2 { SPDFLGC[X] = 1; //开启C卖平处理 BPDFLGP[X] = 1; //开启P买平处理 KCFLG[X] = 0; //开仓处理标志归0 KPFLG[X] = 0; //开平处理标志归0 } } } } } //------------------------委托处理------------------------// If(OPFLG == 1) //委托处理 { For X = 0 To CDN - 1 //遍历期权合约 { If(BKDFLGC[X] == 1) //如果已开启C买开处理 { If(BKFLGC[X] == 0) //如果没有C买开委托 { BKMC[X] = N; //C买开委托手数 BKPC[X] = ASKPC[X]; //C买开委托价格 Commentary("【多头开仓:C买开委托" + Text(X + 1) + "发出!】"); BKIDC[X] = CODC[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMC[X],BKPC[X]); //发出C买开委托 BKTC[X] = NOW; //C买开委托时间 BKFLGC[X] = 1; //已发出C买开委托 } } If(SPDFLGC[X] == 1) //如果已开启C卖平处理 { If(SPFLGC[X] == 0) //如果没有C卖平委托 { If(BRPC[X] >= N) //如果C多头可用持仓达到N手 { SPMC[X] = N; //C卖平委托手数 SPPC[X] = BIDPC[X]; //C卖平委托价格 Commentary("【多头平仓:C卖平委托" + Text(X + 1) + "发出!】"); SPIDC[X] = CODC[X].A_SendOrder(Enum_Sell,Enum_Exit,SPMC[X],SPPC[X]); //发出C卖平委托 SPFLGC[X] = 1; //已发出C卖平委托 } } } If(SKDFLGC[X] == 1) //如果已开启C卖开处理 { If(SKFLGC[X] == 0) //如果没有C卖开委托 { SKMC[X] = N; //C卖开委托手数 SKPC[X] = BIDPC[X]; //C卖开委托价格 Commentary("【空头开仓:C卖开委托" + Text(X + 1) + "发出!】"); SKIDC[X] = CODC[X].A_SendOrder(Enum_Sell,Enum_Entry,SKMC[X],SKPC[X]); //发出C卖开委托 SKTC[X] = NOW; //C卖开委托时间 SKFLGC[X] = 1; //已发出C卖开委托 } } If(BPDFLGC[X] == 1) //如果已开启C买平处理 { If(BPFLGC[X] == 0) //如果没有C买平委托 { If(SRPC[X] >= N) //如果C空头可用持仓达到N手 { BPMC[X] = N; //C买平委托手数 BPPC[X] = ASKPC[X]; //C买平委托价格 Commentary("【空头平仓:C买平委托" + Text(X + 1) + "发出!】"); BPIDC[X] = CODC[X].A_SendOrder(Enum_Buy,Enum_Exit,BPMC[X],BPPC[X]); //发出C买平委托 BPFLGC[X] = 1; //已发出C买平委托 } } } If(BKDFLGP[X] == 1) //如果已开启P买开处理 { If(BKFLGP[X] == 0) //如果没有P买开委托 { BKMP[X] = N; //P买开委托手数 BKPP[X] = ASKPP[X]; //P买开委托价格 Commentary("【多头开仓:P买开委托" + Text(X + 1) + "发出!】"); BKIDP[X] = CODP[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMP[X],BKPP[X]); //发出P买开委托 BKTP[X] = NOW; //P买开委托时间 BKFLGP[X] = 1; //已发出P买开委托 } } If(SPDFLGP[X] == 1) //如果已开启P卖平处理 { If(SPFLGP[X] == 0) //如果没有P卖平委托 { If(BRPP[X] >= N) //如果P多头可用持仓达到N手 { SPMP[X] = N; //P卖平委托手数 SPPP[X] = BIDPP[X]; //P卖平委托价格 Commentary("【多头平仓:P卖平委托" + Text(X + 1) + "发出!】"); SPIDP[X] = CODP[X].A_SendOrder(Enum_Sell,Enum_Exit,SPMP[X],SPPP[X]); //发出P卖平委托 SPFLGP[X] = 1; //已发出P卖平委托 } } } If(SKDFLGP[X] == 1) //如果已开启P卖开处理 { If(SKFLGP[X] == 0) //如果没有P卖开委托 { SKMP[X] = N; //P卖开委托手数 SKPP[X] = BIDPP[X]; //P卖开委托价格 Commentary("【空头开仓:P卖开委托" + Text(X + 1) + "发出!】"); SKIDP[X] = CODP[X].A_SendOrder(Enum_Sell,Enum_Entry,SKMP[X],SKPP[X]); //发出P卖开委托 SKTP[X] = NOW; //P卖开委托时间 SKFLGP[X] = 1; //已发出P卖开委托 } } If(BPDFLGP[X] == 1) //如果已开启P买平处理 { If(BPFLGP[X] == 0) //如果没有P买平委托 { If(SRPP[X] >= N) //如果P空头可用持仓达到N手 { BPMP[X] = N; //P买平委托手数 BPPP[X] = ASKPP[X]; //P买平委托价格 Commentary("【空头平仓:P买平委托" + Text(X + 1) + "发出!】"); BPIDP[X] = CODP[X].A_SendOrder(Enum_Buy,Enum_Exit,BPMP[X],BPPP[X]); //发出P买平委托 BPFLGP[X] = 1; //已发出P买平委托 } } } } } End
①日历价差套利策略简介
日历价差是指买进到期日较远的期权,同时又卖出相同行权价格、相同数量但到期日较近的期权,赚取两个不同期权隐含波动率的差价或者其它期权定价参数的差价,以获得利润的期权套利交易策略。
②推导套利条件
1)、开仓策略:
X=V(近月隐含波动率)-V (历史波动率)+V (近月隐含波动率 )-V(远月隐含波动率) X>0,通过卖出一份近月看涨期权,同时买出一份远月看涨期权,建立日历价差组合。
2)、平仓策略:
考虑到该组合并没有对冲标的物价格变动的风险,在近月期权到期前几天,组合的Gamma风险值可能比较大,标的物价格的变化会大幅增加收益的波动率。因此,策略采取在当月合约到期前8天进行平仓。
③将上述策略编写为策略模型
Data CODA0:"c2209-C-3160"; //近月合约 CODB0:"c2211-C-3160"; //远月合约 CODA1:"c2209-C-3120"; //近月合约 CODB1:"c2211-C-3120"; //远月合约 CODA2:"c2209-C-3080"; //近月合约 CODB2:"c2211-C-3080"; //远月合约 CODA3:"c2209-C-3040"; //近月合约 CODB3:"c2211-C-3040"; //远月合约 CODA4:"c2209-C-3000"; //近月合约 CODB4:"c2211-C-3000"; //远月合约 COD:"c2209"; //标的期货 Vars StringArray CODA; //看涨期权 StringArray CODB; //看跌期权 String CODF; //标的期货 Numeric CDN; //期权合约数量 Numeric N; //下单手数 Numeric Length; //周期 Numeric X; //循环变量 Numeric D; //日期间隔 Numeric TKN; //数据区长度 Numeric TOD; //当前日期 Numeric HVLF; //F价格波动率 Numeric OPFLG; //模型处理标志 Var_TickData TKD; //数据区 NumericArray LNP; //自然对数 NumericArray EPDA; //行权日期 NumericArray RTS; //距行权日剩余天数 NumericArray STRA; //近月合约隐含波动率 NumericArray STRB; //远月合约隐含波动率 NumericArray PRC; //隐含波动率综合价差 NumericArray NEWPA; //A最新价 NumericArray NEWPB; //B最新价 NumericArray RLPA,FLPA; //A涨跌停价 NumericArray RLPB,FLPB; //B涨跌停价 NumericArray BIDPA,ASKPA; //A买卖一价 NumericArray BIDPB,ASKPB; //B买卖一价 NumericArray BRPA,SRPA; //A多空头可用持仓 NumericArray BRPB,SRPB; //B多空头可用持仓 NumericArray BKDFLG; //买开处理标志 NumericArray SKDFLG; //卖开处理标志 NumericArray BPDFLG; //买平处理标志 NumericArray SPDFLG; //卖平处理标志 Global_NumericArray BCFLG; //多头处理标志 Global_NumericArray SCFLG; //空头处理标志 Global_NumericArray BKIDA,SKIDA; //A开仓委托 Global_NumericArray BKIDB,SKIDB; //B开仓委托 Global_NumericArray BPIDA,SPIDA; //A平仓委托 Global_NumericArray BPIDB,SPIDB; //B平仓委托 Global_NumericArray BKFLGA,SKFLGA; //A开仓标志 Global_NumericArray BKFLGB,SKFLGB; //B开仓标志 Global_NumericArray BPFLGA,SPFLGA; //A平仓标志 Global_NumericArray BPFLGB,SPFLGB; //B平仓标志 Global_NumericArray BKMA,SKMA; //A开仓委托手数 Global_NumericArray BKMB,SKMB; //B开仓委托手数 Global_NumericArray BPMA,SPMA; //A平仓委托手数 Global_NumericArray BPMB,SPMB; //B平仓委托手数 Global_NumericArray BKPA,SKPA; //A开仓委托价格 Global_NumericArray BKPB,SKPB; //B开仓委托价格 Global_NumericArray BPPA,SPPA; //A平仓委托价格 Global_NumericArray BPPB,SPPB; //B平仓委托价格 Begin //------------------------处理开启------------------------// If(1) //处理开启 { CODA[0] = "c2209-C-3160"; //近月合约 CODB[0] = "c2211-C-3160"; //远月合约 CODA[1] = "c2209-C-3120"; //近月合约 CODB[1] = "c2211-C-3120"; //远月合约 CODA[2] = "c2209-C-3080"; //近月合约 CODB[2] = "c2211-C-3080"; //远月合约 CODA[3] = "c2209-C-3040"; //近月合约 CODB[3] = "c2211-C-3040"; //远月合约 CODA[4] = "c2209-C-3000"; //近月合约 CODB[4] = "c2211-C-3000"; //远月合约 CDN = GetStringArraySize(CODA); //期权合约数量 CODF = "c2209"; //标的期货 OPFLG = 1; //开启模型处理 If(CODF.A_IsExchangeOpen() != 1) //如果非开盘状态 { OPFLG = 2; //关闭模型处理 } For X = 0 To CDN - 1 //遍历期权合约 { If(CODA[X].A_IsExchangeOpen() != 1 || CODB[X].A_IsExchangeOpen() != 1) //如果非开盘状态 { OPFLG = 2; //关闭模型处理 } } } //------------------------变量赋值------------------------// If(OPFLG == 1) //变量赋值 { N = 10; //下单手数 Length = 5; //周期 D = 5; //日期间隔 TOD = CurrentDate(); //当前日期 For X = 0 To CDN - 1 //遍历期权合约 { NEWPA[X] = CODA[X].Price("New"); //A最新价 NEWPB[X] = CODB[X].Price("New"); //B最新价 BIDPA[X] = CODA[X].Price("Bid1"); //A买一价 ASKPA[X] = CODA[X].Price("Ask1"); //A卖一价 BIDPB[X] = CODB[X].Price("Bid1"); //B买一价 ASKPB[X] = CODB[X].Price("Ask1"); //B卖一价 RLPA[X] = CODA[X].Price("RiseLimit"); //A涨停价 FLPA[X] = CODA[X].Price("FallLimit"); //A跌停价 RLPB[X] = CODB[X].Price("RiseLimit"); //B涨停价 FLPB[X] = CODB[X].Price("FallLimit"); //B跌停价 BIDPA[X] = IIF(BIDPA[X] == 0 && NEWPA[X] == FLPA[X],FLPA[X],BIDPA[X]); //A买一价 ASKPA[X] = IIF(ASKPA[X] == 0 && NEWPA[X] == RLPA[X],RLPA[X],ASKPA[X]); //A卖一价 BIDPB[X] = IIF(BIDPB[X] == 0 && NEWPB[X] == FLPB[X],FLPB[X],BIDPB[X]); //B买一价 ASKPB[X] = IIF(ASKPB[X] == 0 && NEWPB[X] == RLPB[X],RLPB[X],ASKPB[X]); //B卖一价 STRA[X] = CODA[X].Price("Stdderiation"); //A隐含波动率 STRB[X] = CODB[X].Price("Stdderiation"); //B隐含波动率 EPDA[X] = CODA[X].Price("ExpirationDate"); //A行权日期 RTS[X] = DateDiff(TOD,EPDA[X] ); //距行权日剩余天数 BRPA[X] = CODA[X].F_BuyRemainPosition(); //A多头可用持仓 SRPA[X] = CODA[X].F_SellRemainPosition(); //A空头可用持仓 BRPB[X] = CODB[X].F_BuyRemainPosition(); //B多头可用持仓 SRPB[X] = CODB[X].F_SellRemainPosition(); //B空头可用持仓 BRPA[X] = Min(BRPA[X],CODA[X].A_BuyRemainPosition()); //A多头可用持仓 SRPA[X] = Min(SRPA[X],CODA[X].A_SellRemainPosition()); //A空头可用持仓 BRPB[X] = Min(BRPB[X],CODB[X].A_BuyRemainPosition()); //B多头可用持仓 SRPB[X] = Min(SRPB[X],CODB[X].A_SellRemainPosition()); //B空头可用持仓 } } //------------------------数据取值------------------------// If(OPFLG == 1) //数据取值 { TKD = Def_TickData(CODF,1,6); //数据区 If(TKD.State == 1) //如果数据区有效 { TKN = TKD.Num; //数据区长度 For X = 1 To TKN - 1 //遍历数据区 { LNP[X - 1] = Ln(TKD[X].TickPrice / TKD[X - 1].TickPrice); //自然对数 } HVLF = StandardDevArray(LNP,2) * Sqrt(252) ; //F价格波动率 For X = 0 To CDN - 1 //遍历期权合约 { PRC[X] = STRA[X] - HVLF + STRA[X] - STRB[X]; //隐含波动率综合价差 } } Else //如果数据区无效 { OPFLG = 2; //关闭模型处理 } } //------------------------成交判断------------------------// If(OPFLG == 1) //成交判断 { For X = 0 To CDN - 1 //遍历期权合约 { If(SKFLGA[X] == 1) //如果有A卖开委托 { If(F_OrderStatus(SKIDA[X]) == Enum_Filled) //如果A卖开委托成交 { Commentary("【空头开仓:A卖开委托" + Text(X + 1) + "成交!】"); SKFLGA[X] = 0; //A卖开标志归0 } Else If(F_OrderStatus(SKIDA[X]) == Enum_Deleted) //如果A卖开委托废单 { Commentary("【空头开仓:A卖开委托" + Text(X + 1) + "废单!】"); SKFLGA[X] = 0; //A卖开标志归0 } } If(BKFLGB[X] == 1) //如果有B买开委托 { If(F_OrderStatus(BKIDB[X]) == Enum_Filled) //如果B买开委托成交 { Commentary("【空头开仓:B买开委托" + Text(X + 1) + "成交!】"); BKFLGB[X] = 0; //B买开标志归0 } Else If(F_OrderStatus(BKIDB[X]) == Enum_Deleted) //如果B买开委托废单 { Commentary("【空头开仓:B买开委托" + Text(X + 1) + "废单!】"); BKFLGB[X] = 0; //B买开标志归0 } } If(BPFLGA[X] == 1) //如果有A买平委托 { If(F_OrderStatus(BPIDA[X]) == Enum_Filled) //如果A买平委托成交 { Commentary("【空头平仓:A买平委托" + Text(X + 1) + "成交!】"); BPFLGA[X] = 0; //A买平标志归0 } Else If(F_OrderStatus(BPIDA[X]) == Enum_Deleted) //如果A买平委托废单 { Commentary("【空头平仓:A买平委托" + Text(X + 1) + "废单!】"); BPFLGA[X] = 0; //A买平标志归0 } } If(SPFLGB[X] == 1) //如果有B卖平委托 { If(F_OrderStatus(SPIDB[X]) == Enum_Filled) //如果B卖平委托成交 { Commentary("【空头平仓:B卖平委托" + Text(X + 1) + "成交!】"); SPFLGB[X] = 0; //B卖平标志归0 } Else If(F_OrderStatus(SPIDB[X]) == Enum_Deleted) //如果B卖平委托废单 { Commentary("【空头平仓:B卖平委托" + Text(X + 1) + "废单!】"); SPFLGB[X] = 0; //B卖平标志归0 } } } } //------------------------空头处理------------------------// If(OPFLG == 1) //空头处理 { For X = 0 To CDN - 1 //遍历期权合约 { If(SKFLGA[X] == 0 && BKFLGB[X] == 0 && BPFLGA[X] == 0 && SPFLGB[X] == 0) //如果没有开平仓委托 { If(SCFLG[X] == 0) //如果未执行空头开仓 { If(PRC[X] > 0 && RTS[X] > D) //如果满足空头开仓条件 { SKDFLG[X] = 1; //开启卖开处理 SCFLG[X] = 1; //已执行空头开仓 } } Else If(SCFLG[X] == 1) //如果已执行空头开仓 { If(RTS[X] <= D) //如果满足空头平仓条件 { BPDFLG[X] = 1; //开启买平处理 SCFLG[X] = 0; //空头处理标志归0 } } } } } //------------------------委托处理------------------------// If(OPFLG == 1) //委托处理 { For X = 0 To CDN - 1 //遍历期权合约 { If(SKDFLG[X] == 1) //如果已开启卖开处理 { If(SKFLGA[X] == 0 && BKFLGB[X] == 0) //如果没有开仓委托 { SKMA[X] = N; //A卖开委托手数 SKPA[X] = BIDPA[X]; //A卖开委托价格 Commentary("【空头开仓:A卖开委托" + Text(X + 1) + "发出!】"); SKIDA[X] = CODA[X].A_SendOrder(Enum_Sell,Enum_Entry,SKMA[X],SKPA[X]); //发出A卖开委托 SKFLGA[X] = 1; //已发出A卖开委托 BKMB[X] = N; //B买开委托手数 BKPB[X] = ASKPB[X]; //B买开委托价格 Commentary("【空头开仓:B买开委托" + Text(X + 1) + "发出!】"); BKIDB[X] = CODB[X].A_SendOrder(Enum_Buy,Enum_Entry,BKMB[X],BKPB[X]); //发出B买开委托 BKFLGB[X] = 1; //已发出B买开委托 } } If(BPDFLG[X] == 1) //如果已开启买平处理 { If(BPFLGA[X] == 0 && SPFLGB[X] == 0) //如果没有平仓委托 { If(SRPA[X] >= N) //如果A空头可用持仓达到N手 { BPMA[X] = N; //A买平委托手数 BPPA[X] = ASKPA[X]; //A买平委托价格 Commentary("【空头平仓:A买平委托" + Text(X + 1) + "发出!】"); BPIDA[X] = CODA[X].A_SendOrder(Enum_Buy,Enum_Exit,BPMA[X],BPPA[X]); //发出A买平委托 BPFLGA[X] = 1; //已发出A买平委托 } If(BRPB[X] >= N) //如果B多头可用持仓达到N手 { SPMB[X] = N; //B卖平委托手数 SPPB[X] = BIDPB[X]; //B卖平委托价格 Commentary("【空头平仓:B卖平委托" + Text(X + 1) + "发出!】"); SPIDB[X] = CODB[X].A_SendOrder(Enum_Sell,Enum_Exit,SPMB[X],SPPB[X]); //发出B卖平委托 SPFLGB[X] = 1; //已发出B卖平委托 } } } } } End
机构量化交易资金管理规模大,单笔委托手数太大,直接下单会产生较大冲击成本。这时需要算法交易来帮忙!
T9支持趋势模型中写入智能拆单算法代码,使用算法交易策略控制交易过程,根据盘口情况智能拆单,不仅能够提高交易效率,降低冲击成本,还能隐匿机构交易行为,防止下单对趋势产生影响。
随着量化交易资金规模逐渐增大,单笔委托手数太大,直接下单会产生较大冲击成本。
对量化交易策略附加智能分批算法,使用算法交易策略控制交易过程,可在下单时由系统自动拆分成合适的小单进行分批委托,起到降低冲击成本的作用。
例如:
当出现买入信号时,我们可以使用对手盘的挂单量计算每批手数,每次委托手数为对手盘挂单量的20%,委托发出3秒后撤掉未成交挂单,并按照新的价格重新委托,直到全部成交。同时设定10个最小变动价位的成交范围,超出范围自动撤单并停止后续交易。
源码如下:
Data
COD:"cu2005"; //合约编码
Vars
Numeric N; //下单手数
Numeric KN; //单笔限制手数
Numeric M; //价位倍数
Numeric T; //时间间隔
Numeric P; //数值比例
Numeric NOW; //当前时间
Numeric NEWP; //最新价
Numeric ASKP; //卖一价
Numeric ASKV; //卖一量
Numeric MINP; //最小变动价位
Numeric OPFLG; //模型处理标志
Global_Numeric BCFLG; //多头处理标志
Global_Numeric BKDFLG; //买开处理标志
Global_Numeric BKID; //买开委托
Global_Numeric BKFLG; //买开标志
Global_Numeric BKDEL; //买开撤单标志
Global_Numeric BKSM; //买开委托总手数
Global_Numeric BKNP; //买开最新价
Global_Numeric BKM; //买开委托手数
Global_Numeric BKP; //买开委托价格
Global_Numeric BKT; //买开委托时间
Begin
//------------------------处理开启------------------------//
If(A_IsExchangeOpen() == 1) //如果是开盘状态
{
OPFLG = 1; //开启模型处理
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
N = 5000; //下单手数
KN = 20; //单笔限制手数
M = 10; //价位倍数
T = 3; //时间间隔
P = 0.2; //数值比例
NOW = CurrentTime(); //当前时间
NEWP = Price("New"); //最新价
ASKP = Price("Ask1"); //卖一价
ASKV = Price("AskVol1"); //卖一量
MINP = Price("MinPrice"); //最小变动价位
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
If(BKFLG == 1) //如果有买开委托
{
If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
{
Commentary("【多头开仓:买开委托成交!】");
If(BKSM == 0) //如果没有剩余买开手数
{
Commentary("【多头开仓:买开委托完成!】");
BCFLG = 3; //已完成多头开仓
}
BKFLG = 0; //买开标志归0
BKDEL = 0; //买开撤单标志归0
}
Else If(F_OrderStatus(BKID) == Enum_Canceled) //如果买开委托已撤
{
Commentary("【多头开仓:买开委托已撤!】");
If(BKDEL == 1) //如果是追价撤单
{
If(F_OrderFilledLot(BKID) > 0) //如果买开委托部分成交
{
BKM = BKM - F_OrderFilledLot(BKID); //买开委托手数
}
If(BKM > 0) //如果买开委托手数大于0
{
BKP = ASKP; //买开委托价格
Commentary("【多头开仓:买开委托追价!】");
BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP); //发出买开委托
BKT = NOW; //买开委托时间
}
}
Else If(BKDEL == 2 || BKDEL == 3 || BKDEL == 4) //如果是多头开仓撤单
{
BKFLG = 0; //买开标志归0
}
BKDEL = 0; //买开撤单标志归0
}
Else If(F_OrderStatus(BKID) == Enum_Declared || F_OrderStatus(BKID) == Enum_FillPart) //如果买开委托挂单
{
If(BKDEL == 0) //如果未撤单
{
If(TimeDiff(BKT,NOW) >= T) //如果时间间隔T秒
{
Commentary("【多头开仓:买开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
BKDEL = 1; //已发出撤掉买开委托挂单
}
}
Else If(BKDEL == 3) //如果是多头开仓撤单
{
Commentary("【多头开仓:买开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
BKDEL = 4; //多头开仓撤掉买开委托挂单
}
}
}
}
//------------------------多头处理-----------------------//
If(OPFLG == 1) //多头处理
{
If(BCFLG == 0) //如果未执行多头开仓
{
BKSM = N; //买开总手数
BKNP = NEWP; //买开最新价
BKDFLG = 1; //开启买开处理
BCFLG = 1; //已发出多头开仓
}
Else If(BCFLG == 1) //如果已发出多头开仓
{
If(Abs(NEWP - BKNP) > M * MINP) //如果滑点超过M个价位
{
Commentary("【多头开仓:滑点超过" + Text(M) + "点!】");
If(BKFLG == 1) //如果有买开委托
{
If(BKDEL == 0) //如果未撤单
{
If(F_OrderStatus(BKID) == Enum_Declared || F_OrderStatus(BKID) == Enum_FillPart) //如果买开委托挂单
{
Commentary("【多头开仓:买开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
BKDEL = 2; //多头开仓撤掉买开委托挂单
}
Else //如果买开委托未挂单
{
BKDEL = 3; //多头开仓撤掉买开委托挂单
}
}
Else //如果已发出撤单
{
BKDEL = 3; //多头开仓撤掉买开委托挂单
}
}
BKDFLG = 0; //买开处理标志归0
BCFLG = 2; //已发出多头撤单
}
}
If(BCFLG == 2) //如果已发出多头撤单
{
If(BKFLG == 0) //如果没有买开委托
{
Commentary("【多头开仓:买开委托取消!】");
BCFLG = 3; //已完成多头开仓
}
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(BKDFLG == 1) //如果已开启买开处理
{
If(BKFLG == 0) //如果没有买开委托
{
BKM = Min1(Floor(P * ASKV),KN,BKSM); //买开委托手数
If(BKM > 0) //如果买开委托手数大于0
{
BKP = ASKP; //买开委托价格
Commentary("【多头开仓:买开委托发出!】");
BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP); //发出买开委托
BKSM = BKSM - BKM; //买开总手数
BKT = NOW; //买开委托时间
BKFLG = 1; //已发出买开委托
}
If(BKSM == 0) //如果没有剩余买开手数
{
BKDFLG = 0; //买开处理标志归0
}
}
}
}
End
随着资金规模的增大,传统的手动委托方式,委托速度慢、误操作多、成交滑点大、止损不及时的劣势会越来越明显。
算法交易通过对期货账号交易信息的获取,实现了替代手动控制交易过程的功能。通过对持仓信息、挂单信息,成交回报,委托回报等信息的监控,实现了对交易过程的精细化控制,再配合算法交易委托速度快,对市场变化反应迅速等特点,可进一步减少成交滑点,降低交易风险。
算法交易也支持同时对多个交易账号进行委托控制,解决了手动交易无法同时管理多个账号批量委托的问题。
对于大资金客户,是否有下单单量大,唯恐惊扰行情趋势的困扰?是否有欲拉动行情却缕缕付出了大量成本的困扰?这里我们以此为例通过手动下单调用算法交易的方式介绍两个案例来解决你的困扰。
策略1:通过算法根据买卖量,自动拆分大单
交易思路解析:
1、取得手动下单的合约、手数、买卖方向、开平方向;自设委托价格。
2、如果为开仓:
(1)如果为买入开仓:
分批买入,取盘口卖一量*1/2与剩余下单手数比较,取数量较小值根据盘口卖一价买入,第一批成交后,再委托第二批,直至完全成交后退出算法。
(2)如果为卖出开仓:
分批卖出,取盘口买一量*1/2与剩余下单手数比较,取数量较小值根据盘口买一价卖出,第一批成交后,再委托第二批,直至完全成交后退出算法。
3、如果为平仓:
(1)如果为买入平仓:
分批处理同买入开仓。
(2)如果为卖出平仓:
分批处理同卖出开仓。
源码实现:
Vars Global_String AID; //账号 Global_String COD; //合约编码 Numeric P; //数值比例 Numeric SH; //上海合约标志 Numeric BSF; //买卖方向 Numeric EEF; //开平方向 Numeric BIDP,ASKP; //买卖一价 Numeric BIDV,ASKV; //买卖一量 Numeric BRP0,BRP1,BRP; //多头可用持仓 Numeric SRP0,SRP1,SRP; //空头可用持仓 Numeric OPFLG; //模型处理标志 Global_Numeric VLM; //交易手数 Global_Numeric KPFLG; //委托处理标志 Global_Numeric BKDFLG; //买开处理标志 Global_Numeric SKDFLG; //卖开处理标志 Global_Numeric BPDFLG; //买平处理标志 Global_Numeric SPDFLG; //卖平处理标志 Global_Numeric BKID,SKID; //开仓委托 Global_Numeric BKFLG,SKFLG; //开仓标志 Global_Numeric BKM,SKM; //开仓委托手数 Global_Numeric BKP,SKP; //开仓委托价格 Global_Numeric BPID0,BPID1,BPID; //买平委托 Global_Numeric SPID0,SPID1,SPID; //卖平委托 Global_Numeric BPFLG0,BPFLG1,BPFLG; //买平标志 Global_Numeric SPFLG0,SPFLG1,SPFLG; //卖平标志 Global_Numeric BPM0,BPM1,BPM; //买平委托手数 Global_Numeric SPM0,SPM1,SPM; //卖平委托手数 Global_Numeric BPP0,BPP1,BPP; //买平委托价格 Global_Numeric SPP0,SPP1,SPP; //卖平委托价格 Begin //------------------------委托获取------------------------// If(1) //委托获取 { If(KPFLG == 0) //如果未开启委托处理 { AID = M_GetTradeAccount(0); //账号 COD = M_GetTradeContract(); //合约编码 BSF = M_GetTradeBuySell(); //买卖方向 EEF = M_GetTradeEntryExit(); //开平方向 VLM = M_GetTradeVol(); //交易手数 If(VLM > 0 && COD.A_IsExchangeOpen() == 1) //如果交易手数大于0,且当前状态是开盘 { If(BSF == Enum_Buy && EEF == Enum_Entry) //如果是买开方向 { Commentary("【" + COD + "做多:手动买开" + Text(VLM) + "手!】"); BKDFLG = 1; //开启买开处理 } Else If(BSF == Enum_Sell && EEF == Enum_Exit) //如果是卖平方向 { Commentary("【" + COD + "平多:手动卖平" + Text(VLM) + "手!】"); SPDFLG = 1; //开启卖平处理 } Else If(BSF == Enum_Sell && EEF == Enum_ExitToday) //如果是卖平今方向 { Commentary("【" + COD + "平今多:手动卖平" + Text(VLM) + "手!】"); SPDFLG = 2; //开启卖平今仓处理 } Else If(BSF == Enum_Sell && EEF == Enum_Entry) //如果是卖开方向 { Commentary("【" + COD + "做空:手动卖开" + Text(VLM) + "手!】"); SKDFLG = 1; //开启卖开处理 } Else If(BSF == Enum_Buy && EEF == Enum_Exit) //如果是买平方向 { Commentary("【" + COD + "平空:手动买平" + Text(VLM) + "手!】"); BPDFLG = 1; //开启买平处理 } Else If(BSF == Enum_Buy && EEF == Enum_ExitToday) //如果是买平今方向 { Commentary("【" + COD + "平今空:手动买平" + Text(VLM) + "手!】"); BPDFLG = 2; //开启买平今仓处理 } KPFLG = 1; //开启委托处理 } } If(KPFLG == 1 && COD.A_IsExchangeOpen() == 1) //如果已开启委托处理,且当前状态是开盘 { OPFLG = 1; //开启模型处理 } } //------------------------变量赋值------------------------// If(OPFLG == 1) //变量赋值 { P = 0.5; //数值比例 BIDP = COD.Price("Bid1"); //买一价 ASKP = COD.Price("Ask1"); //卖一价 BIDV = COD.Price("BidVol1"); //买一量 ASKV = COD.Price("AskVol1"); //卖一量 SH = COD.A_IsSHCode(); //上海合约标志 BRP = COD.A_BuyRemainPosition(); //多头可用持仓 SRP = COD.A_SellRemainPosition(); //空头可用持仓 If(SH == 1) //如果是上期所合约 { BRP0 = COD.A_TodayBuyRemainPosition(); //多头今仓可用持仓 SRP0 = COD.A_TodaySellRemainPosition(); //空头今仓可用持仓 BRP1 = BRP - BRP0; //多头老仓可用持仓 SRP1 = SRP - SRP0; //空头老仓可用持仓 } } //------------------------成交判断------------------------// If(OPFLG == 1) //成交判断 { If(BKFLG == 1) //如果有买开委托 { If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交 { Commentary("【" + COD + "做多:买开委托成交!】"); VLM = VLM - BKM; //交易手数 BKFLG = 0; //买开标志归0 } } If(SH == 1) //如果是上期所合约 { If(SPFLG0 == 1) //如果有卖平今仓委托 { If(F_OrderStatus(SPID0) == Enum_Filled) //如果卖平今仓委托成交 { Commentary("【" + COD + "平多:卖平今仓委托成交!】"); VLM = VLM - SPM0; //交易手数 SPFLG0 = 0; //卖平今仓标志归0 } } If(SPFLG1 == 1) //如果有卖平老仓委托 { If(F_OrderStatus(SPID1) == Enum_Filled) //如果卖平老仓委托成交 { Commentary("【" + COD + "平多:卖平老仓委托成交!】"); VLM = VLM - SPM1; //交易手数 SPFLG1 = 0; //卖平老仓标志归0 } } } Else //如果非上期所合约 { If(SPFLG == 1) //如果有卖平委托 { If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交 { Commentary("【" + COD + "平多:卖平委托成交!】"); VLM = VLM - SPM; //交易手数 SPFLG = 0; //卖平标志归0 } } } If(SKFLG == 1) //如果有卖开委托 { If(F_OrderStatus(SKID) == Enum_Filled) //如果卖开委托成交 { Commentary("【" + COD + "做空:卖开委托成交!】"); VLM = VLM - SKM; //交易手数 SKFLG = 0; //卖开标志归0 } } If(SH == 1) //如果是上期所合约 { If(BPFLG0 == 1) //如果有买平今仓委托 { If(F_OrderStatus(BPID0) == Enum_Filled) //如果买平今仓委托成交 { Commentary("【" + COD + "平空:买平今仓委托成交!】"); VLM = VLM - BPM0; //交易手数 BPFLG0 = 0; //买平今仓标志归0 } } If(BPFLG1 == 1) //如果有买平老仓委托 { If(F_OrderStatus(BPID1) == Enum_Filled) //如果买平老仓委托成交 { Commentary("【" + COD + "平空:买平老仓委托成交!】"); VLM = VLM - BPM1; //交易手数 BPFLG1 = 0; //买平老仓标志归0 } } } Else //如果非上期所合约 { If(BPFLG == 1) //如果有买平委托 { If(F_OrderStatus(BPID) == Enum_Filled) //如果买平委托成交 { Commentary("【" + COD + "平空:买平委托成交!】"); VLM = VLM - BPM; //交易手数 BPFLG = 0; //买平标志归0 } } } } //------------------------委托处理------------------------// If(OPFLG == 1) //委托处理 { If(BKDFLG == 1) //如果已开启买开处理 { If(BKFLG == 0) //如果没有买开委托 { BKM = Min(Ceiling(P * ASKV,1),VLM); //买开委托手数 If(BKM > 0) //如果买开委托手数大于0 { BKP = ASKP; //买开委托价格 Commentary("【" + COD + "做多:公式买开" + Text(BKM) + "手!】"); Commentary("【" + COD + "做多:买开委托发出!】"); BKID = COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP,AID); //发出买开委托 BKFLG = 1; //已发出买开委托 } Else If(BKM == 0) //如果买开委托手数等于0 { Commentary("【" + COD + "做多:买开委托完成!】"); Exit(); //退出公式 } } } If(SPDFLG > 0) //如果已开启卖平处理 { If(SPFLG0 == 0 && SPFLG1 == 0 && SPFLG == 0) //如果没有卖平委托 { SPM = Min(Ceiling(P * BIDV,1),VLM); //卖平委托手数 If(BRP > 0 && SPM > 0) //如果有多头可用持仓,且卖平委托手数大于0 { If(SH == 1) //如果是上期所合约 { If(SPDFLG == 2) //如果是卖平今仓处理 { If(BRP0 > 0) //如果有多头今仓可用持仓 { SPM0 = Min(BRP0,SPM); //卖平今仓委托手数 SPP0 = BIDP; //卖平今仓委托价格 Commentary("【" + COD + "平多:公式卖平今仓" + Text(SPM0) + "手!】"); Commentary("【" + COD + "平多:卖平今仓委托发出!】"); SPID0 = COD.A_SendOrder(Enum_Sell,Enum_ExitToday,SPM0,SPP0,AID); //发出卖平今仓委托 SPFLG0 = 1; //已发出卖平今仓委托 } Else If(BRP0 == 0) //如果没有多头今仓可用持仓 { Commentary("【" + COD + "平多:卖平委托完成!】"); Exit(); //退出公式 } } Else If(SPDFLG == 1) //如果是卖平处理 { If(BRP1 > 0) //如果有多头老仓可用持仓 { SPM1 = Min(BRP1,SPM); //卖平老仓委托手数 SPP1 = BIDP; //卖平老仓委托价格 Commentary("【" + COD + "平多:公式卖平老仓" + Text(SPM1) + "手!】"); Commentary("【" + COD + "平多:卖平老仓委托发出!】"); SPID1 = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM1,SPP1,AID); //发出卖平老仓委托 SPFLG1 = 1; //已发出卖平老仓委托 } Else If(BRP1 == 0) //如果没有多头老仓可用持仓 { Commentary("【" + COD + "平多:卖平委托完成!】"); Exit(); //退出公式 } } } Else //如果非上期所合约 { SPM = Min(BRP,SPM); //卖平委托手数 SPP = BIDP; //卖平委托价格 Commentary("【" + COD + "平多:公式卖平" + Text(SPM) + "手!】"); Commentary("【" + COD + "平多:卖平委托发出!】"); SPID = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP,AID); //发出卖平委托 SPFLG = 1; //已发出卖平委托 } } Else If(BRP == 0 || SPM == 0) //如果没有多头可用持仓,或卖平委托手数等于0 { Commentary("【" + COD + "平多:卖平委托完成!】"); Exit(); //退出公式 } } } If(SKDFLG == 1) //如果已开启卖开处理 { If(SKFLG == 0) //如果没有卖开委托 { SKM = Min(Ceiling(P * BIDV,1),VLM); //卖开委托手数 If(SKM > 0) //如果卖开委托手数大于0 { SKP = BIDP; //卖开委托价格 Commentary("【" + COD + "做空:公式卖开" + Text(SKM) + "手!】"); Commentary("【" + COD + "做空:卖开委托发出!】"); SKID = COD.A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP,AID); //发出卖开委托 SKFLG = 1; //已发出卖开委托 } Else If(SKM == 0) //如果卖开委托手数等于0 { Commentary("【" + COD + "做空:卖开委托完成!】"); Exit(); //退出公式 } } } If(BPDFLG > 0) //如果已开启买平处理 { If(BPFLG0 == 0 && BPFLG1 == 0 && BPFLG == 0) //如果没有买平委托 { BPM = Min(Ceiling(P * ASKV,1),VLM); //买平委托手数 If(SRP > 0 && BPM > 0) //如果有空头可用持仓,且买平委托手数大于0 { If(SH == 1) //如果是上期所合约 { If(BPDFLG == 2) //如果是买平今仓处理 { If(SRP0 > 0) //如果有空头今仓可用持仓 { BPM0 = Min(SRP0,BPM); //买平今仓委托手数 BPP0 = ASKP; //买平今仓委托价格 Commentary("【" + COD + "平空:公式买平今仓" + Text(BPM0) + "手!】"); Commentary("【" + COD + "平空:买平今仓委托发出!】"); BPID0 = COD.A_SendOrder(Enum_Buy,Enum_ExitToday,BPM0,BPP0,AID); //发出买平今仓委托 BPFLG0 = 1; //已发出买平今仓委托 } Else If(SRP0 == 0) //如果没有空头今仓可用持仓 { Commentary("【" + COD + "平空:买平委托完成!】"); Exit(); //退出公式 } } Else If(BPDFLG == 1) //如果是买平处理 { If(SRP1 > 0) //如果有空头老仓可用持仓 { BPM1 = Min(SRP1,BPM); //买平老仓委托手数 BPP1 = ASKP; //买平老仓委托价格 Commentary("【" + COD + "平空:公式买平老仓" + Text(BPM1) + "手!】"); Commentary("【" + COD + "平空:买平老仓委托发出!】"); BPID1 = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM1,BPP1,AID); //发出买平老仓委托 BPFLG1 = 1; //已发出买平老仓委托 } Else If(SRP1 == 0) //如果没有空头老仓可用持仓 { Commentary("【" + COD + "平空:买平委托完成!】"); Exit(); //退出公式 } } } Else //如果非上期所合约 { BPM = Min(SRP,BPM); //买平委托手数 BPP = ASKP; //买平委托价格 Commentary("【" + COD + "平空:公式买平" + Text(BPM) + "手!】"); Commentary("【" + COD + "平空:买平委托发出!】"); BPID = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM,BPP,AID); //发出买平委托 BPFLG = 1; //已发出买平委托 } } Else If(SRP == 0 || BPM == 0) //如果没有空头可用持仓,或买平委托手数等于0 { Commentary("【" + COD + "平空:买平委托完成!】"); Exit(); //退出公式 } } } } End
通过右上方菜单【运行】->【算法交易运行池】->【算法交易下单】,调出算法交易下单界面,按下图所示方法实现手动下单调用算法交易:
注:①如果再次下单,会再次加载该模型,不会直接在前一个算法交易模型中继续执行。
②如果模型中含有函数Exit(),则模型执行完毕后会自动退出。
策略2:拉动行情,每次自动把买盘吃光,将行情往上拉,反之亦然。
交易思路解析:
1、取值手动下单的合约、手数、买卖方向、开平方向;自设委托价格。
2、如果为开仓:
(1)如果为买入开仓:
分批买入,取盘口卖一量与剩余下单手数比较,取数量较小值根据盘口卖一价买入。
如果盘口卖一价变化,撤单并重新委托,取盘口卖一量与该批剩余下单手数比较,取数量较小值根据盘口卖一价买入,直至完全成交完全成交后退出算法。
(2)如果为卖出开仓:
分批卖出,取盘口买一量与剩余下单手数比较,取数量较小值根据盘口买一价卖出。
如果盘口买一价变化,撤单并重新委托,取盘口买一量与该批剩余下单手数比较,取数量较小值根据盘口买一价卖出,直至完全成交完全成交后退出算法。
3、如果为平仓:
(1)如果为买入平仓:
分批处理同买入开仓。
(2)如果为卖出开仓:
分批处理同卖出开仓。
源码实现:
Vars Global_String AID; //账号 Global_String COD; //合约编码 Numeric SH; //上海合约标志 Numeric BSF; //买卖方向 Numeric EEF; //开平方向 Numeric BIDP,ASKP; //买卖一价 Numeric BIDV,ASKV; //买卖一量 Numeric BRP0,BRP1,BRP; //多头可用持仓 Numeric SRP0,SRP1,SRP; //空头可用持仓 Numeric OPFLG; //模型处理标志 Global_Numeric VLM; //交易手数 Global_Numeric KPFLG; //委托处理标志 Global_Numeric BKDFLG; //买开处理标志 Global_Numeric SKDFLG; //卖开处理标志 Global_Numeric BPDFLG; //买平处理标志 Global_Numeric SPDFLG; //卖平处理标志 Global_Numeric BKID,SKID; //开仓委托 Global_Numeric BKFLG,SKFLG; //开仓标志 Global_Numeric BKDEL,SKDEL; //开仓撤单标志 Global_Numeric BKCM,SKCM; //开仓成交手数 Global_Numeric BKM,SKM; //开仓委托手数 Global_Numeric BKP,SKP; //开仓委托价格 Global_Numeric BPID0,BPID1,BPID; //买平委托 Global_Numeric SPID0,SPID1,SPID; //卖平委托 Global_Numeric BPFLG0,BPFLG1,BPFLG; //买平标志 Global_Numeric SPFLG0,SPFLG1,SPFLG; //卖平标志 Global_Numeric BPDEL0,BPDEL1,BPDEL; //买平撤单标志 Global_Numeric SPDEL0,SPDEL1,SPDEL; //卖平撤单标志 Global_Numeric BPCM0,BPCM1,BPCM; //买平成交手数 Global_Numeric SPCM0,SPCM1,SPCM; //卖平成交手数 Global_Numeric BPM0,BPM1,BPM; //买平委托手数 Global_Numeric SPM0,SPM1,SPM; //卖平委托手数 Global_Numeric BPP0,BPP1,BPP; //买平委托价格 Global_Numeric SPP0,SPP1,SPP; //卖平委托价格 Begin //------------------------委托获取------------------------// If(1) //委托获取 { If(KPFLG == 0) //如果未开启委托处理 { AID = M_GetTradeAccount(0); //账号 COD = M_GetTradeContract(); //合约编码 BSF = M_GetTradeBuySell(); //买卖方向 EEF = M_GetTradeEntryExit(); //开平方向 VLM = M_GetTradeVol(); //交易手数 If(VLM > 0 && COD.A_IsExchangeOpen() == 1) //如果交易手数大于0,且当前状态是开盘 { If(BSF == Enum_Buy && EEF == Enum_Entry) //如果是买开方向 { Commentary("【" + COD + "做多:手动买开" + Text(VLM) + "手!】"); BKDFLG = 1; //开启买开处理 } Else If(BSF == Enum_Sell && EEF == Enum_Exit) //如果是卖平方向 { Commentary("【" + COD + "平多:手动卖平" + Text(VLM) + "手!】"); SPDFLG = 1; //开启卖平处理 } Else If(BSF == Enum_Sell && EEF == Enum_ExitToday) //如果是卖平今方向 { Commentary("【" + COD + "平今多:手动卖平" + Text(VLM) + "手!】"); SPDFLG = 2; //开启卖平今仓处理 } Else If(BSF == Enum_Sell && EEF == Enum_Entry) //如果是卖开方向 { Commentary("【" + COD + "做空:手动卖开" + Text(VLM) + "手!】"); SKDFLG = 1; //开启卖开处理 } Else If(BSF == Enum_Buy && EEF == Enum_Exit) //如果是买平方向 { Commentary("【" + COD + "平空:手动买平" + Text(VLM) + "手!】"); BPDFLG = 1; //开启买平处理 } Else If(BSF == Enum_Buy && EEF == Enum_ExitToday) //如果是买平今方向 { Commentary("【" + COD + "平今空:手动买平" + Text(VLM) + "手!】"); BPDFLG = 2; //开启买平今仓处理 } KPFLG = 1; //开启委托处理 } } If(KPFLG == 1 && COD.A_IsExchangeOpen() == 1) //如果已开启委托处理,且当前状态是开盘 { OPFLG = 1; //开启模型处理 } } //------------------------变量赋值------------------------// If(OPFLG == 1) //变量赋值 { BIDP = COD.Price("Bid1"); //买一价 ASKP = COD.Price("Ask1"); //卖一价 BIDV = COD.Price("BidVol1"); //买一量 ASKV = COD.Price("AskVol1"); //卖一量 SH = COD.A_IsSHCode(); //上海合约标志 BRP = COD.A_BuyRemainPosition(); //多头可用持仓 SRP = COD.A_SellRemainPosition(); //空头可用持仓 If(SH == 1) //如果是上期所合约 { BRP0 = COD.A_TodayBuyRemainPosition(); //多头今仓可用持仓 SRP0 = COD.A_TodaySellRemainPosition(); //空头今仓可用持仓 BRP1 = BRP - BRP0; //多头老仓可用持仓 SRP1 = SRP - SRP0; //空头老仓可用持仓 } } //------------------------成交判断------------------------// If(OPFLG == 1) //成交判断 { If(BKFLG == 1) //如果有买开委托 { If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交 { Commentary("【" + COD + "做多:买开委托成交!】"); VLM = VLM - BKM; //交易手数 BKFLG = 0; //买开标志归0 BKDEL = 0; //买开撤单标志归0 } Else If(F_OrderStatus(BKID) == Enum_Canceled) //如果买开委托已撤 { Commentary("【" + COD + "做多:买开委托已撤!】"); BKCM = F_OrderFilledLot(BKID); //买开成交手数 If(BKCM > 0) //如果买开委托部分成交 { BKM = BKM - BKCM; //买开委托手数 VLM = VLM - BKCM; //交易手数 } BKM = Min(ASKV,BKM); //买开委托手数 If(BKM > 0) //如果买开委托手数大于0 { BKP = ASKP; //买开委托价格 Commentary("【" + COD + "做多:公式买开" + Text(BKM) + "手!】"); Commentary("【" + COD + "做多:买开委托追价!】"); BKID = COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP,AID); //发出买开委托 } BKDEL = 0; //买开撤单标志归0 } Else If(F_OrderStatus(BKID) == Enum_Declared || F_OrderStatus(BKID) == Enum_FillPart) //如果买开委托挂单 { If(ASKP != BKP && BKDEL == 0) //如果卖一价不等于买开委托价格,且未撤单 { Commentary("【" + COD + "做多:买开委托撤单!】"); F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单 BKDEL = 1; //已发出撤掉买开委托挂单 } } } If(SH == 1) //如果是上期所合约 { If(SPFLG0 == 1) //如果有卖平今仓委托 { If(F_OrderStatus(SPID0) == Enum_Filled) //如果卖平今仓委托成交 { Commentary("【" + COD + "平多:卖平今仓委托成交!】"); VLM = VLM - SPM0; //交易手数 SPFLG0 = 0; //卖平今仓标志归0 SPDEL0 = 0; //卖平今仓撤单标志归0 } Else If(F_OrderStatus(SPID0) == Enum_Canceled) //如果卖平今仓委托已撤 { Commentary("【" + COD + "平多:卖平今仓委托已撤!】"); SPCM0 = F_OrderFilledLot(SPID0); //卖平今仓成交手数 If(SPCM0 > 0) //如果卖平今仓委托部分成交 { SPM0 = SPM0 - SPCM0; //卖平今仓委托手数 VLM = VLM - SPCM0; //交易手数 } SPM0 = Min1(BRP0,BIDV,SPM0); //卖平今仓委托手数 If(BRP0 > 0 && SPM0 > 0) //如果有多头今仓可用持仓,且卖平今仓委托手数大于0 { SPP0 = BIDP; //卖平今仓委托价格 Commentary("【" + COD + "平多:公式卖平今仓" + Text(SPM0) + "手!】"); Commentary("【" + COD + "平多:卖平今仓委托追价!】"); SPID0 = COD.A_SendOrder(Enum_Sell,Enum_ExitToday,SPM0,SPP0,AID); //发出卖平今仓委托 } SPDEL0 = 0; //卖平今仓撤单标志归0 } Else If(F_OrderStatus(SPID0) == Enum_Declared || F_OrderStatus(SPID0) == Enum_FillPart) //如果卖平今仓委托挂单 { If(BIDP != SPP0 && SPDEL0 == 0) //如果买一价不等于卖平今仓委托价格,且未撤单 { Commentary("【" + COD + "平多:卖平今仓委托撤单!】"); F_DeleteOrder(F_OrderContractNo(SPID0)); //撤掉卖平今仓委托挂单 SPDEL0 = 1; //已发出撤掉卖平今仓委托挂单 } } } If(SPFLG1 == 1) //如果有卖平老仓委托 { If(F_OrderStatus(SPID1) == Enum_Filled) //如果卖平老仓委托成交 { Commentary("【" + COD + "平多:卖平老仓委托成交!】"); VLM = VLM - SPM1; //交易手数 SPFLG1 = 0; //卖平老仓标志归0 SPDEL1 = 0; //卖平老仓撤单标志归0 } Else If(F_OrderStatus(SPID1) == Enum_Canceled) //如果卖平老仓委托已撤 { Commentary("【" + COD + "平多:卖平老仓委托已撤!】"); SPCM1 = F_OrderFilledLot(SPID1); //卖平老仓成交手数 If(SPCM1 > 0) //如果卖平老仓委托部分成交 { SPM1 = SPM1 - SPCM1; //卖平老仓委托手数 VLM = VLM - SPCM1; //交易手数 } SPM1 = Min1(BRP1,BIDV,SPM1); //卖平老仓委托手数 If(BRP1 > 0 && SPM1 > 0) //如果有多头老仓可用持仓,且卖平老仓委托手数大于0 { SPP1 = BIDP; //卖平老仓委托价格 Commentary("【" + COD + "平多:公式卖平老仓" + Text(SPM1) + "手!】"); Commentary("【" + COD + "平多:卖平老仓委托追价!】"); SPID1 = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM1,SPP1,AID); //发出卖平老仓委托 } SPDEL1 = 0; //卖平老仓撤单标志归0 } Else If(F_OrderStatus(SPID1) == Enum_Declared || F_OrderStatus(SPID1) == Enum_FillPart) //如果卖平老仓委托挂单 { If(BIDP != SPP1 && SPDEL1 == 0) //如果买一价不等于卖平老仓委托价格,且未撤单 { Commentary("【" + COD + "平多:卖平老仓委托撤单!】"); F_DeleteOrder(F_OrderContractNo(SPID1)); //撤掉卖平老仓委托挂单 SPDEL1 = 1; //已发出撤掉卖平老仓委托挂单 } } } } Else //如果非上期所合约 { If(SPFLG == 1) //如果有卖平委托 { If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交 { Commentary("【" + COD + "平多:卖平委托成交!】"); VLM = VLM - SPM; //交易手数 SPFLG = 0; //卖平标志归0 SPDEL = 0; //卖平撤单标志归0 } Else If(F_OrderStatus(SPID) == Enum_Canceled) //如果卖平委托已撤 { Commentary("【" + COD + "平多:卖平委托已撤!】"); SPCM = F_OrderFilledLot(SPID); //卖平成交手数 If(SPCM > 0) //如果卖平委托部分成交 { SPM = SPM - SPCM; //卖平委托手数 VLM = VLM - SPCM; //交易手数 } SPM = Min1(BRP,BIDV,SPM); //卖平委托手数 If(BRP > 0 && SPM > 0) //如果有多头可用持仓,且卖平委托手数大于0 { SPP = BIDP; //卖平委托价格 Commentary("【" + COD + "平多:公式卖平" + Text(SPM) + "手!】"); Commentary("【" + COD + "平多:卖平委托追价!】"); SPID = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP,AID); //发出卖平委托 } SPDEL = 0; //卖平撤单标志归0 } Else If(F_OrderStatus(SPID) == Enum_Declared || F_OrderStatus(SPID) == Enum_FillPart) //如果卖平委托挂单 { If(BIDP != SPP && SPDEL == 0) //如果买一价不等于卖平委托价格,且未撤单 { Commentary("【" + COD + "平多:卖平委托撤单!】"); F_DeleteOrder(F_OrderContractNo(SPID)); //撤掉卖平委托挂单 SPDEL = 1; //已发出撤掉卖平委托挂单 } } } } If(SKFLG == 1) //如果有卖开委托 { If(F_OrderStatus(SKID) == Enum_Filled) //如果卖开委托成交 { Commentary("【" + COD + "做空:卖开委托成交!】"); VLM = VLM - SKM; //交易手数 SKFLG = 0; //卖开标志归0 SKDEL = 0; //卖开撤单标志归0 } Else If(F_OrderStatus(SKID) == Enum_Canceled) //如果卖开委托已撤 { Commentary("【" + COD + "做空:卖开委托已撤!】"); SKCM = F_OrderFilledLot(SKID); //卖开成交手数 If(SKCM > 0) //如果卖开委托部分成交 { SKM = SKM - SKCM; //卖开委托手数 VLM = VLM - SKCM; //交易手数 } SKM = Min(BIDV,SKM); //卖开委托手数 If(SKM > 0) //如果卖开委托手数大于0 { SKP = BIDP; //卖开委托价格 Commentary("【" + COD + "做空:公式卖开" + Text(SKM) + "手!】"); Commentary("【" + COD + "做空:卖开委托追价!】"); SKID = COD.A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP,AID); //发出卖开委托 } SKDEL = 0; //卖开撤单标志归0 } Else If(F_OrderStatus(SKID) == Enum_Declared || F_OrderStatus(SKID) == Enum_FillPart) //如果卖开委托挂单 { If(BIDP != SKP && SKDEL == 0) //如果买一价不等于卖开委托价格,且未撤单 { Commentary("【" + COD + "做空:卖开委托撤单!】"); F_DeleteOrder(F_OrderContractNo(SKID)); //撤掉卖开委托挂单 SKDEL = 1; //已发出撤掉卖开委托挂单 } } } If(SH == 1) //如果是上期所合约 { If(BPFLG0 == 1) //如果有买平今仓委托 { If(F_OrderStatus(BPID0) == Enum_Filled) //如果买平今仓委托成交 { Commentary("【" + COD + "平空:买平今仓委托成交!】"); VLM = VLM - BPM0; //交易手数 BPFLG0 = 0; //买平今仓标志归0 BPDEL0 = 0; //买平今仓撤单标志归0 } Else If(F_OrderStatus(BPID0) == Enum_Canceled) //如果买平今仓委托已撤 { Commentary("【" + COD + "平空:买平今仓委托已撤!】"); BPCM0 = F_OrderFilledLot(BPID0); //买平今仓成交手数 If(BPCM0 > 0) //如果买平今仓委托部分成交 { BPM0 = BPM0 - BPCM0; //买平今仓委托手数 VLM = VLM - BPCM0; //交易手数 } BPM0 = Min1(SRP0,ASKV,BPM0); //买平今仓委托手数 If(SRP0 > 0 && BPM0 > 0) //如果有空头今仓可用持仓,且买平今仓委托手数大于0 { BPP0 = ASKP; //买平今仓委托价格 Commentary("【" + COD + "平空:公式买平今仓" + Text(BPM0) + "手!】"); Commentary("【" + COD + "平空:买平今仓委托追价!】"); BPID0 = COD.A_SendOrder(Enum_Buy,Enum_ExitToday,BPM0,BPP0,AID); //发出买平今仓委托 } BPDEL0 = 0; //买平今仓撤单标志归0 } Else If(F_OrderStatus(BPID0) == Enum_Declared || F_OrderStatus(BPID0) == Enum_FillPart) //如果买平今仓委托挂单 { If(ASKP != BPP0 && BPDEL0 == 0) //如果卖一价不等于买平今仓委托价格,且未撤单 { Commentary("【" + COD + "平空:买平今仓委托撤单!】"); F_DeleteOrder(F_OrderContractNo(BPID0)); //撤掉买平今仓委托挂单 BPDEL0 = 1; //已发出撤掉买平今仓委托挂单 } } } If(BPFLG1 == 1) //如果有买平老仓委托 { If(F_OrderStatus(BPID1) == Enum_Filled) //如果买平老仓委托成交 { Commentary("【" + COD + "平空:买平老仓委托成交!】"); VLM = VLM - BPM1; //交易手数 BPFLG1 = 0; //买平老仓标志归0 BPDEL1 = 0; //买平老仓撤单标志归0 } Else If(F_OrderStatus(BPID1) == Enum_Canceled) //如果买平老仓委托已撤 { Commentary("【" + COD + "平空:买平老仓委托已撤!】"); BPCM1 = F_OrderFilledLot(BPID1); //买平老仓成交手数 If(BPCM1 > 0) //如果买平老仓委托部分成交 { BPM1 = BPM1 - BPCM1; //买平老仓委托手数 VLM = VLM - BPCM1; //交易手数 } BPM1 = Min1(SRP1,ASKV,BPM1); //买平老仓委托手数 If(SRP1 > 0 && BPM1 > 0) //如果有空头老仓可用持仓,且买平老仓委托手数大于0 { BPP1 = ASKP; //买平老仓委托价格 Commentary("【" + COD + "平空:公式买平老仓" + Text(BPM1) + "手!】"); Commentary("【" + COD + "平空:买平老仓委托追价!】"); BPID1 = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM1,BPP1,AID); //发出买平老仓委托 } BPDEL1 = 0; //买平老仓撤单标志归0 } Else If(F_OrderStatus(BPID1) == Enum_Declared || F_OrderStatus(BPID1) == Enum_FillPart) //如果买平老仓委托挂单 { If(ASKP != BPP1 && BPDEL1 == 0) //如果卖一价不等于买平老仓委托价格,且未撤单 { Commentary("【" + COD + "平空:买平老仓委托撤单!】"); F_DeleteOrder(F_OrderContractNo(BPID1)); //撤掉买平老仓委托挂单 BPDEL1 = 1; //已发出撤掉买平老仓委托挂单 } } } } Else //如果非上期所合约 { If(BPFLG == 1) //如果有买平委托 { If(F_OrderStatus(BPID) == Enum_Filled) //如果买平委托成交 { Commentary("【" + COD + "平空:买平委托成交!】"); VLM = VLM - BPM; //交易手数 BPFLG = 0; //买平标志归0 BPDEL = 0; //买平撤单标志归0 } Else If(F_OrderStatus(BPID) == Enum_Canceled) //如果买平委托已撤 { Commentary("【" + COD + "平空:买平委托已撤!】"); BPCM = F_OrderFilledLot(BPID); //买平成交手数 If(BPCM > 0) //如果买平委托部分成交 { BPM = BPM - BPCM; //买平委托手数 VLM = VLM - BPCM; //交易手数 } BPM = Min1(SRP,ASKV,BPM); //买平委托手数 If(SRP > 0 && BPM > 0) //如果有空头可用持仓,且买平委托手数大于0 { BPP = ASKP; //买平委托价格 Commentary("【" + COD + "平空:公式买平" + Text(BPM) + "手!】"); Commentary("【" + COD + "平空:买平委托追价!】"); BPID = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM,BPP,AID); //发出买平委托 } BPDEL = 0; //买平撤单标志归0 } Else If(F_OrderStatus(BPID) == Enum_Declared || F_OrderStatus(BPID) == Enum_FillPart) //如果买平委托挂单 { If(ASKP != BPP && BPDEL == 0) //如果卖一价不等于买平委托价格,且未撤单 { Commentary("【" + COD + "平空:买平委托撤单!】"); F_DeleteOrder(F_OrderContractNo(BPID)); //撤掉买平委托挂单 BPDEL = 1; //已发出撤掉买平委托挂单 } } } } } //------------------------委托处理------------------------// If(OPFLG == 1) //委托处理 { If(BKDFLG == 1) //如果已开启买开处理 { If(BKFLG == 0) //如果没有买开委托 { BKM = Min(ASKV,VLM); //买开委托手数 If(BKM > 0) //如果买开委托手数大于0 { BKP = ASKP; //买开委托价格 Commentary("【" + COD + "做多:公式买开" + Text(BKM) + "手!】"); Commentary("【" + COD + "做多:买开委托发出!】"); BKID = COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP,AID); //发出买开委托 BKFLG = 1; //已发出买开委托 } Else If(BKM == 0) //如果买开委托手数等于0 { Commentary("【" + COD + "做多:买开委托完成!】"); Exit(); //退出公式 } } } If(SPDFLG > 0) //如果已开启卖平处理 { If(SPFLG0 == 0 && SPFLG1 == 0 && SPFLG == 0) //如果没有卖平委托 { SPM = Min(BIDV,VLM); //卖平委托手数 If(BRP > 0 && SPM > 0) //如果有多头可用持仓,且卖平委托手数大于0 { If(SH == 1) //如果是上期所合约 { If(SPDFLG == 2) //如果是卖平今仓处理 { If(BRP0 > 0) //如果有多头今仓可用持仓 { SPM0 = Min(BRP0,SPM); //卖平今仓委托手数 SPP0 = BIDP; //卖平今仓委托价格 Commentary("【" + COD + "平多:公式卖平今仓" + Text(SPM0) + "手!】"); Commentary("【" + COD + "平多:卖平今仓委托发出!】"); SPID0 = COD.A_SendOrder(Enum_Sell,Enum_ExitToday,SPM0,SPP0,AID); //发出卖平今仓委托 SPFLG0 = 1; //已发出卖平今仓委托 } Else If(BRP0 == 0) //如果没有多头今仓可用持仓 { Commentary("【" + COD + "平多:卖平委托完成!】"); Exit(); //退出公式 } } Else If(SPDFLG == 1) //如果是卖平处理 { If(BRP1 > 0) //如果有多头老仓可用持仓 { SPM1 = Min(BRP1,SPM); //卖平老仓委托手数 SPP1 = BIDP; //卖平老仓委托价格 Commentary("【" + COD + "平多:公式卖平老仓" + Text(SPM1) + "手!】"); Commentary("【" + COD + "平多:卖平老仓委托发出!】"); SPID1 = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM1,SPP1,AID); //发出卖平老仓委托 SPFLG1 = 1; //已发出卖平老仓委托 } Else If(BRP1 == 0) //如果没有多头老仓可用持仓 { Commentary("【" + COD + "平多:卖平委托完成!】"); Exit(); //退出公式 } } } Else //如果非上期所合约 { SPM = Min(BRP,SPM); //卖平委托手数 SPP = BIDP; //卖平委托价格 Commentary("【" + COD + "平多:公式卖平" + Text(SPM) + "手!】"); Commentary("【" + COD + "平多:卖平委托发出!】"); SPID = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP,AID); //发出卖平委托 SPFLG = 1; //已发出卖平委托 } } Else If(BRP == 0 || SPM == 0) //如果没有多头可用持仓,或卖平委托手数等于0 { Commentary("【" + COD + "平多:卖平委托完成!】"); Exit(); //退出公式 } } } If(SKDFLG == 1) //如果已开启卖开处理 { If(SKFLG == 0) //如果没有卖开委托 { SKM = Min(BIDV,VLM); //卖开委托手数 If(SKM > 0) //如果卖开委托手数大于0 { SKP = BIDP; //卖开委托价格 Commentary("【" + COD + "做空:公式卖开" + Text(SKM) + "手!】"); Commentary("【" + COD + "做空:卖开委托发出!】"); SKID = COD.A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP,AID); //发出卖开委托 SKFLG = 1; //已发出卖开委托 } Else If(SKM == 0) //如果卖开委托手数等于0 { Commentary("【" + COD + "做空:卖开委托完成!】"); Exit(); //退出公式 } } } If(BPDFLG > 0) //如果已开启买平处理 { If(BPFLG0 == 0 && BPFLG1 == 0 && BPFLG == 0) //如果没有买平委托 { BPM = Min(ASKV,VLM); //买平委托手数 If(SRP > 0 && BPM > 0) //如果有空头可用持仓,且买平委托手数大于0 { If(SH == 1) //如果是上期所合约 { If(BPDFLG == 2) //如果是买平今仓处理 { If(SRP0 > 0) //如果有空头今仓可用持仓 { BPM0 = Min(SRP0,BPM); //买平今仓委托手数 BPP0 = ASKP; //买平今仓委托价格 Commentary("【" + COD + "平空:公式买平今仓" + Text(BPM0) + "手!】"); Commentary("【" + COD + "平空:买平今仓委托发出!】"); BPID0 = COD.A_SendOrder(Enum_Buy,Enum_ExitToday,BPM0,BPP0,AID); //发出买平今仓委托 BPFLG0 = 1; //已发出买平今仓委托 } Else If(SRP0 == 0) //如果没有空头今仓可用持仓 { Commentary("【" + COD + "平空:买平委托完成!】"); Exit(); //退出公式 } } Else If(BPDFLG == 1) //如果是买平处理 { If(SRP1 > 0) //如果有空头老仓可用持仓 { BPM1 = Min(SRP1,BPM); //买平老仓委托手数 BPP1 = ASKP; //买平老仓委托价格 Commentary("【" + COD + "平空:公式买平老仓" + Text(BPM1) + "手!】"); Commentary("【" + COD + "平空:买平老仓委托发出!】"); BPID1 = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM1,BPP1,AID); //发出买平老仓委托 BPFLG1 = 1; //已发出买平老仓委托 } Else If(SRP1 == 0) //如果没有空头老仓可用持仓 { Commentary("【" + COD + "平空:买平委托完成!】"); Exit(); //退出公式 } } } Else //如果非上期所合约 { BPM = Min(SRP,BPM); //买平委托手数 BPP = ASKP; //买平委托价格 Commentary("【" + COD + "平空:公式买平" + Text(BPM) + "手!】"); Commentary("【" + COD + "平空:买平委托发出!】"); BPID = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM,BPP,AID); //发出买平委托 BPFLG = 1; //已发出买平委托 } } Else If(SRP == 0 || BPM == 0) //如果没有空头可用持仓,或买平委托手数等于0 { Commentary("【" + COD + "平空:买平委托完成!】"); Exit(); //退出公式 } } } } End
通过右上方菜单【运行】->【算法交易运行池】->【算法交易下单】,调出算法交易下单界面,按下图所示方法实现手动下单调用算法交易:
注:①如果再次下单,会再次加载该模型,不会直接在前一个算法交易模型中继续执行。
②如果模型中含有函数Exit(),则模型执行完毕后会自动退出。
算法交易也支持多账号智能分批,既可以对多个账号进行分组,设定不同的委托手数倍率,也可以根据对手盘情况实时计算每一批的总手数,实现多账号智能分批、统一管理。
例如:
我们有三个交易账号需要同时开仓,三个交易账号的委托手数倍率分别为1、1.5、2.5。我们可在多账号下单界面中勾选要参与交易的账号,并设定好下单倍率。
我们可以一键对这三个账号一起进行智能分批下单。
首先,要求每批的总手数不超过盘口对手盘量的20%。首批三个账户按照1 : 1.5 :2.5的比例进行下单。
其次,任何一个账号第一批成交以后(三个账号不一定同时都成交),第二批委托数量为对手盘量的20%乘以该账号的比例,直到全部成交。
最后,任何一个账号如3秒未全部成交,则执行追价策略确保成交。
这样既保证了总手数不对趋势造成影响,也同时完成了三个账号的分批建仓。
代码如下:
Vars
Global_StringArray AID; //账号
Global_String COD; //合约编码
Numeric X; //循环变量
Numeric T; //时间间隔
Numeric P; //数值比例
Numeric BSF; //买卖方向
Numeric EEF; //开平方向
Numeric VLM; //交易手数
Numeric NOW; //当前时间
Numeric ASKP; //卖一价
Numeric ASKV; //卖一量
Numeric ODFLG; //挂单判断标志
Numeric OPFLG; //模型处理标志
Global_Numeric ADN; //账号数量
Global_Numeric TP; //下单总倍数
Global_Numeric KPFLG; //委托处理标志
Global_Numeric BKDFLG; //买开处理标志
Global_NumericArray NP; //下单倍数
Global_NumericArray BKID; //买开委托
Global_NumericArray BKFLG; //买开标志
Global_NumericArray BKDEL; //买开撤单标志
Global_NumericArray BKSM; //买开总手数
Global_NumericArray BKM; //买开委托手数
Global_NumericArray BKP; //买开委托价格
Global_NumericArray BKT; //买开委托时间
Begin
//------------------------委托获取------------------------//
If(1) //委托获取
{
If(KPFLG == 0) //如果未开启委托处理
{
ADN = M_GetTradeAccountNum; //账号数量
COD = M_GetTradeContract(); //合约编码
BSF = M_GetTradeBuySell(); //买卖方向
EEF = M_GetTradeEntryExit(); //开平方向
VLM = M_GetTradeVol(); //交易手数
For X = 0 To ADN - 1 //遍历账号
{
AID[X] = M_GetTradeAccount(X); //账号
NP[X] = M_GetTradeOrderRatio(X);
//下单倍数
BKSM[X] = NP[X] * VLM; //买开总手数
TP = TP + NP[X]; //下单总倍数
}
If(VLM > 0 && COD.A_IsExchangeOpen() == 1)
//如果交易手数大于0,且当前状态是开盘
{
If(BSF == Enum_Buy && EEF == Enum_Entry)
//如果是买开方向
{
Commentary("【" + COD +
"做多:手动买开" + Text(VLM) + "手!】");
BKDFLG = 1;
//开启买开处理
}
KPFLG = 1; //开启委托处理
}
}
If(KPFLG == 1 && COD.A_IsExchangeOpen() == 1) //如果已开启委托处理,且当前状态是开盘
{
OPFLG = 1; //开启模型处理
}
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
T = 3; //时间间隔
P = 0.2; //数值比例
NOW = CurrentTime(); //当前时间
ASKP = COD.Price("Ask1"); //卖一价
ASKV = COD.Price("AskVol1"); //卖一量
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
For X = 0 To ADN - 1 //遍历账号
{
If(BKFLG[X] == 1) //如果有买开委托
{
If(F_OrderStatus(BKID[X]) == Enum_Filled)
//如果买开委托成交
{
Commentary("【" + COD +
"做多:买开委托成交!" + AID[X] + "】");
BKFLG[X] = 0;
//买开标志归0
BKDEL[X] = 0;
//买开撤单标志归0
}
Else If(F_OrderStatus(BKID[X]) ==
Enum_Canceled) //如果买开委托已撤
{
Commentary("【" + COD +
"做多:买开委托已撤!" + AID[X] + "】");
If(F_OrderFilledLot(BKID[X])
> 0) //如果买开委托部分成交
{
BKM[X]
= BKM[X] - F_OrderFilledLot(BKID[X]); //买开委托手数
}
If(BKM[X] > 0)
//如果买开委托手数大于0
{
BKP[X]
= ASKP; //买开委托价格
Commentary("【"
+ COD + "做多:买开委托追价!" + AID[X] + "】");
BKID[X]
= COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM[X],BKP[X],AID[X]); //发出买开委托
BKT[X]
= NOW; //买开委托时间
}
BKDEL[X] = 0;
//买开撤单标志归0
}
Else If(F_OrderStatus(BKID[X]) ==
Enum_Declared || F_OrderStatus(BKID[X]) == Enum_FillPart) //如果买开委托挂单
{
If(BKDEL[X] == 0)
//如果未撤单
{
If(TimeDiff(BKT[X],NOW)
>= T) //如果时间间隔T秒
{
Commentary("【"
+ COD + "做多:买开委托撤单!" + AID[X] + "】");
COD.F_DeleteOrder(F_OrderContractNo(BKID[X]),AID[X]);
//撤掉买开委托挂单
BKDEL[X]
= 1; //已发出撤掉买开委托挂单
}
}
}
}
}
ODFLG = 1; //挂单判断标志初始化
For X = 0 To ADN - 1 //遍历账号
{
If(BKFLG[X] == 1 || BKSM[X] > 0) //如果有买开委托,或有剩余买开手数
{
ODFLG = 2; //存在买开委托挂单
}
}
If(ODFLG == 1) //如果没有买开委托
{
Commentary("【" + COD + "做多:买开委托完成!】");
BKDFLG = 0; //买开处理标志归0
Exit(); //退出公式
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(BKDFLG == 1) //如果已开启买开处理
{
For X = 0 To ADN - 1 //遍历账号
{
If(BKFLG[X] == 0) //如果没有买开委托
{
If(TP > 0)
//如果已取得下单总倍数
{
BKM[X]
= Min(Floor((NP[X] / TP) * P * ASKV),BKSM[X]); //买开委托手数
}
If(BKM[X] > 0)
//如果买开委托手数大于0
{
BKP[X]
= ASKP; //买开委托价格
Commentary("【"
+ COD + "做多:公式买开" + Text(BKM[X]) + "手!" + AID[X] + "】");
Commentary("【"
+ COD + "做多:买开委托发出!" + AID[X] + "】");
BKID[X]
= COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM[X],BKP[X],AID[X]); //发出买开委托
BKSM[X]
= BKSM[X] - BKM[X]; //买开总手数自减买开处理手数
BKT[X]
= NOW; //买开委托时间
BKFLG[X]
= 1; //已发出买开委托
}
}
}
}
}
End
模组是基金经理的模型运行管理操作页面,不仅支持投资组合,支持“将鸡蛋放到不同的篮子里”,通过多品种、多策略、多周期的组合交易,构建最优的投资组合。
模组还支持一个模型运行单元带多账号下单的场景。通过对每个资金账号可设置不同的交易倍率,模型发出交易指令时,每个资金账号按照模型交易手数乘以账户倍率为交易手数进行交易,高效且方便管理。
模组提供申购/赎回辅助处理的功能,针对手动资金管理的场景,以及用模型做自动资金管理的场景都有对应的支持。
手动资金管理的场景:当某一只资管产品进行申购/赎回管理时,只需在多账号管理界面,修改账号组中产品账户的下单倍率,即可自动变更此产品账户全部运行单元的交易手数,运行单元会自动从下一个开仓指令开始使用新的手数交易。
用模型做自动资金管理的场景:用户在模型里写了资金头寸函数实现按理论资金比例下单的情况下,当一只资管产品需要进行申购/赎回时,可在资金管理界面,对该产品的运行单元进行出入金操作,后续的开仓指令,模型会根据调整后的资金重新计算下单手数。对于各个运行单元的当前持仓需要进行辅助的手动处理:申购时可手动点击“加仓”按钮,进行加仓交易;赎回时可点击“减仓”按钮,进行减仓交易。
文华精选期货因子库囊括量价、动量、期限结构、持仓、基本面、BETA六大类精选因子,以期货市场全品种为标的,提供单因子筛选、多因子排名分析等功能。可借助多因子综合排名信息,筛选出能够带来更多超额收益的品种,构建最优组合。还可以对多个品种在同一时间点的强弱情况进行对比分析,建立对冲组合,实现期货横截面交易策略。
不管是CTA策略还是日内策略,均有选择品种的需求。在传统的量价因子基础上,我们可以借助更多的风格因子,通过多因子综合排名,筛选出能够带来更多超额收益的品种构建投资组合。
因子类型 | 超额收益率来源 |
动量因子 | 前期价格表现较强的品种带来的风险溢价 |
期限结构因子 | 展期收益大的品种所带来的风险溢价 |
持仓因子 | 大量资金流入所带来的的风险溢价 |
基本面因子 | 供求关系变化带来的风险溢价 |
BETA因子 | 对通货膨胀敏感带来的风险溢价 |
1、我们先从量价因子入手,选择“沉淀资金”因子和“流动性”因子,筛选出受欢迎度高、流动性好的品种,此类品种出现趋势的概率大,也更适合大资金建仓;
2、在动量因子中,我们选择了“涨幅占比”因子,用于筛选出近期涨跌幅度更大的品种,以期待获得更高的收益率;
3、在期限结构因子中,选择“主力现货贴水率”因子,筛选出期货价格远低于现货价格的品种,更适合做多交易;
4、在持仓因子中,选择“会员持买单量增减”因子,跟随多头主力资金动向;
5、基本面因子中,选择“仓单变化率”因子,筛选出受供求关系影响大的品种,短期趋势更明显;
6、BETA因子中,选择“CPI因子”,该因子计算了CPI变化率与指数合约收益率的回归系数,可以衡量指数收益率受通货膨胀影响的大小。
根据系统筛选结果,初选出IC、焦煤、棕榈、沪镍、豆油、沪锡、橡胶、焦炭、IF、铁矿这10个备选品种,再根据每个因子的实际数值,剔除掉一些单因子表现极为不好的品种,例如IC、IF在“会员持买单量增减”因子中表现很差。
最终,根据多因子综合排名和单因子表现确认焦煤、棕榈、豆油、橡胶、焦炭、铁矿为交易品种,构建投资组合,并以多头交易为主。
横截面策略是一种泛套利策略,主要是指的是在同一个时间点(或者同一个时间区间),同时做多、做空多个品种,形成一个多空对冲组合,以期多空两个方向交易的盈利之和为正的一类策略。
横截面策略盈利的逻辑是基于期货价格的变动:强者恒强/弱者恒弱的假设,即期货价格具有一定惯性,趋势行情具有延续性,前期上涨幅度较大的合约,后期还能够继续上涨的概率更大。
例如:使用期货品种筛选功能,对市场所有的期货品种按照涨跌幅和资金流向进行排序:
根据综合排名结果做多前5的品种,做空后5的品种,形成一个对冲组合。
如下图,一周以后,共盈利639950元,收益率5.18%。
T9提供了引用自有数据的功能,可使用自有数据管理系统对机构自有数据进行维护和管理。自有数据可通过函数引用到T9的公式中,将自有数据与期货数据相结合,形成机构独特的量化交易策略,不仅支持绘制图表,还支持模型历史数据回测并提供测试报告。自有数据实时更新,可通过期货运行模组实现全自动交易。
套期保值是期货市场的主要功能之一,一个成熟的期货市场,套保者应该是市场的中坚力量。出现期货溢价或现货溢价是由于套保者多空不平衡导致,这种现象需要投机者的介入方能保持平衡。这就是期货市场通过将价格风险转移给投机者,而为套保者提供保险的原因,投机者理应获得一定的风险溢价。
我们可以引用现货价格到软件中,对比现货价格与期货价格的大小,当出现现货大幅溢价时买入期货合约,等待期货价格向现货价格回归以后平仓出场,赚取期货的展期收益。
我们使用“自有数据管理系统”导入沪铜现货数据,并在公式中使用GetCustomData函数将沪铜现货数据引用到策略中。
代码如下:
Vars
//------------------------信号变量------------------------//
NumericSeries GIP; //自有现货价格数据
//------------------------算法变量------------------------//
Numeric N; //下单手数
Numeric SH; //上海合约标志
……//省略部分源码
Begin
//------------------------信号发出------------------------//
If(1) //信号发出
{
GIP =
GetCustomData(3/*现货铜 (3)*/);
If(GIP > Close)
//如果满足多头开仓条件
{
BK;
//发出BK信号
}
If(GIP
<= Close) //如果满足多头平仓条件
{
SP;
//发出SP信号
}
If(GIP
< Close) //如果满足空头开仓条件
{
SK; //发出SK信号
}
If(GIP >= Close)
//如果满足空头平仓条件
{
BP; //发出BP信号
}
}
//------------------------处理开启------------------------//
If(A_IsExchangeOpen() == 1) //如果是开盘状态
{
If(BarStatus()
== 2) //如果不带入历史值
{
If(RunStart()
== 1) //如果已初始化成功
{
OPFLG
= 1; //开启模型处理
}
}
}
……//省略部分源码
End
模型回测参数:
合约:沪铜主连
初始资金:100万
k线周期:日k线
回测时间:20150101~20201014
交易手数:10手
模型回测报告:
基本面分析是指从商品的实际供给和需求对商品价格的影响这一角度来进行分析的方法。对基本面进行分析可以帮助交易者确定交易的大方向,形成交易的信心基础。基本面信息除了包括商品的生产量、消费量、进出口量、库存量等数据以外,突发的新闻事件也是一个重要的参考数据,突发事件往往会造成商品价格短期的剧烈波动,如果模型不能及时对这些信息做出反应,模型的盈利能力就会大打折扣了。
一些机构会收集市场中大量的突发新闻事件,并评估其对期货价格的影响,当出现较大影响的新闻时,需要及时对模型的信号进行调整。
我们使用“自有数据管理系统”导入突发新闻事件数据,当数据为0时,表示没有突发事件发生;当数据为1时,表示有利于多头的突发事件发生;当数据为2时,表示有利于空头的突发事件发生。
我们在原有策略的基础上增加突发事件判断条件,当出现突发事件时,及时调整信号方向,代码如下:
Params
Numeric ATRs(3); //几倍ATR止盈
Numeric ATRLength(10); //ATR周期
Vars
//------------------------信号变量------------------------//
NumericSeries GIP; //自有突发事件数据
Numeric ATRVal; //ATR(平均真实波幅)
NumericSeries WAvgPrice; //K线加权均值
NumericSeries Resistance; //阻力线
NumericSeries Support; //支撑线
NumericSeries MyExitPrice;
//开仓BAR根据当时的ATR计算出的止盈价
//------------------------算法变量------------------------//
Numeric N; //下单手数
Numeric N; //下单手数
……//省略部分源码
Begin
//------------------------信号发出------------------------//
If(1) //信号发出
{
GIP =
GetCustomData(1/*突发事件 (1)*/);
WAvgPrice =
(High + Low + (Close * 2)) / 4;
Resistance =
(WAvgPrice * 2) - Low;
Support =
(WAvgPrice * 2) - High;
PlotNumeric("Resistance",Resistance[1]);
PlotNumeric("Support",Support[1]);
ATRVal =
AvgTrueRange(ATRLength);
If(MarketPosition
== 0 && High >= Resistance[1] + MinMove * PriceScale && Vol > 0)
{
BK(DefaultVol,Max(Open,Resistance[1]
+ MinMove * PriceScale));
}
If(MarketPosition
== 0 && Low <= Support[1] - MinMove * PriceScale && Vol> 0)
{
SK(DefaultVol,Min(Open,Support[1]
- MinMove * PriceScale));
}
If(MarketPosition
== 1 && BarsSinceEntry == 0)
{
MyExitPrice = EntryPrice + ATRVal * ATRs;
}
If(MarketPosition
== -1 && BarsSinceEntry == 0)
{
MyExitPrice = EntryPrice - ATRVal * ATRs;
}
If(MarketPosition
== 1 && BarsSinceEntry > 0 && Vol > 0)
{
If(Low
<= Support[1] - MinMove * PriceScale)
{
MyExitPrice = EntryPrice - ATRVal * ATRs;
}
}
If(MarketPosition
== -1 && BarsSinceEntry > 0 && Vol > 0)
{
If(High
>= Resistance[1] + MinMove * PriceScale)
{
BP(DefaultVol,Max(Open,Resistance[1] + MinMove * PriceScale));
}
}
If(GIP==1)
//如果美元降息,满足反手做多条件
{
BPK; //发出BPK信号
}
If(GIP==2)
//如果美元加息,满足反手做空条件
{
SPK; //发出SPK信号
}
}
//------------------------处理开启------------------------//
If(A_IsExchangeOpen() == 1) //如果是开盘状态
{
If(BarStatus()
== 2) //如果不带入历史值
{
……//省略部分源码
End
对比前后两个策略发现,加入了突发事件判断以后,不仅策略的盈利能力大幅提高,在回撤的控制上,也优于原有策略。
主连链回测(点击查看运行机制)是一种严谨的利用长期数据验证策略的方法,不再使用品种主连自身的k线数据计算信号,而是采用各个月份合约自挂牌以来的全部k线数据计算信号,规避品种主连k线数据换月跳空、新旧合约趋势相反等情况对量化计算准确性的影响。主力换月时,旧主力月份平仓,新主力根据月份合约的k线数据重新计算开仓信号。
模型不要写入Trade_Other语句,把模型直接加载到品种主连上,系统会自动调用主连链回测。
回测后提供策略在主力合约上连续运行的效果分析报告和资金曲线,供投资者分析策略连续交易的整体效果,便于调试交易策略,降低试错成本。
我们发现在2019年12月6日螺纹2001换月到螺纹2005时,2001合约是空头开仓信号,2005合约是多头开仓信号,换月时,系统对2001合约进行了平仓操作,并重新计算了05合约的开仓信号,规避了2005合约行情上涨的损失。
主连链回测还提供从信号计算开始时间到数据结束时间范围内,所有主力合约的独立回测报告和资金曲线。可查看到策略在每一个主力合约上的盈利效果,对策略在不同测试区间内分段调试。
问题一:超长的K线数据有什么用?
以上期所的螺纹钢合约为例,螺纹钢2009年挂牌交易至今,已经有超过10年的历史数据。前8年的历史数据囊括了2009年的急涨急跌行情、2011年的震荡行情、2015年的V字形翻转行情,这些数据为模型的回测提供了充足的数据样本和各种极端行情变化。
我们可以使用前8年的数据进行模型的优化,然后使用最近两年的数据对模型进行最终的验证。这样即可以判断模型是否过度优化,也能检验模型是否能够适应当前的行情变化。
问题二:k线数据的准确性至关重要
这些看似不符合正常行情变化的“异常K线”是错误数据吗?经过文华多方面求证,确认这些数据都是准确无误的,是极端行情的真实反应。
类似的极端行情还有很多,2012年橡胶合约交易单位扩大至每手10吨,导致大量橡胶日内模型持续亏损;2013年上期所正式上线贵金属夜盘交易,导致大量贵金属策略失效;2015年股指期货大幅提高保证金,导致多家投资机构资金大幅回撤。。。
只有准确无误的历史数据才能确保策略回测的真实有效,才能全面检验各种极端行情对策略收益的影响,从而防止“黑天鹅”事件的发生。
问题三:文华为你提供的超长的、准确的k线数据
保证数据的准确性,仅依靠计算机自动处理是远远不够的,数据在网络传输、软件处理过程中是无法避免出错的,必须由有多年数据处理经验的专业人士来进行人工校验。
文华财经创立于1996年,是国内四家期货交易所官方授权行情转发商,具有20多年行情转发和数据处理经验。
为了保证数据准确,我们配备了一支专业的数据处理团队,从交易所的数据源到文华数据中心,再到文华客户端,在各个环节进精准的校对和修正。
使用足够多的历史数据进行测试,才能反映出一个模型的真实情况,少量的历史数据往往带有欺骗性,容易使交易者对策略进行错误的评估。
模型:金肯特纳策略
测试合约周期:铁矿15分钟
模型代码:
Params
Numeric AvgLength(40); //三价均线参数
Numeric AtrLength(40); //真实波幅参数
Numeric Lots(DefaultVol); //交易手数
Vars
NumericSeries MovAvgVal(0); //三价均线
NumericSeries UpBand(0); //通道上轨
NumericSeries DnBand(0); //通道下轨
NumericSeries LiquidPoint(0); //出场条件
Begin
// 三价均线
MovAvgVal =Ma((High + Low + Close) / 3, AvgLength);
// 通道上轨
UpBand = MovAvgVal + AvgTrueRange(AtrLength);
//通道下轨
DnBand = MovAvgVal - AvgTrueRange(AtrLength);
// 出场条件
LiquidPoint = MovAvgVal;
// 画线
PlotNumeric("movAvgVal", MovAvgVal);
PlotNumeric("upBand", UpBand);
PlotNumeric("DnBand", DnBand);
// 三价均线向上,并且价格上破通道上轨,开多单
If ( MarketPosition != 1 And MovAvgVal[1] > MovAvgVal[2] And High >=
UpBand[1] ) BK(Lots, Max ( Open, UpBand[1] ) );
// 三价均线向下,并且价格下破通道下轨,开空单
If ( MarketPosition != -1 And MovAvgVal[1] < MovAvgVal[2] And Low
<= DnBand[1] ) SK ( Lots, Min ( Open, DnBand[1] ) );
// 持有多单时,价格下破三价均线,平多单
If ( MarketPosition == 1 And BarsSinceEntry >= 1 And Low <=
LiquidPoint[1] ) SP ( DefaultVol, Min ( Open, LiquidPoint[1] ) );
// 持有空单时,价格上破三价均线,平空单
If ( MarketPosition == -1 And BarsSinceEntry >= 1 And High >=
LiquidPoint[1] ) BP ( DefaultVol, Max ( Open, LiquidPoint[1] ) );
End
测试1:
从16年1月1日开始测试至今,初始资金1万元,固定手数为1手
从回测报告来看,这是一个年化收益率达180%以上,最大回撤控制在40%以内,看起来还不错的策略
测试2:
将测试时间修改为从上市日起至今,则发现同样的策略竟然在15年就已经严重亏损,早就爆仓出局了。
对比分析:
回顾一下铁矿的历史数据不难发现,在14年底,铁矿有长达三个月的震荡行情,使得价格在三价均线上下反复徘徊,产生大量的连续亏损信号。事实上,在这种相对极端的行情下,很多趋势类策略都很难产生较好的收益,而我们的金肯特纳策略也恰恰无法适配从而导致爆仓。
由此可见,一个看似还不错的策略,如果没有长时间历史数据测试的支撑,在极端行情下可能会面临着很大的风险。为了避免这种情况的发生,拉长历史数据、进行样本外回测,不失为一种规避风险的方法,而这一切的基础,便是要拥有足够长时间的历史数据。
盘中难免会遇到电脑、网络及其他交易环境故障引起的模组运行中断,信号没有有效执行的情况,这时通常是需要对模组单元持仓手动进行调整。对于拥有大量运行单元的机构用户,手动调整时就会比较麻烦。
T9软件提供的持仓匹配校验功能,可一键校验各个合约的理论持仓和账户实际持仓是否匹配。
如下图,点击持仓匹配校验按钮可一键查询模组中合约理论持仓、单元持仓与实际账户持仓的匹配情况,便于及时手动处理。
手动处理的方法如下:单元持仓小于理论持仓且实际账户中有持仓时,可在模组里点击“导入现有持仓”按钮,将实际账户中已有的持仓带入到模组中,完成持仓匹配。
模组会根据输入的价格和持仓数量计算权益,后续对导入的持仓按策略信号执行平仓操作。
量化交易离不开资金管理。模组支持分区管理,每个分区包含多个运行单元,模组可为每个单元分配独立的资金,各单元自负盈亏,独立计算可用资金及权益变化。
模组根据模型的实盘交易数据,绘制出每个模组分区的收益曲线和权益曲线。当一只资管产品的各个交易账号加载到同一个分区交易时,分区综合收益/权益曲线,就是该产品的收益/权益曲线。也支持出入金管理,可展现产品历史出入金记录及资金变动情况,方便实时关注产品的真实盈亏和权益变化。
模组还提供每个模组单元的权益、收益率、回撤比数据,可以在模型的运行过程中,了解每一个模型的资金分配情况和权益变化,及时发现运行效果不理想或已经失效的模型,灵活调整投资组合,提高组合收益,降低投资风险;各个运行单元独立的头寸计算,也是后期投入资金不断增加,制定资金再分配计划的保障。