开发和分析交易系统的最佳方法·综合运用
🧩

开发和分析交易系统的最佳方法·综合运用

(3/3)·很多人把 EA 写越长越当护身符,结果 2000 行空转、50 行反而跨货币稳跑,差在哪?

案例拆解新手友好 第 3/3 篇
别以为参数越多系统越可靠。堆了几千行代码却零产出的 EA 很常见,而二三十行清晰逻辑反而能跑完多个货币对历史。先想清楚价格由什么驱动,再动手写,比闭眼加过滤器省几个月弯路。

「柱形里到底存了哪六个值」

在 MT5 里,一根柱(烛形)不是把这段时间所有 tick 都留下来,而是只压成六个字段:Open、Close、High、Low、Time、Volume。前四个是价格点,Time 是柱开启时刻,类型是 datetime——本质是 1970年1月1日 起算的秒数;Volume 在这套数据模型里等于该柱内的报价点数量,不是成交手数。 价格四个值是核心,但 Time 和 Volume 不能丢。比如跨会话的柱,Time 跳变本身就能暴露流动性断层;Volume 异常放大往往先于形态破位。能把这些字段对齐处理,信号才有底。 柱和烛形指的是同一组数据,差别只在画法。纯裸 K 形态学别当圣经,它只是图形数据,单独用容易骗线;接上指标或其他方法交叉验证,命中概率才站得住。外汇和贵金属杠杆高,这类结构信号失效也快,验证前先想好止损位。

用成交量与时间窗搭一个轻量EA

成交量激增往往暴露出大资金的关键决策点,而限制EA只在特定服务器时段内交易,能让我们聚焦那些成交量真正有信息量的价格区域。这一节直接落一个可改写的EA骨架:以柱内成交量、收盘价位移和线性衰减为权重,把最近50根柱(CandlesE=50)压缩成一个综合信号,再按信号强度与单边占比开平仓。 模式枚举只留四种(MODE_1到MODE_4),作者习惯不超过四种变体,避免假设正确时反而看不清哪个方程最贴市场。前两种权重不向分析区远端衰减,后两种是带线性衰减的副本——衰减越简单越省算力,先试线性的够用。 输入块里时间窗由TradeHour=0/ TradeMinute=1 到 TradeHourEnd=23/ TradeMinuteEnd=59 框定;Signal=200是信号功率门槛,PercentE=52意味着某侧信号占比过半才倾向触发。SLE与TPE都设3000点,Lot=0.01,MagicF=15670867,外汇与贵金属杠杆品种下这些默认值请先模拟盘验证,实盘波动可能瞬间扫掉止损。 核心计算塞进一个全静态类,单函数里数出看涨/看跌柱并归并信号分量;主体函数按信号强度和百分比质量开仓,只要信号轻微反转就平仓。柱不参与历史远端计算,越远权重越低,这是市场物理属性的粗略近似,下一篇再展开。 下面这段代码是EA头部的库引用、枚举、输入参数与数组预备函数,逐行拆完你就能在MT5里建文件跑起来:

MQL5 / C++
class="macro">#include <Trade\PositionInfo.mqh>
class="macro">#include <Trade\Trade.mqh>
CPositionInfo  m_position=CPositionInfo();class=class="str">"cmt">// trade position object
CTrade         m_trade=CTrade();          class=class="str">"cmt">// trading object
enum MODE_CALCULATE
  {
  MODE_1=class="num">0,
  MODE_2=class="num">1,
  MODE_3=class="num">2,
  MODE_4=class="num">3
  };
class="kw">input MODE_CALCULATE MODEE=MODE_1;class=class="str">"cmt">//Mode
class="kw">input class="type">int TradeHour=class="num">0;class=class="str">"cmt">//Start Trading Hour
class="kw">input class="type">int TradeMinute=class="num">1;class=class="str">"cmt">//Start Trading Minute
class="kw">input class="type">int TradeHourEnd=class="num">23;class=class="str">"cmt">//End Trading Hour
class="kw">input class="type">int TradeMinuteEnd=class="num">59;class=class="str">"cmt">//End Trading Minute
class="kw">input class="type">bool bWriteValuesE=class="kw">false;class=class="str">"cmt">//Log
class="kw">input class="type">int CandlesE=class="num">50;class=class="str">"cmt">//Bars To Analyse
class="kw">input class="type">int Signal=class="num">200;class=class="str">"cmt">//Signal Power
class="kw">input class="type">int PercentE=class="num">52;class=class="str">"cmt">//Percent Signals To One Side
class="kw">input class="type">bool bInvert=class="kw">false;class=class="str">"cmt">//Trade Invert
class="kw">input class="type">int SLE=class="num">3000;class=class="str">"cmt">//Stop Loss Points
class="kw">input class="type">int TPE=class="num">3000;class=class="str">"cmt">//Take Profit Points
class="kw">input class="type">class="kw">double Lot=class="num">0.01;class=class="str">"cmt">//Lot
class="kw">input class="type">int MagicF=class="num">15670867;class=class="str">"cmt">//Magic
class="type">MqlTick LastTick;class=class="str">"cmt">//the last tick
class="type">class="kw">double High[];
class="type">class="kw">double Low[];
class="type">class="kw">double Close[];
class="type">class="kw">double Open[];
class="type">class="kw">datetime Time[];
class="type">long Volume[];
class="type">void DimensionAllMQL5Values()class=class="str">"cmt">//prepare the arrays
  {
  ArrayResize(Time,CandlesE,class="num">0);
  ArrayResize(High,CandlesE,class="num">0);
  ArrayResize(Close,CandlesE,class="num">0);
  ArrayResize(Open,CandlesE,class="num">0);
  ArrayResize(Low,CandlesE,class="num">0);
  ArrayResize(Volume,CandlesE,class="num">0);
  }
class="type">void CalcAllMQL5Values()class=class="str">"cmt">//recalculate the arrays
  {

◍ 先把数组方向掰正再灌数据

在 MT5 里用 CopyHigh / CopyLow 这类函数往自定义数组塞历史行情时,数组默认索引方向会直接影响后面循环里的 i=0 到底指向最新 K 线还是最老 K 线。上面这段做法很直接:先统一 ArraySetAsSeries(...,false) 把 High、Low、Close、Open、Time、Volume 全部改成正序(i=0 为最早一根),再执行 Copy 系列把最近 CandlesE 根行情填进去,最后又全部 ArraySetAsSeries(...,true) 翻回倒序(i=0 为最新一根)。 这么绕一圈的意义在于:Copy 函数本身不挑数组方向,但 TickBox::CalculateAll 里的 for(i=0;i<CandlesE;i++) 配合 Open[i]<Close[i] 统计阳线根数时,若数组已是倒序,i 的遍历顺序就是自新向旧,逻辑可读性更高。你可以在 EA 的 OnCalculate 开头照抄这 12 行 SetAsSeries + Copy,把 CandlesE 设成 500 跑一遍,观察数组[0] 的 Time[0] 是否等于当前柱时间。 TickBox 这个类把多空统计做成静态成员:BarsUp、BarsDown 计阳线阴线数,PowerUp / PowerDown 累实体幅度,Percent 系列留作后续算占比。CalculateAll 接收 MODE_CALCULATE 参数,当 MODE0==MODE_1 时清零后遍历 CandlesE 根,Open[i]<Close[i] 就 BarsUp++。外汇与贵金属波动受杠杆影响,统计结果仅反映样本内概率倾向,实盘前务必在策略测试器用真实点差校验。

MQL5 / C++
ArraySetAsSeries(High,class="kw">false);
ArraySetAsSeries(Low,class="kw">false);
ArraySetAsSeries(Close,class="kw">false);
ArraySetAsSeries(Open,class="kw">false);
ArraySetAsSeries(Time,class="kw">false);
ArraySetAsSeries(Volume,class="kw">false);
CopyHigh(_Symbol,_Period,class="num">0,CandlesE,High);
CopyLow(_Symbol,_Period,class="num">0,CandlesE,Low);
CopyClose(_Symbol,_Period,class="num">0,CandlesE,Close);
CopyOpen(_Symbol,_Period,class="num">0,CandlesE,Open);
CopyTime(_Symbol,_Period,class="num">0,CandlesE,Time);
CopyTickVolume(_Symbol,_Period,class="num">0,CandlesE,Volume);
ArraySetAsSeries(High,true);
ArraySetAsSeries(Low,true);
ArraySetAsSeries(Close,true);
ArraySetAsSeries(Open,true);
ArraySetAsSeries(Time,true);
ArraySetAsSeries(Volume,true);
}
class TickBox
  {
  class="kw">public:
  class="kw">static class="type">int BarsUp;
  class="kw">static class="type">int BarsDown;
  class="kw">static class="type">class="kw">double PowerUp;
  class="kw">static class="type">class="kw">double PowerDown;
  class="kw">static class="type">class="kw">double PercentUp;
  class="kw">static class="type">class="kw">double PercentDown;
  class="kw">static class="type">class="kw">double PercentPowerUp;
  class="kw">static class="type">class="kw">double PercentPowerDown;
  class="kw">static class="type">void CalculateAll(MODE_CALCULATE MODE0)class=class="str">"cmt">//calculate all the necessary parameters
    {
    BarsUp=class="num">0;
    BarsDown=class="num">0;
    PercentUp=class="num">0.0;
    PercentDown=class="num">0.0;
    PowerUp=class="num">0.0;
    PowerDown=class="num">0.0;
    if ( MODE0 == MODE_1 )
      {
      for ( class="type">int i=class="num">0; i<CandlesE; i++ )
        {
        if ( Open[i] < Close[i] )
          {
          BarsUp++;

「多模式力度累加的写法差异」

这段逻辑在遍历最近 CandlesE 根 K 线,按收阳或收阴分别累加 BarsUp / BarsDown 计数,以及 PowerUp / PowerDown 的加权力度。核心区别在于 MODE0 的取值决定分母与权重系数,直接影响力度数值的量纲。 MODE_2 用 _Point 作分母,把实体绝对值换算成点数再乘成交量;其中阴线分支写了除以 -_Point,会得到负值,若后续取绝对值需留意符号。MODE_3 在 MODE_2 基础上乘了 (CandlesE-i)/CandlesE 的线性衰减权重,越靠近当前的 K 线权重越接近 1,远端趋近 0。 MODE_4 则把 MODE_3 的 _Point 分母换成 (High[i]-Low[i]) 即振幅,相当于用实体占振幅比来标准化,再乘衰减权重与成交量。外汇与贵金属波动跳空频繁,High-Low 可能为极小值,实盘前应在 MT5 用 Print 打出几组 PowerUp 数值确认无异常溢出。

MQL5 / C++
PowerUp+=(MathAbs(Open[i] - Close[i])/(High[i] - Low[i]))*Volume[i];
}
if ( Open[i] > Close[i] )
  {
  BarsDown++;
  PowerDown+=(MathAbs(Open[i] - Close[i])/(High[i] - Low[i]))*Volume[i];
  }
}

if ( MODE0 == MODE_2 )
  {
  for ( class="type">int i=class="num">0; i<CandlesE; i++ )
    {
    if ( Open[i] < Close[i] )
      {
      BarsUp++;
      PowerUp+=(MathAbs(Open[i] - Close[i])/_Point)*Volume[i];
      }
    if ( Open[i] > Close[i] )
      {
      BarsDown++;
      PowerDown+=(MathAbs(Open[i] - Close[i])/-_Point)*Volume[i];
      }
    }
  }

if ( MODE0 == MODE_3 )
  {
  for ( class="type">int i=class="num">0; i<CandlesE; i++ )
    {
    if ( Open[i] < Close[i] )
      {
      BarsUp++;
      PowerUp+=(class="type">class="kw">double(CandlesE-i)/class="type">class="kw">double(CandlesE))*(MathAbs(Open[i] - Close[i])/_Point)*Volume[i];
      }
    if ( Open[i] > Close[i] )
      {
      BarsDown++;
      PowerDown+=(class="type">class="kw">double(CandlesE-i)/class="type">class="kw">double(CandlesE))*(MathAbs(Open[i] - Close[i])/_Point)*Volume[i];
      }
    }
  }

if ( MODE0 == MODE_4 )
  {
  for ( class="type">int i=class="num">0; i<CandlesE; i++ )
    {
    if ( Open[i] < Close[i] )
      {
      BarsUp++;
      PowerUp+=(class="type">class="kw">double(CandlesE-i)/class="type">class="kw">double(CandlesE))*(MathAbs(Open[i] - Close[i])/(High[i] - Low[i]))*Volume[i];
      }
    if ( Open[i] > Close[i] )
      {

多空统计落地到平仓触发

上面那段累计逻辑跑完以后,类静态成员里就存了 BarsUp、BarsDown 以及带成交量加权的 PowerUp、PowerDown。接着用两组百分比把「数量差」和「力度差」分开表达:PercentUp 是上涨 bar 数占比,PercentPowerUp 是上涨力度占总力度的比例,两者分母不同,前者看次数、后者看加权能量。 Trade() 函数先取当前 tick 和时间结构,把小时分钟折成 MinuteEquivalent(例如 14:30 就是 870),再和交易时段边界做比对。当上下 bar 数差绝对值 ≥ 1.0 且 PercentPowerUp ≥ 50.0 时,倾向认为多头力度占优,此时若未反转参数就平掉买仓,反转则平卖仓;对称条件下平另一方向。 时段判断里有个易踩的坑:若起始分钟大于结束分钟(如 22:00 到次日 02:00),代码用「不落在结束~起始区间内」来放行交易。此时若仓数为 0、数量差 ≥ 外部 Signal 参数、且 PercentPowerUp ≥ PercentE,就按 bInvert 决定卖还是买,并挂上以 _Point 为单位的 SL/TP。外汇与贵金属波动剧烈,这套触发在高滑点时段可能偏离回测表现,上 MT5 用策略测试器跑不同 Signal 值验证最稳妥。

MQL5 / C++
            BarsDown++;
            PowerDown+=(class="type">class="kw">double(CandlesE-i)/class="type">class="kw">double(CandlesE))*(MathAbs(Open[i] - Close[i])/(High[i] - Low[i]))*Volume[i];
            }
            }
            }

   if ( BarsUp > class="num">0 && BarsDown > class="num">0 )
      {
      PercentUp=(class="type">class="kw">double(BarsUp)/class="type">class="kw">double(BarsUp+BarsDown))*class="num">100.0;
      PercentDown=(class="type">class="kw">double(BarsDown)/class="type">class="kw">double(BarsUp+BarsDown))*class="num">100.0;
      PercentPowerUp=(class="type">class="kw">double(PowerUp)/class="type">class="kw">double(PowerUp+PowerDown))*class="num">100.0;
      PercentPowerDown=(class="type">class="kw">double(PowerDown)/class="type">class="kw">double(PowerUp+PowerDown))*class="num">100.0;
      }             
   };
   class="type">int TickBox::BarsUp=class="num">0;
   class="type">int TickBox::BarsDown=class="num">0;
   class="type">class="kw">double TickBox::PowerUp=class="num">0;
   class="type">class="kw">double TickBox::PowerDown=class="num">0;   
   class="type">class="kw">double TickBox::PercentUp=class="num">0;
   class="type">class="kw">double TickBox::PercentDown=class="num">0;
   class="type">class="kw">double TickBox::PercentPowerUp=class="num">0;
   class="type">class="kw">double TickBox::PercentPowerDown=class="num">0;
class="type">void Trade()
   {
   SymbolInfoTick(Symbol(),LastTick);
   class="type">MqlDateTime tm;
   TimeToStruct(LastTick.time,tm);
   class="type">int MinuteEquivalent=tm.hour*class="num">60+tm.min;
   class="type">int BorderMinuteStartTrade=HourCorrect(TradeHour)*class="num">60+MinuteCorrect(TradeMinute);
   class="type">int BorderMinuteEndTrade=HourCorrect(TradeHourEnd)*class="num">60+MinuteCorrect(TradeMinuteEnd);
   if ( MathAbs(TickBox::BarsUp-TickBox::BarsDown) >= class="num">1.0 && TickBox::PercentPowerUp >= class="num">50.0 )
      {
      if ( !bInvert ) ClosePosition(POSITION_TYPE_BUY);
      else ClosePosition(POSITION_TYPE_SELL);
      }
      
   if ( MathAbs(TickBox::BarsUp-TickBox::BarsDown) >= class="num">1.0 && TickBox::PercentPowerDown >= class="num">50.0 )
      {
      if ( !bInvert ) ClosePosition(POSITION_TYPE_SELL);
      else ClosePosition(POSITION_TYPE_BUY);
      }
      
     if ( BorderMinuteStartTrade > BorderMinuteEndTrade )
         {
         if ( PositionsTotal() == class="num">0 && !(MinuteEquivalent>=BorderMinuteEndTrade && MinuteEquivalent<= BorderMinuteStartTrade) )
            {
            if ( MathAbs(TickBox::BarsUp-TickBox::BarsDown) >= Signal && TickBox::PercentPowerUp >= PercentE )
               {
               if ( !bInvert ) m_trade.Sell(Lot,_Symbol,LastTick.ask,LastTick.ask+class="type">class="kw">double(SLE)*_Point,LastTick.bid-class="type">class="kw">double(TPE)*_Point);

◍ 交易触发与平仓的双向分支

这段逻辑把开仓拆成了两段对称结构:先判断当前无持仓且分钟数落在 BorderMinuteStartTrade 到 BorderMinuteEndTrade 区间内,再比对 TickBox 里记录的上下行 bars 差与能量百分比。 当 BarsUp 与 BarsDown 差值的绝对值不小于 Signal,且 PercentPowerUp 达到 PercentE 时,默认方向走 Sell,反转标志 bInvert 为真则改为 Buy;下行动能达标时方向相反。止损用 SLE*_Point 偏移挂出,止盈用 TPE*_Point,外汇与贵金属杠杆高,实盘前务必把 SLE、TPE 按品种波动性重算。 ClosePosition 只处理本 EA 的 MagicF 标签仓位,且要求传入方向与实际持仓类型一致才调用 PositionClose,避免误平其他策略单。 下面这段是原文开仓与平仓调用的核心片段,可在 MT5 里直接对照函数签名验证:

MQL5 / C++
else m_trade.Buy(Lot,_Symbol,LastTick.ask,LastTick.bid-class="type">class="kw">double(SLE)*_Point,LastTick.ask+class="type">class="kw">double(TPE)*_Point);
}

if ( MathAbs(TickBox::BarsUp-TickBox::BarsDown) >= Signal && TickBox::PercentPowerDown >= PercentE )
  {
   if ( !bInvert ) m_trade.Buy(Lot,_Symbol,LastTick.ask,LastTick.bid-class="type">class="kw">double(SLE)*_Point,LastTick.ask+class="type">class="kw">double(TPE)*_Point);
   else m_trade.Sell(Lot,_Symbol,LastTick.ask,LastTick.ask+class="type">class="kw">double(SLE)*_Point,LastTick.bid-class="type">class="kw">double(TPE)*_Point);
  }
 }

if ( PositionsTotal() == class="num">0 && BorderMinuteStartTrade <= BorderMinuteEndTrade )
  {
   if ( MinuteEquivalent>=BorderMinuteStartTrade && MinuteEquivalent<= BorderMinuteEndTrade )
    {
     if ( MathAbs(TickBox::BarsUp-TickBox::BarsDown) >= Signal && TickBox::PercentPowerUp >= PercentE )
      {
       if ( !bInvert ) m_trade.Sell(Lot,_Symbol,LastTick.ask,LastTick.ask+class="type">class="kw">double(SLE)*_Point,LastTick.bid-class="type">class="kw">double(TPE)*_Point);
       else m_trade.Buy(Lot,_Symbol,LastTick.ask,LastTick.bid-class="type">class="kw">double(SLE)*_Point,LastTick.ask+class="type">class="kw">double(TPE)*_Point);
      }
     if ( MathAbs(TickBox::BarsUp-TickBox::BarsDown) >= Signal && TickBox::PercentPowerDown >= PercentE )
      {
       if ( !bInvert ) m_trade.Buy(Lot,_Symbol,LastTick.ask,LastTick.bid-class="type">class="kw">double(SLE)*_Point,LastTick.ask+class="type">class="kw">double(TPE)*_Point);
       else m_trade.Sell(Lot,_Symbol,LastTick.ask,LastTick.ask+class="type">class="kw">double(SLE)*_Point,LastTick.bid-class="type">class="kw">double(TPE)*_Point);
      }
    }
  }
 }
class="type">void ClosePosition(class="type">ENUM_POSITION_TYPE Direction)class=class="str">"cmt">//close a position by a symbol
  {
   class="type">bool ord;
   ord=PositionSelect(Symbol());
   if ( ord && class="type">int(PositionGetInteger(POSITION_MAGIC)) == MagicF && Direction == class="type">ENUM_POSITION_TYPE(PositionGetInteger(POSITION_TYPE)) )
    {
     if(m_position.SelectByIndex(class="num">0)) m_trade.PositionClose(m_position.Ticket());
    }
  }
class="type">int OnInit()
{

「用 K 线闭合触发计算而非逐 tick 空转」

EA 在 OnInit 里先给持仓打上魔术号 m_trade.SetExpertMagicNumber(MagicF),再调用 DimensionAllMQL5Values() 把预定义数组维度化,最后返回 INIT_SUCCEEDED。这一步没做扎实,后续 OnTick 里取到的指标数组可能是空的。 真正的节奏控制藏在 bNewBar() 里:每 tick 调 CopyTime 取当前品种周期最新 1 根 K 线时间到 TimeX[0],跟模块级变量 Time0 比。Time0 初值为 0,首根 K 线只记录时间、返回 false;之后只要 Time0 小于新时间,就刷新 Time0 并返回 true,代表一根新柱走完。 OnTick 只在 bNewBar() 返回 true 时才干活:重算指标、跑 TickBox::CalculateAll(MODEE),若 bWriteValuesE 开关打开,就把多空占比、信号强度、阻支撑强度打印到日志。外汇与贵金属波动剧烈,这种按柱触发的方式能省掉大量无谓计算,但信号滞后一根柱,实盘前请在 MT5 策略测试器里用不同周期验证打印数值是否符合预期。

MQL5 / C++
  m_trade.SetExpertMagicNumber(MagicF);class=class="str">"cmt">//set the magic number for positions
  DimensionAllMQL5Values();class=class="str">"cmt">//prepare the predefined arrays
  class="kw">return(INIT_SUCCEEDED);
  }
class="type">class="kw">datetime Time0;
class="type">class="kw">datetime TimeX[class="num">1];
class="type">bool bNewBar()
  {
  CopyTime(_Symbol,_Period,class="num">0,class="num">1,TimeX);
  if ( Time0 < TimeX[class="num">0] )
    {
    if (Time0 != class="num">0)
      {
      Time0=TimeX[class="num">0];
      class="kw">return true;
      }
    else
      {
      Time0=TimeX[class="num">0];
      class="kw">return class="kw">false;
      }
    }
  else class="kw">return class="kw">false;
  }
class="type">void OnTick()
  {
  if ( bNewBar())class=class="str">"cmt">//work by bars
    {
    CalcAllMQL5Values();
    TickBox::CalculateAll(MODEE);
    if (bWriteValuesE)
      {
      Print("% Sit in buy = ",TickBox::PercentUp);
      Print("% Sit in sell = ",TickBox::PercentDown);
      Print("Signal = ",MathAbs(TickBox::BarsDown-TickBox::BarsUp));
      Print("% Resistance = ",TickBox::PercentPowerUp);
      Print("% Support = ",TickBox::PercentPowerDown);      
      Print("***************************************************************************");
      }
    Trade();
    }
  }

EURUSD M5 上的十年回测观察

拿 EA 在 EURUSD M5 跑,先从去年一段开始验,再拉长到 10 年区间交替换操作模式与服务器时间间隔。按这套交替办法,大约在 30–60 分钟窗口里能抓出一组可接受的参数。 去年切片图形干净,信号延续性明显;抽 10 年里的第一年回测,图不如近期漂亮,但净值变化方向还在,说明底层逻辑没断。取中间年份段测,也能看到全局模式痕迹,拉满整段区间后虽不完美,但存在有效阶段,可考虑加过滤器或深优化。 外汇与贵金属属高风险品种,多货币能力要靠持续改代码实现,本文不展开。当前形态交易次数少但跨品种有效,最简代码也能当想法底座。若死磕系统却挤不出结果,大概率该换思路而非硬修。 柱形图参数对各货币对影响不同,极端值组合因对而异;也有全对通用的极端值,但得靠时间与实践捞。读者可自开 MT5 用去年切片先复一遍再扩区间。

◍ 记住这一条就够了

写系统最忌堆复杂度。参数一多,自由度就膨胀,回测时间也跟着拉长,EURUSD M5 上挂满数千个指标的图表我见过太多,最后没人能从中看出稳定结构。 简单系统反而更经得起验证:少几个输入变量,过拟合空间就被砍掉一大块。别信「30 笔成交就稳了」这种说法,样本量太小说明不了任何事。 动手前先定一个可接受的时间表和目标,做不到再缩范围,也比盲写强。外汇和贵金属杠杆高,陷进去容易脱身难,把它当爱好还是当盈利工具,得先想清楚。

交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到成交量异动和柱形结构预警,你专注判断该不该上过滤器。

常见问题

实体占比高往往代表单边推动强,影线长说明该周期分歧大;用这两个值做粗筛,比直接套指标少写很多冗余代码。
可以,小布的品种页能直接显示历史成交量分布和回测区间,你把文中 20–50 行逻辑填进策略框就能看初步信号,不用自己搭环境。
单系统磨几个月容易过拟合,多简单系统并行能更快暴露哪些逻辑真的跨品种有效,哪些只是碰巧。
重点看最大回撤出现在哪类行情、成交量突变日是否失效;这些比总收益更能预示实盘会不会突然断线。