Martingale MACD 策略
该策略在 StockSharp 框架中重现 MQL "MartGreg_1" 智能交易系统。它使用两个移动平均线收敛/发散 (MACD) 指标来寻找反转,并采用马丁格尔方法管理仓位规模。
工作原理
- 第一个 MACD 监控最近三根已完成 K 线,寻找局部峰谷。
- 第二个 MACD 比较最近两根 K 线的数值以判断动量方向。
- 当第一个 MACD 形成谷值且第二个 MACD 下降时开多单。
- 当第一个 MACD 形成峰值且第二个 MACD 上升时开空单。
- 每次亏损后,下单数量按照马丁格尔规则加倍,直到达到设定的上限。
- 止损和止盈以绝对价格点数设置。
参数
Shape– 账户余额除数,用于计算初始手数。Doubling Count– 允许连续加倍的最大次数。Stop Loss– 止损点数。Take Profit– 止盈点数。MACD1 Fast/Slow– 第一个 MACD 的周期。MACD2 Fast/Slow– 第二个 MACD 的周期。Candle Type– 分析使用的时间框。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Martingale strategy based on dual MACD indicators.
/// Buys when MACD1 turns up and MACD2 turns down (divergence).
/// Sells on the opposite condition.
/// </summary>
public class MartingaleMacdStrategy : Strategy
{
private readonly StrategyParam<int> _macd1Fast;
private readonly StrategyParam<int> _macd1Slow;
private readonly StrategyParam<int> _macd2Fast;
private readonly StrategyParam<int> _macd2Slow;
private readonly StrategyParam<DataType> _candleType;
private decimal? _macd1Prev1;
private decimal? _macd1Prev2;
private decimal? _macd2Prev;
public int Macd1Fast { get => _macd1Fast.Value; set => _macd1Fast.Value = value; }
public int Macd1Slow { get => _macd1Slow.Value; set => _macd1Slow.Value = value; }
public int Macd2Fast { get => _macd2Fast.Value; set => _macd2Fast.Value = value; }
public int Macd2Slow { get => _macd2Slow.Value; set => _macd2Slow.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MartingaleMacdStrategy()
{
_macd1Fast = Param(nameof(Macd1Fast), 5)
.SetDisplay("MACD1 Fast", "Fast EMA for first MACD", "Indicators");
_macd1Slow = Param(nameof(Macd1Slow), 20)
.SetDisplay("MACD1 Slow", "Slow EMA for first MACD", "Indicators");
_macd2Fast = Param(nameof(Macd2Fast), 10)
.SetDisplay("MACD2 Fast", "Fast EMA for second MACD", "Indicators");
_macd2Slow = Param(nameof(Macd2Slow), 15)
.SetDisplay("MACD2 Slow", "Slow EMA for second MACD", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macd1Prev1 = null;
_macd1Prev2 = null;
_macd2Prev = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_macd1Prev1 = null;
_macd1Prev2 = null;
_macd2Prev = null;
var macd1 = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = Macd1Fast },
LongMa = { Length = Macd1Slow }
};
var macd2 = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = Macd2Fast },
LongMa = { Length = Macd2Slow }
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(macd1, macd2, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd1);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal macd1Val, decimal macd2Val)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_macd1Prev1.HasValue && _macd1Prev2.HasValue && _macd2Prev.HasValue)
{
var t0 = macd1Val;
var t1 = _macd1Prev1.Value;
var t2 = _macd1Prev2.Value;
var k0 = macd2Val;
var k1 = _macd2Prev.Value;
// MACD1 turns up (V-shape) and MACD2 turns down => buy
if (t0 > t1 && t1 < t2 && k1 > k0 && Position <= 0)
BuyMarket();
// MACD1 turns down (inverted V) and MACD2 turns up => sell
else if (t0 < t1 && t1 > t2 && k1 < k0 && Position >= 0)
SellMarket();
}
_macd1Prev2 = _macd1Prev1;
_macd1Prev1 = macd1Val;
_macd2Prev = macd2Val;
}
}
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 MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class martingale_macd_strategy(Strategy):
def __init__(self):
super(martingale_macd_strategy, self).__init__()
self._macd1_fast = self.Param("Macd1Fast", 5) \
.SetDisplay("MACD1 Fast", "Fast EMA for first MACD", "Indicators")
self._macd1_slow = self.Param("Macd1Slow", 20) \
.SetDisplay("MACD1 Slow", "Slow EMA for first MACD", "Indicators")
self._macd2_fast = self.Param("Macd2Fast", 10) \
.SetDisplay("MACD2 Fast", "Fast EMA for second MACD", "Indicators")
self._macd2_slow = self.Param("Macd2Slow", 15) \
.SetDisplay("MACD2 Slow", "Slow EMA for second MACD", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._macd1_prev1 = None
self._macd1_prev2 = None
self._macd2_prev = None
@property
def macd1_fast(self):
return self._macd1_fast.Value
@property
def macd1_slow(self):
return self._macd1_slow.Value
@property
def macd2_fast(self):
return self._macd2_fast.Value
@property
def macd2_slow(self):
return self._macd2_slow.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(martingale_macd_strategy, self).OnReseted()
self._macd1_prev1 = None
self._macd1_prev2 = None
self._macd2_prev = None
def OnStarted2(self, time):
super(martingale_macd_strategy, self).OnStarted2(time)
self._macd1_prev1 = None
self._macd1_prev2 = None
self._macd2_prev = None
macd1 = MovingAverageConvergenceDivergence()
macd1.ShortMa.Length = self.macd1_fast
macd1.LongMa.Length = self.macd1_slow
macd2 = MovingAverageConvergenceDivergence()
macd2.ShortMa.Length = self.macd2_fast
macd2.LongMa.Length = self.macd2_slow
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(macd1, macd2, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd1)
self.DrawOwnTrades(area)
def process_candle(self, candle, macd1_val, macd2_val):
if candle.State != CandleStates.Finished:
return
macd1_val = float(macd1_val)
macd2_val = float(macd2_val)
if self._macd1_prev1 is not None and self._macd1_prev2 is not None and self._macd2_prev is not None:
t0 = macd1_val
t1 = self._macd1_prev1
t2 = self._macd1_prev2
k0 = macd2_val
k1 = self._macd2_prev
if t0 > t1 and t1 < t2 and k1 > k0 and self.Position <= 0:
self.BuyMarket()
elif t0 < t1 and t1 > t2 and k1 < k0 and self.Position >= 0:
self.SellMarket()
self._macd1_prev2 = self._macd1_prev1
self._macd1_prev1 = macd1_val
self._macd2_prev = macd2_val
def CreateClone(self):
return martingale_macd_strategy()