唐奇安通道
策略核心逻辑
唐奇安通道交易法用到两个核心指标:Donchian Channel(唐奇安通道)和ATR。
代码实现步骤
1. 加载库与基础设定
- 加载库与声明对象:引入必要的函数库,声明指标对象。
- 模式号与止损方式:设定策略编号、止损依据(如基于ATR或固定点数)。
- 可优化参数:
- 时间周期(如M15、H1等)
- 止损系数(SLFactor,例:1代表止损距离为通道上下轨间距)
- 止盈系数(TPFactor,例:2代表盈亏比2:1)
- 唐奇安通道与长期ATR参数(共用或拆分,本文共用简化参数)
- 止盈类型(TPType:1为中线止盈,2为边线止盈)
2. 指标逻辑与过滤器
- 唐奇安通道:计算上轨(N周期最高价)、下轨(N周期最低价)、中线(上下轨均值)。
- 双ATR过滤器:
- 短期ATR(近期波动)与长期ATR(远期波动)对比,若短期ATR > 长期ATR,表明当前波动增大,允许开仓;否则跳过。
3.代码实现
cpp
#property strict
#include <Util/Util.mqh>
Util util;
// 不可以做参数优化的参数
input group "固定和输入通用部分";
sinput int MagicNum = 12345; // 魔术编号
sinput int SlType = 1; // 止损方式(1:固定金额{100}美金 2:账户{1}% 3:固定{0.1}手 4:子账户总账户{1}%)
sinput int SlParam = 100; // 止损依据(根据上方【止损方式】的不同,所代表的含义也不同)
input group "参数优化" ;
input ENUM_TIMEFRAMES Inp_PERIOD = PERIOD_H1; // 时间周期
input double Inp_tpFactor = 2; // 止盈系数(0=没有止盈)
input double Inp_slFactor = 2; // 止损系数
input int Inp_atrSlowAndDonchianChannelParam = 100; // 唐奇安通道&长期ATR
input int Inp_tpType = 2; // 止盈方式 1 中线 2边线
input int Inp_atrFastParam = 50; // 短期ATR
int barsTotal;
int handle;
int initBalance;
int atrFast_handle;
int atrSlow_handle;
int dc_handle;
double atrFast_Buffer[];
double atrSlow_Buffer[];
double dcUpper_Buffer[];
double dcMid_Buffer[];
double dcLower_Buffer[];
double point;
MqlTick currentTick;
int OnInit()
{
atrFast_handle = iATR(_Symbol,Inp_PERIOD,Inp_atrFastParam);
atrSlow_handle = iATR(_Symbol,Inp_PERIOD,Inp_atrSlowAndDonchianChannelParam);
dc_handle = iCustom(_Symbol,Inp_PERIOD,"donchian_channel",Inp_atrSlowAndDonchianChannelParam);
if (atrFast_handle == INVALID_HANDLE)
{
Alert((string)MagicNum + " ", _Symbol, " atrFast_handle 创建失败");
return INIT_FAILED;
}
if (atrSlow_handle == INVALID_HANDLE)
{
Alert((string)MagicNum + " ", _Symbol, " atrSlow_handle 创建失败");
return INIT_FAILED;
}
if (dc_handle == INVALID_HANDLE)
{
Alert((string)MagicNum + " ", _Symbol, " dc_handle 创建失败");
return INIT_FAILED;
}
ArraySetAsSeries(atrFast_Buffer, true);
ArraySetAsSeries(atrSlow_Buffer, true);
ArraySetAsSeries(dcUpper_Buffer, true);
ArraySetAsSeries(dcMid_Buffer, true);
ArraySetAsSeries(dcLower_Buffer, true);
Print(MagicNum, " ", _Symbol, "实例化成功");
// 当用户没有打开参数对应的图表周期,自动帮他打开对应周期
if (MQLInfoInteger(MQL_TESTER))
{
ChartSetInteger(0,CHART_SHOW_GRID,false);
}else{
ChartSetSymbolPeriod(0,_Symbol, Inp_PERIOD);
}
initBalance = SlParam;
Comment("MagicNum:", MagicNum, " SlType: ", SlType, " SlParam: ", SlParam);
return INIT_SUCCEEDED;
}
// 节省内存后 买VPS就可以买小一点
void OnDeinit(const int reason)
{
if (handle != INVALID_HANDLE)
{
IndicatorRelease(handle);
}
}
void OnTick()
{
if(!util.isMarketOpen(_Symbol,TimeCurrent())) return;
if (!util.isNewBar(_Symbol, Inp_PERIOD, barsTotal))
return;
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int) SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
CopyBuffer(dc_handle,0,0,2,dcUpper_Buffer);
CopyBuffer(dc_handle,1,0,2,dcLower_Buffer);
CopyBuffer(dc_handle,2,0,2,dcMid_Buffer);
CopyBuffer(atrFast_handle,0,0,2,atrFast_Buffer);
CopyBuffer(atrSlow_handle,0,0,2,atrSlow_Buffer);
int cntBuy, cntSell;
if (!util.countOpenPositions(MagicNum, cntBuy, cntSell))
return;
int signal = checkSignal();
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double bullSignal = cntBuy==0 && signal == 1;
if (bullSignal)
{
double et = iClose(_Symbol,Inp_PERIOD,1);
double slRange = (dcUpper_Buffer[1] - dcLower_Buffer[1])*Inp_slFactor;
double sl = dcLower_Buffer[1] - slRange;
double tp = Inp_tpFactor ==0 ?0 : NormalizeDouble(et+(et-sl)*Inp_tpFactor,digits);
double slp = SlParam;
if (SlType == 4)
{
slp = util.calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
}
util.orderSend(_Symbol, MagicNum, et, sl, SlType, slp, Inp_tpFactor, "DC"+(string)MagicNum);
}
bool bearSignal = cntSell ==0 && signal == -1;
if (bearSignal)
{
double et = iClose(_Symbol,Inp_PERIOD,1);
double slRange = (dcUpper_Buffer[1] - dcLower_Buffer[1])*Inp_slFactor;
double sl = dcUpper_Buffer[1] + slRange;
double tp = Inp_tpFactor ==0 ?0 : NormalizeDouble(et+(et-sl)*Inp_tpFactor,digits);
double slp = SlParam;
if (SlType == 4)
{
slp = util.calculateEachEaBalance(MagicNum, initBalance); // 子账户百分之一
}
util.orderSend(_Symbol, MagicNum, et, sl, SlType, slp, Inp_tpFactor, "DC"+(string)MagicNum);
}
// 出场
if(!util.countOpenPositions(MagicNum, cntBuy, cntSell)) return;
if(Inp_tpType == 2){
if(cntBuy > 0 && iClose(_Symbol,Inp_PERIOD,1) >= dcUpper_Buffer[1] ){
util.closePositions(MagicNum, POSITION_TYPE_BUY);
}
if(cntSell > 0 && iClose(_Symbol,Inp_PERIOD,1) <= dcLower_Buffer[1] ){
util.closePositions(MagicNum, POSITION_TYPE_SELL);
}
}
else if(Inp_tpType == 1){
if(cntBuy > 0 && iClose(_Symbol,Inp_PERIOD,1) >= dcMid_Buffer[1] ){
util.closePositions(MagicNum, POSITION_TYPE_BUY);
}
if(cntSell > 0 && iClose(_Symbol,Inp_PERIOD,1) <= dcMid_Buffer[1] ){
util.closePositions(MagicNum, POSITION_TYPE_SELL);
}
}
}
// 多 收盘价在下轨之下
int checkSignal(){
bool atrStates = atrFast_Buffer[1]> atrSlow_Buffer[1];
// 判断大波动
if(!atrStates) return 0;
double close1 = iClose(_Symbol,Inp_PERIOD,1);
if (close1 < dcLower_Buffer[1]) return 1;
else if(close1 >dcUpper_Buffer[1]) return -1;
return 0;
}