在 GitHub 上查看
MultiCurrency Template MT5 策略
概述
MultiCurrency Template MT5 策略 复刻了同名 MetaTrader 专家顾问的行为。策略在日线级别寻找由两根 K 线组成的形态,并允许同时管理多个品种的持仓篮子。只有当前一根日线的多空走势满足条件时才会开出初始仓位,随后在更快的控制周期上执行风控管理。当行情逆势运行达到预设的 MetaTrader 点数时,马丁格尔模块会补加仓位;退出逻辑同时结合了固定止盈、均价止盈以及可选的移动止损。
移植到 StockSharp 后仍然保留了多品种管理能力,用户可以通过逗号分隔的列表指定多个交易标的。每个标的都拥有独立的上下文、持仓篮子以及资金管理数据;当 TradeMultipair 关闭时,策略仅交易实例所附加的 Security。
信号生成
- 订阅
SignalCandleType(默认日线)并缓存最近两根收盘完成的 K 线。
- 当最新收盘价低于上一根开盘价且上一根 K 线收阳时触发做多信号。
- 当最新收盘价高于上一根开盘价且上一根 K 线收阴时触发做空信号。
- 任意时刻仅允许一个方向的持仓;当前篮子未平仓之前忽略新的信号。
下单逻辑
- 按
Lots 参数设置的手数以市价成交开仓。
- 开启
NewBarTrade 后,策略会等待 TradeCandleType 周期内形成一根新的完成 K 线后才允许开仓。该标志在首次决策时被消费,用于模拟原始 EA 的“只在新柱开单”行为。
- 初始止损、止盈均使用 MetaTrader 的“点”/“点值”换算,以保持与原策略一致的距离。
- 启用
EnableMartingale 时,当价格与当前篮子最佳入场价的偏离达到 StepPoints 时会加仓,手数按 NextLotMultiplier 的幂次递增。
仓位管理
EnableTakeProfitAverage 控制止盈方式:
- 关闭时,止盈始终保持在
TakeProfitPips 对应的固定距离。
- 开启且篮子中至少有两笔订单时,止盈移动到盈亏平衡价再加上
TakeProfitOffsetPoints。
- 每次成交后都会重新计算止损,以反映当前篮子中最不利的价格。
- 当仅剩一笔订单时启用移动止损:价格突破
TrailingStopPoints + TrailingStepPoints 后先移动到盈亏平衡价以上 TrailingStopPoints,之后继续按相同距离跟随。
- 风控触发时会以市价一次性平掉当前方向的全部篮子。
参数
| 参数 |
说明 |
Lots |
每个篮子首笔订单的基础手数。 |
StopLossPips |
初始止损距离(MetaTrader 点)。 |
TakeProfitPips |
初始止盈距离(MetaTrader 点)。 |
TrailingStopPoints |
仅剩一笔订单时的移动止损距离(MetaTrader 点)。 |
TrailingStepPoints |
再次调整移动止损前需要的额外缓冲(点)。 |
SlippagePoints |
为保持一致性保留的滑点输入,不影响实际成交。 |
NewBarTrade |
是否启用按新柱过滤开仓。 |
TradeCandleType |
用于检测新柱和执行风控的心跳周期。 |
TradeMultipair |
是否启用多品种模式。 |
PairsToTrade |
逗号分隔的其他证券代码,通过 GetSecurity 解析。 |
Commentary |
订单备注,仅用于记录。 |
EnableMartingale |
是否启动逆势加仓模块。 |
NextLotMultiplier |
每次加仓手数相对于上一笔的乘数。 |
StepPoints |
触发下一次加仓的点数偏离。 |
EnableTakeProfitAverage |
是否对多笔订单使用均价止盈。 |
TakeProfitOffsetPoints |
启用均价止盈时在盈亏平衡价基础上额外增加的点数。 |
SignalCandleType |
构建两根 K 线形态使用的周期(默认日线)。 |
备注
- 策略全部采用市价单开平仓,MetaTrader 的保护单在内部模拟实现。
PairsToTrade 中的代码必须能被连接的交易通道识别,未知代码会被忽略。
- 马丁格尔与移动止损均按品种独立运行,每个标的维护自己的篮子。
SlippagePoints 仅作为占位参数,在 StockSharp 中不会影响实际成交。
using System;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Multi Currency Template MT5" MetaTrader expert.
/// Uses candlestick pattern (bearish followed by bullish or vice versa) for entries
/// with simple reversal logic.
/// </summary>
public class MultiCurrencyTemplateMt5Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _lookback;
private ICandleMessage _prevCandle;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
public MultiCurrencyTemplateMt5Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signal generation", "General");
_lookback = Param(nameof(Lookback), 1)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Number of prior candles for pattern", "Signals");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevCandle is null)
{
_prevCandle = candle;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Buy signal: previous candle was bullish (close > open), current closes below previous open
// This is a reversal pattern - bearish candle after bullish suggests exhaustion, buy the dip
var minBody = _prevCandle.OpenPrice * 0.001m;
var buySignal = candle.ClosePrice < _prevCandle.OpenPrice - minBody && _prevCandle.ClosePrice > _prevCandle.OpenPrice + minBody;
// Sell signal: previous candle was bearish (close < open), current closes above previous open
var sellSignal = candle.ClosePrice > _prevCandle.OpenPrice + minBody && _prevCandle.ClosePrice < _prevCandle.OpenPrice - minBody;
if (buySignal)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (sellSignal)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevCandle = candle;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevCandle = null;
base.OnReseted();
}
}
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.Strategies import Strategy
class multi_currency_template_mt5_strategy(Strategy):
def __init__(self):
super(multi_currency_template_mt5_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._prev_open = None
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(multi_currency_template_mt5_strategy, self).OnReseted()
self._prev_open = None
self._prev_close = None
def OnStarted2(self, time):
super(multi_currency_template_mt5_strategy, self).OnStarted2(time)
self._prev_open = None
self._prev_close = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
if self._prev_open is None or self._prev_close is None:
self._prev_open = open_price
self._prev_close = close
return
min_body = self._prev_open * 0.001
# Buy: previous candle was bullish (close > open), current closes below previous open
buy_signal = (close < self._prev_open - min_body and
self._prev_close > self._prev_open + min_body)
# Sell: previous candle was bearish (close < open), current closes above previous open
sell_signal = (close > self._prev_open + min_body and
self._prev_close < self._prev_open - min_body)
if buy_signal:
if self.Position <= 0:
self.BuyMarket()
elif sell_signal:
if self.Position >= 0:
self.SellMarket()
self._prev_open = open_price
self._prev_close = close
def CreateClone(self):
return multi_currency_template_mt5_strategy()