在 GitHub 上查看
简单马丁模板策略
概述
该策略将 MT4 的 “Simple Martingale Template” 概念迁移到 StockSharp 平台。策略处理所选 CandleType 周期的已完成 K 线,利用一快一慢两条简单移动平均线(SMA)结合突破过滤条件进行入场判定。仓位规模采用马丁格尔资金管理:每次亏损后下笔交易的手数乘以系数,盈利后恢复为基础手数。
交易逻辑
- 订阅指定
CandleType 周期的蜡烛,仅在蜡烛收盘后才参与信号计算。
- 计算基于收盘价的快线 SMA(
FastPeriod)与慢线 SMA(SlowPeriod)。
- 当以下条件成立时发出做多信号:
- 最近已完成蜡烛的收盘价高于快线 SMA;
- 快线 SMA 位于慢线 SMA 之上;
- 前一根蜡烛中快线 SMA 位于慢线 SMA 之下;
- 最近蜡烛的收盘价高于两根之前蜡烛的最高价。
- 当出现对称的反向条件(包括收盘价跌破两根之前蜡烛的最低价)时发出做空信号。
- 当无持仓且没有活动委托时,根据当前马丁格尔手数发送市价单入场。
- 通过程序方式记录止损与止盈价格,并在后续蜡烛的最高价/最低价触及相应水平时立即平仓。
- 仓位关闭且组合余额更新后:
- 若余额上升,则手数恢复为
BaseVolume;
- 若余额下降,则将上一笔交易的手数乘以
Multiplier 并按品种的最小手数对齐。
参数说明
| 参数 |
描述 |
StopLossPoints |
入场价到止损价的距离(以点数表示)。 |
TakeProfitPoints |
入场价到止盈价的距离(以点数表示)。 |
BaseVolume |
马丁格尔第一步使用的基础手数。 |
Multiplier |
亏损后用于放大下一笔手数的倍数。 |
FastPeriod |
快线 SMA 的周期。 |
SlowPeriod |
慢线 SMA 的周期。 |
CandleType |
策略分析使用的蜡烛类型(时间框架)。 |
资金管理
- 只有在没有持仓且没有挂单时才会检查账户余额,从而调整下一笔交易的手数;微小波动(±0.01 货币单位)会被忽略。
- 下单前会根据
VolumeStep、MinVolume、MaxVolume 对手数进行合法化处理,保证满足交易所约束。
- 止损与止盈通过监控蜡烛极值实现,行为与原始 MQL 方案一致,即使用市价单离场。
使用建议
- 在启用策略前请确保历史数据足够,使两条 SMA 都已完成初始化(至少需要两个以上的收盘蜡烛)。
StopLossPoints 与 TakeProfitPoints 为点数而非价格差,请结合标的的最小跳动值调整。
- 马丁格尔资金管理增长迅速,请合理设置
Multiplier 与 BaseVolume 以匹配账户承受能力。
- 策略启动时会调用
StartProtection(),以便与 StockSharp 的保护机制协同工作。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Simple Martingale Template strategy: SMA crossover with momentum confirmation.
/// Buys on fast SMA crossing above slow SMA, sells on crossing below.
/// </summary>
public class SimpleMartingaleTemplateStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public SimpleMartingaleTemplateStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new SimpleMovingAverage { Length = FastPeriod };
var slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
BuyMarket();
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
SellMarket();
}
else
{
if (fastValue > slowValue && Position <= 0)
BuyMarket();
else if (fastValue < slowValue && Position >= 0)
SellMarket();
}
_prevFast = fastValue;
_prevSlow = slowValue;
_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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class simple_martingale_template_strategy(Strategy):
def __init__(self):
super(simple_martingale_template_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._fast_period = self.Param("FastPeriod", 5)
self._slow_period = self.Param("SlowPeriod", 15)
self._prev_fast = 0.0
self._prev_slow = 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 FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
def OnReseted(self):
super(simple_martingale_template_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(simple_martingale_template_strategy, self).OnStarted2(time)
self._has_prev = False
fast = SimpleMovingAverage()
fast.Length = self.FastPeriod
slow = SimpleMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if self._has_prev:
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
self.SellMarket()
else:
if fast_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif fast_val < slow_val and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
def CreateClone(self):
return simple_martingale_template_strategy()