|
交易策略的代码写法会因为交易思想及编程习惯因人而异,在此按常用的功能点列出代码示例,用户可根据自己的需要选择对应的代码进行组合。
止赢止损
跟踪止损
加仓减仓
多品种交易
集合竞价数据过滤
收盘平仓
A函数下单撤单和全局变量操作
数据库读写
模板以止赢30跳,止损20跳为例,也可以转换为开仓价格的百分比值,或其任何设置的变量进行处理。
Vars
Numeric MinPoint; // 一个最小变动单位,也就是一跳
Numeric MyEntryPrice; // 开仓价格,本例是开仓均价,也可根据需要设置为某次入场的价格
Numeric TakeProfitSet(30); // 止赢设置
Numeric StopLossSet(20); // 止损设置
Numeric MyExitPrice; // 平仓价格
Begin
...
MinPoint = MinMove*PriceScale;
MyEntryPrice = AvgEntryPrice;
If(MarketPosition==1) // 有多仓的情况
{
If(High >= MyEntryPrice + TakeProfitSet*MinPoint) // 止赢条件表达式
{
MyExitPrice = MyEntryPrice + TakeProfitSet*MinPoint;
If(Open > MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
Sell(0,MyExitPrice);
}else if(Low <= MyEntryPrice - StopLossSet*MinPoint)// 止损条件表达式
{
MyExitPrice = MyEntryPrice - StopLossSet*MinPoint;
If(Open < MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
Sell(0,MyExitPrice);
}
}else if(MarketPosition==-1) // 有空仓的情况
{
If(Low <= MyEntryPrice - TakeProfitSet*MinPoint) // 止赢条件表达式
{
MyExitPrice = MyEntryPrice - TakeProfitSet*MinPoint;
If(Open < MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
BuyToCover(0,MyExitPrice);
}else if(High >= MyEntryPrice + StopLossSet*MinPoint)// 止损条件表达式
{
MyExitPrice = MyEntryPrice + StopLossSet*MinPoint;
If(Open > MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
BuyToCover(0,MyExitPrice);
}
}
...
End
注意事项:
因无法确认开仓Bar最高/低价和开仓价的先后顺序,因此以上写法一般忽略开仓Bar的处理。
如果某个Bar最高/低价相差很大,可能出现止赢止损同时满足的情况,这种情况下需要切换到更小的周期进行交易,或者扩大止赢/损幅度。
跟踪止损有很多种方式,本模板的规则如下:当盈利达到50跳之后启动第一级跟踪止损,止损的回撤值为30跳,当盈利达到80跳之后启动第二级的跟踪止损,止损的回撤值为20跳。您也可以将这些固定的设置修改为盈利百分比,或者是某个价格的百分比。
Vars
Numeric MinPoint; // 一个最小变动单位,也就是一跳
Numeric MyEntryPrice; // 开仓价格,本例是开仓均价,也可根据需要设置为某次入场的价格
Numeric TrailingStart1(50); // 跟踪止损启动设置1
Numeric TrailingStart2(80); // 跟踪止损启动设置2
Numeric TrailingStop1(30); // 跟踪止损设置1
Numeric TrailingStop2(20); // 跟踪止损设置2
Numeric StopLossSet(50); // 止损设置
Numeric MyExitPrice; // 平仓价格
NumericSeries HighestAfterEntry; // 开仓后出现的最高价
NumericSeries LowestAfterEntry; // 开仓后出现的最低价
Begin
...
If(BarsSinceentry == 0)
{
HighestAfterEntry = Close;
LowestAfterEntry = Close;
If(MarketPosition <> 0)
{
HighestAfterEntry = Max(HighestAfterEntry,AvgEntryPrice); // 开仓的Bar,将开仓价和当时的收盘价的较大值保留到HighestAfterEntry
LowestAfterEntry = Min(LowestAfterEntry,AvgEntryPrice); // 开仓的Bar,将开仓价和当时的收盘价的较小值保留到LowestAfterEntry
}
}else
{
HighestAfterEntry = Max(HighestAfterEntry,High); // 记录下当前Bar的最高点,用于下一个Bar的跟踪止损判断
LowestAfterEntry = Min(LowestAfterEntry,Low); // 记录下当前Bar的最低点,用于下一个Bar的跟踪止损判断
}
Commentary("HighestAfterEntry="+Text(HighestAfterEntry));
Commentary("LowestAfterEntry="+Text(LowestAfterEntry));
MinPoint = MinMove*PriceScale;
MyEntryPrice = AvgEntryPrice;
If(MarketPosition==1) // 有多仓的情况
{
If(HighestAfterEntry[1] >= MyEntryPrice + TrailingStart2*MinPoint) // 第二级跟踪止损的条件表达式
{
If(Low <= HighestAfterEntry[1] - TrailingStop2*MinPoint)
{
MyExitPrice = HighestAfterEntry[1] - TrailingStop2*MinPoint;
If(Open < MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
Sell(0,MyExitPrice);
}
}else if(HighestAfterEntry[1] >= MyEntryPrice + TrailingStart1*MinPoint)// 第一级跟踪止损的条件表达式
{
If(Low <= HighestAfterEntry[1] - TrailingStop1*MinPoint)
{
MyExitPrice = HighestAfterEntry[1] - TrailingStop1*MinPoint;
If(Open < MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
Sell(0,MyExitPrice);
}
}else if(Low <= MyEntryPrice - StopLossSet*MinPoint)//可以在这里写上初始的止损处理
{
MyExitPrice = MyEntryPrice - StopLossSet*MinPoint;
If(Open < MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
Sell(0,MyExitPrice);
}
}else if(MarketPosition==-1) // 有空仓的情况
{
If(LowestAfterEntry[1] <= MyEntryPrice - TrailingStart2*MinPoint) // 第二级跟踪止损的条件表达式
{
If(High >= LowestAfterEntry[1] + TrailingStop2*MinPoint)
{
MyExitPrice = LowestAfterEntry[1] + TrailingStop2*MinPoint;
If(Open > MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
BuyToCover(0,MyExitPrice);
}
}else if(LowestAfterEntry[1] <= MyEntryPrice - TrailingStart1*MinPoint)// 第一级跟踪止损的条件表达式
{
If(High >= LowestAfterEntry[1] + TrailingStop1*MinPoint)
{
MyExitPrice = LowestAfterEntry[1] + TrailingStop1*MinPoint;
If(Open > MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
BuyToCover(0,MyExitPrice);
}
}else If(High >= MyEntryPrice + StopLossSet*MinPoint)//可以在这里写上初始的止损处理
{
MyExitPrice = MyEntryPrice + StopLossSet*MinPoint;
If(Open > MyExitPrice) MyExitPrice = Open; // 如果该Bar开盘价有跳空触发,则用开盘价代替
BuyToCover(0,MyExitPrice);
}
}
...
End
注意事项:
因无法确认开仓Bar最高/低价和开仓价的先后顺序,因此以上写法一般忽略开仓Bar的处理。
如果某个Bar最高/低价相差很大,可能出现创新高之后跟踪止损的情况,但系统无法确认最高价和最低价的先后顺序,因此本模板只用前一个Bar的最高/低价计算最大赢利位置。
本例仅以做多为例,做空类似。模板以首次开仓2手后每赢利30跳加仓一次,每次1手,最多加仓3次;开仓后每亏损30跳减仓1手。也可以转换为开仓价格的百分比值,或波动率的百分比等其任何设置的变量进行处理。
Vars
Numeric MinPoint; // 一个最小变动单位,也就是一跳
NumericSeries firstPrice; // 第一次开仓价格
NumericSeries LastPrice; // 最后一次开仓价格
Numeric AddSet(30); // 加仓设置
Numeric SubSet(30); // 减仓设置
Bool FirstEntryCon; // 首次开仓条件
Begin
FirstEntryCon = ...
MinPoint = MinMove*PriceScale;
If(MarketPosition==0)
{
If(FirstEntryCon)
{
firstPrice = Open;
LastPrice = firstPrice;
Buy(2,firstPrice);
}
}else If(MarketPosition==1) // 有多仓的情况
{
While(CurrentEntries < 4 && High >= LastPrice + AddSet*MinPoint) // 加仓
{
LastPrice = LastPrice + AddSet*MinPoint;
if(Open > LastPrice) LastPrice = Open;
Buy(1,LastPrice);
}
While(CurrentEntries > 0 && Low <= firstPrice - SubSet*MinPoint) // 减仓
{
firstPrice = firstPrice - SubSet*MinPoint;
if(Open < firstPrice) firstPrice = Open;
Sell(1,firstPrice);
}
}
...
End
注意事项:
因无法确认开仓Bar最高/低价和开仓价的先后顺序,忽略开仓Bar的加减仓处理。
如果某个Bar最高/低价相差很大,可能出现加仓减仓同时满足的情况,这种情况下需要切换到更小的周期进行交易,或者扩大加仓/减仓幅度设置。
模板以常用的双均线系统为例,对主图商品和叠加商品分别进行交易。
Params
Numeric FastLength1(5); // Data0的短周期参数
Numeric SlowLength1(20); // Data0的长周期参数
Numeric FastLength2(5); // Data1的短周期参数
Numeric SlowLength2(20); // Data1的长周期参数
Vars
NumericSeries AvgValue11;
NumericSeries AvgValue12;
NumericSeries AvgValue21;
NumericSeries AvgValue22;
Begin
AvgValue11 = AverageFC(Data0.Close,FastLength1);
AvgValue12 = AverageFC(Data0.Close,SlowLength1);
AvgValue21 = AverageFC(Data1.Close,FastLength2);
AvgValue22 = AverageFC(Data1.Close,SlowLength2);
If(Data0.MarketPosition <>1 && AvgValue11[1] > AvgValue12[1])
{
Data0.Buy(1,Data0.Open);
}
If(Data0.MarketPosition <>-1 && AvgValue11[1] < AvgValue12[1])
{
Data0.SellShort(1,Data0.Open);
}
If(Data1.MarketPosition <>1 && AvgValue21[1] > AvgValue22[1])
{
Data1.Buy(1,Data1.Open);
}
If(Data1.MarketPosition <>-1 && AvgValue21[1] < AvgValue22[1])
{
Data1.SellShort(1,Data1.Open);
}
End
注意事项:
针对不同的商品的数据进行计算或交易,需通过Data#这样的方式添加前缀,Data0可与省略不写。
Data#的顺序和超级图表中商品设置界面的顺序相同,必须要叠加足够的商品才能保证代码正常执行。
集合竞价时,会产生一个Tick,这个Tick会驱动超级图表计算交易策略,如果条件满足,则会马上发送委托单,但此时交易所并未开市,就会产生废单,为了处理这种情况,可以采取下面方法:
Begin
If(BarStatus==2 && Time==0.090000 && High==Low) return; // 第一种写法
If(BarStatus==2 && Time==0.090000 && CurrentTime <= 0.090000) return; // 第二种写法
...
End
注意事项:
本例是以国内商品期货交易所开市时间举例,股指期货或其他市场需调整写法。
若按第一种写法,当商品的高低价不产生变化时,会忽略这些Tick,即使是已经开市;第二种写法需保证本机的时间准确。
收盘平仓分为两部分,一部分负责处理历史测试,一部分负责处理实时交易。在测试时我们可以以每天的收盘价平仓,在实时交易时我们选择14:59分平仓。
Begin
...
If((Date[-1]!=InvalidInteger && Date!=Date[-1])||(Date[-1]==InvalidInteger && Date < CurrentDate))
{
Sell(0,Close);
BuyToCover(0,Close);
}Else If(Date==CurrentDate && Time==0.1455 && CurrentTime>=0.1459)
{
Sell(0,Close);
BuyToCover(0,Close);
}
...
End
注意事项:
本例是以国内商品期货交易所收市时间举例,股指期货或其他市场需调整写法。
本例是针对5分钟周期的收盘平仓所写,针对不同的周期需改写为合适的最后Bar时间。
本例在每天收盘前N分钟的时候自动撤掉超级图表中商品的挂单,并全部平仓。通过A_SendOrder进行下单,A_DeleteOrder进行撤单,并使用全局变量记录Tick计数和撤单标志。
Params
Numeric offSet(1); // 委托价格偏移,为了保证成交
Numeric BeforeMins(5); // 收盘前几分钟开始操作
Vars
Numeric tempPos; // 仓位
Numeric DeleteOrderTickCounter;
Numeric HasSendOrder(0);
Begin
If(BarStatus == 0)
{
DeleteOrderTickCounter = 9999;
HasSendOrder = 0;
SetGlobalVar(0,DeleteOrderTickCounter);
SetGlobalVar(1,HasSendOrder);
}Else
{
DeleteOrderTickCounter = GetGlobalVar(0);
HasSendOrder = GetGlobalVar(1);
}
If(CurrentTime > (0.1459 - 0.0001*(BeforeMins-1)) && BarStatus == 2 && HasSendOrder == 0)
{
If(Data0.Close != InvalidNumeric && Data0.A_GetOpenOrderCount()>0) // 商品0全部撤单
{
Data0.A_DeleteOrder();
DeleteOrderTickCounter = 1;
}
If(Data1.Close != InvalidNumeric && Data1.A_GetOpenOrderCount()>0) // 商品1全部撤单
{
Data1.A_DeleteOrder();
DeleteOrderTickCounter = 1;
}
If(Data2.Close != InvalidNumeric && Data2.A_GetOpenOrderCount()>0) // 商品2全部撤单
{
Data2.A_DeleteOrder();
DeleteOrderTickCounter = 1;
}
DeleteOrderTickCounter = DeleteOrderTickCounter + 1;
SetGlobalVar(0,DeleteOrderTickCounter);
If(DeleteOrderTickCounter < 5) Return; // 撤单后需要延迟几个Tick才平仓
tempPos = Data0.A_BuyPosition();
If(tempPos > 0) // 平多单
{
Data0.A_SendOrder(Enum_Sell,Enum_Exit,tempPos,Data0.Q_BidPrice-offSet*Data0.MinMove*Data0.PriceScale);
}
tempPos = Data0.A_SellPosition();
If(tempPos > 0) //平空单
{
Data0.A_SendOrder(Enum_Buy,Enum_Exit,tempPos,Data0.Q_AskPrice+offSet*Data0.MinMove*Data0.PriceScale);
}
tempPos = Data1.A_BuyPosition;
If(tempPos > 0) // 平多单
{
Data1.A_SendOrder(Enum_Sell,Enum_Exit,tempPos,Data1.Q_BidPrice-offSet*Data1.MinMove*Data1.PriceScale);
}
tempPos = Data1.A_SellPosition;
If(tempPos > 0) //平空单
{
Data1.A_SendOrder(Enum_Buy,Enum_Exit,tempPos,Data1.Q_AskPrice+offSet*Data1.MinMove*Data1.PriceScale);
}
tempPos = Data2.A_BuyPosition;
If(tempPos > 0) // 平多单
{
Data2.A_SendOrder(Enum_Sell,Enum_Exit,tempPos,Data2.Q_BidPrice-offSet*Data2.MinMove*Data2.PriceScale);
}
tempPos = Data2.A_SellPosition;
If(tempPos > 0) //平空单
{
Data2.A_SendOrder(Enum_Buy,Enum_Exit,tempPos,Data2.Q_AskPrice+offSet*Data2.MinMove*Data2.PriceScale);
}
HasSendOrder = 1;
SetGlobalVar(1,HasSendOrder);
}
End
注意事项:
本例是以国内商品期货交易所收市时间举例,股指期货或其他市场需调整写法。
本例假设撤单后5个Tick委托状态能同步成功,实际情况中因网络延时等原因并不一定能够保证成功。
本例以5分钟周期调用日线指标数据举例讲解具体应用。
操作步骤如下:
新建一个工作区,包含上下两个图表窗体,上面选择日线周期,下面选择5分钟周期。
新建一个公式应用,命名为MyDayMA。编译成功后插入日线图表中。详细代码如下:
Params
Numeric length(10);
Vars
Numeric MA;
string strkey;
string strValue;
Begin
MA = AverageFC(Close,length);
strKey = DateToString(Date);
strValue = Text(MA);
SetTBProfileString("DayMA",strKey,strValue);
PlotNumeric("MA",MA);
End
新建一个公式应用,My5MinMA。编译成功后插入5分钟图表中,详细代码如下:
Vars
NumericSeries DayMAValue;
string strKey;
string strValue;
Begin
strKey = DateToString(Date);
strValue = GetTBProfileString("DayMA",strKey);
If(strValue != InvalidString)
{
DayMAValue = Value(strValue);
}Else
{
DayMAValue = DayMAValue[1];
}
PlotNumeric("DayMA",DayMAValue);
End
上面的公式实际使用了未来数据,用来写技术分析是可以的,但用来进行自动交易就会出问题,为了更准确合理的使用跨周期数据,我们应该稍作修改,代码如下:
Vars
NumericSeries DayMAValue;
StringSeries strKey;
string strValue;
Begin
If(Date!=Date[1])
{
strKey = DateToString(Date[1]);
}Else
{
strKey = strKey[1];
}
strValue = GetTBProfileString("DayMA",strKey);
If(strValue != InvalidString)
{
DayMAValue = Value(strValue);
}Else
{
DayMAValue = DayMAValue[1];
}
PlotNumeric("DayMA",DayMAValue);
End
本例运行时界面如下图所示:
|