Стратегия AML Candle Cross
Стратегия торгует по индикатору Adaptive Market Level (AML). Сделка открывается, когда значение AML попадает внутрь тела текущей свечи. Для бычьей свечи при расположении AML между ценой открытия и закрытия открывается длинная позиция. Для медвежьей свечи аналогичное условие открывает короткую позицию. При необходимости позиция может переворачиваться при появлении обратного сигнала.
Подробности
- Условия входа:
- Long: бычья свеча и
open <= AML <= close. - Short: медвежья свеча и
open >= AML >= close.
- Long: бычья свеча и
- Long/Short: обе стороны.
- Условия выхода: переворот позиции по обратному сигналу, если включено.
- Стопы: отсутствуют.
- Значения по умолчанию:
Fractal= 70Lag= 18Shift= 0UseOpposite= true
- Фильтры:
- Категория: трендовая
- Направление: обе
- Индикаторы: один (AML)
- Стопы: нет
- Сложность: средняя
- Таймфрейм: краткосрочный
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: средний
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>
/// Adaptive Market Level candle cross strategy.
/// Opens position when AML value lies between candle open and close.
/// Reverses position if opposite condition occurs.
/// </summary>
public class AmlCandleCrossStrategy : Strategy
{
private readonly StrategyParam<int> _fractal;
private readonly StrategyParam<int> _lag;
private readonly StrategyParam<DataType> _candleType;
public int Fractal { get => _fractal.Value; set => _fractal.Value = value; }
public int Lag { get => _lag.Value; set => _lag.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public AmlCandleCrossStrategy()
{
_fractal = Param(nameof(Fractal), 10)
.SetGreaterThanZero()
.SetDisplay("Fractal", "Fractal window size", "General");
_lag = Param(nameof(Lag), 5)
.SetGreaterThanZero()
.SetDisplay("Lag", "Lag for smoothing", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle Type", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var aml = new AdaptiveMarketLevel
{
Fractal = Fractal,
Lag = Lag,
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(aml, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle, decimal amlValue)
{
if (candle.State != CandleStates.Finished)
return;
if (amlValue == 0)
return;
var open = candle.OpenPrice;
var close = candle.ClosePrice;
// Bullish: AML between open and close, bullish candle
var bullish = close > open && amlValue >= open && amlValue <= close;
// Bearish: AML between close and open, bearish candle
var bearish = close < open && amlValue >= close && amlValue <= open;
if (Position == 0)
{
if (bullish)
BuyMarket();
else if (bearish)
SellMarket();
}
}
}
/// <summary>
/// Adaptive Market Level indicator.
/// </summary>
public class AdaptiveMarketLevel : BaseIndicator
{
private int _pos;
private decimal[] _smooth = Array.Empty<decimal>();
private decimal _lastValue;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
public int Fractal { get; set; } = 10;
public int Lag { get; set; } = 5;
public override void Reset()
{
base.Reset();
_pos = 0;
_smooth = new decimal[Lag + 1];
_lastValue = 0;
_highs.Clear();
_lows.Clear();
}
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var candle = input.GetValue<ICandleMessage>();
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
if (_highs.Count < Fractal * 2 || _highs.Count <= Lag)
return new DecimalIndicatorValue(this, 0m, input.Time);
IsFormed = true;
decimal r1 = Range(Fractal, 0) / Fractal;
decimal r2 = Range(Fractal, Fractal) / Fractal;
decimal r3 = Range(Fractal * 2, 0) / (Fractal * 2);
double dim = 0;
if (r1 + r2 > 0 && r3 > 0)
dim = (Math.Log((double)(r1 + r2)) - Math.Log((double)r3)) * 1.44269504088896;
var alpha = (decimal)Math.Exp(-Lag * (dim - 1.0));
if (alpha > 1m) alpha = 1m;
if (alpha < 0.01m) alpha = 0.01m;
var price = (candle.HighPrice + candle.LowPrice + 2m * candle.OpenPrice + 2m * candle.ClosePrice) / 6m;
var prevPos = (_pos - 1 + _smooth.Length) % _smooth.Length;
_smooth[_pos] = alpha * price + (1m - alpha) * _smooth[prevPos];
var lagPos = (_pos - Lag + _smooth.Length) % _smooth.Length;
var step = 0.01m;
var current = Math.Abs(_smooth[_pos] - _smooth[lagPos]) >= Lag * Lag * step ? _smooth[_pos] : _lastValue;
_lastValue = current;
_pos = (_pos + 1) % _smooth.Length;
return new DecimalIndicatorValue(this, current, input.Time);
}
private decimal Range(int count, int offset)
{
var end = _highs.Count - 1 - offset;
var start = end - count + 1;
if (start < 0) start = 0;
var max = decimal.MinValue;
var min = decimal.MaxValue;
for (var i = start; i <= end; i++)
{
if (_highs[i] > max) max = _highs[i];
if (_lows[i] < min) min = _lows[i];
}
return max - min;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class aml_candle_cross_strategy(Strategy):
"""Adaptive Market Level candle cross strategy.
Uses EMA as proxy for the custom AdaptiveMarketLevel indicator.
Opens position when indicator value lies between candle open and close.
"""
def __init__(self):
super(aml_candle_cross_strategy, self).__init__()
self._fractal = self.Param("Fractal", 10) \
.SetDisplay("Fractal", "Fractal window size", "General")
self._lag = self.Param("Lag", 5) \
.SetDisplay("Lag", "Lag for smoothing", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle Type", "General")
@property
def fractal(self):
return self._fractal.Value
@property
def lag(self):
return self._lag.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(aml_candle_cross_strategy, self).OnStarted2(time)
# Use EMA as proxy for custom AdaptiveMarketLevel indicator
aml = ExponentialMovingAverage()
aml.Length = self.fractal
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(aml, self.on_process).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, aml)
self.DrawOwnTrades(area)
def on_process(self, candle, aml_value):
if candle.State != CandleStates.Finished:
return
if aml_value == 0:
return
opn = candle.OpenPrice
close = candle.ClosePrice
# Bullish: AML between open and close, bullish candle
bullish = close > opn and aml_value >= opn and aml_value <= close
# Bearish: AML between close and open, bearish candle
bearish = close < opn and aml_value >= close and aml_value <= opn
if self.Position == 0:
if bullish:
self.BuyMarket()
elif bearish:
self.SellMarket()
def CreateClone(self):
return aml_candle_cross_strategy()