利用文斯 (Vince) 进行资金管理。 作为 MQL5 向导模块实现·综合运用
(3/3)·从旋转投币推演到向导模块封装,解决复利下注反而亏钱的实战配置盲区
很多交易者以为只要信号准就能盈利,却忽略下注比例本身就会扭转胜负。文斯用最简单的旋转投币证明:满仓必死,分仓才有续命空间,而最优分仓比例需要专门计算而非拍脑袋。
「双因子仓位里的历史盈亏判定」
这段逻辑把「上一笔交易赚没赚」当成切换仓位因子的开关:盈利时用 factor1 放大敞口,亏损时用 factor2 收敛,属于典型的两因子资金管理思路。外汇与贵金属杠杆高,因子设错可能在连续亏损后把净值拖进强平区,实盘前务必在 MT5 策略测试器跑一遍。 CheckLoss() 只回看最近一笔同品种成交的 Profit(),若 deals==1 直接返回 1,意味着首笔交易前默认按因子1 手数计算。注意它用 HistorySelect(0,TimeCurrent()) 拉全量历史,再倒序遍历,遇到非当前品种就 continue,找到即 break,效率上只取一条。 CheckOpenLong() 里用 OrderProfitCheck 算固定止损距离下的单手亏损额 loss,再用 MathFloor(Balance()*factor/loss/stepvol)*stepvol 把仓位对齐券商最小步长。p>0 走 factor1,p<0 走 factor2,两者公式完全对称,差别只在乘数。 别把首笔返回 1 当万能默认值。若账户余额很小而 factor1 偏大,第一单就可能下重手,建议把 deals==1 的分支改成返回 0 或显式小因子再观察。
class="type">class="kw">double CMoneyTwoFact::CheckLoss() { class="type">class="kw">double lot=class="num">0.0; HistorySelect(class="num">0,TimeCurrent()); class="type">int deals=HistoryDealsTotal(); class=class="str">"cmt">// 历史记录中的成交数量 CDealInfo deal; class=class="str">"cmt">//--- 搜索以前的交易 if(deals==class="num">1) class="kw">return class="num">1; for(class="type">int i=deals-class="num">1;i>=class="num">0;i--) { if(!deal.SelectByIndex(i)) { printf(__FUNCTION__+": 选择指定索引的交易失败"); class="kw">break; } class=class="str">"cmt">//--- 根据品种或其它参数选择交易 if(deal.Symbol()!=m_symbol.Name()) class="kw">continue; class=class="str">"cmt">//--- 返回交易结果 lot=deal.Profit(); class="kw">break; } class="kw">return(lot); } class="type">class="kw">double CMoneyTwoFact::CheckOpenLong(class="type">class="kw">double price,class="type">class="kw">double sl) { class="type">class="kw">double lot=class="num">0.0; class="type">class="kw">double p=CheckLoss(); class=class="str">"cmt">/* ExpertBase.mqh class="type">int digits_adjust=(m_symbol.Digits()==class="num">3 || m_symbol.Digits()==class="num">5) ? class="num">10 : class="num">1; m_adjusted_point=m_symbol.Point()*digits_adjust; */ class="type">class="kw">double loss; if(price==class="num">0.0)price =m_symbol.Ask(); if(p>class="num">0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_BUY,class="num">1.0,price,price - m_max_loss*m_adjusted_point); class="type">class="kw">double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor1/loss/stepvol)*stepvol; } if(p<class="num">0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_BUY,class="num">1.0,price,price - m_max_loss*m_adjusted_point); class="type">class="kw">double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor2/loss/stepvol)*stepvol; } class="kw">return(lot); }
空单手数按账户权益动态切分
做空信号触发时,手数不是写死的,而是拿账户余额去反向除单笔最大亏损估算值,再按品种最小步长向下取整。核心落在 CMoneyTwoFact::CheckOpenShort 里,先取 CheckLoss() 的返回值 p 判定当前处于盈利保护还是回补模式。 p>0 走 m_factor1 系数,p<0 走 m_factor2 系数,二者都调用 OrderProfitCheck 以 1.0 标准手、入场价、止损价(price + m_max_loss*m_adjusted_point)试算一次名义亏损。外汇与贵金属杠杆高,试算亏损对点值敏感,m_adjusted_point 已按 3/5 位小数品种乘 10 修正,避免跨品种手数偏差。 lot = MathFloor(Balance()*系数 / loss / stepvol) * stepvol 这一行决定真实下单量。若 Balance 为 10000、loss 估出 200、stepvol 0.01,m_factor1 取 0.02,则 lot = floor(10000*0.02/200/0.01)*0.01 = 1.00 手;改 m_factor1 为 0.05 就跳到 2.00 手。开 MT5 把这两因子和 m_max_loss 接上历史 tick 回测,能直接看到权益回撤曲线的切换。
class="type">class="kw">double CMoneyTwoFact::CheckOpenShort(class="type">class="kw">double price,class="type">class="kw">double sl) { class="type">class="kw">double lot=class="num">0.0; class="type">class="kw">double p=CheckLoss(); class=class="str">"cmt">/* class="type">int digits_adjust=(m_symbol.Digits()==class="num">3 || m_symbol.Digits()==class="num">5) ? class="num">10 : class="num">1; m_adjusted_point=m_symbol.Point()*digits_adjust;*/ class="type">class="kw">double loss; if(price==class="num">0.0)price =m_symbol.Ask(); if(p>class="num">0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_SELL,class="num">1.0,price,price+m_max_loss*m_adjusted_point); class="type">class="kw">double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor1/loss/stepvol)*stepvol; } if(p<class="num">0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_SELL,class="num">1.0,price,price+m_max_loss*m_adjusted_point); class="type">class="kw">double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor2/loss/stepvol)*stepvol; } class="kw">return(lot); }
◍ 把回测盈亏流喂给最优 f 求解器
随文附带的 Programs.mq5 里有一个 void ReadFile(string file, double&arr[]) 函数,作用是把策略测试器导出的逐笔结果读进数组,供后续按固定手数反推最优 f 系数使用。Vince.mq5 与 Vince_2.mq5 则是文中参数化求解方法的源码,MoneyF1.mqh、MoneyF1F2.mqh 为资金管理模块头文件,整套按 MetaEditor 目录结构打包在 MQL5.zip(9.01 KB)内。 更省事的做法是:在策略测试器用固定手数跑一遍,把报告存成 Open XML(Excel 可读),手动把隔夜费与佣金并入 Profit 列得到 PL 流,再导出为 txt 或 csv。这样每一行就是一笔独立交易的盈亏,ReadFile() 直接吃进 arr[],你就能对任意固定手数策略求自己的 f。外汇与贵金属品种点差滑点敏感,这套流程仅降低手工解析成本,实盘资金曲线仍可能因流动性断裂而偏离回测,属高风险验证动作。 别把正态当圣经 原文提到有人想写完整类去解析 HTML 报告,其实没必要。固定手数 + Excel 改列 + 文本落地,三步就能绕开报告解析的工程坑,把精力留给 f 系数本身。
「记住这一条就够了」
拿两个简单交易序列算笔账:第一笔赚50%、第二笔亏40%,不把利润滚入下一笔,净结果是+10%;若把第一笔盈利再投进第二笔,账户变成100×1.5×0.6-100=-10%,正期望被复利结构反手扭成亏损。 作者原文漏了两种边界:两笔都赚时是100×1.5×1.5-100=125,两笔都亏时是100×0.6×0.6-100=-36(原文写64系笔误,按0.6乘数应为-36)。规律很直白——加号系统经再投资一般仍是加号,但单笔大回撤会吞噬前期利润。 资金管理(MM)既不能把负系统救成正系统,也守得住正系统不滑向负。外汇与贵金属杠杆高,这套算术在实盘里会被点差和滑点放大变形,开MT5用策略测试器跑一遍上述乘数组合,比任何口诀都实在。