基于预定义的风险和风险/回报比建立互动式半自动拖放EA 交易·综合运用
(3/3)·手算仓位和手绘止损线太低效?这篇把前三篇拆开的逻辑拼成可拖放执行的完整EA
◍ 下单与风控参数的实时联动
这段逻辑把可视面板上的 SL/TP、手数和权益风险绑成了同一条链:每次拖箭头或改 RR,EA 都会按买/卖方向重算 TP,并调用资金管理模块校验允许手数。 当计算出的 TP 不大于 SL 时,代码直接把 SL/TP 清零、删掉风险奖励矩形并返回,避免发出无效订单——这是防止鼠标误拖导致 0 或负盈亏比的硬保险。 EA_placeOrder() 里用 trade.Buy / trade.Sell 下市价单,SL、TP 和 lots 全部取自 visualRRDialog 的当前值;也就是说你面板上看到什么,单子就是什么,没有隐藏覆盖。 EA_onTick() 中若 SL_price 非零且未下单,会按品种实时买价/卖价重新算允许手数,并把手数上限回写面板;若当前设置手数大于上限则自动下调。外汇与贵金属杠杆高,这类自动裁剪能压住单笔回撤概率,但是否成交仍看流动性与滑点。
SL_price=maxSLAllowed; SL_arrow.SetDouble(OBJPROP_PRICE,class="num">0,maxSLAllowed); } visualRRDialog.SetSL(SL_price); visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio()); if(visualRRDialog.GetTP()>SL_price) { visualRRDialog.SetSL(class="num">0.0); visualRRDialog.SetTP(class="num">0.0); SL_arrow.SetDouble(OBJPROP_PRICE,symbolInfo.Bid()); rectReward.Delete(); rectRisk.Delete(); class="kw">return; } class="type">class="kw">double lotSize=MM.CheckOpenShort(symbolInfo.Bid(),SL_price); visualRRDialog.SetMaxAllowedLots(lotSize); visualRRDialog.SetOrderLots(lotSize); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Bid(), SL_price, lotSize)); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, lotSize, visualRRDialog.GetRRRRatio())); visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); } class="type">bool EA_placeOrder() { symbolInfo.RefreshRates(); visualRRDialog.ResetButtons(); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) orderPlaced=trade.Buy(visualRRDialog.GetOrderLots(),symbolInfo.Ask(), visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent())); else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) orderPlaced=trade.Sell(visualRRDialog.GetOrderLots(),symbolInfo.Bid(), visualRRDialog.GetSL(),visualRRDialog.GetTP(),TimeToString(TimeCurrent())); class="kw">return orderPlaced; } class="type">void EA_editParamsUpdate() { MM.Percent(visualRRDialog.GetRiskPercent()); SL_arrow.GetDouble(OBJPROP_PRICE, class="num">0, SL_price); SL_arrow.GetInteger(OBJPROP_TIME, class="num">0, startTime); symbolInfo.RefreshRates(); currentTime=TimeCurrent(); class="type">class="kw">double allowedLots=MM.CheckOpenLong(symbolInfo.Ask(),SL_price); class="type">class="kw">double lowestSLAllowed=MM.GetMaxSLPossible(symbolInfo.Ask(),ORDER_TYPE_BUY); if(SL_price<lowestSLAllowed) { SL_price=lowestSLAllowed; ObjectSetDouble(class="num">0,slButtonID,OBJPROP_PRICE,lowestSLAllowed); } visualRRDialog.SetSL(SL_price); visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio()); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots())); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio())); visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); ChartRedraw(); } class="type">void EA_onTick() { if(SL_price!=class="num">0.0 && orderPlaced==false) { class="type">class="kw">double lotSize=class="num">0.0; SL_price=visualRRDialog.GetSL(); symbolInfo.RefreshRates(); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) lotSize=MM.CheckOpenLong(symbolInfo.Ask(),SL_price); else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) lotSize=MM.CheckOpenShort(symbolInfo.Ask(),SL_price); visualRRDialog.SetMaxAllowedLots(lotSize); if(visualRRDialog.GetOrderLots()>lotSize) visualRRDialog.SetOrderLots(lotSize); visualRRDialog.SetMaxTicksLoss(MM.CalcMaxTicksLoss()); if(visualRRDialog.GetOrderType()==ORDER_TYPE_BUY) { visualRRDialog.SetTP(symbolInfo.Ask()+(symbolInfo.Ask()-SL_price)*visualRRDialog.GetRRRRatio());
买卖双向的 RR 矩形怎么画
这段逻辑处理的是可视化 RR 面板里,根据订单方向实时算 TP、风险收益额,并把报酬区与风险区画成矩形。买单方用 Ask 作为入场基准,空单方用 Bid,SL 价格由面板传入,TP 直接按 RR 倍数从 SL 反推。 以空单为例:TP 价 = Bid - (SL - Bid) * RR,风险额由 CalcOrderEquityRisk(Bid, SL, lots) 得出,报酬额由 CalcOrderEquityReward(Bid, SL, lots, RR) 得出。函数末尾调 Refresh() 和 rectUpdate(),再 ChartRedraw(0) 强制重绘。 rectUpdate() 里先 RefreshRates() 拿最新报价,取 SL 箭头的时间作为矩形起点。买单方报酬矩形上边界是 Ask + (Ask - SL) * RR、下边界 Ask,风险矩形夹在 SL 与 Ask 之间;空单方镜像处理。颜色硬编码 LightGreen / LightPink 且 Background(true),即半透明衬底,不挡 K 线。 开 MT5 把这段塞进你的 RR 脚本,改 LightGreen 为 clrForestGreen 之类,能立刻看出报酬区在深色模板下是否够醒目;外汇与贵金属杠杆高,可视化再直观也只是辅助,实际滑点可能让风险矩形边界失真。
visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots())); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Ask(), SL_price, visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio())); } else if(visualRRDialog.GetOrderType()==ORDER_TYPE_SELL) { visualRRDialog.SetTP(symbolInfo.Bid()-(SL_price-symbolInfo.Bid())*visualRRDialog.GetRRRRatio()); visualRRDialog.SetOrderEquityRisk(MM.CalcOrderEquityRisk( symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots())); visualRRDialog.SetOrderEquityReward(MM.CalcOrderEquityReward(symbolInfo.Bid(), SL_price, visualRRDialog.GetOrderLots(), visualRRDialog.GetRRRRatio())); } visualRRDialog.Refresh(); rectUpdate(visualRRDialog.GetOrderType()); } ChartRedraw(class="num">0); } class="type">void rectUpdate(ENUM_ORDER_TYPE orderType) { symbolInfo.RefreshRates(); currentTime=TimeCurrent(); SL_arrow.GetInteger(OBJPROP_TIME,class="num">0,startTime); if(orderType==ORDER_TYPE_BUY) { rectReward.Create(class="num">0,rewardRectID,class="num">0,startTime,symbolInfo.Ask(),currentTime,symbolInfo.Ask()+ (symbolInfo.Ask()-visualRRDialog.GetSL())*visualRRDialog.GetRRRRatio()); rectReward.Color(LightGreen); rectReward.Background(true); rectRisk.Create(class="num">0,riskRectID,class="num">0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Ask()); rectRisk.Color(LightPink); rectRisk.Background(true); } else if(orderType==ORDER_TYPE_SELL) { rectReward.Create(class="num">0,rewardRectID,class="num">0,startTime,symbolInfo.Bid(),currentTime,symbolInfo.Bid()- (visualRRDialog.GetSL()-symbolInfo.Bid())*visualRRDialog.GetRRRRatio()); rectReward.Color(LightGreen); rectReward.Background(true); rectRisk.Create(class="num">0,riskRectID,class="num">0,startTime,visualRRDialog.GetSL(),currentTime,symbolInfo.Bid()); rectRisk.Color(LightPink); rectRisk.Background(true); } }
「实盘回看:周一开盘反弹后的卖单是怎么挂的」
一段 2010 年 1 月 11 日(周一)的行情回放里,能看到大反弹刚起没多久就下了卖单。那天亚盘开盘后不久价格快速拉升,EA 在动量转弱的第一根吞没阴线触发了逆势入场。 视频建议开 480p 全屏,注释直接打在画面里,能看清下单价位和后续止损移动轨迹。外汇与贵金属这类品种跳空和滑点频繁,逆势单风险偏高,只看演示不验证参数等于白看。 想复现的话,把这段行情加载到 MT5 的「日期跳转」里,对照 EA 日志里的 tick 时间戳,就能确认它是在反弹后第几根 K 线发的单。
◍ 一点提醒
前面几节把基于预定风险与回报比搭交互式 EA 的思路讲完了,标准类画图、图表事件拖放输入也都落了地。附带的 visualrrea.mq5(27.41 KB)和 moneyfixedriskext.mqh(14.36 KB)是直接能开 MT5 编译验证的实物,不用自己从头写框架。 这类半自动辅助工具只解决下单流程和风控参数可视化,不替你判断方向。外汇和贵金属杠杆高、滑点跳空频繁,哪怕固定风险比例设得再漂亮,实盘仍可能连亏或瞬间扩损。 想继续扩能力的,作者后来用 CChartObject 做了 Spinner、ProgressBar 等新 GUI 组件,比本文拖图方式更顺手,有空可以顺着那个思路改你自己的面板。