Flex ATR戦略
Flex ATRは現在の時間軸に基づいてEMA、RSI、ATRの期間を動的に選択します。高速EMAが低速EMAを上抜けしRSIが50を超えるとロングエントリーします。逆のクロスオーバーでRSIが50を下回るとショートエントリーします。エグジットはATRベースのストップ・ターゲット、またはオプションのトレーリングストップを使用します。
詳細
- エントリー条件: 高速EMAと低速EMAのクロス + RSIフィルター。
- ロング/ショート: 両方。
- エグジット条件: ATRベースのストップまたはターゲット、オプションのトレーリングストップ。
- ストップ: あり。
- デフォルト値:
AtrStopMult= 3AtrProfitMult= 1.5EnableTrailingStop= trueAtrTrailMult= 1CandleType= TimeSpan.FromMinutes(30)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: EMA, RSI, ATR
- ストップ: あり
- 複雑さ: 上級
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Flex ATR strategy.
/// EMA, RSI and ATR periods are chosen from the candle timeframe: up to 5 minutes 8/21 EMAs, RSI 9 and ATR 10; up to 30 minutes
/// 12/26, RSI 14, ATR 14; up to 4 hours 20/50, RSI 14, ATR 14; longer 50/200, RSI 14, ATR 20. A fast EMA crossing above the slow
/// one with RSI above 50 goes long and the opposite cross with RSI below 50 goes short, reversing an opposite position. A position
/// closes at a stop AtrStopMult ATRs away or a target AtrProfitMult ATRs away, both set at entry; with EnableTrailingStop the stop
/// also follows the best close at AtrTrailMult ATRs.
/// </summary>
public class FlexAtrStrategy : Strategy
{
private const decimal _rsiMiddle = 50m;
private readonly StrategyParam<decimal> _atrStopMult;
private readonly StrategyParam<decimal> _atrProfitMult;
private readonly StrategyParam<bool> _enableTrailingStop;
private readonly StrategyParam<decimal> _atrTrailMult;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal _stopPrice;
private decimal _targetPrice;
/// <summary>
/// Stop distance in ATRs.
/// </summary>
public decimal AtrStopMult
{
get => _atrStopMult.Value;
set => _atrStopMult.Value = value;
}
/// <summary>
/// Target distance in ATRs.
/// </summary>
public decimal AtrProfitMult
{
get => _atrProfitMult.Value;
set => _atrProfitMult.Value = value;
}
/// <summary>
/// Let the stop follow the best close.
/// </summary>
public bool EnableTrailingStop
{
get => _enableTrailingStop.Value;
set => _enableTrailingStop.Value = value;
}
/// <summary>
/// Trailing distance in ATRs.
/// </summary>
public decimal AtrTrailMult
{
get => _atrTrailMult.Value;
set => _atrTrailMult.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FlexAtrStrategy()
{
_atrStopMult = Param(nameof(AtrStopMult), 3m)
.SetNotNegative()
.SetDisplay("ATR Stop Mult", "Stop distance in ATRs", "Risk");
_atrProfitMult = Param(nameof(AtrProfitMult), 1.5m)
.SetNotNegative()
.SetDisplay("ATR Profit Mult", "Target distance in ATRs", "Risk");
_enableTrailingStop = Param(nameof(EnableTrailingStop), true)
.SetDisplay("Trailing Stop", "Let the stop follow the best close", "Risk");
_atrTrailMult = Param(nameof(AtrTrailMult), 1m)
.SetNotNegative()
.SetDisplay("ATR Trail Mult", "Trailing distance in ATRs", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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()
{
_prevFast = null;
_prevSlow = null;
_stopPrice = 0m;
_targetPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var (fastLength, slowLength, rsiLength, atrLength) = SelectPeriods(CandleType.Arg is TimeSpan frame ? frame : TimeSpan.Zero);
var fast = new ExponentialMovingAverage { Length = fastLength };
var slow = new ExponentialMovingAverage { Length = slowLength };
var rsi = new RelativeStrengthIndex { Length = rsiLength };
var atr = new AverageTrueRange { Length = atrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private static (int fast, int slow, int rsi, int atr) SelectPeriods(TimeSpan frame)
{
if (frame <= TimeSpan.FromMinutes(5))
return (8, 21, 9, 10);
if (frame <= TimeSpan.FromMinutes(30))
return (12, 26, 14, 14);
if (frame <= TimeSpan.FromHours(4))
return (20, 50, 14, 14);
return (50, 200, 14, 20);
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal rsi, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (prevFast is not decimal lastFast || prevSlow is not decimal lastSlow)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var crossUp = lastFast <= lastSlow && fast > slow;
var crossDown = lastFast >= lastSlow && fast < slow;
if (crossUp && rsi > _rsiMiddle && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = AtrStopMult > 0 ? close - AtrStopMult * atr : 0m;
_targetPrice = AtrProfitMult > 0 ? close + AtrProfitMult * atr : 0m;
return;
}
if (crossDown && rsi < _rsiMiddle && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = AtrStopMult > 0 ? close + AtrStopMult * atr : 0m;
_targetPrice = AtrProfitMult > 0 ? close - AtrProfitMult * atr : 0m;
return;
}
if (Position > 0)
{
if ((_stopPrice > 0 && candle.LowPrice <= _stopPrice) || (_targetPrice > 0 && candle.HighPrice >= _targetPrice))
{
SellMarket(Position);
return;
}
if (EnableTrailingStop)
{
var trail = close - AtrTrailMult * atr;
if (trail > _stopPrice)
_stopPrice = trail;
}
}
else if (Position < 0)
{
if ((_stopPrice > 0 && candle.HighPrice >= _stopPrice) || (_targetPrice > 0 && candle.LowPrice <= _targetPrice))
{
BuyMarket(-Position);
return;
}
if (EnableTrailingStop)
{
var trail = close + AtrTrailMult * atr;
if (_stopPrice <= 0 || trail < _stopPrice)
_stopPrice = trail;
}
}
}
}
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, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
RSI_MIDDLE = 50.0
class flex_atr_strategy(Strategy):
"""
Flex ATR strategy.
EMA, RSI and ATR periods are chosen from the candle timeframe: up to 5 minutes 8/21 EMAs, RSI 9 and ATR 10; up to 30 minutes
12/26, RSI 14, ATR 14; up to 4 hours 20/50, RSI 14, ATR 14; longer 50/200, RSI 14, ATR 20. A fast EMA crossing above the slow
one with RSI above 50 goes long and the opposite cross with RSI below 50 goes short, reversing an opposite position. A position
closes at a stop AtrStopMult ATRs away or a target AtrProfitMult ATRs away, both set at entry; with EnableTrailingStop the stop
also follows the best close at AtrTrailMult ATRs.
"""
def __init__(self):
super(flex_atr_strategy, self).__init__()
self._atr_stop_mult = self.Param("AtrStopMult", 3.0).SetNotNegative().SetDisplay("ATR Stop Mult", "Stop distance in ATRs", "Risk")
self._atr_profit_mult = self.Param("AtrProfitMult", 1.5).SetNotNegative().SetDisplay("ATR Profit Mult", "Target distance in ATRs", "Risk")
self._enable_trailing_stop = self.Param("EnableTrailingStop", True).SetDisplay("Trailing Stop", "Let the stop follow the best close", "Risk")
self._atr_trail_mult = self.Param("AtrTrailMult", 1.0).SetNotNegative().SetDisplay("ATR Trail Mult", "Trailing distance in ATRs", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = None
self._prev_slow = None
self._stop_price = 0.0
self._target_price = 0.0
def OnReseted(self):
super(flex_atr_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(flex_atr_strategy, self).OnStarted2(time)
self._reset_state()
arg = self.candle_type.Arg
minutes = float(arg.TotalMinutes) if hasattr(arg, "TotalMinutes") else 0.0
fast_length, slow_length, rsi_length, atr_length = self._select_periods(minutes)
fast = ExponentialMovingAverage()
fast.Length = fast_length
slow = ExponentialMovingAverage()
slow.Length = slow_length
rsi = RelativeStrengthIndex()
rsi.Length = rsi_length
atr = AverageTrueRange()
atr.Length = atr_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, rsi, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _select_periods(self, minutes):
if minutes <= 5:
return 8, 21, 9, 10
if minutes <= 30:
return 12, 26, 14, 14
if minutes <= 240:
return 20, 50, 14, 14
return 50, 200, 14, 20
def _process_candle(self, candle, fast_value, slow_value, rsi_value, atr_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
rsi = float(rsi_value)
atr = float(atr_value)
last_fast = self._prev_fast
last_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if last_fast is None or last_slow is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
stop_mult = float(self._atr_stop_mult.Value)
profit_mult = float(self._atr_profit_mult.Value)
trail_mult = float(self._atr_trail_mult.Value)
cross_up = last_fast <= last_slow and fast > slow
cross_down = last_fast >= last_slow and fast < slow
if cross_up and rsi > RSI_MIDDLE and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - stop_mult * atr if stop_mult > 0 else 0.0
self._target_price = close + profit_mult * atr if profit_mult > 0 else 0.0
return
if cross_down and rsi < RSI_MIDDLE and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + stop_mult * atr if stop_mult > 0 else 0.0
self._target_price = close - profit_mult * atr if profit_mult > 0 else 0.0
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0:
if (self._stop_price > 0 and low <= self._stop_price) or (self._target_price > 0 and high >= self._target_price):
self.SellMarket(self.Position)
return
if self._enable_trailing_stop.Value:
trail = close - trail_mult * atr
if trail > self._stop_price:
self._stop_price = trail
elif self.Position < 0:
if (self._stop_price > 0 and high >= self._stop_price) or (self._target_price > 0 and low <= self._target_price):
self.BuyMarket(-self.Position)
return
if self._enable_trailing_stop.Value:
trail = close + trail_mult * atr
if self._stop_price <= 0 or trail < self._stop_price:
self._stop_price = trail
def CreateClone(self):
return flex_atr_strategy()