开发多币种 EA 交易(第 12 部分):开发自营交易级别风险管理器·综合运用
(3/3)·从基础余额到每日水平,手把手把自营公司风控模型写进EA最后一块拼图
「策略主循环里的五个关键调用」
在 EA 的 OnTick 收尾阶段,有一串固定顺序的方法调用决定了策略能否正常运转。看懂这段,你就能定位为什么自己的面板不刷新、虚拟仓位不更新。 CAdvisor::Tick() 负责策略信号计算,必须最先跑;随后 m_riskManager.Tick() 处理虚拟持仓的风控逻辑,背景高亮那段就是它。顺序反了,风控可能读到上一根 K 的旧信号。 m_receiver.Correct() 做市场成交量修正,Save() 落盘状态,m_interface.Redraw() 重绘界面。外汇与贵金属波动剧烈,若 Redraw 被跳过,你看到的可能是几分钟前的假报价,实盘高风险。 把这五个调用直接抄进你的 OnTick 末尾做验证,缺哪个 MT5 日志里都不会报错,但功能就是残的。
class=class="str">"cmt">// Start handling in strategies CAdvisor::Tick(); class=class="str">"cmt">// Risk manager handles class="kw">virtual positions m_riskManager.Tick(); class=class="str">"cmt">// Adjusting market volumes m_receiver.Correct(); class=class="str">"cmt">// Save status Save(); class=class="str">"cmt">// Render the interface m_interface.Redraw(); }
把风控六参数拆进独立EA
验证风险管理器不用改写整套逻辑,只要在 EA 输入区把它的六个参数露出来,再在 OnInit() 里拼好初始化串即可。下面这段代码把日亏上限、总亏上限等都做成了 input,默认日亏上限 200、总亏上限 500,基准余额 10000。 OnInit() 里先调 CMoney 设好固定余额和仓位占比 1.0,随后用 StringFormat 分别生成策略实例串、单策略组串、风控器串。把策略参数单独存进 strategyParams 变量,组串里只嵌这个变量和 scale_,结构比原先揉在一起清晰,改参数时不易误伤别的逻辑。 把文件存为 SimpleVolumesExpertSingle.mq5 后,MT5 里直接挂测就能看风控是否按 200/500 的阈值拦截订单。外汇与贵金属波动剧烈,这类硬阈值只降低极端回撤概率,不保证规避所有穿仓风险。
input group "=== Risk management" input class="type">bool rmIsActive_ = true; input class="type">class="kw">double rmStartBaseBalance_ = class="num">10000; input ENUM_RM_CALC_LIMIT rmCalcDailyLossLimit_ = RM_CALC_LIMIT_FIXED; input class="type">class="kw">double rmMaxDailyLossLimit_ = class="num">200; input ENUM_RM_CALC_LIMIT rmCalcOverallLossLimit_ = RM_CALC_LIMIT_FIXED; input class="type">class="kw">double rmMaxOverallLossLimit_ = class="num">500; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { CMoney::FixedBalance(fixedBalance_); CMoney::DepoPart(class="num">1.0); class=class="str">"cmt">// Prepare the initialization class="type">class="kw">string for a single strategy instance class="type">class="kw">string strategyParams = StringFormat( "class CSimpleVolumesStrategy(\"%s\",%d,%d,%.2f,%.2f,%d,%.2f,%.2f,%d,%d)", Symbol(), Period(), signalPeriod_, signalDeviation_, signaAddlDeviation_, openDistance_, stopLevel_, takeLevel_, ordersExpiration_, maxCountOfOrders_ ); class=class="str">"cmt">// Prepare the initialization class="type">class="kw">string for a group with one strategy instance class="type">class="kw">string groupParams = StringFormat( "class CVirtualStrategyGroup(\n" " [\n" " %s\n" " ],%f\n" " )", strategyParams, scale_ ); class=class="str">"cmt">// Prepare the initialization class="type">class="kw">string for the risk manager class="type">class="kw">string riskManagerParams = StringFormat( "class CVirtualRiskManager(\n"
◍ 把风控参数拼进虚拟顾问初始化串
这段 MQL5 片段做的是把已构造好的策略组参数与风险管理器参数,拼成一个 CVirtualAdvisor 类的初始化字符串,再据此 NEW 出处理虚拟持仓的 EA 实例。 StringFormat 里第一组 %s 填 groupParams,第二组 %s 填 riskManagerParams,后面 %d,%s,%d 依次对应 magic_(整型魔数)、策略名字符串 "SimpleVolumesSingle"、以及布尔值 true(代表启用单策略组模式)。 前面风控管理器那段格式化串 " %d,%.2f,%d,%.2f,%d,%.2f" 实际塞了 6 个量:rmIsActive_(是否激活)、rmStartBaseBalance_(起始基线余额)、rmCalcDailyLossLimit_ 与 rmMaxDailyLossLimit_(当日亏损计算限与硬上限,均保留两位小数)、rmCalcOverallLossLimit_ 与 rmMaxOverallLossLimit_(总亏损计算限与硬上限)。 PrintFormat 会把最终 expertParams 原样打印到日志,开 MT5 跑一下就能在专家日志里看到完整嵌套文本,用来核对参数有没有串位。外汇与贵金属品种波动大,虚拟风控的限值若设错,实盘切换后可能放大回撤风险。
" %d,%.2f,%d,%.2f,%d,%.2f"
" )",
rmIsActive_, rmStartBaseBalance_,
rmCalcDailyLossLimit_, rmMaxDailyLossLimit_,
rmCalcOverallLossLimit_, rmMaxOverallLossLimit_
);
class=class="str">"cmt">// Prepare the initialization class="type">class="kw">string for an EA with a group of a single strategy and the risk manager
class="type">class="kw">string expertParams = StringFormat(
"class CVirtualAdvisor(\n"
" %s,\n"
" %s,\n"
" %d,%s,%d\n"
")",
groupParams,
riskManagerParams,
magic_, "SimpleVolumesSingle", true
);
PrintFormat(__FUNCTION__" | Expert Params:\n%s", expertParams);
class=class="str">"cmt">// Create an EA handling class="kw">virtual positions
expert = NEW(expertParams);
if(!expert) class="kw">return INIT_FAILED;
class="kw">return(INIT_SUCCEEDED);
}「用模型策略实跑验证风控开关」
拿前几轮优化出来的模型策略参数直接跑单趟测试,先在 2021–2022 年关掉风险管理器。结果资金曲线出现多次明显回撤,最大的是 2021 年 10 月底约 380 美元、2022 年 6 月约 840 美元。 打开风险管理器,设每日最大亏损 150 美元、总亏损 450 美元。2021 年 10 月它两次平掉亏损市价仓,但虚拟仓没平,第二天市价仓重开在不利价位,总回撤反而略高于不用风控时的净值回撤,且没吃到原该有的微利反而亏了。2022 年 6 月触发七次日亏限额,同样低价重开造成连锁损失。 不过这里有个细节:总亏损按基础余额算,基准 10000 减 450 即 9550 美元才触发。因前期利润垫着,资金没破 9550,EA 继续开仓。把 scale_ 提到 50 后,2022 年 6 月资金跌破 9550,交易直接停摆,之后不再开仓——两个限制工具都按设计工作了。 把风控接到自营规则外也成立。优化时把总损设为日亏的 10%,一年标准化利润从 1560 美元增至 2276 美元,接近一倍半。最佳通行里 scale_=88、限额 7.6% 与 10%,全程未触总损,EA 持续交易。单实例看意义不大,但多实例 EA 逻辑一致,风控模块倾向确有实用价值。外汇与贵金属品种波动剧烈,回测结论仅代表历史样本,实盘仍属高风险。
管理器能落地但别急着上实盘
这套基础交易风险管理器已经能守住两个硬指标:每日最大亏损与总亏损上限。核心判定就一行——当 m_dailyProfit 低于负的 DailyLoss() 且当前仓位占用保证金大于 0 时,禁止继续开仓。 它目前有个致命短板:EA 重启后状态不保留,所以千万别直接丢进真实账户。外汇和贵金属杠杆高、跳空频繁,重启丢状态可能让限额形同虚设。 后续可补的料不少:按交易时段禁开(比如重要数据发布前后)、亏损过半时仓位砍半、只在回撤破线后才恢复手数。这些改起来都不算难,作者说留到后面篇章。 代码层看,m_dailyProfit 每个 tick 用净值减日初基准重算,逻辑透明。想验证就开 MT5 把 VirtualRiskManager.mqh 挂上,跑模拟盘看每日熔断是否准时触发。
if(m_dailyProfit < -DailyLoss() && CMoney::DepoPart() > class="num">0) { ... } m_dailyProfit = m_equity - m_baseDailyLevel;