ColorX2MA Digit 策略
该策略来源于 MQL5 专家 Exp_ColorX2MA_Digit。 原始算法根据双重平滑移动平均线的斜率改变线条颜色,并用颜色来生成交易信号。 在 C# 版本中,我们使用两条简单移动平均线来近似这一行为,并基于它们的交叉进行交易。
交易逻辑
- 快速移动平均线对价格序列进行平滑。
- 慢速移动平均线对快速平均线的结果再平滑。
- 当快速均线向上穿越慢速均线时,开多仓并平掉任何空仓。
- 当快速均线向下穿越慢速均线时,开空仓并平掉任何多仓。
- 只有在蜡烛收盘后才处理信号。
参数
FastLength– 第一条均线的周期(默认 12)。SlowLength– 第二条均线的周期(默认 5)。CandleType– 用于计算的蜡烛时间框架。
该策略仅使用高级 API:通过 SubscribeCandles 和 Bind 向指标提供数据,并用 BuyMarket/SellMarket 管理仓位。代码中的注释为英文,便于维护。
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>
/// Trend following strategy based on two sequential moving averages.
/// Trades on crossovers of fast and slow SMAs.
/// </summary>
public class ColorX2MaDigitStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorX2MaDigitStrategy()
{
_fastLength = Param(nameof(FastLength), 8)
.SetGreaterThanZero()
.SetDisplay("Fast MA Length", "Length of the first smoothing", "Parameters");
_slowLength = Param(nameof(SlowLength), 21)
.SetGreaterThanZero()
.SetDisplay("Slow MA Length", "Length of the second smoothing", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fastMa = new ExponentialMovingAverage { Length = FastLength };
var slowMa = new ExponentialMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastMa, decimal slowMa)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevFast is null || _prevSlow is null)
{
_prevFast = fastMa;
_prevSlow = slowMa;
return;
}
var wasAbove = _prevFast > _prevSlow;
var isAbove = fastMa > slowMa;
// Fast MA crossed above slow MA -> buy
if (!wasAbove && isAbove && Position <= 0)
BuyMarket();
// Fast MA crossed below slow MA -> sell
else if (wasAbove && !isAbove && Position >= 0)
SellMarket();
_prevFast = fastMa;
_prevSlow = slowMa;
}
}
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 color_x2_ma_digit_strategy(Strategy):
def __init__(self):
super(color_x2_ma_digit_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 8) \
.SetDisplay("Fast MA Length", "Length of the first smoothing", "Parameters")
self._slow_length = self.Param("SlowLength", 21) \
.SetDisplay("Slow MA Length", "Length of the second smoothing", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._prev_fast = None
self._prev_slow = None
@property
def fast_length(self):
return self._fast_length.Value
@property
def slow_length(self):
return self._slow_length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_x2_ma_digit_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(color_x2_ma_digit_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.fast_length
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.slow_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ma, slow_ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast_ma, slow_ma):
if candle.State != CandleStates.Finished:
return
fast_ma = float(fast_ma)
slow_ma = float(slow_ma)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fast_ma
self._prev_slow = slow_ma
return
was_above = self._prev_fast > self._prev_slow
is_above = fast_ma > slow_ma
if not was_above and is_above and self.Position <= 0:
self.BuyMarket()
elif was_above and not is_above and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_ma
self._prev_slow = slow_ma
def CreateClone(self):
return color_x2_ma_digit_strategy()