在 GitHub 上查看
More Orders After BreakEven(StockSharp移植版)
本目录提供 MetaTrader 4 智能交易系统 “More Orders After BreakEven”(MQL 源编号 35609)的 C# / StockSharp 实现。原策略在已有仓位被移动到保本之后继续加仓多单。本移植版保留了按单管理的特性,并使用 StockSharp 的高级 API 完成下单、监控与出场。
策略概要
- 交易方向:仅做多。每次交易都会向主标的提交市价买入。
- 核心思想:只要尚未进入保本状态的订单数量少于
MaximumOrders,就继续买入。当某笔交易达到 BreakEvenPips 指定的盈利距离时,其止损会上移到开仓价,从而不再限制后续加仓。
- 出场方式:每个订单维护独立的止损与止盈。价格触发保本后止损会被提升到入场价;当买价触碰止盈或止损时,策略会以市价卖出剩余仓位。
- 行情处理:原始 MQL 代码基于
OnTick。移植版通过订阅 Level1(最优买卖价)数据来模拟同样的逐笔逻辑。
参数说明
| 参数 |
含义 |
默认值 |
MaximumOrders |
尚未保本的多单数量上限。数量低于该值时允许继续开仓。 |
1 |
TakeProfitPips |
入场价到止盈价的距离(MetaTrader 点)。为 0 表示不开启止盈。 |
100 |
StopLossPips |
初始止损距离(MetaTrader 点)。为 0 表示无初始止损。 |
200 |
BreakEvenPips |
触发保本的盈利距离(MetaTrader 点)。为 0 表示只要价格略高于入场价就移动到保本。 |
10 |
TradeVolume |
每次市价买入的手数。 |
0.01 |
DebugMode |
是否输出与 MetaTrader Comment() 类似的调试日志。 |
true |
所有基于“点”的距离都会根据品种的小数位数自动调整,以重现原策略中 points 变量对 4/2 和 5/3 位报价的处理。
运行流程
- 订阅 Level1 行情:当最优买价与最优卖价均可用时调用
ProcessPrices,对应 MQL 的 OnTick 循环。
- 统计有效订单:计算尚未移动到保本的订单数量,对应原脚本的
OrdersCounter()。
- 开仓:若数量小于
MaximumOrders,以 TradeVolume 的手数买入。成交后记录入场价、止损与止盈水平。
- 移动止损到保本:一旦买价超过
Entry + BreakEvenPips,将止损设置为入场价,并标记该单已进入保本。
- 离场检测:买价达到止盈或跌破止损(包括保本止损)时,发送市价卖出指令关闭对应仓位。
- 仓位跟踪:通过
OnOwnTradeReceived 维护一个 FIFO 列表,模拟 MetaTrader 逐单管理的行为,即便 StockSharp 以净头寸的方式展示账户状态。
与原版的差异
- 仅实现多头开仓,因为原始 EA 并不会做空。
- 在 StockSharp 高级 API 中,个别订单的止损/止盈需要由策略自行监控并以市价单执行,不会像 MetaTrader 那样修改服务器端订单属性。
- 调试输出通过 StockSharp 的日志系统实现,而不是在图表上调用
Comment()。
使用建议
- 将策略连接到能够提供 Level1 数据的行情源或交易通道。
- 根据交易品种的波动性和券商限制调整各项“点”参数。
- 在测试阶段启用
DebugMode 观察加仓与保本行为,确认无误后可关闭以减少日志量。
- 注意账户可用资金,确保能够承受在多笔订单进入保本后继续加仓的需求。
相关文件
- 原始 MQL4 文件:
MQL/35609/More Orders After BreakEven.mq4。
- 转换后的 C# 策略:
CS/MoreOrdersAfterBreakEvenStrategy.cs。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// More Orders After BreakEven strategy: TEMA crossover.
/// Buys when price crosses above TEMA, sells when price crosses below TEMA.
/// </summary>
public class MoreOrdersAfterBreakEvenStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal _prevClose;
private decimal _prevTema;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public MoreOrdersAfterBreakEvenStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 50)
.SetGreaterThanZero()
.SetDisplay("Period", "TEMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevTema = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevTema = 0;
_hasPrev = false;
var tema = new ExponentialMovingAverage { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(tema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal temaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevClose <= _prevTema && candle.ClosePrice > temaValue && Position <= 0)
BuyMarket();
else if (_prevClose >= _prevTema && candle.ClosePrice < temaValue && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevTema = temaValue;
_hasPrev = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class more_orders_after_break_even_strategy(Strategy):
def __init__(self):
super(more_orders_after_break_even_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 50)
self._prev_close = 0.0
self._prev_tema = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
def OnReseted(self):
super(more_orders_after_break_even_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_tema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(more_orders_after_break_even_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_tema = 0.0
self._has_prev = False
tema = ExponentialMovingAverage()
tema.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(tema, self._process_candle).Start()
def _process_candle(self, candle, tema_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
tema_val = float(tema_value)
if self._has_prev:
if self._prev_close <= self._prev_tema and close > tema_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_close >= self._prev_tema and close < tema_val and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_tema = tema_val
self._has_prev = True
def CreateClone(self):
return more_orders_after_break_even_strategy()