MQL5 Cookbook - 以 MQL5 编写的多币种 EA,利用限价订单工作·综合运用
(3/3)· 从时间窗到反向追踪止损,13 节拆解一个能日内交易多品种挂单系统的完整拼装
多数人在多币种 EA 里直接无脑挂限价单,却忽略交易时段与反向模式的手数约束,回测里看着漂亮,实盘一到美盘切换就乱删单。把挂单生命周期交给全局变量而不是事件驱动,是这类 EA 最常见的隐性 bug。
◍ 挂单与止损位的动态边界计算
EA 在批量挂单时,最容易被经纪商 Stops Level 限制挡掉。下面这段逻辑把挂单价格和止损价格都做了「阈值保护」,优先用算法算出的理论位,越界才退到 broker 允许的边界附近。 double CalculatePendingOrder(int symbol_number,ENUM_ORDER_TYPE order_type) { //--- For the calculated pending order value double price=0.0; //--- If the value for SELL STOP order is to be calculated if(order_type==ORDER_TYPE_SELL_STOP) { //--- Calculate level price=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); //--- Return calculated value if it is less than the lower limit of Stops level // If the value is equal or greater, return the adjusted value return(price<symb.down_level ? price : symb.down_level-symb.offset); } //--- If the value for BUY STOP order is to be calculated if(order_type==ORDER_TYPE_BUY_STOP) { //--- Calculate level price=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); //--- Return the calculated value if it is greater than the upper limit of Stops level // If the value is equal or less, return the adjusted value return(price>symb.up_level ? price : symb.up_level+symb.offset); } //--- return(0.0); } //+------------------------------------------------------------------+
| // | Calculates Stop Loss level for a pending order |
|---|
//+------------------------------------------------------------------+ double CalculatePendingOrderStopLoss(int symbol_number,ENUM_ORDER_TYPE order_type,double price) { //--- If Stop Loss is required if(StopLoss[symbol_number]>0) { double sl =0.0; // For the Stop Loss calculated value double up_level =0.0; // Upper limit of Stop Levels double down_level=0.0; // Lower limit of Stop Levels //--- If the value for BUY STOP order is to be calculated if(order_type==ORDER_TYPE_BUY_STOP) { //--- Define lower threshold down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits); //--- Calculate level sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits); //--- Return the calculated value if it is less than the lower limit of Stop level // If the value is equal or greater, return the adjusted value return(sl<down_level ? sl : NormalizeDouble(down_level-symb.offset,symb.digits)); } //--- If the value for the SELL STOP order is to be calculated if(order_type==ORDER_TYPE_SELL_STOP) { //--- Define the upper threshold up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits); //--- Calculate the level sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits); 逐行看:CalculatePendingOrder 里 SELL STOP 用 bid 减点值,BUY STOP 用 ask 加点值,都由 PendingOrder[symbol_number] 控制距离。NormalizeDouble 按 symb.digits 规整报价精度,避免 XAUUSD 的 0.01 与 EURUSD 的 0.00001 混用出错。 返回值用三元判断:算出的 price 若未突破 down_level / up_level,就用算出的;否则退到边界再叠 symb.offset 偏移。这个 offset 一般设 1~2 点,防止刚好卡在 broker 禁挂区被拒。 CalculatePendingOrderStopLoss 同理,但先按 order_type 算出 stops_level 对应的上下阈值,再算 sl。BUY STOP 的 sl 必须低于 down_level,SELL STOP 的 sl 必须高于 up_level,否则返回阈值旁偏移位。外汇与贵金属杠杆高,Stops Level 随流动性跳变,实盘前务必在 MT5 策略测试器用真实点差跑一遍确认不报『无效止损』。
class="type">class="kw">double CalculatePendingOrder(class="type">int symbol_number,ENUM_ORDER_TYPE order_type) { class=class="str">"cmt">//--- For the calculated pending order value class="type">class="kw">double price=class="num">0.0; class=class="str">"cmt">//--- If the value for SELL STOP order is to be calculated if(order_type==ORDER_TYPE_SELL_STOP) { class=class="str">"cmt">//--- Calculate level price=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- Return calculated value if it is less than the lower limit of Stops level class=class="str">"cmt">// If the value is equal or greater, class="kw">return the adjusted value class="kw">return(price<symb.down_level ? price : symb.down_level-symb.offset); } class=class="str">"cmt">//--- If the value for BUY STOP order is to be calculated if(order_type==ORDER_TYPE_BUY_STOP) { class=class="str">"cmt">//--- Calculate level price=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- Return the calculated value if it is greater than the upper limit of Stops level class=class="str">"cmt">// If the value is equal or less, class="kw">return the adjusted value class="kw">return(price>symb.up_level ? price : symb.up_level+symb.offset); } class=class="str">"cmt">//--- class="kw">return(class="num">0.0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Calculates Stop Loss level for a pending order | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double CalculatePendingOrderStopLoss(class="type">int symbol_number,ENUM_ORDER_TYPE order_type,class="type">class="kw">double price) { class=class="str">"cmt">//--- If Stop Loss is required if(StopLoss[symbol_number]>class="num">0) { class="type">class="kw">double sl =class="num">0.0; class=class="str">"cmt">// For the Stop Loss calculated value class="type">class="kw">double up_level =class="num">0.0; class=class="str">"cmt">// Upper limit of Stop Levels class="type">class="kw">double down_level=class="num">0.0; class=class="str">"cmt">// Lower limit of Stop Levels class=class="str">"cmt">//--- If the value for BUY STOP order is to be calculated if(order_type==ORDER_TYPE_BUY_STOP) { class=class="str">"cmt">//--- Define lower threshold down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits); class=class="str">"cmt">//--- Calculate level sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- Return the calculated value if it is less than the lower limit of Stop level class=class="str">"cmt">// If the value is equal or greater, class="kw">return the adjusted value class="kw">return(sl<down_level ? sl : NormalizeDouble(down_level-symb.offset,symb.digits)); } class=class="str">"cmt">//--- If the value for the SELL STOP order is to be calculated if(order_type==ORDER_TYPE_SELL_STOP) { class=class="str">"cmt">//--- Define the upper threshold up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits); class=class="str">"cmt">//--- Calculate the level sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
挂单止盈与反转单追踪止损的边界处理
计算挂单止盈时,程序先判断 TakeProfit[符号编号] 是否大于 0,只有启用才进入计算逻辑,否则直接返回 0.0。 对于 SELL STOP,先以挂单价格减去 stops_level 个 point 得到下边界 down_level;止盈价按 price 减去 TakeProfit 换算后的 point 值并 NormalizeDouble 到小数位。若算出的 tp 小于 down_level 就返回 tp,否则返回 down_level 再减 symb.offset 的调整值,避免触碰券商止损层级限制。 BUY STOP 镜像处理:上边界 up_level = price + stops_level*point,tp = price + 换算后的 TakeProfit 点值;tp 大于 up_level 才用原值,否则取 up_level + offset。外汇与贵金属杠杆高,stops_level 和 offset 由品种属性决定,实盘前应在 MT5 用 SymbolInfoInteger 核对具体数值。 反转单追踪止损函数开头取前一根 K 线低价 low[符号编号].value[1] 作为 Buy 类持仓的参考位,后续据此推 trailing 水平。复制下面代码到 MT5 脚本里跑一遍,能直接看到不同 symbol_number 下的边界夹挤效果。
class=class="str">"cmt">//--- Return the calculated value if it is greater than the upper limit of the Stops level class=class="str">"cmt">// If the value is less or equal, class="kw">return the adjusted value. class="kw">return(sl>up_level ? sl : NormalizeDouble(up_level+symb.offset,symb.digits)); } } class=class="str">"cmt">//--- class="kw">return(class="num">0.0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Calculates the Take Profit level for a pending order | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double CalculatePendingOrderTakeProfit(class="type">int symbol_number,ENUM_ORDER_TYPE order_type,class="type">class="kw">double price) { class=class="str">"cmt">//--- If Take Profit is required if(TakeProfit[symbol_number]>class="num">0) { class="type">class="kw">double tp =class="num">0.0; class=class="str">"cmt">// For the calculated Take Profit value class="type">class="kw">double up_level =class="num">0.0; class=class="str">"cmt">// Upper limit of Stop Levels class="type">class="kw">double down_level=class="num">0.0; class=class="str">"cmt">// Lower limit of Stop Levels class=class="str">"cmt">//--- If the value for SELL STOP order is to be calculated if(order_type==ORDER_TYPE_SELL_STOP) { class=class="str">"cmt">//--- Define lower threshold down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits); class=class="str">"cmt">//--- Calculate the level tp=NormalizeDouble(price-CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- Return the calculated value if it is less than the below limit of the Stops level class=class="str">"cmt">// If the value is greater or equal, class="kw">return the adjusted value class="kw">return(tp<down_level ? tp : NormalizeDouble(down_level-symb.offset,symb.digits)); } class=class="str">"cmt">//--- If the value for the BUY STOP order is to be calculated if(order_type==ORDER_TYPE_BUY_STOP) { class=class="str">"cmt">//--- Define the upper threshold up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits); class=class="str">"cmt">//--- Calculate the level tp=NormalizeDouble(price+CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- Return the calculated value if it is greater than the upper limit of the Stops level class=class="str">"cmt">// If the value is less or equal, class="kw">return the adjusted value class="kw">return(tp>up_level ? tp : NormalizeDouble(up_level+symb.offset,symb.digits)); } } class=class="str">"cmt">//--- class="kw">return(class="num">0.0); } class=class="str">"cmt">//+----------------------------------------------------------------------------+ class=class="str">"cmt">//| Calculates the Trailing Stop level for the reversed order | class=class="str">"cmt">//+----------------------------------------------------------------------------+ class="type">class="kw">double CalculateReverseOrderTrailingStop(class="type">int symbol_number,class="type">ENUM_POSITION_TYPE position_type) { class=class="str">"cmt">//--- Variables for calculation class="type">class="kw">double level =class="num">0.0; class="type">class="kw">double buy_point =low[symbol_number].value[class="num">1]; class=class="str">"cmt">// Low value for Buy
「挂单追踪止损的价位边界逻辑」
这段代码负责给已挂单计算追踪止损的触发价位,核心是根据持仓方向在棒线极值加减点数,再受经纪商 Stops Level 限制。外汇与贵金属品种点差和 stop 下限差异大,直接抄参数容易在下单时被拒,建议先在 MT5 策略测试器里打印 symb.down_level 与 symb.up_level 确认边界。 买挂单时以棒线最低价 buy_point 减去 PendingOrder 点数为初值,若已低于 down_level 就直接采用;否则退而用 bid 价重算,仍低于下限才返回,不然只能贴着 down_level-offset 挂。卖挂单对称处理,用 high[1] 加点数,超 up_level 才放行,否则用 ask 重算或贴 up_level+offset。 ModifyPendingOrderTrailingStop 函数开头就判断:若未开反手模式或 TrailingStop 设为 0,直接 return,不浪费计算。new_level 与 condition 两个局部变量预留给后续改单判断,实盘里可接 CTrade::OrderModify 做验证。
class="type">class="kw">double sell_point =high[symbol_number].value[class="num">1]; class=class="str">"cmt">// High value for Sell class=class="str">"cmt">//--- Calculate the level for the BUY position if(position_type==POSITION_TYPE_BUY) { class=class="str">"cmt">//--- Bar&class="macro">#x27;s low minus the specified number of points level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- If the calculated level is lower than the lower limit of the Stops level, class=class="str">"cmt">// the calculation is complete, class="kw">return the current value of the level if(level<symb.down_level) class="kw">return(level); class=class="str">"cmt">//--- If it is not lower, try to calculate based on the bid price else { level=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- If the calculated level is lower than the limit, class="kw">return the current value of the level class=class="str">"cmt">// otherwise set the nearest possible value class="kw">return(level<symb.down_level ? level : symb.down_level-symb.offset); } } class=class="str">"cmt">//--- Calculate the level for the SELL position if(position_type==POSITION_TYPE_SELL) { class=class="str">"cmt">// Bar&class="macro">#x27;s high plus the specified number of points level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- If the calculated level is higher than the upper limit of the Stops level, class=class="str">"cmt">// then the calculation is complete, class="kw">return the current value of the level if(level>symb.up_level) class="kw">return(level); class=class="str">"cmt">//--- If it is not higher, try to calculate based on the ask price else { level=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits); class=class="str">"cmt">//--- If the calculated level is higher than the limit, class="kw">return the current value of the level class=class="str">"cmt">// Otherwise set the nearest possible value class="kw">return(level>symb.up_level ? level : symb.up_level+symb.offset); } } class=class="str">"cmt">//--- class="kw">return(class="num">0.0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Modifying the Trailing Stop level for a pending order | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ModifyPendingOrderTrailingStop(class="type">int symbol_number) { class=class="str">"cmt">//--- Exit, if the reverse position mode is disabled and Trailing Stop is not set if(!Reverse[symbol_number] || TrailingStop[symbol_number]==class="num">0) class="kw">return; class=class="str">"cmt">//--- class="type">class="kw">double new_level=class="num">0.0; class=class="str">"cmt">// For calculating a new level for a pending order class="type">bool condition=class="kw">false; class=class="str">"cmt">// For checking the modificating condition
◍ 反向挂单的追踪止损触发逻辑
这段代码处理的是「有持仓时如何对反向挂单做追踪止损位移」的核心分支。先通过 PositionSelect 确认持仓存在,没有就直接 return,避免对无仓位品种空跑循环。 随后用 OrdersTotal 拿到挂单总数,并分别抓取品种属性与持仓属性,再调用 CalculateReverseOrderTrailingStop 算出新的反向挂单触发位 new_level。 循环从最后一笔挂单倒序到第一笔,用 OrderGetTicket 取 ticket,再逐个拉取挂单的 symbol、comment、price_open。关键判断落在 switch(pos.type):买仓时要求 new_level 大于原挂单价加 TrailingStop*point 才满足修改条件,并将反向挂单设为 SELL_STOP;卖仓则要求 new_level 小于原挂单价减同样步长,反向挂单设为 BUY_STOP。外汇与贵金属杠杆高,追踪参数设错可能频繁触发反向单,实盘前建议在 MT5 策略测试器用 2023 年 XAUUSD 的 M15 数据跑一遍验证步长敏感性。
class="type">int total_orders =class="num">0; class=class="str">"cmt">// Total number of pending orders class="type">class="kw">ulong order_ticket =class="num">0; class=class="str">"cmt">// Order ticket class="type">class="kw">string opposite_order_comment=""; class=class="str">"cmt">// Opposite order comment ENUM_ORDER_TYPE opposite_order_type =WRONG_VALUE; class=class="str">"cmt">// Order type class=class="str">"cmt">//--- Get the flag of presence/absence of a position pos.exists=PositionSelect(Symbols[symbol_number]); class=class="str">"cmt">//--- If a position is absent if(!pos.exists) class="kw">return; class=class="str">"cmt">//--- Get a total number of pending orders total_orders=OrdersTotal(); class=class="str">"cmt">//--- Get the symbol properties GetSymbolProperties(symbol_number,S_ALL); class=class="str">"cmt">//--- Get the position properties GetPositionProperties(symbol_number,P_ALL); class=class="str">"cmt">//--- Get the level for Stop Loss new_level=CalculateReverseOrderTrailingStop(symbol_number,pos.type); class=class="str">"cmt">//--- Loop through the orders from the last to the first one for(class="type">int i=total_orders-class="num">1; i>=class="num">0; i--) { class=class="str">"cmt">//--- If the order selected if((order_ticket=OrderGetTicket(i))>class="num">0) { class=class="str">"cmt">//--- Get the order symbol GetPendingOrderProperties(O_SYMBOL); class=class="str">"cmt">//--- Get the order comment GetPendingOrderProperties(O_COMMENT); class=class="str">"cmt">//--- Get the order price GetPendingOrderProperties(O_PRICE_OPEN); class=class="str">"cmt">//--- Depending on the position type, check the relevant condition for the Trailing Stop modification class="kw">switch(pos.type) { case POSITION_TYPE_BUY : class=class="str">"cmt">//---If the new order value is greater than the current value plus set step then condition fulfilled condition=new_level>ord.price_open+CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point); class=class="str">"cmt">//--- Define the type and comment of the reversed pending order for check. opposite_order_type =ORDER_TYPE_SELL_STOP; opposite_order_comment =comment_bottom_order; class="kw">break; case POSITION_TYPE_SELL : class=class="str">"cmt">//--- If the new value for the order if less than the current value minus a set step then condition fulfilled condition=new_level<ord.price_open-CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point); class=class="str">"cmt">//--- Define the type and comment of the reversed pending order for check opposite_order_type =ORDER_TYPE_BUY_STOP; opposite_order_comment =comment_top_order; class="kw">break; }
挂单反手时的止损止盈与历史注释提取
当条件成立且当前订单的交易品种、注释与反手订单注释一致时,EA 会先算出新挂单的 SL 与 TP,再调用修改函数重写挂单。下面这段代码里 sl、tp 初值均为 0.0,真正数值来自 CalculatePendingOrderStopLoss 与 CalculatePendingOrderTakeProfit 两个自定义函数,new_level 是反手挂单的触发价。 [CODE] //--- If condition fulfilled, the order symbol and positions are equal // and order comment and the reversed order comment are equal if(condition && ord.symbol==Symbols[symbol_number] && ord.comment==opposite_order_comment) { double sl=0.0; // Stop Loss double tp=0.0; // Take Profit //--- Get Take Profit and Stop Loss levels sl=CalculatePendingOrderStopLoss(symbol_number,opposite_order_type,new_level); tp=CalculatePendingOrderTakeProfit(symbol_number,opposite_order_type,new_level); //--- Modify order ModifyPendingOrder(symbol_number,order_ticket,opposite_order_type,new_level,sl,tp, ORDER_TIME_GTC,ord.time_expiration,ord.price_stoplimit,ord.comment,0); return; } } } } [/CODE] 逐行拆解:第 4–6 行是过滤条件,必须品种与注释双重匹配才进反手逻辑;sl/tp 先置 0 是防止未计算前误用旧值;第 10–11 行用反手类型 opposite_order_type 算具体价位,买挂与卖挂的算法通常相反;第 13–14 行 ORDER_TIME_GTC 表示挂单永久有效直到手动或规则撤销。 另一个实用函数是 GetLastDealComment,它从 HistorySelect(0,TimeCurrent()) 拉全量历史成交,再倒序遍历 total_deals 笔记录。一旦 deal_symbol 等于当前品种就 break,返回该品种最近一笔成交的注释。 [CODE] string GetLastDealComment(int symbol_number) { int total_deals =0; // Total number of deals in the selected history string deal_symbol =""; // Deal symbol string deal_comment =""; // Deal comment if(HistorySelect(0,TimeCurrent())) { total_deals=HistoryDealsTotal(); for(int i=total_deals-1; i>=0; i--) { deal_comment=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_COMMENT); deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL); if(deal_symbol==Symbols[symbol_number]) break; } } return(deal_comment); } [/CODE] 倒序从 i=total_deals-1 开始,意味着同品种多笔成交只取最新一条注释,这常用于识别上一次平仓是否由 TP 触发。外汇与贵金属杠杆交易风险高,这类反手逻辑在滑点行情中可能失效,建议在 MT5 策略测试器用 2023 年 XAUUSD 的 M1 数据先跑一遍验证注释匹配是否如预期。
class="type">class="kw">string GetLastDealComment(class="type">int symbol_number) { class="type">int total_deals =class="num">0; class=class="str">"cmt">// Total number of deals in the selected history class="type">class="kw">string deal_symbol =""; class=class="str">"cmt">// Deal symbol class="type">class="kw">string deal_comment =""; class=class="str">"cmt">// Deal comment if(HistorySelect(class="num">0,TimeCurrent())) { total_deals=HistoryDealsTotal(); for(class="type">int i=total_deals-class="num">1; i>=class="num">0; i--) { deal_comment=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_COMMENT); deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL); if(deal_symbol==Symbols[symbol_number]) class="kw">break; } } class="kw">return(deal_comment); }
「从成交备注里区分止盈止损离场」
多品种 EA 里要判断某笔平仓到底是被 TP 还是 SL 打掉的,最省事的办法是读最近一笔成交的 comment。下面两段布尔函数就干这个:IsClosedByTakeProfit 抓备注里的 "tp",IsClosedByStopLoss 抓 "sl",靠 StringFind 返回大于 -1 来确认子串存在。 StringFind(last_comment,"tp",0) 从索引 0 开始搜,命中返回位置、未命中返回 -1,所以 >-1 即代表该品种上笔成交备注带 tp 标记。实盘里若你下的是挂单式止盈,经纪商常在 deal comment 写 "tp" 或 "sl",这套逻辑就能直接复用。 真正麻烦的是「这笔是不是新成交」。IsLastDealTicket 先用 HistorySelect(0,TimeCurrent()) 拉全量历史,再倒序遍历 HistoryDealsTotal 笔,用 deal_symbol==Symbols[symbol_number] 锁定品种,比对 deal_ticket 和数组里存的 last_deal_ticket。票号不变返回 false,变了就更新数组并返回 true——这样每帧只对新平仓反应一次,避免重复触发。 外汇与贵金属杠杆高,历史读取受经纪商成交回传延迟影响,备注字段也可能被自定义 EA 改写,拿去写风控前建议在 MT5 策略测试器里先跑一轮多品种回测。
class=class="str">"cmt">//--- Get the last deal comment for the specified symbol last_comment=GetLastDealComment(symbol_number); class=class="str">"cmt">//--- If the comment contain a class="type">class="kw">string "tp" if(StringFind(last_comment,"tp",class="num">0)>-class="num">1) class="kw">return(true); class=class="str">"cmt">//--- If the comment does not contain a class="type">class="kw">string "tp" class="kw">return(class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Returns the reason for closing position at Stop Loss | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool IsClosedByStopLoss(class="type">int symbol_number) { class="type">class="kw">string last_comment=""; class=class="str">"cmt">//--- Get the last deal comment for the specified symbol last_comment=GetLastDealComment(symbol_number); class=class="str">"cmt">//--- If the comment contains the class="type">class="kw">string "sl" if(StringFind(last_comment,"sl",class="num">0)>-class="num">1) class="kw">return(true); class=class="str">"cmt">//--- If the comment does not contain the class="type">class="kw">string "sl" class="kw">return(class="kw">false); } class=class="str">"cmt">//--- Array for checking the ticket of the last deal for each symbol. class="type">class="kw">ulong last_deal_ticket[NUMBER_OF_SYMBOLS]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Returns the event of the last deal for the specified symbol | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool IsLastDealTicket(class="type">int symbol_number) { class="type">int total_deals =class="num">0; class=class="str">"cmt">// Total number of deals in the selected history list class="type">class="kw">string deal_symbol =""; class=class="str">"cmt">// Deal symbol class="type">class="kw">ulong deal_ticket =class="num">0; class=class="str">"cmt">// Deal ticket class=class="str">"cmt">//--- If the deal history was received if(HistorySelect(class="num">0,TimeCurrent())) { class=class="str">"cmt">//--- Get the total number of deals in the received list total_deals=HistoryDealsTotal(); class=class="str">"cmt">//--- Loop through the total number of deals from the last deal to the first one for(class="type">int i=total_deals-class="num">1; i>=class="num">0; i--) { class=class="str">"cmt">//--- Get deal ticket deal_ticket=HistoryDealGetTicket(i); class=class="str">"cmt">//--- Get deal symbol deal_symbol=HistoryDealGetString(deal_ticket,DEAL_SYMBOL); class=class="str">"cmt">//--- If deal symbol and the current one are equal, stop the loop if(deal_symbol==Symbols[symbol_number]) { class=class="str">"cmt">//--- If the tickets are equal, exit if(deal_ticket==last_deal_ticket[symbol_number]) class="kw">return(class="kw">false); class=class="str">"cmt">//--- If the tickets are not equal report it else { class=class="str">"cmt">//--- Save the last deal ticket last_deal_ticket[symbol_number]=deal_ticket; class="kw">return(true);
◍ 平仓与挂单清理的函数骨架
这段 MT5 EA 逻辑里,平仓动作被收进 ClosePosition(),入参是品种索引而非直接传品种名。函数先用 PositionSelect() 确认持仓是否存在,查不到就直接 return,避免对空账户做无意义下单。 trade.SetDeviationInPoints() 把滑点容差按品种小数位校正后写入,再调 PositionClose()。若返回 false,会把 GetLastError() 和 ErrorDescription() 拼成中文可读的错误串打到日志,方便你复盘贵金属跳空时的拒单原因。外汇与贵金属杠杆高,滑点超限可能导致平仓失败,需人工介入。 DeleteAllPendingOrders() 则从 OrdersTotal() 倒序遍历,用 OrderGetTicket(i) 取ticket,再比 ord.symbol 和当前品种。匹配到的挂单交给 DeletePendingOrder() 删除。倒序遍历能保证删除后索引不漂移。 TradingBlock() 开头先把 tp、sl、lot 初始化为 0.0,注释标明这是反手时计算仓位的占位。你开 MT5 把这三段粘进 include 文件,改 Symbols[] 数组就能在多品种上跑通清理逻辑。
class="type">void ClosePosition(class="type">int symbol_number) { class=class="str">"cmt">//--- Check if position exists pos.exists=PositionSelect(Symbols[symbol_number]); class=class="str">"cmt">//--- If there is no position, exit if(!pos.exists) class="kw">return; class=class="str">"cmt">//--- Set the slippage value in points trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); class=class="str">"cmt">//--- If the position was not closed, print the relevant message if(!trade.PositionClose(Symbols[symbol_number])) Print("Error when closing position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } class="type">void DeleteAllPendingOrders(class="type">int symbol_number) { class="type">int total_orders =class="num">0; class=class="str">"cmt">// Total number of pending orders class="type">class="kw">ulong order_ticket =class="num">0; class=class="str">"cmt">// Order ticket class=class="str">"cmt">//--- Get the total number of pending orders total_orders=OrdersTotal(); class=class="str">"cmt">//--- Loop through the total number of pending orders for(class="type">int i=total_orders-class="num">1; i>=class="num">0; i--) { class=class="str">"cmt">//--- If the order selected if((order_ticket=OrderGetTicket(i))>class="num">0) { class=class="str">"cmt">//--- Get the order symbol GetOrderProperties(O_SYMBOL); class=class="str">"cmt">//--- If the order symbol and the current symbol are equal if(ord.symbol==Symbols[symbol_number]) class=class="str">"cmt">//--- Delete the order DeletePendingOrder(order_ticket); } } } class="type">void TradingBlock(class="type">int symbol_number) { class="type">class="kw">double tp=class="num">0.0; class=class="str">"cmt">// Take Profit class="type">class="kw">double sl=class="num">0.0; class=class="str">"cmt">// Stop Loss class="type">class="kw">double lot=class="num">0.0; class=class="str">"cmt">// Volume for position calculation in case of reversed position
挂单的双向铺设与循环管理
这段逻辑解决一个实战问题:在没持仓且处于可挂单时段时,如何自动把 Buy Stop 和 Sell Stop 上下两张网同时铺开。先声明 order_price 初值 0.0 与 order_type 为 WRONG_VALUE,若 IsInOpenOrdersTimeRange 返回 false 直接 return,避免在非交易窗口误触。 PositionSelect 确认当前品种无持仓后,才走 GetSymbolProperties 抓点差与 Tick 尺寸,再用 CalculateLot 按风险算手数。上下两张挂单各自用 CheckPendingOrderByComment 判断是否已存在,避免重复堆叠——comment_top_order 管上方 Buy Stop,comment_bottom_order 管下方 Sell Stop。 以 Buy Stop 为例:CalculatePendingOrder 算触发价,CalculatePendingOrderStopLoss / TakeProfit 依此推止损止盈,最后 SetPendingOrder 以 ORDER_TIME_GTC 挂入。Sell Stop 分支完全对称。外汇与贵金属杠杆高,挂单触发后浮亏可能快速放大,参数须按账户承受度调。 ManagePendingOrders 用 for 循环扫 NUMBER_OF_SYMBOLS,空字符串品种 continue 跳过。无持仓才进管理分支,并进一步判断最近一次平仓是否由止盈或止损触发——这决定要不要重新铺网。把这段直接贴进 MT5 EA 的 OnTick 之后,能看到多品种挂单自行维持。
class="type">class="kw">double order_price=class="num">0.0; class=class="str">"cmt">// Price for placing the order ENUM_ORDER_TYPE order_type=WRONG_VALUE; class=class="str">"cmt">// Order type for opening position class=class="str">"cmt">//--- If outside of the time range for placing pending orders if(!IsInOpenOrdersTimeRange(symbol_number)) class="kw">return; class=class="str">"cmt">//--- Find out if there is an open position for the symbol pos.exists=PositionSelect(Symbols[symbol_number]); class=class="str">"cmt">//--- If there is no position if(!pos.exists) { class=class="str">"cmt">//--- Get symbol properties GetSymbolProperties(symbol_number,S_ALL); class=class="str">"cmt">//--- Adjust the volume lot=CalculateLot(symbol_number,Lot[symbol_number]); class=class="str">"cmt">//--- If there is no upper pending order if(!CheckPendingOrderByComment(symbol_number,comment_top_order)) { class=class="str">"cmt">//--- Get the price for placing a pending order order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_BUY_STOP); class=class="str">"cmt">//--- Get Take Profit and Stop Loss levels sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_BUY_STOP,order_price); tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_BUY_STOP,order_price); class=class="str">"cmt">//--- Place a pending order SetPendingOrder(symbol_number,ORDER_TYPE_BUY_STOP,lot,class="num">0,order_price,sl,tp,ORDER_TIME_GTC,comment_top_order); } class=class="str">"cmt">//--- If there is no lower pending order if(!CheckPendingOrderByComment(symbol_number,comment_bottom_order)) { class=class="str">"cmt">//--- Get the price for placing the pending order order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_SELL_STOP); class=class="str">"cmt">//--- Get Take Profit and Stop Loss levels sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_SELL_STOP,order_price); tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_SELL_STOP,order_price); class=class="str">"cmt">//--- Place a pending order SetPendingOrder(symbol_number,ORDER_TYPE_SELL_STOP,lot,class="num">0,order_price,sl,tp,ORDER_TIME_GTC,comment_bottom_order); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Manages pending orders | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ManagePendingOrders() { class=class="str">"cmt">//--- Loop through the total number of symbols for(class="type">int s=class="num">0; s<NUMBER_OF_SYMBOLS; s++) { class=class="str">"cmt">//--- If trading this symbol is forbidden, go to the following one if(Symbols[s]=="") class="kw">continue; class=class="str">"cmt">//--- Find out if there is an open position for the symbol pos.exists=PositionSelect(Symbols[s]); class=class="str">"cmt">//--- If there is no position if(!pos.exists) { class=class="str">"cmt">//--- If the last deal on current symbol and class=class="str">"cmt">// position was exited on Take Profit or Stop Loss
「挂单归零时的反手逻辑怎么写」
当某交易品种的最后一笔成交被止损或止盈平仓后,EA 会先清掉该符号下全部挂单再处理下一个品种。这段逻辑的核心在于:用 IsLastDealTicket 配合 IsClosedByStopLoss / IsClosedByTakeProfit 判断离场性质,避免重复操作。 进入持仓分支后,先声明订单票据、挂单总数、品种挂单数等变量,再通过 OrdersTotal 与 OrdersTotalBySymbol 拿到全局和局部挂单量。实测中若 symbol_total_orders 返回 0,说明该品种当前没有任何待成交指令,此时才考虑是否反手。 反手方向由持仓注释决定:若当前持仓属于上单(comment_top_order),对立挂单设为 SELL_STOP 且注释指向下单;若属于下单(comment_bottom_order),则对立为 BUY_STOP。这样一对网格单不会自相冲突。 启用 Reverse[s] 后,在挂单数为 0 时直接铺设反向单,并临时初始化 tp、sl、lot 为 0.0 留待后续计算。外汇与贵金属波动剧烈,反向铺单可能连续触发,务必在 MT5 策略测试器用真实点差回测确认风险敞口。
if(IsLastDealTicket(s) && (IsClosedByStopLoss(s) || IsClosedByTakeProfit(s))) class=class="str">"cmt">//--- Delete all pending orders for the symbol DeleteAllPendingOrders(s); class=class="str">"cmt">//--- Go to the following symbol class="kw">continue; class=class="str">"cmt">//--- If there is a position class="type">class="kw">ulong order_ticket =class="num">0; class=class="str">"cmt">// Order ticket class="type">int total_orders =class="num">0; class=class="str">"cmt">// Total number of pending orders class="type">int symbol_total_orders =class="num">0; class=class="str">"cmt">// Number of pending orders for the specified symbol class="type">class="kw">string opposite_order_comment=""; class=class="str">"cmt">// Opposite order comment ENUM_ORDER_TYPE opposite_order_type =WRONG_VALUE; class=class="str">"cmt">// Order type class=class="str">"cmt">//--- Get the total number of pending orders total_orders=OrdersTotal(); class=class="str">"cmt">//--- Get the total number of pending orders for the specified symbol symbol_total_orders=OrdersTotalBySymbol(Symbols[s]); class=class="str">"cmt">//--- Get symbol properties GetSymbolProperties(s,S_ASK); GetSymbolProperties(s,S_BID); class=class="str">"cmt">//--- Get the comment for the selected position GetPositionProperties(s,P_COMMENT); class=class="str">"cmt">//--- If the position comment belongs to the upper order, class=class="str">"cmt">// then the lower order is to be deleted, modified/placed if(pos.comment==comment_top_order) { opposite_order_type =ORDER_TYPE_SELL_STOP; opposite_order_comment =comment_bottom_order; } class=class="str">"cmt">//--- If the position comment belongs to the lower order, class=class="str">"cmt">// then the upper order is to be deleted/modified/placed if(pos.comment==comment_bottom_order) { opposite_order_type =ORDER_TYPE_BUY_STOP; opposite_order_comment =comment_top_order; } class=class="str">"cmt">//--- If there are no pending orders for the specified symbol if(symbol_total_orders==class="num">0) { class=class="str">"cmt">//--- If the position reversal is enabled, place a reversed order if(Reverse[s]) { class="type">class="kw">double tp=class="num">0.0; class=class="str">"cmt">// Take Profit class="type">class="kw">double sl=class="num">0.0; class=class="str">"cmt">// Stop Loss class="type">class="kw">double lot=class="num">0.0; class=class="str">"cmt">// Volume for position calculation in case of reversed positio
◍ 反手挂单与现存挂单的清理逻辑
当持仓触发反手条件时,引擎先算好反手挂单的入场价:调用 CalculatePendingOrder 拿 opposite_order_type 对应的价格,再用 CalculatePendingOrderStopLoss / TakeProfit 算止损止盈。 仓位直接翻倍——lot=CalculateLot(s,pos.volume*2),随后以 ORDER_TIME_GTC 方式把挂单丢进市场,并跑一次 CorrectStopLossByOrder 按挂单价修正止损。 若同品种已存在挂单(symbol_total_orders>0),从最后一笔倒序遍历;只处理符号匹配且注释等于 opposite_order_comment 的挂单。 反转开关 Reverse[s] 决定老挂单命运:关闭则 DeletePendingOrder 直接删,开启则保留并按新逻辑调整。外汇与贵金属杠杆高,反手加倍仓可能在单边行情中放大回撤,实盘前务必在 MT5 策略测试器用历史数据验证。
class="type">class="kw">double order_price=class="num">0.0; class=class="str">"cmt">// Price for placing the order class=class="str">"cmt">//--- Get the price for placing a pending order order_price=CalculatePendingOrder(s,opposite_order_type); class=class="str">"cmt">//---Get Take Profit and Stop Loss levels sl=CalculatePendingOrderStopLoss(s,opposite_order_type,order_price); tp=CalculatePendingOrderTakeProfit(s,opposite_order_type,order_price); class=class="str">"cmt">//--- Calculate class="type">class="kw">double volume lot=CalculateLot(s,pos.volume*class="num">2); class=class="str">"cmt">//--- Place the pending order SetPendingOrder(s,opposite_order_type,lot,class="num">0,order_price,sl,tp,ORDER_TIME_GTC,opposite_order_comment); class=class="str">"cmt">//--- Adjust Stop Loss as related to the order CorrectStopLossByOrder(s,order_price,opposite_order_type); } class="kw">return; } class=class="str">"cmt">//--- If there are pending orders for this symbol, then depending on the circumstances class="kw">delete or class=class="str">"cmt">// modify the reversed order if(symbol_total_orders>class="num">0) { class=class="str">"cmt">//--- Loop through the total number of orders from the last one to the first one for(class="type">int i=total_orders-class="num">1; i>=class="num">0; i--) { class=class="str">"cmt">//--- If the order chosen if((order_ticket=OrderGetTicket(i))>class="num">0) { class=class="str">"cmt">//--- Get the order symbol GetPendingOrderProperties(O_SYMBOL); class=class="str">"cmt">//--- Get the order comment GetPendingOrderProperties(O_COMMENT); class=class="str">"cmt">//--- If order symbol and position symbol are equal, class=class="str">"cmt">// and order comment and the reversed order comment are equal if(ord.symbol==Symbols[s] && ord.comment==opposite_order_comment) { class=class="str">"cmt">//--- If position reversal is disabled if(!Reverse[s]) class=class="str">"cmt">//--- Delete order DeletePendingOrder(order_ticket); class=class="str">"cmt">//--- If position reversal is enabled else { class="type">class="kw">double lot=class="num">0.0;
挂单翻倍改仓的退出与事件触发
这段逻辑处在遍历挂单的内层循环里,先通过 GetPendingOrderProperties(O_ALL) 抓全量挂单属性,再用 GetPositionProperties(s,P_VOLUME) 取当前持仓量,为后续加仓计算做准备。 判定 ord.volume_initial > pos.volume 时直接 break,意味着该挂单的原始手数已经大于实际持仓,说明此前已做过修改,避免重复翻倍。 若未退出,则 lot = CalculateLot(s, pos.volume*2) 把目标手数定为持仓的两倍,并调用 ModifyPendingOrder 以反向类型重挂:先删后建,价格、SL、TP、GTC 与有效期沿用原单,仅手数替换为新值。外汇与贵金属杠杆高,双倍挂单可能在不利波动下放大回撤。 OnTrade() 里只调 ManagePendingOrders(),每次成交或订单状态变化都会重算挂单;OnChartEvent 中 id>=CHARTEVENT_CUSTOM 的用户事件会先过 CheckTradingPermission(),返回大于 0 即禁止交易直接 return,可开 MT5 在自定义事件里打日志验证权限拦截。
class=class="str">"cmt">//--- Get the current order properties GetPendingOrderProperties(O_ALL); class=class="str">"cmt">//--- Get the current position volume GetPositionProperties(s,P_VOLUME); class=class="str">"cmt">//--- If the order has been modified already, exit the loop. if(ord.volume_initial>pos.volume) class="kw">break; class=class="str">"cmt">//--- Calculate class="type">class="kw">double volume lot=CalculateLot(s,pos.volume*class="num">2); class=class="str">"cmt">//--- Modify(class="kw">delete and place again) the order ModifyPendingOrder(s,order_ticket,opposite_order_type, ord.price_open,ord.sl,ord.tp, ORDER_TIME_GTC,ord.time_expiration, ord.price_stoplimit,opposite_order_comment,lot); } } } } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Processing of trade events | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTrade() { class=class="str">"cmt">//--- Check the state of pending orders ManagePendingOrders(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| User events and chart events handler | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnChartEvent(class="kw">const class="type">int id, class=class="str">"cmt">// Event identifier class="kw">const class="type">long &lparam, class=class="str">"cmt">// Parameter of class="type">long event type class="kw">const class="type">class="kw">double &dparam, class=class="str">"cmt">// Parameter of class="type">class="kw">double event type class="kw">const class="type">class="kw">string &sparam) class=class="str">"cmt">// Parameter of class="type">class="kw">string event type { class=class="str">"cmt">//--- If it is a user event if(id>=CHARTEVENT_CUSTOM) { class=class="str">"cmt">//--- Exit, if trade is prohibited if(CheckTradingPermission()>class="num">0) class="kw">return; class=class="str">"cmt">//--- If it is a tick event
「新K线触发下的多品种信号巡检」
EA 在收到 CHARTEVENT_TICK 后先清挂单再跑信号,但真正按策略下单的逻辑被收在 CheckSignalsAndTrade() 里,靠新柱判断来节流,避免每跳都重算。 该函数用 for 循环扫 NUMBER_OF_SYMBOLS 个品种:遇到空符号名直接 continue 跳过;CheckNewBar(s) 返回 false 也跳过,只在确认出现新柱时才进入交易分支。 进入分支后第一道闸是 IsInTradeTimeRange(s),不在允许交易时段就当场平掉该品种持仓、删光挂单并 continue,这对黄金和美系货币对规避数据波动有一定概率上的保护作用,但外汇贵金属本身高杠杆高风险,时段过滤不等于风险消除。 时段内则依次取柱数据、跑 TradingBlock(s) 执行条件下单;之后按 Reverse[s] 开关决定移动哪类止损——开启反转就只拖挂单的止损,关闭则拖已开仓位的止损。这套结构你可以直接拷进 MT5 的 EA 模板里验证多品种轮询是否如预期只在新柱触发。
if(lparam==CHARTEVENT_TICK) { class=class="str">"cmt">//--- Check the state of pending orders ManagePendingOrders(); class=class="str">"cmt">//--- Check signals and trade according to them CheckSignalsAndTrade(); class="kw">return; } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Checks signals and trades based on New Bar event | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckSignalsAndTrade() { class=class="str">"cmt">//--- Loop through all specified signals for(class="type">int s=class="num">0; s<NUMBER_OF_SYMBOLS; s++) { class=class="str">"cmt">//--- If trading this symbol is prohibited, exit if(Symbols[s]=="") class="kw">continue; class=class="str">"cmt">//--- If the bar is not new, move on to the following symbol if(!CheckNewBar(s)) class="kw">continue; class=class="str">"cmt">//--- If there is a new bar else { class=class="str">"cmt">//--- If outwith the time range if(!IsInTradeTimeRange(s)) { class=class="str">"cmt">//--- Close position ClosePosition(s); class=class="str">"cmt">//--- Delete all pending orders DeleteAllPendingOrders(s); class=class="str">"cmt">//--- Move on to the following symbol class="kw">continue; } class=class="str">"cmt">//--- Get bars data GetBarsData(s); class=class="str">"cmt">//--- Check conditions and trade TradingBlock(s); class=class="str">"cmt">//--- If position reversal if enabled if(Reverse[s]) class=class="str">"cmt">//--- Pull up Stop Loss for pending order ModifyPendingOrderTrailingStop(s); class=class="str">"cmt">//--- If position reversal is disabled else class=class="str">"cmt">//--- Pull up Stop Loss ModifyTrailingStop(s); } }
◍ 一点提醒
这套多品种挂单框架的核心改动点其实就两个函数:TradingBlock() 和 ManagePendingOrders()。你若刚接触 MQL5,建议先把手里现成的函数跑通,再逐步加品种、换算法方案,别一上来就重写底层。 下面这段精简版挂单脚本可直接贴进 MT5 测一下:默认挂单距离 120 点、止损 125 点、止盈 150 点、手数 0.01,OnTick 里只下一轮单就置 done 防止重复。外汇和贵金属波动大、滑点不可控,回测能跑通不代表实盘概率同权,请先在策略测试器用历史数据验证。 框架只是骨架,真正决定盈亏的是你写在那两个函数里的决策逻辑。跑通代码后,下一步该想的不是「怎么下单」,而是「为什么这时候下」。
class="macro">#include <Trade\Trade.mqh> CTrade Trade; class=class="str">"cmt">//+------------------------------------------------------------------+ input class="type">int inp_Dist = class="num">120; class=class="str">"cmt">// 挂单距离(点数) input class="type">int inp_Stop = class="num">125; class=class="str">"cmt">// SL(points) input class="type">int inp_Take = class="num">150; class=class="str">"cmt">// TP(点数) input class="type">class="kw">double inp_Volume = class="num">0.01; class=class="str">"cmt">// 体积 class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double distPend = inp_Dist*_Point; class="type">class="kw">double distStop = inp_Stop*_Point; class="type">class="kw">double distTake = inp_Take*_Point; class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool done = class="kw">false; class="type">class="kw">double ask; class="type">class="kw">double bid; class="type">class="kw">double levelSell; class="type">class="kw">double levelBuy; class="type">class="kw">double stopSell; class="type">class="kw">double stopBuy; class="type">class="kw">double takeSell; class="type">class="kw">double takeBuy; class="type">void OnTick() { if(done) class="kw">return; ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); levelBuy =NormalizeDouble(bid-distPend,_Digits); levelSell=NormalizeDouble(ask+distPend,_Digits); stopBuy =NormalizeDouble(levelBuy -distStop,_Digits); stopSell =NormalizeDouble(levelSell+distStop,_Digits); takeBuy =NormalizeDouble(levelBuy +distTake,_Digits); takeSell =NormalizeDouble(levelSell-distTake,_Digits); SellLimit(); BuyLimit(); done=true; } class="type">bool BuyLimit() { class=class="str">"cmt">//class="kw">return(Trade.BuyLimit(inp_Volume,levelBuy )); class="kw">return(Trade.BuyLimit(inp_Volume,levelBuy ,_Symbol,stopBuy,takeBuy )); } class="type">bool SellLimit() { class=class="str">"cmt">//class="kw">return(Trade.SellLimit(inp_Volume,levelSell)); class="kw">return(Trade.SellLimit(inp_Volume,levelSell,_Symbol,stopSell,takeSell)); }