自动交易的传说: 是少或多?·综合运用
🤖

自动交易的传说: 是少或多?·综合运用

(3/3)·从手工猜突破到脚本自动下单,这篇把前两篇的原理压进一套能实盘试错的交易机器人

案例拆解 第 3/3 篇
很多人把点数图当纯画线玩具,以为突破信号肉眼看就够了,结果漏单和扛单同时发生。用固定手数硬接止损,回撤一旦超 10% 就心态崩盘。外汇贵金属高杠杆下,这种无优化入场是慢性自杀。

◍ 点数图挂单的买卖切换与纠错

在点数图(PNF)策略里,挂单方向由最新列的阻力/支撑换算价与实时卖价的关系决定。若 10 的 resist_price 次幂不小于 SYMBOL_ASK,则按 BUY STOP 处理;反之若小于 ASK,则走 SELL STOP 分支。 trade_request.price 与 sl 都用 NormalizeDouble 配合 MathPow(10, …) 算出,digit_2_orders 控制小数位。以买分支为例,sl 取 resist_price 减去 (reverse-1)*double_box 后的幂值,卖分支则加回,框数偏移直接决定止损距离。 每轮先 OrderSelect(ticket) 看旧挂单在不在。不在就直接 PlaceOrder() 并把 order_col_number 设为当前 column_count;在的话再比对类型与开价——买分支里若旧单是 SELL_STOP,或虽是 BUY_STOP 但价格不等于请求价,就先 TRADE_ACTION_REMOVE 撤掉,再 PlaceOrder() 重挂。 外汇与贵金属杠杆高,这种自动撤补逻辑可能在快速波动中连续触发手续费与滑点,实盘前务必在 MT5 策略测试器用历史数据跑一遍 reverse 与 double_box 组合。

MQL5 / C++
trade_request.price=NormalizeDouble(MathPow(class="num">10,pnf[column_count-class="num">1].resist_price),digit_2_orders);
trade_request.sl=NormalizeDouble(MathPow(class="num">10,pnf[column_count-class="num">1].resist_price-(reverse-class="num">1)*double_box),digit_2_orders);
trade_request.type=ORDER_TYPE_BUY_STOP;
if(OrderSelect(ticket)==class="kw">false)
  class=class="str">"cmt">//--- No pending orders - place an order
  {
   PlaceOrder();
   order_col_number=column_count;
  }
else
 class=class="str">"cmt">//--- or there is a pending order
  {
   class=class="str">"cmt">//--- what is the type and price of the pending order?
   if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) ||
      ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
     {
      class=class="str">"cmt">//--- The wrong type or the price differs - close the order
      trade_request.order=ticket;
      trade_request.action=TRADE_ACTION_REMOVE;
      if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
      class=class="str">"cmt">//--- open with the desired price
      PlaceOrder();
      order_col_number=column_count;
     };
  };
class="kw">return;
 }
else
 if(MathPow(class="num">10,pnf[column_count-class="num">1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK))
  {
   class=class="str">"cmt">//--- Conditions for SELL met; let&class="macro">#x27;s see if there are any pending Sell orders for the symbol with the price we need?
   trade_request.price=NormalizeDouble(MathPow(class="num">10,pnf[column_count-class="num">1].supp_price),digit_2_orders);
   trade_request.sl=NormalizeDouble(MathPow(class="num">10,pnf[column_count-class="num">1].supp_price+(reverse-class="num">1)*double_box),digit_2_orders);
   trade_request.type=ORDER_TYPE_SELL_STOP;
   if(OrderSelect(ticket)==class="kw">false)
     class=class="str">"cmt">//--- No pending orders - place an order
     {
      PlaceOrder();
      order_col_number=column_count;
     }
   else
     class=class="str">"cmt">//--- If there is a pending order
     {
      class=class="str">"cmt">//--- what is the type and price of the pending order?
      if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) ||
         ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
        {

挂单失配时的撤单重发与止损跟随

当检测到挂单类型不对或价格偏离预期,代码先走撤单逻辑:把 ticket 填回请求结构,action 设为 TRADE_ACTION_REMOVE,调用 OrderSend 删单;若发送失败就打印单号,随后立即用 PlaceOrder() 以目标价格重新挂出,并把 order_col_number 同步为当前 column_count。这一套在 MT5 里跑起来,能避免残单占用仓位额度。 TrailCondition() 则负责移动止损。多头仓用 SYMBOL_ASK 与 10 的(max_column_price - reverse*double_box)次幂取大值,空头仓用 SYMBOL_BID 与对应次幂取小值,再 NormalizeDouble 到 digit_2_orders 精度。只有当新 SL 与 POSITION_SL 不一致时才触发 PlaceTrailOrder(),减少无谓交易指令。 外汇与贵金属杠杆高,这类自动撤重与追损逻辑可能在快速波动中连续触发,实盘前务必用策略测试器按 Tick 模式回测,观察滑点下的重发频率。

MQL5 / C++
      class=class="str">"cmt">//--- The wrong type or the price differs - close the order
      trade_request.order=ticket;
      trade_request.action=TRADE_ACTION_REMOVE;
      if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
      class=class="str">"cmt">//--- open with the desired price
      PlaceOrder();
      order_col_number=column_count;
     };
   };
   class="kw">return;
   }
  else
   class="kw">return;
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The function checks conditions for moving Stop Loss                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void TrailCondition()
  {
  if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
    trade_request.sl=NormalizeDouble(MathMax(SymbolInfoDouble(symbol,SYMBOL_ASK),MathPow(class="num">10,pnf[column_count-class="num">1].max_column_price-reverse*double_box)),digit_2_orders);
  else
    trade_request.sl=NormalizeDouble(MathMin(SymbolInfoDouble(symbol,SYMBOL_BID),MathPow(class="num">10,pnf[column_count-class="num">1].min_column_price+reverse*double_box)),digit_2_orders);
  if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
    PlaceTrailOrder();
  };

「回测数据暴露的品种与箱体规律」

两套预设品种列表已分别装入 symbol_list_1.mhq 与 symbol_list_2.mhq,覆盖外汇直盘交叉盘及道琼斯股票期货。EA 至少需要数根完整价格柱做分析,箱体设到 50 点以上时,一年历史往往凑不出一根柱;本例箱体 100 点,设置页测试起点拉到 2009.01.01,参数页实跑起点设 2012.01.01,优化窗口取 2012–2013。 手数逻辑由余额与 Risk per Trade 驱动(本例每笔 1%,可优化),Max Drawdown 作为 OnTester() 的优化准则,全文仅拿品种和箱体点数两个变量开跑。EURUSD 上即时报价覆盖最好,但 10 点箱体的 2012–2013 横评里它净亏 1058.85,获利因子 0.65;同表 USDCHF 净赚 2166.53、夏普 2.40,AUDCAD 赚 886.56、回撤仅 5.76%。 汇总表更刺眼:策略1股票期货箱体 100,仅 81 笔交易,净值回撤略超 2%,本金增长约 44%;把可承受回撤放宽到 10%、单笔风险提到 4%,两年本金增长倾向冲到 177%(年化约 90%)。外汇 10 点箱体 1659 笔交易反而净亏 10213.52——交易频次和收益在这里是倒挂的。 外汇与贵金属属高杠杆品种,回测里的正向结果只是特定参数区间的概率性表现,实盘滑点、点差扩大都会啃掉样本外的空间,别把 44% 或 177% 当成可复制的承诺。

◍ 回撤可控时再谈放大收益

原文提到的基准策略在回撤不超过10%的前提下,存在通过参数修改放大回报的空间,但这不意味着能无脑加仓。频繁交易只会增加滑点与手续费损耗,对散户存款并不友好。 品种数量比多数人想的重要。如果券商只给20到40个标准外汇品种,相关性很容易把风险叠在一起;更稳妥的做法是找能提供300到400个标的(股票、期货都算)的券商,且标的最好彼此弱关联。 一个可验证的现象是:同一套规则跑股票常常比跑货币对更好看,外汇和贵金属本身高杠杆高波动,实盘前请在MT5用策略测试器先跑一遍样本外数据。

把范式交给眼睛和 EA 去碰

作者上架的 PnF Chartist 脚本能吃 MT4、MT5 或雅虎财经的报价,在文本文件里画点数图。它的定位不是自动找信号,而是把价格范式摊开给人脑扫——原文直言没有比交易者大脑更好的测试/优化器。 看到疑似范式后,直接套前文给的 EA 模板(strategy_one.mq5 与 strategy_two.mq5,分别约 37.87 KB 与 37.39 KB)去回测验证。附件里还带了 symbol_list_1.mqh、symbol_list_2.mqh 做品种清单,省去手填代码。 有用户反馈在测试模式下两版 EA 都崩过:strategy_two.mq5 第 348 行报除零,strategy_one.mq5 第 438 行报数组越界。外汇与贵金属杠杆高、滑点跳空频繁,这类历史bug在实盘前必须自己修掉再跑。 点数是人眼先筛、机器后证,别把脚本当黑箱。开 MT5 把附件丢进 MQL5/Files 跑一遍,比信任何'现成策略'都实在。

把回测重优化交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到回撤与重新优化提示,你只管判断市场结构是否还成立。

常见问题

这是顺势突破的过滤:只有在原有买方列被刷新且站上支撑轴点时才入场,倾向减少假突破带来的频繁止损。
以账户可接受亏损百分比反推手数,Ralph Vince的资金管理思路是把它当可优化变量但限定区间,概率上控制单笔回撤而非追求胜率。
可以,小布已内置回撤监控与品种页诊断,当实时回撤逼近你设的阈值时会提示,省去手动每周跑策略测试员。
该模式不支持对EA输入参数做遍历,只能先锁定品种再枚举可优化变量,否则会跳过参数搜索直接跑固定值。
不够,本篇倾向以回撤是否超最大可接受风险为触发,市场没破阈值就让它继续跑,避免无意义重拟合。