问题一:超长的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年底,铁矿有长达三个月的震荡行情,使得价格在三价均线上下反复徘徊,产生大量的连续亏损信号。事实上,在这种相对极端的行情下,很多趋势类策略都很难产生较好的收益,而我们的金肯特纳策略也恰恰无法适配从而导致爆仓。
由此可见,一个看似还不错的策略,如果没有长时间历史数据测试的支撑,在极端行情下可能会面临着很大的风险。为了避免这种情况的发生,拉长历史数据、进行样本外回测,不失为一种规避风险的方法,而这一切的基础,便是要拥有足够长时间的历史数据。
散户眼中的TICK数据:成交价
机构眼中的TICK数据:成交价+委托价
文华提供的TICK数据:成交价+五档委托价+大单统计数据
相比交易接口提供的一档数据,文华的五档数据能够为算法交易提供更大的发挥空间。
五档的挂单价格预示了市场后续的价格变化,可帮助算法交易先人一步确定更优的委托价格。
利用五档的挂单量数据,可实时计算多空双方的资金规模,为算法交易灵活调整建仓手数、确保成交提供了保障。
开平仓及主动买卖的大单统计数据,可辅助算法交易把握主力资金动向,防范逆势风险,实现对整个交易过程的风险控制。
独立的算法交易模型可以利用盘口的挂单数据进行计算和判断,支持复杂且高频率的交易策略,但如果编写的高频策略未经测试直接用于实盘,也同样会使得交易风险成倍增长。
wh9可针对算法策略进行逐笔Tick精确回测,直观展示每一笔的挂单状态和行情变化,为投资者提供精准的策略检验工具。
我们可以尝试如下策略:
当前TICK的买一量大于前面三笔TICK买一量的最大值,并且价格上涨,则买入开仓。
当前TICK的卖一量大于前面三笔TICK卖一量的最大值,并且价格下跌,则卖出开仓。
开仓后60秒平仓,或5个最小变动价位止损。
源码如下:
Data
data0:"rb1910";
Vars
//--------------------------定义普通变--------------------------
Numeric Lots; //委托数量
Numeric OverPrice_M;//超价参数
Numeric MinPr;//最小变动价位
//--------------------------定义可用持仓-------------------------
Numeric Buy_J;//多头可用今仓
Numeric Buy_L;//多头可用老仓
Numeric Sell_J;//空头可用今仓
Numeric Sell_L;//空头可用老仓
//--------------------------定义全局变量-------------------------
Global_Numeric Bid_Max;//最大买一价
Global_Numeric BidVol_Max; //最大买一量
Global_Numeric Ask_Max;//最大卖一价
Global_Numeric AskVol_Max;//最大卖一量
//--------------------------定义全局变量-------------------------
Global_Numeric PriceBid;//记录多头开仓价位
Global_Numeric PriceBidTime;//记录多头开仓时间
Global_Numeric PriceAsk;//记录空头开仓价位
Global_Numeric PriceAskTime;//记录空头开仓时间
//--------------------------定义全局变量-------------------------
Global_Numeric Coin;
Global_Numeric KF;//开平仓控制
//--------------------------定义数据区--------------------------
Var_TickData data1;
Var_TickData data2;//定义数据区
Begin
Lots = 1;//委托数量
OverPrice_M = 1;//超价参数
MinPr = 1;//最小变动价位
data1 = Def_TickData("rb1910",1,4); //数据容量
data2=Def_TickData("rb1910",1,1);
If(data1.State == 1)
{
BID_Max = Max1( data1[0].Bid1, data1[1].Bid1, data1[2].Bid1);//之前3笔TICK最大买一价(不含当前TICK)
BidVol_Max = Max1(data1[0].BidVol1,data1[1].BidVol1,data1[2].BidVol1);//之前3笔TICK最大买一量(不含当前TICK)
Ask_Max = Max1( data1[0].Ask1, data1[1].Ask1, data1[2].Ask1);//之前3笔TICK最大卖一价(不含当前TICK)
AskVol_Max = Max1(data1[0].AskVol1,data1[1].AskVol1,data1[2].AskVol1);//之前3笔TICK最大卖一价量(不含当前TICK)
Coin = 1;
}
If (Coin == 1)
{
Buy_J = A_TodayBuyRemainPosition(); //获得多头可用持仓量,今仓
Buy_L = F_BuyRemainPosition - A_TodayBuyRemainPosition(); //获得多头可用持仓量,老仓
Sell_J = A_TodaySellRemainPosition(); //获得空头可用持仓量,今仓
Sell_L = F_SellRemainPosition - A_TodaySellRemainPosition(); //获得空头可用持仓量,老仓
If (((Buy_J > 0) || (Buy_L > 0)) && KF == 1)//平多头持仓条件
{
If ((PriceBid - data2[0].Bid1 > 5 * MinPr) || (TimeDiff(PriceBidTime,CurrentTime()) > 60))//5个价位止损或开仓超过60秒
{
If (Buy_J > 0)
{
A_SendOrder(Enum_Sell,Enum_ExitToday,Lots,data2[0].Bid1);
//卖平今仓
}
Else If (Buy_L > 0)
{
A_SendOrder(Enum_Sell,Enum_Exit,Lots,data2[0].Bid1);
//卖平老仓
}
KF=0;
}
}
Else If (((Sell_J > 0) || (Sell_L > 0)) && KF == 1)//平空头持仓条件
{
If ((PriceAsk - data2[0].Ask1> 5 * MinPr) || (TimeDiff(PriceAskTime,CurrentTime()) > 60))//5个价位止损或开仓超过60秒
{
If (Sell_J > 0)
{
A_SendOrder(Enum_Buy,Enum_ExitToday,Lots,data2[0].Ask1);//买平今仓
}
Else If(Sell_L > 0)
{
A_SendOrder(Enum_Buy,Enum_Exit,Lots,data2[0].Ask1);//买平老仓
}
KF=0;
}
}
Else
{
If (KF == 0)
{
If ((data2[0].BidVol1 > BidVol_Max) && (data2[0].Bid1 > Bid_Max))//当前Tick的买1量大于前三笔最大买1量,且价格上涨,买开
{
A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2[0].Ask1 + MinPr * OverPrice_M);
//发出委托,以对手价超M个最小变动价位买开仓
PriceBid = data2[0].Ask1 + Minpr * OverPrice_M;
PriceBidTime = CurrentTime();
KF = 1;
}
If ((data2[0].AskVol1 > AskVol_Max) && (data2[0].Ask1 < Ask_Max))//当前Tick的卖1量大于前三笔最大卖1量,且价格下跌,卖开
{
A_SendOrder(Enum_Sell,Enum_Entry,Lots,data2[0].Bid1 - MinPr * OverPrice_M);
//发出委托,以对手价超M个最小变动价位卖开仓
PriceAsk = data2[0].Bid1 - Minpr * OverPrice_M;
PriceAskTime = CurrentTime();
KF = 1;
}
}
}
}
End
我们选择螺纹1910合约进行算法交易模型回测,回测日志如下:
随着资金规模的增大,传统的手动委托方式,委托速度慢、误操作多、成交滑点大、止损不及时的劣势会越来越明显。
算法交易通过对期货账号交易信息的获取,实现了替代手动控制交易过程的功能。通过对持仓信息、挂单信息,成交回报,委托回报等信息的监控,实现了对交易过程的精细化控制,再配合算法交易委托速度快,对市场变化反应迅速等特点,可进一步减少成交滑点,降低交易风险。
算法交易也支持同时对多个交易账号进行委托控制,解决了手动交易无法同时管理多个账号批量委托的问题。
对于大资金客户,是否有下单单量大,唯恐惊扰行情趋势的困扰?是否有欲拉动行情却缕缕付出了大量成本的困扰?这里我们以此为例通过手动下单调用算法交易的方式介绍两个案例来解决你的困扰。
策略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
随着期货市场量化策略的增加,量化交易的资金规模不断增大,越来越多的交易者发现成交滑点一直处于上升趋势。与其为了提高几毫秒的委托速度花费大量成本到硬件设施上,不如将研究重点转移到算法交易上。
算法交易可以为策略模型解决哪些问题呢?
问题一:收盘价模型入场太晚,指令价模型恢复持仓成本太高怎么办?
算法交易可接管策略模型信号。我们可以完全抛开系统默认的信号处理机制,重新制定一套适合自己策略的信号处理方式。
以趋势突破模型为例,趋势突破模型的信号往往在支撑位上反复出现和消失,但是一旦长时间突破以后,则很少会再次消失,我们可以抓住这种策略的特点,通过算法交易计算每根k线上信号持续的时间,当信号持续时间足够长的时候,再进行下单操作,当k线走完时如果信号消失,则进行恢复持仓处理。这样我们就可以通过调整信号确认的时间,来寻找一个平衡点,尽可能更早入场的同时,降低恢复持仓的成本,从而提高模型整体收益。
问题二:成交滑点大怎么办?
算法交易可接管策略模型控制交易过程。我们可以根据模型的开平仓方向和品种的波动情况,灵活控制交易过程。
例如:我们可以结合模型开平仓的特点,制定一套更优的追价方式。当趋势模型信号发出且行情波动平稳时,我们采用追对价的方式委托,降低成交滑点。同时设定最大偏离范围,当突遇极端行情,价格超过最大偏离范围时,可对开平仓信号分别进行处理。对开仓挂单可进行撤单处理,防止出现较大滑点,对平仓挂单可进行追市价处理,确保迅速离场。
指令价模型虽然可以定位到更好的入场点,但也会出现信号消失的问题。太晚入场,会失去好的入场时机,造成策略整体收益下降;过早入场,可能会频繁出现信号消失,导致恢复持仓成本的增加。我们需要找到两者之间的平衡点,使模型获得相对较高的收益。
我们尝试使用出现信号立刻下单,信号消失且连续T秒没有再次出现则恢复持仓的策略:
Vars
Numeric N; //下单手数
Numeric T; //时间间隔
Numeric SH; //平仓参数
Numeric MA3; //3周期均线
Numeric MA5; //5周期均线
Numeric MA10; //10周期均线
Numeric KCP; //K线位置
Numeric SIGID; //指令序号
Numeric NOW; //当前时间
Numeric BIDP; //买一价
Numeric ASKP; //卖一价
Numeric BRP; //多头可用持仓
Numeric SRP; //空头可用持仓
Numeric OPFLG; //模型处理标志
Numeric BKDFLG; //买开处理标志
Numeric SKDFLG; //卖开处理标志
Numeric BPDFLG; //买平处理标志
Numeric SPDFLG; //卖平处理标志
Global_Numeric SIGIDA; //记录指令序号
Global_Numeric KCPA; //记录K线位置
Global_Numeric KCPB; //记录K线位置
Global_Numeric SIGF; //信号类型标志
Global_Numeric SIGCF; //当前信号类型
Global_Numeric SIGCN; //信号出现次数
Global_Numeric SIGXN; //信号消失次数
Global_Numeric SIGXT; //信号消失时间
Global_Numeric SIGFLG; //信号处理标志
Global_Numeric BKID; //买开委托
Global_Numeric SKID; //卖开委托
Global_Numeric BPID; //买平委托
Global_Numeric SPID; //卖平委托
Global_Numeric BKFLG; //买开标志
Global_Numeric SKFLG; //卖开标志
Global_Numeric BPFLG; //买平标志
Global_Numeric SPFLG; //卖平标志
Global_Numeric BKM; //买开委托手数
Global_Numeric SKM; //卖开委托手数
Global_Numeric BPM; //买平委托手数
Global_Numeric SPM; //卖平委托手数
Setting
SignalNoTrading:1; //出信号不下单
Begin
//------------------------处理开启------------------------//
If(A_IsExchangeOpen() == 1) //如果是开盘状态
{
If(BarStatus() == 2) //如果不带入历史值
{
If(RunStart() == 1) //如果已初始化成功
{
OPFLG = 1; //开启模型处理
}
}
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
N = 2; //下单手数
T = 10; //时间间隔
KCP = CurrentBar; //K线位置
NOW = CurrentTime(); //当前时间
BIDP = Price("Bid1"); //买一价
ASKP = Price("Ask1"); //卖一价
BRP = F_BuyRemainPosition(); //多头可用持仓
SRP = F_SellRemainPosition(); //空头可用持仓
If(A_IsSHCode() == 1) //如果是上期所合约
{
SH = Enum_ExitToday; //平仓参数
}
Else //如果非上期所合约
{
SH = Enum_Exit; //平仓参数
}
}
//------------------------信号发出------------------------//
If(1) //信号发出
{
MA3 = Ma(Close,3); //3周期均线
MA5 = Ma(Close,5); //5周期均线
MA10 = Ma(Close,10); //10周期均线
PlotLine("Ma3",Ma3); //3周期均线
PlotLine("Ma5",Ma5); //5周期均线
PlotLine("Ma10",Ma10); //10周期均线
If(Cross(Ma5,Ma10) == 1 && MarketPosition == 0) //如果满足做多条件
{
BK; //发出做多信号
}
Else If(CrossDown(Ma3,Ma10) == 1 && MarketPosition == 1) //如果满足平多条件
{
SP; //发出平多信号
}
Else If(CrossDown(Ma5,Ma10) == 1 && MarketPosition == 0) //如果满足做空条件
{
SK; //发出做空信号
}
Else If(Cross(Ma3,Ma10) == 1 && MarketPosition == -1) //如果满足平空条件
{
BP; //发出平空信号
}
}
//------------------------信号处理------------------------//
If(OPFLG == 1) //信号处理
{
If(KCP != KCPB) //如果是新K线
{
If(SIGCN > 0) //如果上根K线出现信号
{
Commentary("【上根K线信号出现次数:" + Text(SIGCN) + "次!】");
Commentary("【上根K线信号消失次数:" + Text(SIGXN) + "次!】");
If(SIGFLG == 2) //如果信号已消失
{
Commentary("【距离信号消失时间:" + Text(TimeDiff(SIGXT,NOW)) + "秒!】");
}
}
SIGCN = 0; //信号出现次数
SIGXN = 0; //信号消失次数
KCPB = KCP; //记录K线位置
}
SIGID = F_CurrentSigIndex; //指令序号
If(SIGID != 0) //如果当笔行情发出指令
{
If(SIGID != SIGIDA || KCP != KCPA) //如果当前信号是未处理过的新信号
{
SIGIDA = SIGID; //记录指令序号
KCPA = KCP; //记录K线位置
If(F_CurrentSig == Sig_BK) //如果信号类型为BK
{
Commentary("【BK信号!】");
BKDFLG = 1; //开启BK信号买开处理
SIGF = 1; //BK信号已发出
}
Else If(F_CurrentSig == Sig_SP) //如果信号类型为SP
{
Commentary("【SP信号!】");
SPDFLG = 1; //开启SP信号卖平处理
SIGF = 2; //SP信号已发出
}
Else If(F_CurrentSig == Sig_SK) //如果信号类型为SK
{
Commentary("【SK信号!】");
SKDFLG = 1; //开启SK信号卖开处理
SIGF = 3; //SK信号已发出
}
Else If(F_CurrentSig == Sig_BP) //如果信号类型为BP
{
Commentary("【BP信号!】");
BPDFLG = 1; //开启BP信号买平处理
SIGF = 4; //BP信号已发出
}
SIGCN = SIGCN + 1; //信号出现次数
}
}
If(KCP == KCPA) //如果是当根K线
{
If(SIGCF != SIGF) //如果是新的信号类型
{
SIGFLG = 1; //信号已发出
SIGCF = SIGF; //当前信号类型
}
If(SIGFLG == 1) //如果信号已发出
{
If(F_CurrentSig == 0) //如果信号消失
{
If(SIGCF == 1) //如果当前是BK信号
{
Commentary("【BK信号消失!】");
}
Else If(SIGCF == 2) //如果当前是SP信号
{
Commentary("【SP信号消失!】");
}
Else If(SIGCF == 3) //如果当前是SK信号
{
Commentary("【SK信号消失!】");
}
Else If(SIGCF == 4) //如果当前是BP信号
{
Commentary("【BP信号消失!】");
}
SIGIDA = 0; //记录指令序号归0
SIGXT = NOW; //信号消失时间
SIGXN = SIGXN + 1; //信号消失次数
SIGFLG = 2; //信号已消失
}
}
}
If(SIGFLG == 2) //如果信号已消失
{
If(TimeDiff(SIGXT,NOW) <= T) //如果时间间隔T秒内
{
If(F_CurrentSig > 0) //如果信号出现
{
If(SIGCF == 1) //如果当前是BK信号
{
Commentary("【BK信号重现!】");
}
Else If(SIGCF == 2) //如果当前是SP信号
{
Commentary("【SP信号重现!】");
}
Else If(SIGCF == 3) //如果当前是SK信号
{
Commentary("【SK信号重现!】");
}
Else If(SIGCF == 4) //如果当前是BP信号
{
Commentary("【BP信号重现!】");
}
Commentary("【距离信号消失时间:" + Text(TimeDiff(SIGXT,NOW)) + "秒!】");
SIGFLG = 1; //信号已发出
}
}
Else If(TimeDiff(SIGXT,NOW) > T) //如果时间间隔T秒
{
If(SIGCF == 1) //如果当前是BK信号
{
Commentary("【BK信号取消!】");
SPDFLG = 2; //开启BK信号卖平处理
}
Else If(SIGCF == 2) //如果当前是SP信号
{
Commentary("【SP信号取消!】");
BKDFLG = 2; //开启SP信号买开处理
}
Else If(SIGCF == 3) //如果当前是SK信号
{
Commentary("【SK信号取消!】");
BPDFLG = 2; //开启SK信号买平处理
}
Else If(SIGCF == 4) //如果当前是BP信号
{
Commentary("【BP信号取消!】");
SKDFLG = 2; //开启BP信号卖开处理
}
Commentary("【距离信号消失时间:" + Text(TimeDiff(SIGXT,NOW)) + "秒!】");
SIGFLG = 0; //信号已取消
}
}
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
If(BKFLG > 0) //如果有买开委托
{
If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
{
If(BKFLG == 1) //如果是BK信号买开委托
{
Commentary("【BK信号:买开委托成交!】");
}
Else If(BKFLG == 2) //如果是SP信号买开委托
{
Commentary("【SP信号:买开委托成交!】");
}
BKFLG = 0; //买开标志归0
}
}
If(SPFLG > 0) //如果有卖平委托
{
If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
{
If(SPFLG == 1) //如果是SP信号卖平委托
{
Commentary("【SP信号:卖平委托成交!】");
}
Else If(SPFLG == 2) //如果是BK信号卖平委托
{
Commentary("【BK信号:卖平委托成交!】");
}
SPFLG = 0; //卖平标志归0
}
}
If(SKFLG > 0) //如果有卖开委托
{
If(F_OrderStatus(SKID) == Enum_Filled) //如果卖开委托成交
{
If(SKFLG == 1) //如果是SK信号卖开委托
{
Commentary("【SK信号:卖开委托成交!】");
}
Else If(SKFLG == 2) //如果是BP信号卖开委托
{
Commentary("【BP信号:卖开委托成交!】");
}
SKFLG = 0; //卖开标志归0
}
}
If(BPFLG > 0) //如果有买平委托
{
If(F_OrderStatus(BPID) == Enum_Filled) //如果买平委托成交
{
If(BPFLG == 1) //如果是BP信号买平委托
{
Commentary("【BP信号:买平委托成交!】");
}
Else If(BPFLG == 2) //如果是SK信号买平委托
{
Commentary("【SK信号:买平委托成交!】");
}
BPFLG = 0; //买平标志归0
}
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(BKDFLG > 0) //如果已开启买开处理
{
If(BKFLG == 0) //如果没有买开委托
{
BKM = N - BRP; //买开委托手数
If(BKM > 0) //如果买开委托手数大于0
{
If(BKDFLG == 1) //如果是BK信号买开
{
Commentary("【BK信号:买开委托发出!】");
}
Else If(BKDFLG == 2) //如果是SP信号买开
{
Commentary("【SP信号:买开委托发出!】");
}
BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,ASKP); //发出买开委托
BKFLG = BKDFLG; //已发出买开委托
}
}
}
If(SPDFLG > 0) //如果已开启卖平处理
{
If(SPFLG == 0) //如果没有卖平委托
{
If(BRP > 0) //如果有多头可用持仓
{
SPM = BRP; //卖平委托手数
If(SPDFLG == 1) //如果是SP信号卖平
{
Commentary("【SP信号:卖平委托发出!】");
}
Else If(SPDFLG == 2) //如果是BK信号卖平
{
Commentary("【BK信号:卖平委托发出!】");
}
SPID = A_SendOrder(Enum_Sell,SH,SPM,BIDP); //发出卖平委托
SPFLG = SPDFLG; //已发出卖平委托
}
}
}
If(SKDFLG > 0) //如果已开启卖开处理
{
If(SKFLG == 0) //如果没有卖开委托
{
SKM = N - SRP; //卖开委托手数
If(SKM > 0) //如果卖开委托手数大于0
{
If(SKDFLG == 1) //如果是SK信号卖开
{
Commentary("【SK信号:卖开委托发出!】");
}
Else If(SKDFLG == 2) //如果是BP信号卖开
{
Commentary("【BP信号:卖开委托发出!】");
}
SKID = A_SendOrder(Enum_Sell,Enum_Entry,SKM,BIDP); //发出卖开委托
SKFLG = SKDFLG; //已发出卖开委托
}
}
}
If(BPDFLG > 0) //如果已开启买平处理
{
If(BPFLG == 0) //如果没有买平委托
{
If(SRP > 0) //如果有空头可用持仓
{
BPM = SRP; //买平委托手数
If(BPDFLG == 1) //如果是BP信号买平
{
Commentary("【BP信号:买平委托发出!】");
}
Else If(BPDFLG == 2) //如果是SK信号买平
{
Commentary("【SK信号:买平委托发出!】");
}
BPID = A_SendOrder(Enum_Buy,SH,BPM,ASKP); //发出买平委托
BPFLG = BPDFLG; //已发出买平委托
}
}
}
}
End
该策略可以记录上一根k线信号出现和消失的次数及信号消失的时间。据此可以帮助交易者了解该策略信号消失的频率,并通过调整信号的确认时间,来减小恢复持仓的成本。
策略运行:
随着量化交易资金规模逐渐增大,在交易过程中,很有可能出现一次下单手数过多导致无法成交或直接将价格打穿的情况。
对量化交易策略附加智能分批算法,可在下单时由系统自动拆分成合适的小单进行分批委托,避免影响趋势。同时可配合滑点控制算法,当价差过大时自动放弃后续委托,起到控制摩擦成本的目的。
例如:
当出现买入信号时,我们可以使用对手盘的挂单量计算每批手数,每次委托手数为对手盘挂单量的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
模型的成交滑点已经越来越受到交易者的重视,在实际交易过程中,我们往往会用到追价下单,而单一的追价模式,遇到极端行情会无限制的撤单重发,导致成交滑点不断增大,那么我们是否可以尝试给追价设定一个范围,在保证正常交易的前提下,尽可能的减少滑点呢?
下面这个策略,我们采用了对价委托连续追价,并限定了当价格偏离初始价格过大时不再追价的方式。且在停止追价后,对后续交易进行细化处理:如果是开仓委托,则撤单终止交易;如果是平仓委托,则最后一次委托采用市价,保证尽快离场。
代码如下:
Vars
Numeric N; //下单手数
Numeric T; //时间间隔
Numeric W; //委托价位倍数
Numeric PLM; //偏离价位倍数
Numeric SH; //平仓参数
Numeric MA3; //3周期均线
Numeric MA5; //5周期均线
Numeric MA10; //10周期均线
Numeric KCP; //K线位置
Numeric SIGID; //指令序号
Numeric NOW; //当前时间
Numeric NEWP; //最新价
Numeric BIDP; //买一价
Numeric ASKP; //卖一价
Numeric RLP; //涨停价
Numeric FLP; //跌停价
Numeric MINP; //最小变动价位
Numeric BRP; //多头可用持仓
Numeric SRP; //空头可用持仓
Numeric OPFLG; //模型处理标志
Numeric BKDFLG; //买开处理标志
Numeric SKDFLG; //卖开处理标志
Numeric BPDFLG; //买平处理标志
Numeric SPDFLG; //卖平处理标志
Global_Numeric SIGIDA; //记录指令序号
Global_Numeric KCPA; //记录K线位置
Global_Numeric BKID; //买开委托
Global_Numeric SKID; //卖开委托
Global_Numeric BPID; //买平委托
Global_Numeric SPID; //卖平委托
Global_Numeric BKFLG; //买开标志
Global_Numeric SKFLG; //卖开标志
Global_Numeric BPFLG; //买平标志
Global_Numeric SPFLG; //卖平标志
Global_Numeric BKDEL; //买开撤单标志
Global_Numeric SKDEL; //卖开撤单标志
Global_Numeric BPDEL; //买平撤单标志
Global_Numeric SPDEL; //卖平撤单标志
Global_Numeric BKCP; //买开初始价格
Global_Numeric SKCP; //卖开初始价格
Global_Numeric BPCP; //买平初始价格
Global_Numeric SPCP; //卖平初始价格
Global_Numeric BKM; //买开委托手数
Global_Numeric SKM; //卖开委托手数
Global_Numeric BPM; //买平委托手数
Global_Numeric SPM; //卖平委托手数
Global_Numeric BKP; //买开委托价格
Global_Numeric SKP; //卖开委托价格
Global_Numeric BPP; //买平委托价格
Global_Numeric SPP; //卖平委托价格
Global_Numeric BKT; //买开委托时间
Global_Numeric SKT; //卖开委托时间
Global_Numeric BPT; //买平委托时间
Global_Numeric SPT; //卖平委托时间
Setting
SignalNoTrading:1; //出信号不下单
Begin
//------------------------处理开启------------------------//
If(A_IsExchangeOpen() == 1) //如果是开盘状态
{
If(BarStatus() == 2) //如果不带入历史值
{
If(RunStart() == 1) //如果已初始化成功
{
OPFLG = 1; //开启模型处理
}
}
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
N = 2; //下单手数
T = 5; //时间间隔
PLM = 3; //偏离价位倍数
KCP = CurrentBar; //K线位置
NOW = CurrentTime(); //当前时间
NEWP = Price("New"); //最新价
BIDP = Price("Bid1"); //买一价
ASKP = Price("Ask1"); //卖一价
RLP = Price("RiseLimit"); //涨停价
FLP = Price("FallLimit"); //跌停价
MINP = Price("MinPrice"); //最小变动价位
BRP = F_BuyRemainPosition(); //多头可用持仓
SRP = F_SellRemainPosition(); //空头可用持仓
If(A_IsSHCode() == 1) //如果是上期所合约
{
SH = Enum_ExitToday; //平仓参数
}
Else //如果非上期所合约
{
SH = Enum_Exit; //平仓参数
}
}
//------------------------信号发出------------------------//
If(1) //信号发出
{
MA3 = Ma(Close,3); //3周期均线
MA5 = Ma(Close,5); //5周期均线
MA10 = Ma(Close,10); //10周期均线
PlotLine("Ma3",Ma3); //3周期均线
PlotLine("Ma5",Ma5); //5周期均线
PlotLine("Ma10",Ma10); //10周期均线
If(Ref(Cross(Ma5,Ma10),1) == 1 && MarketPosition == 0) //如果满足做多条件
{
BK; //发出做多信号
}
Else If(Ref(CrossDown(Ma3,Ma10),1) == 1 && MarketPosition == 1) //如果满足平多条件
{
SP; //发出平多信号
}
Else If(Ref(CrossDown(Ma5,Ma10),1) == 1 && MarketPosition == 0) //如果满足做空条件
{
SK; //发出做空信号
}
Else If(Ref(Cross(Ma3,Ma10),1) == 1 && MarketPosition == -1) //如果满足平空条件
{
BP; //发出平空信号
}
}
//------------------------信号处理------------------------//
If(OPFLG == 1) //信号处理
{
SIGID = F_CurrentSigIndex; //指令序号
If(SIGID != 0) //如果当笔行情发出指令
{
If(SIGID != SIGIDA || KCP != KCPA) //如果当前信号是未处理过的新信号
{
SIGIDA = SIGID; //记录指令序号
KCPA = KCP; //记录K线位置
If(F_CurrentSig == Sig_BK) //如果信号类型为BK
{
Commentary("【BK信号!】");
BKDFLG = 1; //开启买开处理
}
Else If(F_CurrentSig == Sig_SP) //如果信号类型为SP
{
Commentary("【SP信号!】");
SPDFLG = 1; //开启卖平处理
}
Else If(F_CurrentSig == Sig_SK) //如果信号类型为SK
{
Commentary("【SK信号!】");
SKDFLG = 1; //开启卖开处理
}
Else If(F_CurrentSig == Sig_BP) //如果信号类型为BP
{
Commentary("【BP信号!】");
BPDFLG = 1; //开启买平处理
}
}
}
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
If(BKFLG == 1) //如果有买开委托
{
If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
{
Commentary("【BK信号:买开委托成交!】");
BKFLG = 0; //买开标志归0
BKDEL = 0; //买开撤单标志归0
}
Else If(F_OrderStatus(BKID) == Enum_Canceled) //如果买开委托已撤
{
Commentary("【BK信号:买开委托已撤!】");
If(BKDEL == 1) //如果是价格偏离过大撤单
{
BKFLG = 0; //买开标志归0
}
Else If(BKDEL == 2) //如果是时间间隔T秒追价
{
If(F_OrderFilledLot(BKID) > 0) //如果买开委托部分成交
{
BKM = BKM - F_OrderFilledLot(BKID); //买开委托手数
}
If(BKM > 0) //如果买开委托手数大于0
{
BKP = ASKP; //买开委托价格
Commentary("【BK信号:买开委托追价!】");
BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP); //发出买开委托
BKT = NOW; //买开委托时间
}
}
BKDEL = 0; //买开撤单标志归0
}
Else If(F_OrderStatus(BKID) == Enum_Declared || F_OrderStatus(BKID) == Enum_FillPart) //如果买开委托挂单
{
If(BKDEL == 0) //如果未撤单
{
If(Abs(NEWP - BKCP) > PLM * MINP) //如果价格偏离过大
{
Commentary("【BK信号:价格偏离过大!】");
Commentary("【BK信号:买开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
BKDEL = 1; //已发出撤掉买开委托挂单
}
Else If(TimeDiff(BKT,NOW) >= T) //如果时间间隔T秒
{
Commentary("【BK信号:买开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
BKDEL = 2; //已发出撤掉买开委托挂单
}
}
}
}
If(SPFLG == 1) //如果有卖平委托
{
If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
{
Commentary("【SP信号:卖平委托成交!】");
SPFLG = 0; //卖平标志归0
SPDEL = 0; //卖平撤单标志归0
}
Else If(F_OrderStatus(SPID) == Enum_Canceled) //如果卖平委托已撤
{
Commentary("【SP信号:卖平委托已撤!】");
If(F_OrderFilledLot(SPID) > 0) //如果卖平委托部分成交
{
SPM = SPM - F_OrderFilledLot(SPID); //卖平委托手数
}
If(BRP > 0 && SPM > 0 && SPM <= BRP) //如果卖平委托手数不超过多头可用持仓
{
If(SPDEL == 1) //如果是价格偏离过大追价
{
SPP = FLP; //卖平委托价格
}
Else If(SPDEL == 2) //如果是时间间隔T秒追价
{
SPP = BIDP; //卖平委托价格
SPT = NOW; //卖平委托时间
SPDEL = 0; //卖平撤单标志归0
}
Commentary("【SP信号:卖平委托追价!】");
SPID = A_SendOrder(Enum_Sell,SH,SPM,SPP); //发出卖平委托
}
}
Else If(F_OrderStatus(SPID) == Enum_Declared || F_OrderStatus(SPID) == Enum_FillPart) //如果卖平委托挂单
{
If(SPDEL == 0) //如果未撤单
{
If(Abs(NEWP - SPCP) > PLM * MINP) //如果价格偏离过大
{
Commentary("【SP信号:价格偏离过大!】");
Commentary("【SP信号:卖平委托撤单!】");
F_DeleteOrder(F_OrderContractNo(SPID)); //撤掉卖平委托挂单
SPDEL = 1; //已发出撤掉卖平委托挂单
}
Else If(TimeDiff(SPT,NOW) >= T) //如果时间间隔T秒
{
Commentary("【SP信号:卖平委托撤单!】");
F_DeleteOrder(F_OrderContractNo(SPID)); //撤掉卖平委托挂单
SPDEL = 2; //已发出撤掉卖平委托挂单
}
}
}
}
If(SKFLG == 1) //如果有卖开委托
{
If(F_OrderStatus(SKID) == Enum_Filled) //如果卖开委托成交
{
Commentary("【SK信号:卖开委托成交!】");
SKFLG = 0; //卖开标志归0
SKDEL = 0; //卖开撤单标志归0
}
Else If(F_OrderStatus(SKID) == Enum_Canceled) //如果卖开委托已撤
{
Commentary("【SK信号:卖开委托已撤!】");
If(SKDEL == 1) //如果是价格偏离过大撤单
{
SKFLG = 0; //卖开标志归0
}
Else If(SKDEL == 2) //如果是时间间隔T秒追价
{
If(F_OrderFilledLot(SKID) > 0) //如果卖开委托部分成交
{
SKM = SKM - F_OrderFilledLot(SKID); //卖开委托手数
}
If(SKM > 0) //如果卖开委托手数大于0
{
SKP = BIDP; //卖开委托价格
Commentary("【SK信号:卖开委托追价!】");
SKID = A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP); //发出卖开委托
SKT = NOW; //卖开委托时间
}
}
SKDEL = 0; //卖开撤单标志归0
}
Else If(F_OrderStatus(SKID) == Enum_Declared || F_OrderStatus(SKID) == Enum_FillPart) //如果卖开委托挂单
{
If(SKDEL == 0) //如果未撤单
{
If(Abs(NEWP - SKCP) > PLM * MINP) //如果价格偏离过大
{
Commentary("【SK信号:价格偏离过大!】");
Commentary("【SK信号:卖开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(SKID)); //撤掉卖开委托挂单
SKDEL = 1; //已发出撤掉卖开委托挂单
}
Else If(TimeDiff(SKT,NOW) >= T) //如果时间间隔T秒
{
Commentary("【SK信号:卖开委托撤单!】");
F_DeleteOrder(F_OrderContractNo(SKID)); //撤掉卖开委托挂单
SKDEL = 2; //已发出撤掉卖开委托挂单
}
}
}
}
If(BPFLG == 1) //如果有买平委托
{
If(F_OrderStatus(BPID) == Enum_Filled) //如果买平委托成交
{
Commentary("【BP信号:买平委托成交!】");
BPFLG = 0; //买平标志归0
BPDEL = 0; //买平撤单标志归0
}
Else If(F_OrderStatus(BPID) == Enum_Canceled) //如果买平委托已撤
{
Commentary("【BP信号:买平委托已撤!】");
If(F_OrderFilledLot(BPID) > 0) //如果买平委托部分成交
{
BPM = BPM - F_OrderFilledLot(BPID); //买平委托手数
}
If(SRP > 0 && BPM > 0 && BPM <= SRP) //如果买平委托手数不超过空头可用持仓
{
If(BPDEL == 1) //如果是价格偏离过大追价
{
BPP = RLP; //买平委托价格
}
Else If(BPDEL == 2) //如果是时间间隔T秒追价
{
BPP = ASKP; //买平委托价格
BPT = NOW; //买平委托时间
BPDEL = 0; //买平撤单标志归0
}
Commentary("【BP信号:买平委托追价!】");
BPID = A_SendOrder(Enum_Buy,SH,BPM,BPP); //发出买平委托
}
}
Else If(F_OrderStatus(BPID) == Enum_Declared || F_OrderStatus(BPID) == Enum_FillPart) //如果买平委托挂单
{
If(BPDEL == 0) //如果未撤单
{
If(Abs(NEWP - BPCP) > PLM * MINP) //如果价格偏离过大
{
Commentary("【BP信号:价格偏离过大!】");
Commentary("【BP信号:买平委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BPID)); //撤掉买平委托挂单
BPDEL = 1; //已发出撤掉买平委托挂单
}
Else If(TimeDiff(BPT,NOW) >= T) //如果时间间隔T秒
{
Commentary("【BP信号:买平委托撤单!】");
F_DeleteOrder(F_OrderContractNo(BPID)); //撤掉买平委托挂单
BPDEL = 2; //已发出撤掉买平委托挂单
}
}
}
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(BKDFLG == 1) //如果已开启买开处理
{
If(BKFLG == 0) //如果没有买开委托
{
BKM = N; //买开委托手数
BKP = ASKP; //买开委托价格
BKCP = BKP; //买开初始价格
Commentary("【BK信号:买开委托发出!】");
BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP); //发出买开委托
BKT = NOW; //买开委托时间
BKFLG = 1; //已发出买开委托
}
}
If(SPDFLG == 1) //如果已开启卖平处理
{
If(SPFLG == 0) //如果没有卖平委托
{
If(BRP > 0) //如果有多头可用持仓
{
SPM = BRP; //卖平委托手数
SPP = BIDP; //卖平委托价格
SPCP = SPP; //卖平初始价格
Commentary("【SP信号:卖平委托发出!】");
SPID = A_SendOrder(Enum_Sell,SH,SPM,SPP); //发出卖平委托
SPT = NOW; //卖平委托时间
SPFLG = 1; //已发出卖平委托
}
}
}
If(SKDFLG == 1) //如果已开启卖开处理
{
If(SKFLG == 0) //如果没有卖开委托
{
SKM = N; //卖开委托手数
SKP = BIDP; //卖开委托价格
SKCP = SKP; //卖开初始价格
Commentary("【SK信号:卖开委托发出!】");
SKID = A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP); //发出卖开委托
SKT = NOW; //卖开委托时间
SKFLG = 1; //已发出卖开委托
}
}
If(BPDFLG == 1) //如果已开启买平处理
{
If(BPFLG == 0) //如果没有买平委托
{
If(SRP > 0) //如果有空头可用持仓
{
BPM = SRP; //买平委托手数
BPP = ASKP; //买平委托价格
BPCP = BPP; //买平初始价格
Commentary("【BP信号:买平委托发出!】");
BPID = A_SendOrder(Enum_Buy,SH,BPM,BPP); //发出买平委托
BPT = NOW; //买平委托时间
BPFLG = 1; //已发出买平委托
}
}
}
}
End
运行情况:
软件中会提供几种默认止盈止损策略,但有时还是无法完全满足用户的多样性需要,算法交易模型的函数可取到盘口价格和持仓合约的信息(持仓价格、数量、和盈亏等),通过编写算法交易模型可定制属于您自己的止盈止损策略。
跟踪止盈是软件默认提供的策略,但如果希望在盈利达到一定值时再启动跟踪止盈策略,就无法实现了。那么在开仓后,算法交易模型就会开始取盘口价格和持仓价格实时监测,满足了条件会自动发平仓委托。源码如下:
//止盈止损
Data
data0:"m1709";
Vars
Global_Numeric N;//下单手数
Global_Numeric M1; //价位倍数1
Global_Numeric M2; //价位倍数2
Global_Numeric M3; //价位倍数3
Global_Numeric BID; //买一价
Global_Numeric ASK; //卖一价
Global_Numeric NEWP; //最新价
Global_Numeric MINP; //最小变动价位
Global_Numeric BKC; //做多条件
Global_Numeric BKID; //买开委托
Global_Numeric BKFLG; //买开标志
Global_Numeric BKAP; //买开委托成交均价
Global_Numeric BHP; //多头最高价
Global_Numeric SPID; //卖平委托
Global_Numeric SPFLG; //卖平标志
Global_Numeric BRP; //多头可用持仓
Global_Numeric BZYFLG; //多头止盈标志
Begin
N = 2; //下单手数
M1 = 3; //价位倍数1
M2 = 1; //价位倍数2
M3 = 3; //价位倍数3
BKC = 1; //做多条件
BID = Price("Bid1"); //买一价
ASK = Price("Ask1"); //卖一价
NEWP = Price("New"); //最新价
MINP = Price("MinPrice");//最小变动价位
BRP = F_BuyRemainPosition(); //多头可用持仓
if(A_IsExchangeOpen() == 1) //如果当前状态是开盘
{
//----------------------成交判断--------------------//
if(BKFLG == 1) //如果有买开委托
{
if(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
{
Commentary("【多头开仓:买开委托成交!】");
BKAP = F_OrderFilledPrice(BKID); //买开委托成交均价
BKFLG = 2; //买开委托已成交
}
}
if(SPFLG > 0) //如果有卖平委托
{
if(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
{
if(SPFLG == 1) //如果是多头止盈卖平委托
{
Commentary("【多头止盈:卖平委托成交!】");
}
else if(SPFLG == 2) //如果是多头止损卖平委托
{
Commentary("【多头止损:卖平委托成交!】");
}
BKFLG = 0; //买开标志归0
SPFLG = 0; //卖平标志归0
BZYFLG = 0; //多头止盈标志归0
}
}
//----------------------委托处理--------------------//
if(BKFLG == 0) //如果没有买开委托
{
if(BKC == 1) //如果满足做多条件
{
Commentary("【多头开仓:买开委托发出!】");
BKID = A_SendOrder(Enum_Buy,Enum_Entry,N,ASK); //以对价发出N手的买开委托
BKFLG = 1; //已发出买开委托
}
}
if(BKFLG == 2 && SPFLG == 0) //如果买开委托已成交,且没有卖平委托
{
if(BZYFLG == 0) //如果多头止盈未开启
{
if(NEWP >= BKAP + M1 * MINP) //如果最新价大于等于买开委托成交均价加M1个价位
{
Commentary("【多头止盈:多头止盈开启!】");
BHP = NEWP; //多头最高价设为最新价
BZYFLG = 1; //多头止盈开启
}
}
else if(BZYFLG == 1) //如果多头止盈已开启
{
BHP = max(NEWP,BHP); //多头最高价
if(NEWP <= BHP - M2 * MINP) //如果最新价小于等于多头最高价减M2个价位
{
if(BRP > 0) //如果有多头可用持仓
{
Commentary("【多头止盈:卖平委托发出!】");
SPID = A_SendOrder(Enum_Sell,Enum_Exit,BRP,BID); //以对价发出多头可用持仓手数的卖平委托
SPFLG = 1; //已发出多头止盈卖平委托
BZYFLG = 2; //多头止盈已卖平
}
}
}
if(BZYFLG != 2) //如果多头止盈未卖平
{
if(NEWP <= BKAP - M3 * MINP) //如果最新价小于等于买开委托成交均价减M3个价位
{
Commentary("【多头止损:卖平委托发出!】");
SPID = A_SendOrder(Enum_Sell,Enum_Exit,BRP,BID); //以对价发出多头可用持仓手数的卖平委托
SPFLG = 2; //已发出多头止损卖平委托
}
}
}
}
End
算法交易模型执行效果如下图所示,开仓之后满足止损平仓条件,会自动发出平仓委托:
期权市场合约众多,各种波动套利影响因素多、算法复杂,即使出现了套利机会,人工也很难捕捉到。而且很多期权合约交易不活跃,即使价格满足套利条件,也很难确保成交。
使用期权算法交易,可对全市场的期权合约报价进行实时监控,当出现套利机会时,算法交易还可以根据当前盘口的挂单状态,自动调整下单手数和委托价格,提高交易成功率。
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
datac:"50ETF1707-C-2.6000"; //看涨期权
datap:"50ETF1707-P-2.600"; //看跌期权
dataf:"50ETF"; //标的合约
Params
Numeric R(1); //无风险收益率
Numeric N(5);
Vars
Numeric HistoryVolatility_C; //看涨期权的历史波动率
Numeric HistoryVolatility_P; //看跌期权的历史波动率
Numeric HistoryVolatility_F; //标的合约的价格波动率
Numeric New_C; //看涨期权的最新价
Numeric New_P; //看跌期权的最新价
Numeric New_F; //标的合约的最新价
Numeric ExercisePrice; //行权价
Numeric TheoreticalPrice_C; //看涨期权的理论价格
Numeric TheoreticalPrice_P; //看跌期权的理论价格
Numeric RTS; //距行权日剩余的天数
Numeric D1; //中间值
Numeric D2; //中间值
Numeric BuyRemainPosition_C; //看涨期权多头可用持仓
Numeric BuyRemainPosition_P; //看跌期权多头可用持仓
Numeric BuyRemainPosition_F; //标的合约多头可用持仓
Numeric SellRemainPosition_F; //标的合约空头可用持仓
Begin
HistoryVolatility_C = datac.Price("HistoricalVolatility");//取看涨期权的历史波动率
HistoryVolatility_P = dataf.Price("HistoricalVolatility");//取看跌期权的历史波动率
New_F = dataf.Price("New");//取50ETF合约的最新价
ExercisePrice = datac.Price("StrikePrice");//取行权价
D1 = (Ln(New_F / ExercisePrice) + (R/10 + 0.5 * Power(HistoryVolatility_F,2)) * RTS) / (HistoryVolatility_F * Power(RTS,0.5));//求期权理论价格的中间值
D2 = D1 - HistoryVolatility_F * Power(RTS,0.5);//求期权理论价格的中间值
TheoreticalPrice_C = New_F * NormDist(D1) - ExercisePrice * Exp(-1 * (R / 10) * RTS) * NormDist(D2);//根据B-S-M公式计算看涨期权理论价格
TheoreticalPrice_P = ExercisePrice * Exp(-1 * (R / 10) * RTS) * (1 - NormDist(D2)) - New_F * (1 - NormDist(D1));//根据B-S-M公式计算看跌期权理论价格
New_C = datac.Price("New");//看涨期权的最新价
New_P = dataf.Price("New");//看跌期权的最新价
BuyRemainPosition_C = datac.F_BuyRemainPosition();//看涨期权多头可用持仓
BuyRemainPosition_P = datap.F_BuyRemainPosition();//看跌期权多头可用持仓
BuyRemainPosition_F = dataf.F_BuyRemainPosition();//标的合约多头可用持仓
SellRemainPosition_F = dataf.F_SellRemainPosition();//标的合约空头可用持仓
If((TimeDiff(Date,datac.Price("ExpirationDate")) <= 5 || TimeDiff(Date,datap.Price("ExpirationDate")) <= 5) && (GetGlobalVar(0) == 1 || GetGlobalVar(1) == 1))
//看涨期权或看跌期权行权日前五天
{
If(BuyRemainPosition_C > 0)//看涨期权有多头可用持仓
{
datac.A_SendOrder(Enum_sell,Enum_Exit,BuyRemainPosition_C,datac.Price("Bid"));//对价卖平看涨期权可用多仓
}
If(BuyRemainPosition_P > 0)//看跌期权有多头可用持仓
{
datap.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_P,datap.Price("Bid"));//对价卖平看跌期权可用多仓
}
If(BuyRemainPosition_F > 0)//标的合约有空头可用持仓
{
dataf.A_SendOrder(Enum_Buy,Enum_Exit,BuyRemainPosition_F,dataf.Price("Ask"));//对价买平标的合约可用空仓
}
If(SellRemainPosition_F > 0)//标的合约有多头可用持仓
{
dataf.A_SendOrder(Enum_Sell,Enum_Exit,SellRemainPosition_F,dataf.Price("Bid"));//对价卖平标的合约可用多仓
}
SetGlobalVar(0,0);
SetGlobalVar(1,0);
}
Else
{
If(New_C < TheoreticalPrice_C * 0.5 && datac.Price("Stdderiation") < HistoryVolatility_C && BuyRemainPosition_C == 0 && SellRemainPosition_F == 0 && GetGlobalVar(0) == 0)
//看涨期权的最新价低于看涨期权理论价格的50%,并且看涨期权的隐含波动率低于历史波动率,并且看涨期权没有多头持仓,对应标的合约没有空头持仓
{
datac.A_SendOrder(Enum_Buy,Enum_Entry,1,datac.Price("Ask"));//对价买开看涨期权1手
dataf.A_SendOrder(Enum_Sell,Enum_Entry,1,dataf.Price("Bid"));//对价卖开标的合约1手
SetGlobalVar(0,1);
}
If(TheoreticalPrice_P > New_P * 0.5 && datap.Price("Stdderiation") > HistoryVolatility_P && BuyRemainPosition_P == 0 && BuyRemainPosition_F == 0 && GetGlobalVar(1) == 0)
//看跌期权理论价格高于看跌期权最新价的50%,并且看跌期权隐含波动率高于历史波动率,并且看跌期权没有多头持仓,对应标的合约没有多头持仓
{
datap.A_SendOrder(Enum_Buy,Enum_Entry,1,datap.Price("Ask"));//对价买开看跌期权1手
dataf.A_SendOrder(Enum_buy,Enum_Entry,1,dataf.Price("Ask"));//对价买开标的合约1手
SetGlobalVar(1,1);
}
}
End
5、策略运行
在日线上编写策略模型,计算历史波动率,装入期权运行池。策略模型直接读取相应期权认沽认购两合约的历史数波动率进行套利计算。
1、买卖权平价套利
①什么是买卖权平价关系?
买卖权平价关系是指具有相同的到期日与执行价格的金融工具,其卖权与买权价格间必然存在的基本关系。如果两者不相同,则存在套利的空间。
欧式期权的平价关系可以表示为:
其中,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
datac:"m1709-C-2850"; //定义看涨期权
datap:"m1709-P-2850"; //定义看跌期权
dataf:"m1709"; //定义期权标的物
Vars
Numeric New_C; //定义看涨期权最新价
Numeric New_P; //定义看跌期权最新价
Numeric New_F; //定义标的物最新价
Numeric OptionStrikePrice; //定义行权价
Numeric BuyRemainPosition_C; //定义看涨期权多头持仓
Numeric SellRemainPosition_C; //定义看涨期权空头持仓
Numeric BuyRemainPosition_P; //定义看跌期权多头持仓
Numeric SellRemainPosition_P; //定义看跌期权空头持仓
Global_NumericArray SKID_C[5000]; //卖看涨期权索引数组
Global_NumericArray BKID_C[5000]; //买看涨期权索引数组
Global_NumericArray SKID_P[5000]; //卖看跌期权索引数组
Global_NumericArray BKID_P[5000]; //买看跌期权索引数组
Global_NumericArray Time1[5000]; //卖看涨期权,买看跌期权时间数组
Global_NumericArray Time2[5000]; //买看涨期权,卖看跌期权时间数组
Global_Numeric i; //卖看涨期权,买看跌期权次数
Global_Numeric j; //买看涨期权,卖看跌期权次数
Global_Numeric Index; //定义全局变量用于撤单
Global_Numeric Coin; //定义全局变量控制卖看涨期权,买看跌期权
Global_Numeric Coin1; //定义全局变量控制买看涨期权,卖看跌期权
Numeric NumOrder; // 是否下单标志 0未下单 1下单
Begin
if(RunStart==1)
{
If(i >= 5000 || j >= 5000)//卖看涨期权,买看跌期权次数或者买看涨期权,卖看跌期权次数>=5000
{
Commentary("记录次数耗尽,请重新加载此模型");//输出记录次数耗尽,请重新加载此模型
}
Else
{
New_C = datac.Price("New");//看涨期权最新价
New_P = datap.Price("New");//看跌期权最新价
New_F = dataf.Price("New");//标的物最新价
OptionStrikePrice = datac.Price("StrikePrice");//行权价
BuyRemainPosition_C=datac.F_BuyRemainPosition();//看涨期权多头持仓
SellRemainPosition_C=datac.F_SellRemainPosition();//看涨期权空头持仓
BuyRemainPosition_P=datap.F_BuyRemainPosition();//看跌期权多头持仓
SellRemainPosition_P=datap.F_SellRemainPosition();//看跌期权空头持仓
If(DateDiff(CurrentDate,datac.Price("ExpirationDate")) <= 5 || DateDiff(CurrentDate,datap.Price("ExpirationDate")) <= 5)//看涨或者看跌期权距离行权日5日内
{
If(BuyRemainPosition_C > 0)//看涨期权多头持仓>0
{
datac.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_C,datac.Price( "Bid1"));//以买1价卖平看涨期权
}
If(BuyRemainPosition_P > 0)//看涨期权空头持仓>0
{
datac.A_SendOrder(Enum_Buy,Enum_Exit,BuyRemainPosition_P,datac.Price("Ask1"));//以卖一价买平看涨期权空头持仓
}
If(BuyRemainPosition_P > 0)//看跌期权多头持仓>0
{
datap.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_P,datap.Price( "Bid1"));//以买一价卖平看跌期权多头持仓
}
If(SellRemainPosition_P > 0)//看跌期权空头持仓>0
{
datap.A_SendOrder(Enum_Buy,Enum_Exit,SellRemainPosition_P,datap.Price( "Ask1"));//以卖一价买平看跌期权
}
}
Else If(New_C - New_P > New_F - OptionStrikePrice)//看涨期权最新价-看跌期权最新价>标的物最新价-行权价
{
If(BuyRemainPosition_C > 0)//看涨期权多头持仓>0
{
datac.A_SendOrder(Enum_sell,Enum_Exit,BuyRemainPosition_C,datac.Price( "Bid1"));//以看涨期权买一价卖平看涨期权多头持仓
Coin = 0;//Coin为0
}
If(SellRemainPosition_P > 0) //看跌期权空头持仓>0
{
datap.A_SendOrder(Enum_Buy,Enum_Exit,SellRemainPosition_P,datap.Price( "Ask1"));//以看跌期权卖一价买平看跌期权空头持仓
Coin = 0;//Coin为0
}
If(BuyRemainPosition_C == 0 && SellRemainPosition_C == 0 && Coin == 0) //如果看涨期权多头持仓0并且看跌期权空头持仓0并且Coin为0
{
SKID_C[i] = datac.A_SendOrder( Enum_Sell,Enum_Entry,1,datac.Price("Bid1"));//以看涨期权买一价卖开看涨期权1手的卖开索引
BKID_P[i] = datap.A_SendOrder( Enum_Buy,Enum_Entry,1,datap.Price( "Ask1"));//以看跌期权卖一价买开看跌期权1手的买开索引
Time1[i] = CurrentTime;//第i个索引的时间
Index = 0;//index为0
Coin = 1;//coin为1
NumOrder = 1;//下单后1
}
While(Index < i)
{
If(F_OrderStatus(SKID_C[Index]) == Enum_Declared && TimeDiff(Time1[Index],CurrentTime) >= 15)//15秒没有成交
{
datac.A_DeleteOrder(F_OrderContractNo(SKID_C[Index]));//撤单
datac.A_SendOrder( Enum_Sell,Enum_Entry,1,datac.Price("FallLimit"));//以跌停价卖开1手看涨期权
}
If(F_OrderStatus(BKID_P[Index]) == Enum_Declared && TimeDiff(Time1[Index],CurrentTime) >= 15)//15秒没有成交
{
datap.A_DeleteOrder(F_OrderContractNo(BKID_P[Index]));//撤单
datap.A_SendOrder( Enum_Buy,Enum_Entry,1,datap.Price("RiseLimit"));//以涨停价买开1手看跌期权
}
Index = Index + 1;
}
If (NumOrder == 1)//每次下单后
{
i = i + 1;//次数加1
NumOrder = 0;//清0
}
}
Else If(New_C - New_P < New_F - OptionStrikePrice)//看涨期权最新价-看跌期权最新价<标的物最新价-行权价
{
If(SellRemainPosition_C > 0)//看涨期权空头持仓>0
{
datac.A_SendOrder( Enum_buy,Enum_Exit,SellRemainPosition_C,datac.Price( "Ask1")); //以卖一价买平看涨期权空头持仓
Coin1=0;//Coin1为0
}
If(BuyRemainPosition_P > 0) //看跌期权多头持仓>0
{
datap.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_P,datap.Price("Bid1"));//以买一价卖平看跌期权多头持仓
Coin1 = 0;//Coin1为0
}
If(SellRemainPosition_C == 0 && BuyRemainPosition_P == 0 && Coin1 == 0) //看涨期权空头持仓为0并且看跌期权多头持仓为0且Coin1为0
{
BKID_C[j] = datac.A_SendOrder( Enum_Buy, Enum_entry, 1, datac.Price("Ask1"));//以卖一价买开看涨期权1手的索引
SKID_P[j] = datap.A_SendOrder( Enum_Sell, Enum_Entry, 1, datap.Price( "Bid1"));//以买一价卖开看跌期权1手的索引
Time2[j] = CurrentTime;//第i个索引的时间
Index = 0;//Index为0
Coin1 = 1;//Coin1为1
NumOrder = 1;//下单后1
}
While(Index < j)
{
If(F_OrderStatus(BKID_C[Index]) == Enum_Declared && TimeDiff(Time2[index],CurrentTime) >= 15)// 15秒没有成交
{
datac.A_DeleteOrder(F_OrderContractNo(BKID_C[Index]));//撤单
datac.A_SendOrder(Enum_Buy,Enum_Entry,1,datac.price("RiseLimit"));//以涨停价买开1手看涨期权
}
If(F_OrderStatus(SKID_P[Index]) == Enum_Declared && TimeDiff(Time2[j],CurrentTime) >= 15)// 15秒没有成交
{
datap.A_DeleteOrder(F_OrderContractNo(SKID_P[Index]));//撤单
datap.A_SendOrder( Enum_Sell,Enum_Entry,1,datap.Price("FallLimit"));//以跌停价卖开1手看跌期权
}
Index = Index + 1;
}
If (NumOrder == 1)//每次下单后
{
j = j + 1;//下单次数+1
NumOrder = 0;//清0
}
}
}
}
End
④策略运行:
将策略模型在期权K线主图进行计算后加载到期权运行池,满足编写的套利条件后,会直接进行套利委托下单。
2、日历价差套利
①日历价差套利策略简介
日历价差是指买进到期日较远的期权,同时又卖出相同行权价格、相同数量但到期日较近的期权,赚取两个不同期权隐含波动率的差价或者其它期权定价参数的差价,以获得利润的期权套利交易策略。
②推导套利条件
1)、开仓策略:
X=V(近月隐含波动率)-V (历史波动率)+V (近月隐含波动率 )-V(远月隐含波动率) X>0,通过卖出一份近月看涨期权,同时买出一份远月看涨期权,建立日历价差组合。
2)、平仓策略:
考虑到该组合并没有对冲标的物价格变动的风险,在近月期权到期前几天,组合的Gamma风险值可能比较大,标的物价格的变化会大幅增加收益的波动率。因此,策略采取在当月合约到期前8天进行平仓。
③将上述策略编写为策略模型
Data
Data1:"m1709-C-2850"; //定义近月合约
Data2:"m1801-C-2850"; //定义远月合约
Params
Numeric Lots(2); //定义参数,下单手数,默认值为2
Numeric Length(5); //定义参数,周期,默认值为5
Vars
Numeric HistoryVolatility; //定义变量,取历史波动率
Numeric Stdderiation1; //定义变量,取近月合约的隐含波动率
Numeric Stdderiation2; //定义变量,取远月合约的隐含波动率
Numeric Cond; //定义变量,判断入场条件
Numeric SellRemainPosition; //定义变量,取空头可用持仓
Numeric BuyRemainPosition; //定义变量,取多头可用持仓
Begin
HistoryVolatility = Std(Ln(Close / Close[1]), Length) * Sqrt(252);//计算标的物历史波动率
Stdderiation1 = data1.Price("Stdderiation");//取近月合约的隐含波动率
Stdderiation2 = data2.Price("Stdderiation");//取远月合约的隐含波动率
Cond = Stdderiation1 - HistoryVolatility + Stdderiation1 - Stdderiation2;//计算隐含波动率的综合价差
If(Cond > 0 && GetGlobalVar(0) == 0)//当价差大于0并且没有持仓时建立日历价差组合
{
data1.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data1.Price("Ask1"));//卖出近月期权合约
data2.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2.Price("Bid1")); //买入远月期权合约
SetGlobalVar(0,1);
}
If(GetGlobalVar(0) == 1 && DateDiff(Date,data1.Price("ExpirationDate")) <= 5)//当距行权日期小于等于5天时平仓
{
BuyRemainPosition = data2.F_BuyRemainPosition();//取远月合约的多头可用持仓
If(BuyRemainPosition > 0)//如果远月合约的多头可用持仓大于0
{
data2.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition,data1.Price("Bid1")); //平多头可用持仓
}
SellRemainPosition= data1.F_SellRemainPosition();//取近月合约的空头可用持仓
If(SellRemainPosition > 0)//如果近月合约的空头可用持仓大于0
{
data1.A_SendOrder(Enum_Buy,Enum_Exit,SellRemainPosition,data1.Price("Ask1")); //平空头可用持仓
}
SetGlobalVar(0,0);
}
End
④策略运行:
日历价差套利策略需要计算近月合约的历史波动率,在日线上编写策略模型,计算历史波动率,装入期权运行池。策略模型直接读取相应期权认沽认购两合约的历史数波动率进行套利计算。
3、牛市看涨期权垂直套利
垂直套利也称货币套利、跨价套利。采用这种套利方式,可以将风险和收益限定在一定范围内。它的交易方式表现为按照不同的履约价格同时买进和卖出同一到期月份的看涨期权或看跌期权。之所以称为“垂直套利”,是因为垂直套利策略除履约价格外,其余都是相同的,而履约价格及其反映的权利金在期权行情表上是垂直排列的。
牛市看涨期权垂直套利是风险和收益都有限的策略。该策略可用作“摸底”之用,投资者预期市价的下跌趋势已经接近尾声,随时可作反弹。无论投资者预期正确与否,组合投资者一样站在较为有利的位置。该策略的损益情况如下图所示:
①标的合约价格分析
编写策略模型,寻找在下跌趋势尾声,即将反弹的价格点,作为套利入场的时机。
②选择期权合约,形成套利策略
在选择期权合约时需要考虑最大风险与回报。如果要风险最低,则选择的买入的履约价格与卖出的履约价格相距越窄越好,因为权利金最少。如果要实现最大回报越高越好,由于最大回报的计算是两个履约价格之差减去净权利金,则买入和卖出的两个履约价格越宽越好。根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略。
Data
data1:"m1709-C-2800"; //定义第一个看涨期权合约
data2:"m1709-C-2850"; //定义第二个看涨期权合约
Params
Numeric Lots(2); //定义手数
Numeric Length1(5); //定义周期数
Numeric Length2(10); //定义周期数
Vars
Numeric MA1; //均线
Numeric MA2; //均线
Numeric OptionStrikePrice1; //行权价
Numeric OptionStrikePrice2; //行权价
Numeric BKfun; //BK条件
Numeric SPfun; //SP条件
Begin
MA1 = Ma(Close,Length1); //5周期均线
MA2 = Ma(Close,Length2); //10周期均线
BKfun = CrossUp(MA1,MA2);//5周期均线上穿10周期均线
SPfun = CrossDown(MA1,MA2);//5周期均线下穿10周期均线
OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价
If(BKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足BK信号条件并且合约2的行权价>合约1的行权价
{
data1.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data1.Price("Ask1"));//对合约1以卖价1 的价格发出买2手开仓委托
data2.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data2.Price("Bid1"));//对合约2以买价1 的价格发出卖2手开仓委托
SetGlobalVar(0,1);//全局变量为1
}
If(SPfun && data1.F_BuyRemainPosition() > 0 && data2.F_SellRemainPosition() > 0&&GetGlobalVar(0) == 1)//满足平仓信号并且合约1有多头持仓并且合约2有空头持仓
{
data1.A_SendOrder(Enum_Sell,Enum_Exit,data1.F_BuyRemainPosition(),data1.Price("Bid1"));//对合约1以买价1 的价格发出全卖平的委托
data2.A_SendOrder(Enum_Buy,Enum_Exit,data2.F_SellRemainPosition(),data2.Price("Ask1"));//对合约2以卖价1 的价格发出全买平的委托
SetGlobalVar(0,0);//全局变量为0
}
End
③策略运行
将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。
4、牛市看跌期权垂直套利
牛市看跌期权垂直套利是看好后市的保守投资策略。投资者看好后市,希望卖出看跌期权赚取权利金,但是又担心市价跌破履约价格会带来风险,因此,用收取的部分权利金买入一个较低履约价格的看跌期权以控制风险。该策略的损益情况如下图所示:
①标的合约价格分析
编写策略模型,判断市场氛围,在价格上涨趋势中,寻找套利入场时机。
②选择期权合约,形成套利策略
根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略
Data
data1:"m1701-P-2800"; //定义第一个看跌期权合约
data2:"m1701-P-2850"; //定义第二个看跌期权合约
Params
Numeric Lots(2); //定义手数
Numeric Length1(5); //定义周期数
Numeric Length2(10); //定义周期数
Vars
Numeric MA1; //均线
Numeric MA2; //均线
Numeric OptionStrikePrice1; //行权价
Numeric OptionStrikePrice2; //行权价
Numeric BKfun; //BK条件
Numeric SPfun; //SP条件
Begin
MA1 = Ma(Close,Length1); //5周期均线
MA2 = Ma(Close,Length2); //10周期均线
BKfun = CrossUp(MA1,MA2);//5周期均线上穿10周期均线
SPfun = CrossDown(MA1,MA2);//5周期均线下穿10周期均线
OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价
If(BKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足BK信号条件并且合约2的行权价>合约1的行权价
{
data1.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data1.Price("Ask1"));//对合约1以卖价1 的价格发出买2手开仓委托
data2.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data2.Price("Bid1"));//对合约2以买价1 的价格发出卖2手开仓委托
SetGlobalVar(0,1);//全局变量为1
}
If(SPfun && data1.F_BuyRemainPosition() > 0 && data2.F_SellRemainPosition() > 0&&GetGlobalVar(0) == 1)//满足SP信号并且合约1有多头持仓并且合约2有空头持仓
{
data1.A_SendOrder(Enum_Sell,Enum_Exit,data1.F_BuyRemainPosition(),data1.Price("Bid1"));//对合约1以买价1 的价格发出全卖平的委托
data2.A_SendOrder(Enum_Buy,Enum_Exit,data2.F_SellRemainPosition(),data2.Price("Ask1"));//对合约2以卖价1 的价格发出全买平的委托
SetGlobalVar(0,0);//全局变量为0
}
End
③策略运行
将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。
5、熊市看涨期权垂直套利
投资者看淡后市,因此卖出低履约价格的看涨期权收取较高的权利金,但是又担心市价会升破履约价格而出现不断增大的风险,因此,为了对冲市场上升的风险,可以买入一张较高履约价格的看涨期权,一旦市价升破较高履约价格,该看涨期权所得的价值可以抵消之前所卖出的看涨期权的风险。该策略的损益情况如下图所示:
①标的合约价格分析
编写策略模型,寻找处于下跌趋势的行情位置,作为套利入场的时机。
②选择期权合约,形成套利策略
根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略
Data
data1:"m1709-C-2800"; //定义第一个看涨期权合约
data2:"m1709-C-2850"; //定义第二个看涨期权合约
Params
Numeric Lots(2); //定义手数
Numeric Length1(5); //定义周期数
Numeric Length2(10); //定义周期数
Vars
Numeric MA1; //均线
Numeric MA2; //均线
Numeric OptionStrikePrice1; //行权价
Numeric OptionStrikePrice2; //行权价
Numeric SKfun; //SK条件
Numeric BPfun; //BP条件
Begin
MA1 = Ma(Close,Length1); //5周期均线
MA2 = Ma(Close,Length2); //10周期均线
SKfun = CrossDown(MA1,MA2); //5周期均线下穿10周期均线
BPfun = CrossUp(MA1,MA2); //5周期均线上穿10周期均线
OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价
If(SKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足SK信号条件并且合约2的行权价>合约1的行权价
{
data2.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2.Price("Ask1"));//对合约2以卖价1 的价格发出买2手开仓委托
data1.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data1.Price("Bid1"));//对合约1以买价1 的价格发出卖2手开仓委托
SetGlobalVar(0,1);//全局变量为1
}
If(BPfun && data2.F_BuyRemainPosition() > 0 && data1.F_SellRemainPosition() > 0&& GetGlobalVar(0) == 1)//满足BP信号并且合约2有多头持仓并且合约1有空头持仓
{
data2.A_SendOrder(Enum_Sell,Enum_Exit,data2.F_BuyRemainPosition(),data2.Price("Bid1"));//对合约2以买价1 的价格发出全卖平的委托
data1.A_SendOrder(Enum_Buy,Enum_Exit,data1.F_SellRemainPosition(),data1.Price("Ask1"));//对合约1以卖价1 的价格发出全买平的委托
SetGlobalVar(0,0);//全局变量为0
}
End
③策略运行
将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。
6、熊市看跌期权垂直套利
该策略是看淡后市的保守投资策略。投资者看淡后市,但认为后市未必后大跌,反而市况可能会进入反复调整,因此时间价值的损耗可能对单独买入看跌期权不利。另外,投资者希望可以减少支出,如果再卖出一份看跌期权,则可收取权利金。于是,买入高履约价格的看跌期权,同时又卖出低履约价格的看跌期权。该策略的损益情况如下图所示:
①标的合约价格分析
编写策略模型,寻找上涨趋势的尾部,行情反转,震荡下跌的位置,作为套利入场的时机。
②选择期权合约,形成套利策略
根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略
Data
data1:"m1709-P-2800"; //定义第一个看跌期权合约
data2:"m1709-P-2850"; //定义第二个看跌期权合约
Params
Numeric Lots(2); //定义手数
Numeric Length1(5); //定义周期数
Numeric Length2(10); //定义周期数
Vars
Numeric MA1; //均线
Numeric MA2; //均线
Numeric OptionStrikePrice1; //行权价
Numeric OptionStrikePrice2; //行权价
Numeric SKfun; //SK条件
Numeric BPfun; //BP条件
Begin
MA1 = Ma(Close,Length1); //5周期均线
MA2 = Ma(Close,Length2); //10周期均线
SKfun = CrossDown(MA1,MA2);//5周期均线下穿10周期均线
BPfun = CrossUp(MA1,MA2); //5周期均线上穿10周期均线
OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价
If(SKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足SK信号条件并且合约2的行权价>合约1的行权价
{
data2.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2.Price("Ask1"));//对合约2以卖价1 的价格发出买2手开仓委托
data1.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data1.Price("Bid1"));//对合约1以买价1 的价格发出卖2手开仓委托
SetGlobalVar(0,1);//全局变量为1
}
If(BPfun && data2.F_BuyRemainPosition() > 0 && data1.F_SellRemainPosition() > 0&&GetGlobalVar(0) == 1)//满足BP信号并且合约2有多头持仓并且合约1有空头持仓
{
data2.A_SendOrder(Enum_Sell,Enum_Exit,data2.F_BuyRemainPosition(),data2.Price("Bid1"));//对合约2以买价1 的价格发出全卖平的委托
data1.A_SendOrder(Enum_Buy,Enum_Exit,data1.F_SellRemainPosition(),data1.Price("Ask1"));//对合约1以卖价1 的价格发出全买平的委托
SetGlobalVar(0,0);//全局变量为0
}
End
③策略运行
将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。
套利是一种稳定性高,但盈利率较低的交易模式。大多数情况下,套利亏损的原因并不是没有好的套利机会,而是当套利机会出现时没有抓住入场时机,出现瘸腿、价差滑点大等问题。那么套利算法交易能够解决什么问题呢?
问题一:合约不活跃,容易瘸腿怎么办?
套利算法交易可同时监控两腿合约的委托状态。当出现一腿合约交易不活跃,成交困难时,可迅速调整委托顺序和委托价格,优先发不活跃合约的委托,确保不活跃的合约优先成交,然后再对活跃合约发送委托,降低瘸腿风险。
套利算法交易可同时读取两腿合约的盘口数据。当出现一腿合约挂单量不足时,可灵活调整分批策略,根据已成交情况,迅速调整两腿合约的委托数量,确保每一批次双边成交,降低瘸腿风险。
问题二:套利成交时间长,价差滑点大怎么办?
套利算法交易可对双边持仓进行监控,当出现极端行情、价差偏离较大时,可实施退场策略。当未配对的单边持仓亏损时,可自动撤掉所有挂单,并平掉未配对持仓,降低单边持仓风险;当未配对的单边持仓盈利时,可对另一腿合约进行追价下单,用单边的盈利,弥补价差的滑点。
移仓在套期保值中是很重要的一个环节。不仅要关注远近两个月合约的价差,寻找最佳换月时点,还需要配合算法交易减少滑点,降低换月成本。在价格波动较大的时候,还要防范换月过程中空仓时间太长导致的单边风险。
移仓附加算法交易,可自动监控远近月价差,搜寻最佳换月时点。可自由编写TWAP等智能分批策略,精细化控制交易过程,减小冲击成本,可实现平仓、建仓同步进行,防范单边风险。
套利是一种高胜率低收益的交易模式,所以交易滑点对整体收益的影响更加明显,我们可以通过算法交易,来制定多种套利下单的精细控制方式。
比如我们可以计算数据的均线和N倍标准差来确定套利交易的上轨和下轨,用于做多和平多。然后根据合约的活跃程度,设定委托的先后顺序,在优先保证不瘸腿的前提下,尽量减少成交滑点。
代码如下:
Data
CODA:"m2001"; //第一腿合约A
CODB:"m1909"; //第二腿合约B
Vars
Numeric N; //下单手数
Numeric X; //循环变量
Numeric UDP; //上下轨比例
Numeric SHA; //A平仓参数
Numeric SHB; //B平仓参数
Numeric NEWPA; //A最新价
Numeric NEWPB; //B最新价
Numeric BIDPA; //A买一价
Numeric ASKPA; //A卖一价
Numeric BIDPB; //B买一价
Numeric ASKPB; //B卖一价
Numeric BIDVB; //B买一量
Numeric ASKVB; //B卖一量
Numeric BSVB; //B买一量和
Numeric ASVB; //B卖一量和
Numeric MAA; //A平均值
Numeric MAB; //B平均值
Numeric SDA; //A标准差
Numeric SDB; //B标准差
Numeric UPLA; //A上轨
Numeric UPLB; //B上轨
Numeric DNLA; //A下轨
Numeric DNLB; //B下轨
Numeric TKNA; //A数据区长度
Numeric TKNB; //B数据区长度
Numeric BRPA; //A多头可用持仓
Numeric SRPB; //B空头可用持仓
Numeric OPFLG; //模型处理标志
Numeric BKDFLGA; //A买开处理标志
Numeric SPDFLGA; //A卖平处理标志
Numeric SKDFLGB; //B卖开处理标志
Numeric BPDFLGB; //B买平处理标志
Var_TickData TKDA; //A数据区
Var_TickData TKDB; //B数据区
NumericArray TKPA; //A数据价格
NumericArray TKPB; //B数据价格
Global_Numeric BCFLG; //多头处理标志
Global_Numeric BKIDA; //A买开委托
Global_Numeric SPIDA; //A卖平委托
Global_Numeric BKFLGA; //A买开标志
Global_Numeric SPFLGA; //A卖平标志
Global_Numeric SKIDB; //B卖开委托
Global_Numeric BPIDB; //B买平委托
Global_Numeric SKFLGB; //B卖开标志
Global_Numeric BPFLGB; //B买平标志
Begin
//------------------------处理开启------------------------//
If(CODA.A_IsExchangeOpen() == 1) //如果A是开盘状态
{
If(CODB.A_IsExchangeOpen() == 1) //如果B是开盘状态
{
OPFLG = 1; //开启模型处理
}
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
N = 2; //下单手数
UDP = 1.645; //上下轨比例
NEWPA = CODA.Price("New"); //A最新价
NEWPB = CODB.Price("New"); //B最新价
BIDPA = CODA.Price("Bid1"); //A买一价
ASKPA = CODA.Price("Ask1"); //A卖一价
BIDPB = CODB.Price("Bid1"); //B买一价
ASKPB = CODB.Price("Ask1"); //B卖一价
BIDVB = CODB.Price("BidVol1");//B买一量
ASKVB = CODB.Price("AskVol1");//B卖一量
TKDA = Def_TickData("m2001",1,20);//A数据区
TKDB = Def_TickData("m1909",1,20);//B数据区
BRPA = CODA.F_BuyRemainPosition(); //A多头可用持仓
SRPB = CODB.F_SellRemainPosition(); //B空头可用持仓
If(CODA.A_IsSHCode() == 1) //如果A是上期所合约
{
SHA = Enum_ExitToday; //A平仓参数
}
Else //如果A非上期所合约
{
SHA = Enum_Exit; //A平仓参数
}
If(CODB.A_IsSHCode() == 1) //如果B是上期所合约
{
SHB = Enum_ExitToday; //B平仓参数
}
Else //如果B非上期所合约
{
SHB = Enum_Exit; //B平仓参数
}
If(TKDA.State == 1) //如果A数据区有效
{
TKNA = TKDA.Num; //A数据区长度
For X = 0 To TKNA - 1 //遍历A数据区
{
TKPA[X] = TKDA[X].TickPrice; //A数据价格
}
}
If(TKDB.State == 1) //如果B数据区有效
{
TKNB = TKDB.Num; //B数据区长度
For X = 0 To TKNB - 1 //遍历B数据区
{
TKPB[X] = TKDB[X].TickPrice; //B数据价格
}
For X = TKNB - 3 To TKNB - 1 //遍历B数据区
{
BSVB = BSVB + TKDB[X].BidVol1;//B买一量和
ASVB = ASVB + TKDB[X].AskVol1;//B卖一量和
}
}
MAA = AverageArray(TKPA); //A平均值
MAB = AverageArray(TKPB); //B平均值
SDA = StandardDevArray(TKPA,1); //A标准差
SDB = StandardDevArray(TKPB,1); //B标准差
UPLA = MAA + UDP * SDA;//A上轨
DNLA = MAA - UDP * SDA;//A下轨
UPLB = MAB + UDP * SDB;//B上轨
DNLB = MAB - UDP * SDB;//B下轨
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
If(BKFLGA == 1) //如果有A买开委托
{
If(F_OrderStatus(BKIDA) == Enum_Filled) //如果A买开委托成交
{
Commentary("【多头开仓:A买开委托成交!】");
BCFLG = 2; //已完成多头开仓
BKFLGA = 0; //A买开标志归0
}
}
If(SKFLGB == 1) //如果有B卖开委托
{
If(F_OrderStatus(SKIDB) == Enum_Filled) //如果B卖开委托成交
{
Commentary("【多头开仓:B卖开委托成交!】");
BKDFLGA = 1; //开启A买开处理
SKFLGB = 0; //B卖开标志归0
}
}
If(SPFLGA == 1) //如果有A卖平委托
{
If(F_OrderStatus(SPIDA) == Enum_Filled) //如果A卖平委托成交
{
Commentary("【多头平仓:A卖平委托成交!】");
BCFLG = 0; //多头处理标志归0
SPFLGA = 0; //A卖平标志归0
}
}
If(BPFLGB == 1) //如果有B买平委托
{
If(F_OrderStatus(BPIDB) == Enum_Filled) //如果B买平委托成交
{
Commentary("【多头平仓:B买平委托成交!】");
SPDFLGA = 1; //开启A卖平处理
BPFLGB = 0; //B买平标志归0
}
}
}
//------------------------套利处理------------------------//
If(OPFLG == 1) //套利处理
{
If(BCFLG == 0) //如果未执行多头开仓
{
If((NEWPA > UPLA && NEWPB > UPLB) || (NEWPA < DNLA && NEWPB < DNLB)) //如果满足多头开仓条件
{
If(BIDVB >= ASKVB / 3 && BSVB < ASVB * 2) //如果满足多头开仓条件
{
Commentary("【多头开仓:满足开仓条件!】");
SKDFLGB = 1; //开启B卖开处理
BCFLG = 1; //已执行多头开仓
}
}
}
Else If(BCFLG == 2) //如果已完成多头开仓
{
If((NEWPA > DNLA && NEWPA < UPLA) && (NEWPB > DNLB && NEWPB < UPLB)) //如果满足多头平仓条件
{
Commentary("【多头平仓:满足平仓条件!】");
BPDFLGB = 1; //开启B买平处理
BCFLG = 3; //已执行多头平仓
}
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(BKDFLGA == 1) //如果已开启A买开处理
{
If(BKFLGA == 0) //如果没有A买开委托
{
Commentary("【多头开仓:A买开委托发出!】");
BKIDA = CODA.A_SendOrder(Enum_Buy,Enum_Entry,N,ASKPA); //发出A买开委托
BKFLGA = 1; //已发出A买开委托
}
}
If(SKDFLGB == 1) //如果已开启B卖开处理
{
If(SKFLGB == 0) //如果没有B卖开委托
{
Commentary("【多头开仓:B卖开委托发出!】");
SKIDB = CODB.A_SendOrder(Enum_Sell,Enum_Entry,N,BIDPB); //发出B卖开委托
SKFLGB = 1; //已发出B卖开委托
}
}
If(SPDFLGA == 1) //如果已开启A卖平处理
{
If(BRPA > 0) //如果有A多头可用持仓
{
Commentary("【多头平仓:A卖平委托发出!】");
SPIDA = CODA.A_SendOrder(Enum_Sell,SHA,BRPA,BIDPA); //发出A卖平委托
SPFLGA = 1; //已发出A卖平委托
}
}
If(BPDFLGB == 1) //如果已开启B买平处理
{
If(SRPB > 0) //如果有B空头可用持仓
{
Commentary("【多头平仓:B买平委托发出!】");
BPIDB = CODB.A_SendOrder(Enum_Buy,SHB,SRPB,ASKPB); //发出B买平委托
BPFLGB = 1; //已发出B买平委托
}
}
}
End
运行效果:
很多交易者在套利交易过程中,往往更重视入场策略,通过设定不同的委托方式保证交易成功,但是任何一种委托方式,都有可能面临交易过程中价差偏移的风险,那么如何规避这种风险呢?我们不妨尝试制定一套退场策略。
在套利分批下单过程中,一旦出现价差偏离超过可承受滑点范围后,马上终止交易、暂停后续批次委托,并且根据实际情况,处理单边持仓。当单边持仓亏损时,马上进行平仓止损,降低瘸腿风险。当单边持仓盈利时,则对另一腿合约进行追价委托,尽快配成套利对,用单边的盈利弥补价差偏离的成本。
代码如下:
Data
CODA:"m2001"; //第一腿合约A
CODB:"m1909"; //第二腿合约B
Vars
Numeric N; //下单手数
Numeric T; //时间间隔
Numeric PLM; //偏离价位倍数
Numeric SHA; //A平仓参数
Numeric SHB; //B平仓参数
Numeric KCP; //开仓条件价差
Numeric PCP; //平仓条件价差
Numeric NOW; //当前时间
Numeric NPC; //最新价差
Numeric NEWPA; //A最新价
Numeric NEWPB; //B最新价
Numeric BIDPA; //A买一价
Numeric ASKPA; //A卖一价
Numeric BIDPB; //B买一价
Numeric ASKPB; //B卖一价
Numeric ASKVA; //A卖一量
Numeric BIDVB; //B买一量
Numeric MINPA; //A最小变动价位
Numeric BPLA; //A多头持仓盈亏
Numeric SPLB; //B空头持仓盈亏
Numeric BRPA; //A多头可用持仓
Numeric SRPB; //B空头可用持仓
Numeric OPFLG; //模型处理标志
Numeric BKDFLGA; //A买开处理标志
Numeric SPDFLGA; //A卖平处理标志
Numeric SKDFLGB; //B卖开处理标志
Numeric BPDFLGB; //B买平处理标志
Global_Numeric NPCA; //记录价差
Global_Numeric BCFLG; //多头处理标志
Global_Numeric CDFLG; //撤单处理标志
Global_Numeric BKIDA; //A买开委托
Global_Numeric SPIDA; //A卖平委托
Global_Numeric BKFLGA; //A买开标志
Global_Numeric SPFLGA; //A卖平标志
Global_Numeric BKDELA; //A买开撤单标志
Global_Numeric BKSMA; //A买开委托总手数
Global_Numeric BKMA; //A买开委托手数
Global_Numeric SPMA; //A卖平委托手数
Global_Numeric BKTA; //A买开委托时间
Global_Numeric SKIDB; //B卖开委托
Global_Numeric BPIDB; //B买平委托
Global_Numeric SKFLGB; //B卖开标志
Global_Numeric BPFLGB; //B买平标志
Global_Numeric SKDELB; //B卖开撤单标志
Global_Numeric SKSMB; //B卖开委托总手数
Global_Numeric SKMB; //B卖开委托手数
Global_Numeric BPMB; //B买平委托手数
Global_Numeric SKTB; //B卖开委托时间
Begin
//------------------------处理开启------------------------//
If(CODA.A_IsExchangeOpen() == 1) //如果A是开盘状态
{
If(CODB.A_IsExchangeOpen() == 1) //如果B是开盘状态
{
OPFLG = 1; //开启模型处理
}
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
N = 20; //下单手数
T = 5; //时间间隔
PLM = 3; //偏离价位倍数
KCP = 5; //开仓条件价差
PCP = 10; //平仓条件价差
NOW = CurrentTime(); //当前时间
NEWPA = CODA.Price("New"); //A最新价
NEWPB = CODB.Price("New"); //B最新价
BIDPA = CODA.Price("Bid1"); //A买一价
ASKPA = CODA.Price("Ask1"); //A卖一价
BIDPB = CODB.Price("Bid1"); //B买一价
ASKPB = CODB.Price("Ask1"); //B卖一价
ASKVA = CODA.Price("AskVol1"); //A卖一量
BIDVB = CODB.Price("BidVol1"); //B买一量
MINPA = CODA.Price("MinPrice"); //A最小变动价位
NPC = NEWPA - NEWPB; //最新价差
BPLA = CODA.F_BuyProfitLoss(); //A多头持仓盈亏
SPLB = CODB.F_SellProfitLoss(); //B空头持仓盈亏
BRPA = CODA.F_BuyRemainPosition(); //A多头可用持仓
SRPB = CODB.F_SellRemainPosition(); //B空头可用持仓
If(CODA.A_IsSHCode() == 1) //如果A是上期所合约
{
SHA = Enum_ExitToday; //A平仓参数
}
Else //如果A非上期所合约
{
SHA = Enum_Exit; //A平仓参数
}
If(CODB.A_IsSHCode() == 1) //如果B是上期所合约
{
SHB = Enum_ExitToday; //B平仓参数
}
Else //如果B非上期所合约
{
SHB = Enum_Exit; //B平仓参数
}
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
If(BKFLGA == 1) //如果有A买开委托
{
If(F_OrderStatus(BKIDA) == Enum_Filled) //如果A买开委托成交
{
Commentary("【多头开仓:A买开委托成交!】");
If(BKDELA == 0 || BKDELA == 1) //如果非撤单处理撤单
{
If(BKSMA > 0) //如果有剩余A买开委托手数
{
BKDFLGA = 1; //开启A买开处理
}
}
Else If(BKDELA == 2) //如果是撤单处理撤单
{
BPDFLGB = 2; //开启B买平处理
}
BKFLGA = 0; //A买开标志归0
BKDELA = 0; //A买开撤单标志归0
}
Else If(F_OrderStatus(BKIDA) == Enum_Canceled) //如果A买开委托已撤
{
Commentary("【多头开仓:A买开委托已撤!】");
If(BKDELA == 1) //如果是追价处理撤单
{
If(F_OrderFilledLot(BKIDA) > 0) //如果A买开委托部分成交
{
BKMA = BKMA - F_OrderFilledLot(BKIDA); //A买开委托手数
}
If(BKMA > 0) //如果A买开委托手数大于0
{
Commentary("【多头开仓:A买开委托追价!】");
BKIDA = CODA.A_SendOrder(Enum_Buy,Enum_Entry,BKMA,ASKPA); //发出A买开委托
BKTA = NOW; //A买开委托时间
}
}
Else If(BKDELA == 2) //如果是撤单处理撤单
{
BPDFLGB = 2; //开启B买平处理
BKFLGA = 0; //A买开标志归0
}
BKDELA = 0; //A买开撤单标志归0
}
Else If(F_OrderStatus(BKIDA) == Enum_Declared || F_OrderStatus(BKIDA) == Enum_FillPart) //如果A买开委托挂单
{
If(BKDELA == 0) //如果未撤单
{
If(CDFLG == 1 && TimeDiff(BKTA,NOW) >= T) //如果已开启追价处理,且时间间隔T秒
{
Commentary("【多头开仓:A买开委托撤单!】");
CODA.F_DeleteOrder(F_OrderContractNo(BKIDA)); //撤掉A买开委托挂单
BKDELA = 1; //已发出撤掉A买开委托挂单
}
Else If(CDFLG == 2) //如果已开启撤单处理
{
Commentary("【多头开仓:A买开委托撤单!】");
CODA.F_DeleteOrder(F_OrderContractNo(BKIDA)); //撤掉A买开委托挂单
BKDELA = 2; //已发出撤掉A买开委托挂单
}
}
}
}
If(SKFLGB == 1) //如果有B卖开委托
{
If(F_OrderStatus(SKIDB) == Enum_Filled) //如果B卖开委托成交
{
Commentary("【多头开仓:B卖开委托成交!】");
If(SKDELB == 0 || SKDELB == 1) //如果非撤单处理撤单
{
If(SKSMB > 0) //如果有剩余B卖开委托手数
{
SKDFLGB = 1; //开启B卖开处理
}
}
Else If(SKDELB == 2) //如果是撤单处理撤单
{
SPDFLGA = 2; //开启A卖平处理
}
SKFLGB = 0; //B卖开标志归0
SKDELB = 0; //B卖开撤单标志归0
}
Else If(F_OrderStatus(SKIDB) == Enum_Canceled) //如果B卖开委托已撤
{
Commentary("【多头开仓:B卖开委托已撤!】");
If(SKDELB == 1) //如果是追价处理撤单
{
If(F_OrderFilledLot(SKIDB) > 0) //如果B卖开委托部分成交
{
SKMB = SKMB - F_OrderFilledLot(SKIDB); //B卖开委托手数
}
If(SKMB > 0) //如果B卖开委托手数大于0
{
Commentary("【多头开仓:B卖开委托追价!】");
SKIDB = CODB.A_SendOrder(Enum_Sell,Enum_Entry,SKMB,BIDPB); //发出B卖开委托
SKTB = NOW; //B卖开委托时间
}
}
Else If(SKDELB == 2) //如果是撤单处理撤单
{
SPDFLGA = 2; //开启A卖平处理
SKFLGB = 0; //B卖开标志归0
}
SKDELB = 0; //B卖开撤单标志归0
}
Else If(F_OrderStatus(SKIDB) == Enum_Declared || F_OrderStatus(SKIDB) == Enum_FillPart) //如果B卖开委托挂单
{
If(SKDELB == 0) //如果未撤单
{
If(CDFLG == 1 && TimeDiff(SKTB,NOW) >= T) //如果已开启追价处理,且时间间隔T秒
{
Commentary("【多头开仓:B卖开委托撤单!】");
CODB.F_DeleteOrder(F_OrderContractNo(SKIDB)); //撤掉B卖开委托挂单
SKDELB = 1; //已发出撤掉B卖开委托挂单
}
Else If(CDFLG == 2) //如果已开启撤单处理
{
Commentary("【多头开仓:B卖开委托撤单!】");
CODB.F_DeleteOrder(F_OrderContractNo(SKIDB)); //撤掉B卖开委托挂单
SKDELB = 2; //已发出撤掉B卖开委托挂单
}
}
}
}
If(SPFLGA == 1) //如果有A卖平委托
{
If(F_OrderStatus(SPIDA) == Enum_Filled) //如果A卖平委托成交
{
Commentary("【多头平仓:A卖平委托成交!】");
SPFLGA = 0; //A卖平标志归0
}
}
If(BPFLGB == 1) //如果有B买平委托
{
If(F_OrderStatus(BPIDB) == Enum_Filled) //如果B买平委托成交
{
Commentary("【多头平仓:B买平委托成交!】");
BPFLGB = 0; //B买平标志归0
}
}
}
//------------------------套利处理------------------------//
If(OPFLG == 1) //套利处理
{
If(BCFLG == 0) //如果未执行多头开仓
{
If(NPC <= KCP) //如果满足多头开仓条件
{
Commentary("【多头开仓:满足开仓条件!】");
NPCA = NPC; //记录价差
BKSMA = N; //A买开委托总手数
SKSMB = N; //B卖开委托总手数
BKDFLGA = 1; //开启A买开处理
SKDFLGB = 1; //开启B卖开处理
CDFLG = 0; //撤单处理标志归0
BCFLG = 1; //已执行多头开仓
}
}
Else If(BCFLG == 1) //如果已执行多头开仓
{
If((BKFLGA == 0 && BKSMA == 0) && (SKFLGB == 0 && SKSMB == 0)) //如果已完成多头开仓
{
Commentary("【多头开仓:多头开仓完成!】");
BCFLG = 2; //已完成多头开仓
}
Else If(BKFLGA == 0 && BKSMA == 0) //如果A已完成多头开仓
{
If(Abs(NPC - NPCA) > PLM * MINPA) //如果价差偏离过大
{
If(BPLA > 0) //如果A多头持仓盈利
{
CDFLG = 1; //开启追价处理
}
Else If(BPLA < 0) //如果A多头持仓亏损
{
CDFLG = 2; //开启撤单处理
}
}
}
Else If(SKFLGB == 0 && SKSMB == 0) //如果B已完成多头开仓
{
If(Abs(NPC - NPCA) > PLM * MINPA) //如果价差偏离过大
{
If(SPLB > 0) //如果B空头持仓盈利
{
CDFLG = 1; //开启追价处理
}
Else If(SPLB < 0) //如果B空头持仓亏损
{
CDFLG = 2; //开启撤单处理
}
}
}
}
If(BCFLG == 2) //如果已完成多头开仓
{
If(NPC >= PCP) //如果满足多头平仓条件
{
Commentary("【多头平仓:满足平仓条件!】");
SPDFLGA = 1; //开启A卖平处理
BPDFLGB = 1; //开启B买平处理
BCFLG = 3; //已执行多头平仓
}
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(BKDFLGA == 1) //如果已开启A买开处理
{
If(BKFLGA == 0) //如果没有A买开委托
{
BKMA = Max(Min(BKSMA,ASKVA),1); //A买开委托手数
BKSMA = BKSMA - BKMA; //A买开委托总手数
Commentary("【委托手数:" + Text(BKMA) + "手!】");
Commentary("【剩余手数:" + Text(BKSMA) + "手!】");
Commentary("【多头开仓:A买开委托发出!】");
BKIDA = CODA.A_SendOrder(Enum_Buy,Enum_Entry,BKMA,ASKPA); //发出A买开委托
BKTA = NOW; //A买开委托时间
BKFLGA = 1; //已发出A买开委托
}
}
If(SKDFLGB == 1) //如果已开启B卖开处理
{
If(SKFLGB == 0) //如果没有B卖开委托
{
SKMB = Max(Min(SKSMB,BIDVB),1); //B卖开委托手数
SKSMB = SKSMB - SKMB; //B卖开委托总手数
Commentary("【委托手数:" + Text(SKMB) + "手!】");
Commentary("【剩余手数:" + Text(SKSMB) + "手!】");
Commentary("【多头开仓:B卖开委托发出!】");
SKIDB = CODB.A_SendOrder(Enum_Sell,Enum_Entry,SKMB,BIDPB); //发出B卖开委托
SKTB = NOW; //B卖开委托时间
SKFLGB = 1; //已发出B卖开委托
}
}
If(SPDFLGA > 0) //如果已开启A卖平处理
{
If(SPFLGA == 0) //如果没有A卖平委托
{
If(BRPA > 0) //如果有A多头可用持仓
{
If(SPDFLGA == 1) //如果是多头平仓卖平
{
SPMA = BRPA; //A卖平委托手数
}
Else If(SPDFLGA == 2) //如果是单腿平仓卖平
{
SPMA = BRPA - SRPB; //A卖平委托手数
}
If(SPMA > 0) //如果A卖平委托手数大于0
{
Commentary("【多头平仓:A卖平委托发出!】");
SPIDA = CODA.A_SendOrder(Enum_Sell,SHA,SPMA,BIDPA); //发出A卖平委托
SPFLGA = 1; //已发出A卖平委托
}
}
}
}
If(BPDFLGB > 0) //如果已开启B买平处理
{
If(BPFLGB == 0) //如果没有B买平委托
{
If(SRPB > 0) //如果有B空头可用持仓
{
If(BPDFLGB == 1) //如果是多头平仓买平
{
BPMB = SRPB; //B买平委托手数
}
Else If(BPDFLGB == 2) //如果是单腿平仓买平
{
BPMB = SRPB - BRPA; //B买平委托手数
}
If(BPMB > 0) //如果B买平委托手数大于0
{
Commentary("【多头平仓:B买平委托发出!】");
BPIDB = CODB.A_SendOrder(Enum_Buy,SHB,BPMB,ASKPB); //发出B买平委托
BPFLGB = 1; //已发出B买平委托
}
}
}
}
}
End
运行效果:
移仓和套利不同,套利交易可以根据两腿合约的流动性等因素,灵活选择两腿合约的交易顺序,而移仓时,因为账户中不一定有足够的可用资金用于增仓,所以往往都需要先平仓,再开仓。
移仓附加算法交易,可自动监控移仓价差。当价差符合条件时,可先对近月合约进行平仓,且平仓成交以后,远月合约自动开仓,降低空仓风险。同时可对移仓交易过程精细化控制,减少成交滑点,降低交易成本。
下面我们来看一个PVC多头套保移仓的策略:
近月合约:PVC2009
远月合约:PVC2101
移仓条件:远月合约价格 - 近月合约价格 <= 100 开始移仓
移仓手数:300手
移仓方式:
1、先平近月,再开远月,分批次移仓。
2、每批平仓开始前判断移仓价差是否符合要求。近月每批次平仓手数为近月合约对手盘的20%。
3、近月合约平仓只要有成交,则远月合约自动发出开仓委托,委托数量为远月合约平仓已成交手数。近月合约部分成交时,远月合约即可发出开仓委托,无需等待上一笔成交,可直接发出多笔开仓委托。
4、首次对手价委托,3秒不成交自动追价,直到全部成交。
策略代码:
Data
CODA:"v2009"; //近月A合约
CODB:"v2101"; //远月B合约
Vars
Numeric N; //下单手数
Numeric X; //循环变量
Numeric P; //条件价格
Numeric T; //时间间隔
Numeric YCP; //移仓比例
Numeric NOW; //当前时间
Numeric NCP; //最新价差
Numeric NEWPA; //A最新价
Numeric NEWPB; //B最新价
Numeric BIDPA; //A买一价
Numeric ASKPB; //B卖一价
Numeric BIDVA; //A买一量
Numeric BRP; //多头可用持仓
Numeric BKVLM; //买开手数
Numeric SPVLM; //卖平手数
Numeric OPFLG; //模型处理标志
Numeric ODFLG; //挂单处理标志
Numeric BKDFLG; //买开处理标志
Numeric SPDFLG; //卖平处理标志
Global_Numeric YCM; //移仓手数
Global_Numeric VLFLG; //变量赋值标志
Global_Numeric YCFLG; //移仓处理标志
Global_Numeric SPID; //卖平委托
Global_Numeric SPFLG; //卖平标志
Global_Numeric SPDEL; //卖平撤单标志
Global_Numeric SPCM; //卖平成交手数
Global_Numeric SPCMA; //记录卖平成交手数
Global_Numeric SPM; //卖平委托手数
Global_Numeric SPP; //卖平委托价格
Global_Numeric SPT; //卖平委托时间
Global_Numeric BKN; //买开委托数量
Global_NumericArray BKID; //买开委托
Global_NumericArray BKFLG; //买开标志
Global_NumericArray BKDEL; //买开撤单标志
Global_NumericArray BKM; //买开委托手数
Global_NumericArray BKP; //买开委托价格
Global_NumericArray BKT; //买开委托时间
Begin
//------------------------处理开启------------------------//
If(CODA.A_IsExchangeOpen() == 1) //如果A是开盘状态
{
If(CODB.A_IsExchangeOpen() == 1) //如果B是开盘状态
{
OPFLG = 1; //开启模型处理
}
}
//------------------------变量赋值------------------------//
If(OPFLG == 1) //变量赋值
{
N = 300; //下单手数
T = 3; //时间间隔
P = 100; //条件价格
YCP = 0.2; //移仓比例
NOW = CurrentTime(); //当前时间
NEWPA = CODA.Price("New"); //A最新价
NEWPB = CODB.Price("New"); //B最新价
BIDPA = CODA.Price("Bid1"); //A卖一价
ASKPB = CODB.Price("Ask1"); //B买一价
BIDVA = CODA.Price("BidVol1"); //A买一量
BRP = CODA.A_BuyRemainPosition(); //A多头可用持仓
NCP = NEWPB - NEWPA; //最新价差
If(VLFLG == 0) //如果变量未赋值
{
YCM = N; //移仓手数
Commentary("【换月移仓:移仓处理开始!】");
YCFLG = 1; //开启换月移仓处理
VLFLG = 1; //变量已赋值
}
}
//------------------------成交判断------------------------//
If(OPFLG == 1) //成交判断
{
If(SPFLG == 1) //如果有卖平委托
{
SPCM = F_OrderFilledLot(SPID); //卖平成交手数
If(SPCM > 0 && SPCM > SPCMA) //如果有新卖平成交手数
{
BKVLM = SPCM - SPCMA; //买开手数
BKDFLG = 1; //开启买开处理
SPCMA = SPCM; //记录卖平成交手数
}
If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
{
Commentary("【多头移仓:A卖平委托成交!】");
SPFLG = 0; //卖平标志归0
SPDEL = 0; //卖平撤单标志归0
}
Else If(F_OrderStatus(SPID) == Enum_Canceled) //如果卖平委托已撤
{
Commentary("【多头移仓:A卖平委托已撤!】");
If(SPCM > 0) //如果卖平委托部分成交
{
SPM = SPM - SPCM; //卖平委托手数
}
If(BRP > 0 && SPM > 0 && SPM <= BRP) //如果卖平委托手数不超过多头可用持仓
{
SPP = BIDPA; //卖平委托价格
Commentary("【多头移仓:A卖平委托追价!】");
SPID = CODA.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP); //发出卖平委托
SPT = NOW; //卖平委托时间
SPCM = 0; //卖平成交手数
SPCMA = 0; //记录卖平成交手数
}
SPDEL = 0; //卖平撤单标志归0
}
Else If(F_OrderStatus(SPID) == Enum_Declared || F_OrderStatus(SPID) == Enum_FillPart) //如果卖平委托挂单
{
If(SPDEL == 0) //如果未撤单
{
If(TimeDiff(SPT,NOW) >= T) //如果时间间隔T秒
{
Commentary("【多头移仓:A卖平委托撤单!】");
CODA.F_DeleteOrder(F_OrderContractNo(SPID)); //撤掉卖平委托挂单
SPDEL = 1; //已发出撤掉卖平委托挂单
}
}
}
}
For X = 0 To BKN - 1 //遍历买开委托
{
If(BKFLG[X] == 1) //如果有买开委托
{
If(F_OrderStatus(BKID[X]) == Enum_Filled) //如果买开委托成交
{
Commentary("【多头移仓:B买开委托" + Text(X + 1) + "成交!】");
BKFLG[X] = 0; //买开标志归0
BKDEL[X] = 0; //买开撤单标志归0
}
Else If(F_OrderStatus(BKID[X]) == Enum_Canceled) //如果买开委托已撤
{
Commentary("【多头移仓:B买开委托" + Text(X + 1) + "已撤!】");
If(F_OrderFilledLot(BKID[X]) > 0) //如果买开委托部分成交
{
BKM[X] = BKM[X] - F_OrderFilledLot(BKID[X]); //买开委托手数
}
If(BKM[X] > 0) //如果买开委托手数大于0
{
BKP[X] = ASKPB; //买开委托价格
Commentary("【多头移仓:B买开委托" + Text(X + 1) + "追价!】");
BKID[X] = CODB.A_SendOrder(Enum_Buy,Enum_Entry,BKM[X],BKP[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("【多头移仓:B买开委托" + Text(X + 1) + "撤单!】");
CODB.F_DeleteOrder(F_OrderContractNo(BKID[X])); //撤掉买开委托挂单
BKDEL[X] = 1; //已发出撤掉买开委托挂单
}
}
}
}
}
ODFLG = 1; //挂单判断标志初始化
If(SPFLG == 1 || BKDFLG == 1) //如果有卖平委托,或已开启买开处理
{
ODFLG = 2; //存在卖平委托挂单或买开处理
}
For X = 0 To BKN - 1 //遍历买开委托
{
If(BKFLG[X] == 1) //如果有买开委托
{
ODFLG = 2; //存在买开委托挂单
}
}
}
//------------------------换月移仓------------------------//
If(OPFLG == 1) //换月移仓
{
If(YCFLG == 1) //如果已开启换月移仓处理
{
If(ODFLG == 1) //如果没有委托挂单
{
If(YCM > 0) //如果有剩余移仓手数
{
If(NCP <= P) //如果满足移仓平仓条件
{
SPVLM = Min(Floor(YCP * BIDVA),YCM); //卖平手数
SPDFLG = 1; //开启卖平处理
}
}
Else If(YCM == 0) //如果没有剩余移仓手数
{
Commentary("【换月移仓:移仓处理完成!】");
YCFLG = 0; //移仓处理标志归0
}
}
}
}
//------------------------委托处理------------------------//
If(OPFLG == 1) //委托处理
{
If(SPDFLG == 1) //如果已开启卖平处理
{
If(SPVLM > 0) //如果卖平手数大于0
{
If(BRP > 0) //如果有多头可用持仓
{
SPM = Min(SPVLM,BRP); //卖平委托手数
SPP = BIDPA; //卖平委托价格
YCM = YCM - SPM; //移仓手数
Commentary("【多头移仓:剩余多仓=" + Text(YCM) + "手!】");
Commentary("【多头移仓:A卖平委托发出!】");
SPID = CODA.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP); //发出卖平委托
SPT = NOW; //卖平委托时间
SPCM = 0; //卖平成交手数
SPCMA = 0; //记录卖平成交手数
SPFLG = 1; //已发出卖平委托
}
}
}
If(BKDFLG == 1) //如果已开启买开处理
{
If(BKVLM > 0) //如果买开手数大于0
{
BKM[BKN] = BKVLM; //买开委托手数
BKP[BKN] = ASKPB; //买开委托价格
Commentary("【多头移仓:B买开委托" + Text(BKN + 1) + "发出!】");
BKID[BKN] = CODB.A_SendOrder(Enum_Buy,Enum_Entry,BKM[BKN],BKP[BKN]); //发出买开委托
BKT[BKN] = NOW; //买开委托时间
BKFLG[BKN] = 1; //已发出买开委托
BKN = BKN + 1; //买开委托数量
}
}
}
End