퐀⣘g·综合运用
⚖️

퐀⣘g·综合运用

(3/3)·固定手数拖垮复利?本篇把仓位大小绑上账户余额,回撤锚定10%心理线

新手友好 第 3/3 篇
很多多币种 EA 跑顺了却不敢加仓,因为手数写死在启动参数里。账户余额涨了仍用旧手数,等于白白浪费低风险扩张空间。把仓位和归一化余额挂钩,回撤比例才不会被余额波动撕开。

「多币种策略组的装配与内存回收」

在 EA 初始化阶段,按枚举 VAG 分支把不同货币对的策略容器挂进 expert 对象,是虚拟策略组(CVirtualStrategyGroup)落地的关键一步。EURGBPUSD_3_3_3 这个分支最值得盯:它先以 1.25、2.24、2.64 三个缩放系数分别 new 出 EG/EU/GU 子组,再包一层父组交给 expert.Add(),相当于把三套权重不同的子系统并成一个复合信号源。 其余分支走的是「用哪组删哪组」的懒回收逻辑。比如选了 VAG_EURGBP 就只 Add 欧英组,顺手把 EU、GU 的指针 delete 掉;选 GBPUSD 则保留 GU、释放 EU 和 EG。这种写法在 MT5 回测里能省下约 30% 的空闲对象占用,但手滑漏删就会内存泄漏。 default 分支直接 return INIT_FAILED,意味着枚举值写错时 EA 连加载都不让。初始化末尾的 expert.Load() 负责恢复上一次退市的状态,成功才返 INIT_SUCCEEDED——开 MT5 把这段贴进 OnInit 尾部,改几个 scale_ 数值就能对比复合组与单组的净值曲线差异。外汇与贵金属品种波动剧烈,多组叠加不意味风险分散,实盘前务必用策略测试器跑足历史样本。

MQL5 / C++
FOREACH(strategiesEG, class="kw">delete strategiesEG[i]);
   FOREACH(strategiesGU, class="kw">delete strategiesGU[i]);
   break;
  }
  case VAG_GBPUSD: {
   expert.Add(CVirtualStrategyGroup(strategiesGU, scale_));
   FOREACH(strategiesEU, class="kw">delete strategiesEU[i]);
   FOREACH(strategiesEG, class="kw">delete strategiesEG[i]);
   break;
  }
  case VAG_EURGBPUSD_9: {
   expert.Add(CVirtualStrategyGroup(strategies, scale_));
   break;
  }
  case VAG_EURGBPUSD_3_3_3: {
   class=class="str">"cmt">// Create a group of three strategy groups
   CVirtualStrategyGroup *groups[] = {
     new CVirtualStrategyGroup(strategiesEG, class="num">1.25),
     new CVirtualStrategyGroup(strategiesEU, class="num">2.24),
     new CVirtualStrategyGroup(strategiesGU, class="num">2.64)
   };
   expert.Add(CVirtualStrategyGroup(groups, scale_));
   break;
  }
  class="kw">default:
   class="kw">return(INIT_FAILED);
  }
class=class="str">"cmt">// Load the previous state if available
expert.Load();
class="kw">return(INIT_SUCCEEDED);
}

用缩放因子压住组合回撤

拿 EURGBP 上跑的三套同策略副本先试水:单实例最大相对回撤 10%,但三实例组合后降到 8%。回撤空间腾出来了,仓位就能加——把缩放因子从 1 调到 1.25(10% ÷ 8%),组合回撤正好回到 10% 边界。 EURUSD 与 GBPUSD 两组照同样逻辑反推,分别落到 2.24 和 2.64 的缩放因子。把这三组各自标准化后的虚拟策略组再套一层,代码里就是直接 new 出三个带因子的 CVirtualStrategyGroup 塞进数组。 九实例一把梭(第四组)时,Scale=1 回撤仅 3%,因子可拉到 3.3 才触 10% 红线。但换种拼法——先按品种归三组、再叠成总组(第五组)——同样 Scale=1 时余额更高却亏 4.57%。把第五组因子推到 2.18 封住 10% 回撤后,测试期内利润比第四组翻倍有余。 第五组挂 FixedBalance=10000 重跑,净值峰值亏损绝对值 995 美元,占账户约 10%,证实资金管理器没失准。绿色框标出的零增长内期从一个月拖到半年,破法只有一条:往更多品种和周期铺实例,把相关性打散。外汇与贵金属组合策略仍属高风险,回撤控制只降低概率不改尾部风险。

MQL5 / C++
class=class="str">"cmt">// Create a group of three strategy groups
CVirtualStrategyGroup *groups[] = {
   new CVirtualStrategyGroup(strategiesEG, class="num">1.25),
   new CVirtualStrategyGroup(strategiesEU, class="num">2.24),
   new CVirtualStrategyGroup(strategiesGU, class="num">2.64)
};

◍ 回测里的回撤只能信一半

给不同初始余额跑同一个 EA,就能人为控制各策略实例分到的仓位权重。余额给得多的实例会开更大的仓,给得少的实例持仓自然缩水。靠反复测试,往往能挑出一组勉强符合你预设最大回撤阈值的参数。 但回撤这件事只有在你跑过的样本里才看得见。换到没参与优化的时间段启动 EA,回撤可能比测试时更高,也可能莫名变低,没有先验保证。每个策略实例都得自己掂量:测试结论能信几分、实盘里怎么用。 评论区有个细节值得记:用 (3+3+3) 分组加缩放比例 2.18 跑 SimpleVolumesExpert,日志有虚拟单但测试器不出真实单,作者回说是初始余额没到 10000 美元门槛。余额不够时并不是每个虚拟仓位都会映射成真单。 [CODE] //+------------------------------------------------------------------+ //| 确定虚拟位置的实际大小 //+------------------------------------------------------------------+ double CMoney::Volume(CVirtualOrder *p_order) { // 申请该虚拟仓位的标准化策略平衡 double fittedBalance = p_order.FittedBalance(); // 如果等于 0,则实际卷等于虚拟卷 if(fittedBalance == 0.0) { return p_order.Volume(); } // 否则,我们将得出贸易总余额的值 double totalBalance = s_fixedBalance > 0 ? s_fixedBalance : AccountInfoDouble(ACCOUNT_BALANCE); // 用虚拟体积返回计算出的实际体积 return p_order.Volume() * totalBalance * s_depoPart / fittedBalance ; } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+

//构造函数

//+------------------------------------------------------------------+ CVirtualStrategy::CVirtualStrategy(double p_fittedBalance = 0, double p_fixedLot = 0.01) : m_fittedBalance(p_fittedBalance), m_fixedLot(p_fixedLot) {} // 如果等于 0,则实际卷等于虚拟卷 [/CODE] 上面这段是仓位映射的核心。逐行看:先取虚拟单的 fittedBalance;为 0 时实盘手数直接等于虚拟手数,不做缩放;非 0 时取总余额(固定值或账户实际余额),最后实盘体积 = 虚拟体积 × 总余额 × 存款占比 / fittedBalance。 CVirtualStrategy 构造里 p_fittedBalance 默认 0、p_fixedLot 默认 0.01。想让可变手数生效,必须给实例传 m_fittedBalance > 0,否则会掉回默认值 0,手数永远不缩放。外汇和贵金属杠杆高,这种映射误差会放大穿仓风险,上线前务必在策略测试器用不同余额验证一遍。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 确定虚拟位置的实际大小
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CMoney::Volume(CVirtualOrder *p_order) {
  class=class="str">"cmt">// 申请该虚拟仓位的标准化策略平衡
  class="type">class="kw">double fittedBalance = p_order.FittedBalance();

  class=class="str">"cmt">// 如果等于 class="num">0,则实际卷等于虚拟卷
  if(fittedBalance == class="num">0.0) {
    class="kw">return p_order.Volume();
  }

  class=class="str">"cmt">// 否则,我们将得出贸易总余额的值
  class="type">class="kw">double totalBalance = s_fixedBalance > class="num">0 ? s_fixedBalance : AccountInfoDouble(ACCOUNT_BALANCE);

  class=class="str">"cmt">// 用虚拟体积返回计算出的实际体积
  class="kw">return p_order.Volume() * totalBalance * s_depoPart / fittedBalance ;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 构造函数|
class=class="str">"cmt">//+------------------------------------------------------------------+
CVirtualStrategy::CVirtualStrategy(class="type">class="kw">double p_fittedBalance = class="num">0,
                                 class="type">class="kw">double p_fixedLot = class="num">0.01) :
  m_fittedBalance(p_fittedBalance),
  m_fixedLot(p_fixedLot) {}
class=class="str">"cmt">// 如果等于 class="num">0,则实际卷等于虚拟卷

「成交量策略的构造与初始化落点」

成交量策略类 CSimpleVolumesStrategy 继承自 CVirtualStrategy,构造函数把交易品种、周期、信号周期、偏差与加仓偏差、挂单距离、止损止盈点数、订单有效期、最大订单数以及拟合余额一次性传入。其中 p_fittedBalance 默认 0,代表不启用资金拟合;一旦非 0,就会在初始化列表里交给 CVirtualStrategy(p_fittedBalance, 0.01) 处理,0.01 是该类固定的基础手数系数。 OnInit 里先通过 CMoney::DepoPart(expectedDrawdown_ / 10.0) 把预期回撤缩放成仓位占比,再用 FixedBalance 锁定计算基准。随后 new 一个 CVirtualAdvisor,名字拼上分组枚举,保证多实例在 MT5 终端里不重名。 实际填充策略数组时,EURGBP H1 上跑了两组参数:一组信号周期 13、偏差 0.3、拟合余额 1600;另一组周期 17、偏差 1.7、拟合余额 900。两组止损分别 465 与 220 点,最大订单数都是 3。外汇与贵金属杠杆品种波动剧烈,这类多实例网格在 EURGBP 上可能放大滑点风险,开 MT5 用策略测试器跑这两组能直接看到拟合余额对仓位的压缩效果。

MQL5 / C++
if(fittedBalance == class="num">0.0) {
      class="kw">return p_order.Volume();
}
class CSimpleVolumesStrategy : class="kw">public CVirtualStrategy {
...
class="kw">public:
  class=class="str">"cmt">//--- 公共方法
  CSimpleVolumesStrategy(
      class="type">class="kw">string          p_symbol,
      ENUM_TIMEFRAMES p_timeframe,
      class="type">int             p_signalPeriod,
      class="type">class="kw">double          p_signalDeviation,
      class="type">class="kw">double          p_signaAddlDeviation,
      class="type">int             p_openDistance,
      class="type">class="kw">double          p_stopLevel,
      class="type">class="kw">double          p_takeLevel,
      class="type">int             p_ordersExpiration,
      class="type">int             p_maxCountOfOrders,
      class="type">class="kw">double          p_fittedBalance = class="num">0
  );                                        class=class="str">"cmt">// 构造函数
  ...
};
...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 构造函数|
class=class="str">"cmt">//+------------------------------------------------------------------+
CSimpleVolumesStrategy::CSimpleVolumesStrategy(
   class="type">class="kw">string          p_symbol,
   ENUM_TIMEFRAMES p_timeframe,
   ...
   class="type">int             p_maxCountOfOrders,
   class="type">class="kw">double          p_fittedBalance = class="num">0) :
class=class="str">"cmt">// 初始化列表
   CVirtualStrategy(p_fittedBalance, class="num">0.01),
   m_symbol(p_symbol),
   m_timeframe(p_timeframe),
   ...
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 专家初始化函数|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit() {
   class=class="str">"cmt">// 在资本管理类中设置参数
   CMoney::DepoPart(expectedDrawdown_ / class="num">10.0);
   CMoney::FixedBalance(fixedBalance_);
   class=class="str">"cmt">// 创建使用虚拟仓位的智能交易系统
   expert = new CVirtualAdvisor(magic_, "SimpleVolumes_" + EnumToString(group_));
   class=class="str">"cmt">// 创建并填充包含所有策略实例的数组
   CVirtualStrategy *strategies[] = {
       new CSimpleVolumesStrategy("EURGBP", PERIOD_H1,  class="num">13, class="num">0.3, class="num">1.0, class="num">0, class="num">10500,  class="num">465,  class="num">1000, class="num">3, class="num">1600),
       new CSimpleVolumesStrategy("EURGBP", PERIOD_H1,  class="num">17, class="num">1.7, class="num">0.5, class="num">0, class="num">16500,  class="num">220,  class="num">1000, class="num">3,  class="num">900),

别急着下结论

上面这段 CSimpleVolumesStrategy 的实例化只给了一行参数,EURGBP 在 H1 周期下用了 51 期窗口、0.5 阈值系数、1.1 量能倍率,后面还跟了 19500/370/22000 这类挂单距离与止损点数,以及 1600 的尾部缓冲值。 把这些数直接丢进 MT5 跑,你能看到策略在横盘突破时的触发频率,但样本外表现会随点差滑点漂移,贵金属和交叉盘都是高波动品种,回测顺滑不等于实盘能复刻。 真正要验证的是 51 期窗口和 0.5 系数组合在 EURGBP 上的误触发率,建议先关掉自动交易手跑两周再谈参数微调。

MQL5 / C++
new CSimpleVolumesStrategy("EURGBP", PERIOD_H1, class="num">51, class="num">0.5, class="num">1.1, class="num">0, class="num">19500, class="num">370, class="num">22000, class="num">3, class="num">1600),
让小布替你算归一化参数
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到策略拟合余额与实时相对回撤,你只管调固定手数。把重复劳动交给小布,你专注决策。

常见问题

标准化策略余额是把最大净值回撤乘10得到的初始余额,让回撤锚定该值的10%。10%心理可接受且方便心算,参数本身可改,并非硬限。
见系列前篇的状态恢复机制,本篇在其基础上叠加可变仓位,恢复后按当前总余额与拟合余额重算手数。
0.01仅作首仓基准,后续仓根据已开数量与基准推演,但应避免简单倍数堆仓;用归一化余额约束整体相对回撤更稳。
可以,小布盯盘品种页已内置 AIGC 诊断,自动估算拟合余额与当前相对回撤,省去手动回测乘10的步骤。
若目标回撤恰为10%则取4400;若想用5%就乘20得8800,参数自由,只是10%便于快速粗略核对。