Params
Numeric AvgLength(5); // 均线周期
Numeric AvgDisplace(5); // 置换均线向后平移Bar数
Numeric ValidBars1(5); // 开仓先决条件之一(收盘价上穿DMA均线)条件值保持有效的BAR数
Numeric ValidBars2(5); // 开仓先决条件之二(上穿后再下穿)条件值保持有效的BAR数
Numeric ValidBars3(5); // 开仓先决条件(上穿再下穿再上穿)条件值保持有效的BAR数
Numeric TrailStopBars(5); // 多少根BAR的最低价作为跟踪止损价
Vars
Series<Numeric> MA; // 均线
Series<Numeric> DMA; // 置换均线
Series<Bool> ConCrossOver; // 当前BAR是否上穿DMA
Series<Bool> ConCrossUnder; // 当前BAR是否下穿DMA
Numeric BarsLastCrsOvr; // 最近一次上穿离现在的BAR数
Numeric BarsFstCrsUnd; // 最近倒数第二次下穿离现在的BAR数
Numeric BarsSecCrsUnd; // 最近的一次下穿离现在的BAR数
Series<Bool> EntryFlag(False); // 开仓标志
Series<Numeric> EntryPoint; // 突破开仓的价格
Series<Numeric> EntryCount; // 满足开仓先决条件的BAR计数
Numeric ReversalPrice; // 趋势反向的平仓价格
Numeric TrailStopPrice; // 跟踪止损的平仓价格
Events
OnBar(ArrayRef<Integer> indexs)
{
// 计算置换均线
MA = Average(Close, AvgLength);
DMA = MA[AvgDisplace];
PlotNumeric("DMA",DMA);
// 判断收盘价是否穿越置换均线
ConCrossOver = CrossOver(Close,DMA);
ConCrossUnder = CrossUnder(Close,DMA);
// 计算最近的一次上穿发生的BAR离当前BAR的根数
BarsLastCrsOvr = NthCon(ConCrossOver==True,1);
// 计算最近的两次下穿发生的BAR离当前BAR的根数
BarsFstCrsUnd = NthCon(ConCrossUnder==True,2);
BarsSecCrsUnd = NthCon(ConCrossUnder==True,1);
// 设置开仓标志
If(ConCrossUnder And BarsLastCrsOvr - BarsSecCrsUnd <= ValidBars2 And BarsFstCrsUnd - BarsLastCrsOvr <= ValidBars1)
{
EntryFlag = True;
EntryPoint = Low - MinMove * PriceScale;
EntryCount = 0;
}
Commentary("EntryFlag = "+iifstring(EntryFlag,"True","False"));
Commentary("EntryPoint = "+Text(EntryPoint));
Commentary("EntryCount = "+Text(EntryCount));
// 开仓
If(MarketPosition == 0 And EntryCount <= ValidBars3)
{
If(EntryFlag And Low <= EntryPoint And Vol > 0)
{
SellShort(0, Min(Open,EntryPoint));
}
Else
{
EntryCount = EntryCount + 1;
}
}
// 开仓或者开仓先决条件已过有效BAR数,修改开仓标志
If(MarketPosition == -1 Or EntryCount > ValidBars3)
{
EntryFlag = False;
}
// 止损价格计算
ReversalPrice = DMA[1] + MinMove * PriceScale;
TrailStopPrice = Highest(High[1],TrailStopBars);
Commentary("ReversalPrice = "+Text(ReversalPrice));
Commentary("TrailStopPrice = "+Text(TrailStopPrice));
// 平仓
If(MarketPosition == -1 And BarsSinceEntry > 0 And Vol > 0)
{
If(High >= Min(ReversalPrice,TrailStopPrice))
{
BuyToCover(0,Max(Open, Min(ReversalPrice,TrailStopPrice) ) );
}
}
}
老师你好,我研究开拓者自带的 基于置换均线的二次穿越突破系统空 这个策略的时候,发现这段代码没有回溯,
If(ConCrossUnder And BarsLastCrsOvr - BarsSecCrsUnd <= ValidBars2 And BarsFstCrsUnd - BarsLastCrsOvr <= ValidBars1)
{
EntryFlag = True;
EntryPoint = Low - MinMove * PriceScale;
EntryCount = 0;
}
而且EntryFlag作为开仓条件之一,他也没回溯,这样不会导致信号闪烁吗? 这是开拓者自带的策略,不可能会闪烁才对,这是什么原理呢?老师指导一下,谢谢
// 开仓
If(MarketPosition == 0 And EntryCount <= ValidBars3)
{
If(EntryFlag And Low <= EntryPoint And Vol > 0)
{
SellShort(0, Min(Open,EntryPoint));
}
Else
{
EntryCount = EntryCount + 1;
}
}
您好,这个内置策略是我当年写的,我有点忘了当时为啥没有加上回溯,看上去确是有信号闪烁的可能,我上午检查并测试一下再回复您吧。感谢您指出我们代码中的问题!
我中午测试了一下,确实有信号闪烁的情况。我重新回忆了一下策略的逻辑,在修复信号闪烁问题的同时,我发现策略实现逻辑上还有问题,因为几次穿越的计算每根BAR都在计算,新的计算结果又会覆盖上次的进场机会。这里就有个选择,当前BAR是继续按上根BAR的进场机会判断,还是按新的计算结果判断。我现在改了一个版本是优先按上根BAR的进场机会。我只写了做空的部分,先发出来供您参考下。内置代码的修改,我会再多测试测试,再提交给研发更新。
//------------------------------------------------------------------------
// 简称: test_doubleyourfun_S
// 名称: 临时测试, 基于置换均线的二次穿越突破系统空
// 类别: 策略应用
// 类型: 用户应用
// 输出: Void
//------------------------------------------------------------------------
//----------------------------------------------------------------------//
// 策略说明:
// 本策略是基于置换均线的二次穿越突破系统
//
// 系统要素:
// 1. 将移动平均K线向后平移一定BAR数即为置换均线
// 2. 相隔一定BAR数的收盘价二次穿越置换均线
// 3. 二次穿越完成时那根BAR的高点(或低点)作为突破进场价
// 4. 完成二次穿越的一定BAR数内突破
// 入场条件:
// 1. 有效期内价格向上突破设定进场价做多
// 2. 有效期内价格向下突破设定进场价做空
// 出场条件:
// 1. 价格反向穿越均线后止损
// 2. 基于N根K线的高低点的跟踪止损
//
// 注: 当前策略仅为做空系统, 如需做多, 请参见CL_DoubleYourFun_L
//----------------------------------------------------------------------//
Params
Numeric AvgLength(5); // 均线周期
Numeric AvgDisplace(5); // 置换均线向后平移Bar数
Numeric ValidBars1(5); // 开仓先决条件之一(收盘价上穿DMA均线)条件值保持有效的BAR数
Numeric ValidBars2(5); // 开仓先决条件之二(上穿后再下穿)条件值保持有效的BAR数
Numeric ValidBars3(5); // 开仓先决条件(上穿再下穿再上穿)条件值保持有效的BAR数
Numeric TrailStopBars(5); // 多少根BAR的最低价作为跟踪止损价
Vars
Series<Numeric> MAVal; // 均线
Series<Numeric> DMA; // 置换均线
Series<Bool> ConCrossOver; // 当前BAR是否上穿DMA
Series<Bool> ConCrossUnder; // 当前BAR是否下穿DMA
Numeric BarsLastCrsOvr; // 最近一次上穿离现在的BAR数
Numeric BarsFstCrsUnd; // 最近倒数第二次下穿离现在的BAR数
Numeric BarsSecCrsUnd; // 最近的一次下穿离现在的BAR数
Series<Bool> EntryFlag(False); // 开仓标志
Series<Numeric> EntryPoint; // 突破开仓的价格
Series<Numeric> EntryCount; // 满足开仓先决条件的BAR计数
Numeric ReversalPrice; // 趋势反向的平仓价格
Numeric TrailStopPrice; // 跟踪止损的平仓价格
Events
OnReady()
{
Range[0:DataSourceSize() - 1]
{
setPlotOption("DMA", "begin-bar", AvgLength);
}
}
OnBar(ArrayRef<Integer> indexs)
{
// 计算置换均线
MAVal = Average(Close, AvgLength);
DMA = MAVal[AvgDisplace];
PlotNumeric("DMA",DMA);
// 开仓
If(MarketPosition == 0 And EntryCount <= ValidBars3)
{
If(EntryFlag[1])
{
If(Low <= EntryPoint And Vol > 0)
{
SellShort(0, Min(Open, EntryPoint));
}
Else
{
EntryCount = EntryCount + 1;
}
}
}
// 开仓或者开仓先决条件已过有效BAR数,修改开仓标志
If(MarketPosition == -1 Or EntryCount > ValidBars3)
{
EntryFlag = False;
}
// 止损价格计算
ReversalPrice = DMA[1] + MinMove * PriceScale;
TrailStopPrice = Highest(High[1],TrailStopBars);
Commentary("ReversalPrice = "+Text(ReversalPrice));
Commentary("TrailStopPrice = "+Text(TrailStopPrice));
// 平仓
If(MarketPosition == -1 And BarsSinceEntry > 0 And Vol > 0)
{
If(High >= Min(ReversalPrice,TrailStopPrice))
{
BuyToCover(0,Max(Open, Min(ReversalPrice,TrailStopPrice) ) );
}
}
// 判断收盘价是否穿越置换均线
ConCrossOver = CrossOver(Close,DMA);
Commentary("均线上穿=" + iifstring(ConCrossOver,"是","否"));
ConCrossUnder = CrossUnder(Close,DMA);
Commentary("均线下穿=" + iifstring(ConCrossUnder,"是","否"));
// 计算最近的一次上穿发生的BAR离当前BAR的根数
BarsLastCrsOvr = NthCon(ConCrossOver==True,1);
Commentary("最近上穿Offset=" + Text(BarsLastCrsOvr));
// 计算最近的两次下穿发生的BAR离当前BAR的根数
BarsFstCrsUnd = NthCon(ConCrossUnder==True,2);
BarsSecCrsUnd = NthCon(ConCrossUnder==True,1);
Commentary("前2次下穿,首次Offset=" + Text(BarsFstCrsUnd));
Commentary("前2次下穿,第2次Offset=" + Text(BarsSecCrsUnd));
// 设置开仓标志
If(BarsLastCrsOvr > 0 And BarsLastCrsOvr - BarsSecCrsUnd <= ValidBars2 And BarsFstCrsUnd - BarsLastCrsOvr <= ValidBars1)
{
EntryFlag = True;
EntryPoint = Low - MinMove * PriceScale;
EntryCount = 0;
}
Commentary("EntryFlag = "+iifstring(EntryFlag,"True","False"));
Commentary("EntryPoint = "+Text(EntryPoint));
Commentary("EntryCount = "+Text(EntryCount));
}
//------------------------------------------------------------------------
// 编译版本 GS2014.10.25
// 版权所有 TradeBlazer Software 2003-2045
// 更改声明 TradeBlazer Software保留对TradeBlazer平
// 台每一版本的TradeBlazer公式修改和重写的权利
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// 编译版本 2026/1/16 63940
// 版权所有 tblaocai
// 更改声明 TradeBlazer Software保留对TradeBlazer平台
// 每一版本的TradeBlazer策略修改和重写的权利
//------------------------------------------------------------------------
😀感谢老师