Eliora Gold 1m Heikin Ashi戦略
この戦略は1分足でHeikin Ashiローソク足を使用します。市場がコンソリデーションしていないときにトレンド方向の強いローソク足でエントリーし、取引間にクールダウンを設けます。エグジットはATRベースのトレーリングストップで管理します。
詳細
- エントリー条件: トレンド方向の強いHeikin Ashiローソク足、コンソリデーションなし、ボラティリティフィルター。
- ロング/ショート: 両方。
- エグジット条件: ATRトレーリングストップ。
- ストップ: あり。
- デフォルト値:
AtrPeriod= 14CooldownBars= 5CandleType= TimeSpan.FromMinutes(1)
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: Heikin Ashi, ATR, SMA, Highest/Lowest
- ストップ: あり
- 複雑さ: 中程度
- 時間軸: イントラデイ (1m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Eliora Gold 1m Heikin Ashi strategy.
/// Heikin Ashi candles are built from the one-minute candles. A strong bullish Heikin Ashi candle (body at least
/// 60% of its range) closing above the trend SMA goes long and a strong bearish one closing below it goes short, when
/// the market is not consolidating (the recent high-low range is wider than a multiple of ATR), volatility is
/// expanding (ATR above its own average) and at least CooldownBars candles passed since the last trade.
/// Positions are exited only by an ATR trailing stop.
/// </summary>
public class ElioraGold1mHeikinAshiStrategy : Strategy
{
private const int _trendLength = 50;
private const int _rangeLength = 20;
private const decimal _strongBodyRatio = 0.6m;
private const decimal _consolidationAtrMultiplier = 2m;
private const decimal _trailAtrMultiplier = 2m;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private Lowest _lowest;
private SimpleMovingAverage _atrAverage;
private decimal? _haOpen;
private decimal? _haClose;
private decimal? _trailStop;
private int _barsSinceTrade;
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Candles to wait after a trade before a new entry.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ElioraGold1mHeikinAshiStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for filters and the trailing stop", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 5)
.SetNotNegative()
.SetDisplay("Cooldown Bars", "Candles to wait after a trade", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_haOpen = null;
_haClose = null;
_trailStop = null;
_barsSinceTrade = int.MaxValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var atr = new AverageTrueRange { Length = AtrPeriod };
var trend = new SimpleMovingAverage { Length = _trendLength };
_highest = new Highest { Length = _rangeLength };
_lowest = new Lowest { Length = _rangeLength };
_atrAverage = new SimpleMovingAverage { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, trend, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, trend);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atr, decimal trend)
{
if (candle.State != CandleStates.Finished)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
var haOpen = _haOpen is decimal po && _haClose is decimal pc ? (po + pc) / 2m : (candle.OpenPrice + candle.ClosePrice) / 2m;
var haHigh = Math.Max(candle.HighPrice, Math.Max(haOpen, haClose));
var haLow = Math.Min(candle.LowPrice, Math.Min(haOpen, haClose));
_haOpen = haOpen;
_haClose = haClose;
var highValue = _highest.Process(new DecimalIndicatorValue(_highest, candle.HighPrice, candle.OpenTime) { IsFinal = true });
var lowValue = _lowest.Process(new DecimalIndicatorValue(_lowest, candle.LowPrice, candle.OpenTime) { IsFinal = true });
var atrAvgValue = _atrAverage.Process(new DecimalIndicatorValue(_atrAverage, atr, candle.OpenTime) { IsFinal = true });
if (_barsSinceTrade < int.MaxValue)
_barsSinceTrade++;
if (!highValue.IsFormed || !lowValue.IsFormed || !atrAvgValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var trailDistance = atr * _trailAtrMultiplier;
if (Position > 0)
{
var candidate = candle.ClosePrice - trailDistance;
_trailStop = _trailStop is decimal s ? Math.Max(s, candidate) : candidate;
if (candle.LowPrice <= _trailStop)
{
SellMarket(Position);
_trailStop = null;
_barsSinceTrade = 0;
}
return;
}
if (Position < 0)
{
var candidate = candle.ClosePrice + trailDistance;
_trailStop = _trailStop is decimal s ? Math.Min(s, candidate) : candidate;
if (candle.HighPrice >= _trailStop)
{
BuyMarket(-Position);
_trailStop = null;
_barsSinceTrade = 0;
}
return;
}
if (_barsSinceTrade < CooldownBars)
return;
var range = highValue.GetValue<decimal>() - lowValue.GetValue<decimal>();
var consolidating = range < atr * _consolidationAtrMultiplier;
var expanding = atr > atrAvgValue.GetValue<decimal>();
if (consolidating || !expanding)
return;
var haRange = haHigh - haLow;
var body = Math.Abs(haClose - haOpen);
var strong = haRange > 0 && body >= haRange * _strongBodyRatio;
if (!strong)
return;
if (haClose > haOpen && haClose > trend)
{
BuyMarket(Volume);
_trailStop = candle.ClosePrice - trailDistance;
_barsSinceTrade = 0;
}
else if (haClose < haOpen && haClose < trend)
{
SellMarket(Volume);
_trailStop = candle.ClosePrice + trailDistance;
_barsSinceTrade = 0;
}
}
}
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 AverageTrueRange, SimpleMovingAverage, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
TREND_LENGTH = 50
RANGE_LENGTH = 20
STRONG_BODY_RATIO = 0.6
CONSOLIDATION_ATR_MULTIPLIER = 2.0
TRAIL_ATR_MULTIPLIER = 2.0
class eliora_gold_1m_heikin_ashi_strategy(Strategy):
"""
Eliora Gold 1m Heikin Ashi strategy.
Heikin Ashi candles are built from the one-minute candles. A strong bullish Heikin Ashi candle (body at least
60% of its range) closing above the trend SMA goes long and a strong bearish one closing below it goes short, when
the market is not consolidating (the recent high-low range is wider than a multiple of ATR), volatility is
expanding (ATR above its own average) and at least CooldownBars candles passed since the last trade.
Positions are exited only by an ATR trailing stop.
"""
def __init__(self):
super(eliora_gold_1m_heikin_ashi_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period for filters and the trailing stop", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 5).SetNotNegative().SetDisplay("Cooldown Bars", "Candles to wait after a trade", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._highest = None
self._lowest = None
self._atr_average = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._ha_open = None
self._ha_close = None
self._trail_stop = None
self._bars_since_trade = None
def OnReseted(self):
super(eliora_gold_1m_heikin_ashi_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(eliora_gold_1m_heikin_ashi_strategy, self).OnStarted2(time)
self._reset_state()
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
trend = SimpleMovingAverage()
trend.Length = TREND_LENGTH
self._highest = Highest()
self._highest.Length = RANGE_LENGTH
self._lowest = Lowest()
self._lowest.Length = RANGE_LENGTH
self._atr_average = SimpleMovingAverage()
self._atr_average.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(atr, trend, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, trend)
self.DrawOwnTrades(area)
def _process_candle(self, candle, atr_value, trend_value):
if candle.State != CandleStates.Finished:
return
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
ha_close = (o + h + l + c) / 4.0
if self._ha_open is not None and self._ha_close is not None:
ha_open = (self._ha_open + self._ha_close) / 2.0
else:
ha_open = (o + c) / 2.0
ha_high = max(h, ha_open, ha_close)
ha_low = min(l, ha_open, ha_close)
self._ha_open = ha_open
self._ha_close = ha_close
high_result = process_float(self._highest, candle.HighPrice, candle.OpenTime, True)
low_result = process_float(self._lowest, candle.LowPrice, candle.OpenTime, True)
atr_avg_result = process_float(self._atr_average, atr_value, candle.OpenTime, True)
if self._bars_since_trade is not None:
self._bars_since_trade += 1
if not high_result.IsFormed or not low_result.IsFormed or not atr_avg_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
atr = float(atr_value)
trend = float(trend_value)
trail_distance = atr * TRAIL_ATR_MULTIPLIER
if self.Position > 0:
candidate = c - trail_distance
self._trail_stop = candidate if self._trail_stop is None else max(self._trail_stop, candidate)
if l <= self._trail_stop:
self.SellMarket(self.Position)
self._trail_stop = None
self._bars_since_trade = 0
return
if self.Position < 0:
candidate = c + trail_distance
self._trail_stop = candidate if self._trail_stop is None else min(self._trail_stop, candidate)
if h >= self._trail_stop:
self.BuyMarket(-self.Position)
self._trail_stop = None
self._bars_since_trade = 0
return
if self._bars_since_trade is not None and self._bars_since_trade < self._cooldown_bars.Value:
return
price_range = float(high_result) - float(low_result)
consolidating = price_range < atr * CONSOLIDATION_ATR_MULTIPLIER
expanding = atr > float(atr_avg_result)
if consolidating or not expanding:
return
ha_range = ha_high - ha_low
body = abs(ha_close - ha_open)
strong = ha_range > 0 and body >= ha_range * STRONG_BODY_RATIO
if not strong:
return
if ha_close > ha_open and ha_close > trend:
self.BuyMarket(self.Volume)
self._trail_stop = c - trail_distance
self._bars_since_trade = 0
elif ha_close < ha_open and ha_close < trend:
self.SellMarket(self.Volume)
self._trail_stop = c + trail_distance
self._bars_since_trade = 0
def CreateClone(self):
return eliora_gold_1m_heikin_ashi_strategy()