首页 / 交易学院 / 小布知识库 / 风险/回报比驱动的半自动拖放 EA:MQL5 GUI 实战
风险/回报比驱动的半自动拖放 EA:MQL5 GUI 实战
🤖

风险/回报比驱动的半自动拖放 EA:MQL5 GUI 实战

EA 开发实战 难度 · 进阶 2013 13 分钟阅读
GUI风险管理拖放

为什么需要“半自动”EA,而不是纯手动或纯自动

纯自动 EA 不帯情绪、执行快,但面对突发形态和消息往往缺乏判断力;纯手动交易相反,人能看懂结构,却很难在几秒内精确算出某条止损线对应的手数和盈亏比。半自动 EA 取二者之长:交易者只负责“看方向、定大概入场”,把止损、止盈、仓位这些需要精确计算、又最容易手抖出错的部分交给程序。本文要做的,就是在一个图表上直接拖一根止损指针,程序立刻算出“这笔最多能下多少手、对应的回报价在哪、当前风险占净值百分之几”,确认后一键市价成交。

整体架构:一张图表上的风险/回报面板

整套界面被封装在一个自定义类 CRRDialog 里。它在图表左上角用一张背景图打底,上面叠加:若干实时刷新的标签(品种名、买价/卖价、净值、当前风险/回报)、三个编辑框(风险比 RRR、风险百分比、手数)、几个按钮(切换多/空、下达订单、退出 EA),以及一个可拖放的止损指针 SL_arrow(位图对象)和两块彩色矩形(粉红代表风险区间、淡绿代表回报区间)。所有对象都通过 CChartObject 系列类的成员方法创建和改属性,而不是散落的 ObjectCreate/ObjectSet* 调用。

用 CChartObject 搭界面,而不是裸 ObjectCreate

MQL5 标准库提供了一组从 CChartObject 派生的图表对象类。用裸 ObjectCreate 时,你得用对象名字符串去调 ObjectGetInteger/SetInteger、ObjectGetDouble/SetDouble、ObjectGetString/SetString,属性名记不住、拼错不报错只能运行时才发现。换成 CChartObject 派生类后,声明一个实例(例如 CChartObjectLabel label),直接 label.Create(...)、label.Description(...)、label.Color(...) 就能改属性,编辑器还有代码补全,逻辑更清晰也更快。CreateCRRDialog(int topX, int leftY) 负责把所有控件摆到正确位置:用 m_baseX/m_baseY 做基准偏移,逐个设置坐标、字体(标签用 Verdana、按钮用宋体且 FontSize(8))和颜色。背景图用 CChartObjectBmpLabel,Create 后调 BmpFileOn(file) 挂上 GIMP 导出的位图。

OnChartEvent:把用户的每次操作翻译成参数

交互全靠 OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)。本文处理三类事件:CHARTEVENT_OBJECT_CLICK(点击按钮或选中 SL_arrow)、CHARTEVENT_OBJECT_DRAG(拖放 SL_arrow)、CHARTEVENT_OBJECT_ENDEDIT(在某个编辑框回车)。分发时靠 sparam 里的对象名判断是谁触发的——点 switchOrderTypeButton 就切多/空,点 placeOrderButton 就市价下单,点 slButton 就切换 SL_arrow 的选中态。一个小技巧:通常要双击才能选中对象,这里用 SL_arrow.Selected(!SL_arrow.Selected()) 把“单击”也变成选中切换,拖起来更顺手。

拖一下 SL,手数和止盈是怎么反算出来的

当你拖动 SL_arrow,触发 CHARTEVENT_OBJECT_DRAG,买入分支 EA_dragBuyHandle 会读回指针的当前价格和锚点时间(GetDouble(OBJPROP_PRICE)、GetInteger(OBJPROP_TIME)),刷新一次报价后调用 CMoneyFixedRiskExt::CheckOpenLong(Ask, SL_price) 算出“在当前止损下最多能下多少手”。如果拖得太狠、导致最小手数(0.1)的风险都超标,代码不会假装下单,而是把 SL 强制拉回允许的最差值(ObjectSetDouble 写回指针),再重新 SetSL/SetTP。止盈按你设的风险比算:TP = Ask + (Ask − SL) × RRR,且必须 TP > SL,否则清空矩形、不画回报区。最后把算出的手数、最大可亏 tick、当前风险/回报写进面板并刷新两块矩形。

✦ 用文中例子直观化
账户余额 10,000、风险 2%,则单笔最多亏 200 基础货币;最小手数 0.1 时,GetMaxSLPossible 会返回“刚好不超 200 亏损”的那条最远止损价。把 SL 拖到比这更远,程序自动拽回来——这正是“程序管风险”的体现。

CMoneyFixedRiskExt:在固定风险模型上再加四道约束

资金管理类继承标准库的 CExpertMoney(其底层已是固定风险模型),新增四个计算方法。GetMaxSLPossible(price, type) 先算最大可亏金额 = 余额 × 风险%,再除以“最小手数 × 每跳亏损 TickValueLoss”得到最大可亏 tick 数,最后用 TickSize 把 tick 数换算回价格距离。CalcMaxTicksLoss 是同一思路的标量版。CalcOrderEquityRisk(price, sl, lots) = lots × TickValueLoss × |price−sl| / TickSize,即“这笔如果被打损,净亏多少”;CalcOrderEquityReward 在此基础上再乘风险比 RRR(用 TickValueProfit)。注意这里用 TickValueLoss/TickValueProfit 而不是粗略的“点值”,因为不同品种、不同手数下每跳价值并不恒定,直接用 tick 价值才能算准。

几个容易踩的坑

常见问题

手动交易容易情绪化调仓,半自动 EA 把风险纪律固定下来,交易者只负责方向判断。
拖放更直观,交易者可以在图表上直接看到止损位置,并立即知道风险/回报比是否合适。
风险固定时,止损距离越近,允许仓位越大;止损距离越远,仓位越小。TP 按风险/回报比从止损位置对称计算。
不太适合。需要预留足够时间做拖动和确认,适合波段或日内波段交易。