Skip to content

唐奇安通道

策略核心逻辑

唐奇安通道交易法用到两个核心指标: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;
}