AML 烛体交叉策略
该策略基于自适应市场水平 (AML) 指标。 当 AML 值落在当前K线实体内部时发起交易: 如果收盘价高于开盘价且 AML 位于其间,则开多单; 若收盘价低于开盘价且 AML 位于其间,则开空单。 当出现相反信号且允许时,策略会反向开仓。
细节
- 入场条件:
- 多头:阳线且
open <= AML <= close。 - 空头:阴线且
open >= AML >= close。
- 多头:阳线且
- 方向:双向。
- 出场条件:启用时在相反信号出现时反向开仓。
- 止损:无。
- 默认值:
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()