移动平均缠绕策略
该策略比较快慢移动平均线,当两者差值超过基于ATR的“死区”时开仓。
参数
- Fast MA
- Slow MA
- ATR Length
- Volatility Period
- Dead Zone %
- Deviation Multiplier
- MA Type
- Candle Type
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MovingAverageEntanglementStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _volatilityPeriod;
private readonly StrategyParam<decimal> _deadZonePercentage;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<MaTypes> _maType;
private readonly StrategyParam<DataType> _candleType;
private IIndicator _fastMa = null!;
private IIndicator _slowMa = null!;
private AverageTrueRange _atr = null!;
private bool _prevBuyCondition;
private bool _prevSellCondition;
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int VolatilityPeriod
{
get => _volatilityPeriod.Value;
set => _volatilityPeriod.Value = value;
}
public decimal DeadZonePercentage
{
get => _deadZonePercentage.Value;
set => _deadZonePercentage.Value = value;
}
public decimal DeviationMultiplier
{
get => _deviationMultiplier.Value;
set => _deviationMultiplier.Value = value;
}
public MaTypes MaTypeParam
{
get => _maType.Value;
set => _maType.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MovingAverageEntanglementStrategy()
{
_fastLength = Param(nameof(FastLength), 3)
.SetDisplay("Fast MA", "Fast MA length", "Indicators")
.SetGreaterThanZero();
_slowLength = Param(nameof(SlowLength), 14)
.SetDisplay("Slow MA", "Slow MA length", "Indicators")
.SetGreaterThanZero();
_atrLength = Param(nameof(AtrLength), 10)
.SetDisplay("ATR Length", "ATR length", "Indicators")
.SetGreaterThanZero();
_volatilityPeriod = Param(nameof(VolatilityPeriod), 10)
.SetDisplay("Volatility Period", "Length for gap MA", "Indicators")
.SetGreaterThanZero();
_deadZonePercentage = Param(nameof(DeadZonePercentage), 40m)
.SetDisplay("Dead Zone %", "ATR dead zone percentage", "Trading")
.SetGreaterThanZero();
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2.5m)
.SetDisplay("Deviation Multiplier", "Std dev multiplier", "Trading");
_maType = Param(nameof(MaTypeParam), MaTypes.Sma)
.SetDisplay("MA Type", "Type of moving average", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBuyCondition = false;
_prevSellCondition = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastMa = CreateMa(MaTypeParam, FastLength);
_slowMa = CreateMa(MaTypeParam, SlowLength);
_atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, _atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastMaValue, decimal slowMaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var gapping = Math.Abs(fastMaValue - slowMaValue);
var atrDeadZone = atrValue * DeadZonePercentage * 0.01m;
var buyCondition = fastMaValue > slowMaValue && gapping > atrDeadZone;
var sellCondition = fastMaValue < slowMaValue && gapping > atrDeadZone;
if (buyCondition && !_prevBuyCondition && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
}
else if (sellCondition && !_prevSellCondition && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
}
_prevBuyCondition = buyCondition;
_prevSellCondition = sellCondition;
}
private static IIndicator CreateMa(MaTypes type, int length)
{
return type switch
{
MaTypes.Ema => new EMA { Length = length },
_ => new SMA { Length = length },
};
}
public enum MaTypes
{
Sma = 1,
Ema
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class moving_average_entanglement_strategy(Strategy):
"""
Moving average entanglement: fast/slow SMA gap vs ATR dead zone filter.
"""
def __init__(self):
super(moving_average_entanglement_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 3).SetDisplay("Fast MA", "Fast SMA length", "Indicators")
self._slow_length = self.Param("SlowLength", 14).SetDisplay("Slow MA", "Slow SMA length", "Indicators")
self._atr_length = self.Param("AtrLength", 10).SetDisplay("ATR Length", "ATR length", "Indicators")
self._dead_zone_pct = self.Param("DeadZonePercentage", 40.0).SetDisplay("Dead Zone %", "ATR dead zone %", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_buy = False
self._prev_sell = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(moving_average_entanglement_strategy, self).OnReseted()
self._prev_buy = False
self._prev_sell = False
def OnStarted2(self, time):
super(moving_average_entanglement_strategy, self).OnStarted2(time)
fast = SimpleMovingAverage()
fast.Length = self._fast_length.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, atr_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
atr = float(atr_val)
gapping = abs(fast - slow)
dead_zone = atr * float(self._dead_zone_pct.Value) * 0.01
buy_cond = fast > slow and gapping > dead_zone
sell_cond = fast < slow and gapping > dead_zone
if buy_cond and not self._prev_buy and self.Position <= 0:
self.BuyMarket()
elif sell_cond and not self._prev_sell and self.Position >= 0:
self.SellMarket()
self._prev_buy = buy_cond
self._prev_sell = sell_cond
def CreateClone(self):
return moving_average_entanglement_strategy()