ATR ステップ トレーダー戦略
概要
ATR ステップ トレーダー戦略は、MetaTrader5 エキスパート アドバイザー atrTrader.mq5 の直接ポートです。高速/低速移動平均フィルターと、平均真範囲 (ATR) ベースのブレークアウト ルールおよびピラミッド ルールを組み合わせます。このポートは、元の EA の足主導のワークフローを維持します。つまり、完了したローソク足のみが処理され、高速な SMA は一定数の足の間、低速な SMA の上または下に位置する必要があり、市場間の距離を正規化するためにすべての決定が ATR の倍数に固定されます。
指標とデータ
- 単純移動平均 (SMA)。 2 つの移動平均 (
FastPeriodとSlowPeriod) は、主要なトレンド フィルターを定義します。どちらもサブスクリプションキャンドルシリーズに適用されます。 - 平均トゥルー レンジ (ATR)。
AverageTrueRangeインジケーター (AtrPeriod) は、ボラティリティを価格距離に変換します。すべてのブレイクアウト、アドオン、ストップの計算では、ATR の倍数が使用されます。 - 最高/最低価格チャネル。
HighestおよびLowestインジケーターは、最新のMomentumPeriodローソク足の極端な高値と安値を追跡します。これらは、MQL コードからのiHighest/iLowest呼び出しを置き換えます。 - 時間枠。 デフォルトのローソク足タイプは 1 時間 (
TimeSpan.FromHours(1)) で、元のスクリプトのPERIOD_CURRENTの動作を反映しています。CandleTypeパラメータを編集することで、他の時間枠に切り替えることができます。
エントリーロジック
- ろうそくの火が消えるまで待ちます。 MT5 OnTick + iTime ガードとの同期を保つために、未完成のローソク足は無視されます。
- 移動平均ストリークカウンターを更新します。高速の SMA が低速の SMA を上回ると、強気の傾向が増します。以下に印刷すると、弱気の縞が増加します。測定値が混在すると、反対のストリークがリセットされます。
- 強気の連続が
MomentumPeriodに達したら、終値がまだ最近の高値を少なくともStepMultiplier * ATR下回っているかどうかを確認します。その場合は市場で購入してください。 - 弱気の連続が
MomentumPeriodに達したら、終値がまだ最近の安値を少なくともStepMultiplier * ATR上回っているかどうかを確認します。その場合は市場で売りましょう。 - 新しいポジションごとに方向性の状態が初期化されます。戦略はサイドごとの最高値と最低値を記憶し、後のピラミッドが参照アンカーを持つようにします。最初の注文にはボラティリティサイズのストップ (
StepMultiplier * StopMultiplier * ATR) も付けられます。
ポジション管理
- ピラミッド型: アクティブなエントリーの数が
PyramidLimitを下回っている間、この戦略は価格が現在の極端な基準から+/- StepsMultiplier * ATRを離れるたびに別のユニットを追加します。これは、EA の「Steps」スケーリング グリッドを反映しており、有利な方向と不利な方向の両方に機能します。 - 保護ストップ: 新しい注文の最初のストップは約定価格から
StepMultiplier * StopMultiplier * ATR離れた位置にあります。ピラミッドがいっぱいになると、ストップは直近終値の後ろ(ロングの場合)または前(ショートの場合)にStepMultiplier * ATRに狭められ、3 つのポジションが空いているときの EA の末尾の更新をエミュレートします。 - 逆方向のエグジット: 価格が追跡された極値を超えて
StepsMultiplier * ATR後退した場合、この戦略は成行注文でその側のすべてのポジションを即座にエグジットします。これは、価格が最新のラダー エッジをブレイクしたときにスタック全体をダンプする EA ロジックをキャプチャしています。 - ステート リセット: 完全なエグジット後、ストリーク カウンタと ATR ストップ リファレンスがリセットされるため、再エントリーする前に新しいトレンド シーケンスを展開する必要があります。
パラメーター
| グループ | 名前 | 説明 | デフォルト |
|---|---|---|---|
| トレンドフィルター | FastPeriod |
短期的な方向性を測定する高速な SMA の長さ。 | 70 |
| トレンドフィルター | SlowPeriod |
長期的な方向性を測定する遅い SMA の長さ。 | 180 |
| トレンドフィルター | MomentumPeriod |
トレンドを確認する必要がある連続終了ローソク足の数。 | 50 |
| ボラティリティ | AtrPeriod |
すべての距離計算に使用される ATR ウィンドウ。 | 100 |
| エントリーロジック | StepMultiplier |
最初のブレイクアウトを防ぐのは ATR 倍です。 | 4 |
| エントリーロジック | StepsMultiplier |
ピラミッド層を区切る ATR の倍数。 | 2 |
| リスク管理 | StopMultiplier |
基本ステップ距離を超えた最初の停止に追加の乗数が適用されます。 | 3 |
| ポジションサイジング | PyramidLimit |
方向ごとの最大エントリ数。 | 3 |
| 取引 | TradeVolume |
各成行注文で送信されたストラテジーのボリューム。 | 1 |
| 一般 | CandleType |
サブスクリプションに使用されるローソクのタイプ (時間枠)。 | TimeFrame(1h) |
実践メモ
- StockSharp バージョンは、サイズ設定に戦略
Volumeプロパティを使用します。公開前に、商品契約のサイズに合わせてTradeVolumeを調整します。 - MT5 の
CTrade.Buy/Sellの使用と同様に、成行注文はすぐに約定すると想定されます。市場が薄い場合は、成行注文を指値注文または逆指値注文に置き換えることができます。 - 高/低参照は、EA の
h_price変数とl_price変数を複製し、新しいレイヤーが追加または削除されるたびに更新されます。これらは、はしごをいつ追加またはフラッシュするかを決定するために不可欠です。 - EA はポジションごとにストップロスを保存し、StockSharp はストップロスを戦略レベルで管理するため、ポートはスタック全体に最も厳しいストップ ロジックを適用します。これにより、管理するブローカー側の注文が少なくなり、同じ動作 (すべてのポジションが同時に決済) が実現します。
- 戦略は常にシミュレーションでテストしてください。 ATR 距離はボラティリティに適応しますが、ギャップやスリッページが大きい市場では、現実のリスクが予測停止距離を超える可能性があります。
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;
/// <summary>
/// Multi-step ATR trend strategy converted from the "atrTrader" MQL5 expert advisor.
/// Filters trends with a dual moving-average stack, opens breakouts, and pyramids positions using ATR distances.
/// </summary>
public class AtrStepTraderStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<int> _pyramidLimit;
private readonly StrategyParam<decimal> _stepMultiplier;
private readonly StrategyParam<decimal> _stepsMultiplier;
private readonly StrategyParam<decimal> _stopMultiplier;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private int _bullishStreak;
private int _bearishStreak;
private decimal? _previousSlow;
private decimal? _longEntryHigh;
private decimal? _longEntryLow;
private decimal? _shortEntryHigh;
private decimal? _shortEntryLow;
private decimal? _longStopPrice;
private decimal? _shortStopPrice;
/// <summary>
/// Fast moving average length.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow moving average length.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// ATR calculation period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Number of consecutive bars that must confirm the trend direction.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Maximum number of stacked entries per direction.
/// </summary>
public int PyramidLimit
{
get => _pyramidLimit.Value;
set => _pyramidLimit.Value = value;
}
/// <summary>
/// ATR multiple used for breakout gating.
/// </summary>
public decimal StepMultiplier
{
get => _stepMultiplier.Value;
set => _stepMultiplier.Value = value;
}
/// <summary>
/// ATR multiple used for pyramiding distance checks.
/// </summary>
public decimal StepsMultiplier
{
get => _stepsMultiplier.Value;
set => _stepsMultiplier.Value = value;
}
/// <summary>
/// Additional multiplier that widens the protective stop distance.
/// </summary>
public decimal StopMultiplier
{
get => _stopMultiplier.Value;
set => _stopMultiplier.Value = value;
}
/// <summary>
/// Base order volume for market entries.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="AtrStepTraderStrategy"/>.
/// </summary>
public AtrStepTraderStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 70)
.SetGreaterThanZero()
.SetDisplay("Fast MA Period", "Length of the fast moving average", "Trend Filter")
.SetOptimize(50, 100, 10);
_slowPeriod = Param(nameof(SlowPeriod), 180)
.SetGreaterThanZero()
.SetDisplay("Slow MA Period", "Length of the slow moving average", "Trend Filter")
.SetOptimize(120, 240, 20);
_atrPeriod = Param(nameof(AtrPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR window used for distance calculations", "Volatility")
.SetOptimize(50, 150, 10);
_momentumPeriod = Param(nameof(MomentumPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Momentum Bars", "Number of consecutive bars required for trend confirmation", "Trend Filter")
.SetOptimize(30, 80, 5);
_pyramidLimit = Param(nameof(PyramidLimit), 3)
.SetGreaterThanZero()
.SetDisplay("Pyramid Limit", "Maximum number of entries per direction", "Position Sizing")
.SetOptimize(2, 4, 1);
_stepMultiplier = Param(nameof(StepMultiplier), 4m)
.SetGreaterThanZero()
.SetDisplay("Step Multiplier", "ATR multiple for breakout validation", "Entry Logic")
.SetOptimize(2m, 6m, 1m);
_stepsMultiplier = Param(nameof(StepsMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Steps Multiplier", "ATR multiple for add-on spacing", "Entry Logic")
.SetOptimize(1m, 3m, 0.5m);
_stopMultiplier = Param(nameof(StopMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Stop Multiplier", "Extra multiplier applied on top of the step distance", "Risk Management")
.SetOptimize(2m, 4m, 0.5m);
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Base order size for market entries", "Trading")
.SetOptimize(0.5m, 2m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame used for processing", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bullishStreak = 0;
_bearishStreak = 0;
_previousSlow = null;
_longEntryHigh = null;
_longEntryLow = null;
_shortEntryHigh = null;
_shortEntryLow = null;
_longStopPrice = null;
_shortStopPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = TradeVolume;
var fastMa = new SimpleMovingAverage { Length = FastPeriod };
var slowMa = new SimpleMovingAverage { Length = SlowPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var highest = new Highest { Length = MomentumPeriod };
var lowest = new Lowest { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, atr, highest, lowest, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawIndicator(area, atr);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue, decimal atrValue, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished)
return;
if (atrValue <= 0m)
return;
UpdateMomentumCounters(fastValue, slowValue);
var price = candle.ClosePrice;
var previousSlow = _previousSlow;
_previousSlow = slowValue;
var volume = Volume;
if (volume <= 0m)
volume = 1m;
var netPosition = Position;
var longCount = netPosition > 0m ? (int)Math.Round(netPosition / volume, MidpointRounding.AwayFromZero) : 0;
var shortCount = netPosition < 0m ? (int)Math.Round(-netPosition / volume, MidpointRounding.AwayFromZero) : 0;
if (longCount == 0 && shortCount == 0)
{
if (previousSlow.HasValue && slowValue > 0m)
{
var bullishReady = _bullishStreak >= MomentumPeriod && price > previousSlow.Value;
if (bullishReady)
{
BuyMarket(Volume);
longCount = 1;
_longEntryHigh = price;
_longEntryLow = price;
_longStopPrice = price - StepMultiplier * StopMultiplier * atrValue;
}
}
if (longCount == 0 && previousSlow.HasValue && slowValue > 0m)
{
var bearishReady = _bearishStreak >= MomentumPeriod && price < previousSlow.Value;
if (bearishReady)
{
SellMarket(Volume);
shortCount = 1;
_shortEntryHigh = price;
_shortEntryLow = price;
_shortStopPrice = price + StepMultiplier * StopMultiplier * atrValue;
}
}
}
else if (longCount > 0 && shortCount == 0)
{
ManageLongPosition(ref longCount, price, atrValue);
}
else if (shortCount > 0 && longCount == 0)
{
ManageShortPosition(ref shortCount, price, atrValue);
}
}
private void UpdateMomentumCounters(decimal fastValue, decimal slowValue)
{
if (fastValue > slowValue)
{
_bullishStreak++;
_bearishStreak = 0;
}
else if (fastValue < slowValue)
{
_bearishStreak++;
_bullishStreak = 0;
}
else
{
_bullishStreak++;
_bearishStreak++;
}
}
private void ManageLongPosition(ref int longCount, decimal price, decimal atrValue)
{
if (_longEntryHigh is not decimal high || _longEntryLow is not decimal low)
return;
var stepsDistance = StepsMultiplier * atrValue;
var stepDistance = StepMultiplier * atrValue;
if (_longStopPrice.HasValue && price <= _longStopPrice.Value)
{
SellMarket(Position);
longCount = 0;
ResetLongState();
return;
}
if (longCount < PyramidLimit)
{
if (price >= high + stepsDistance || price <= low - stepsDistance)
{
BuyMarket(Volume);
longCount++;
_longEntryHigh = Math.Max(high, price);
_longEntryLow = Math.Min(low, price);
UpdateLongStopAfterEntry(price, atrValue);
return;
}
}
if (price <= low - stepsDistance)
{
SellMarket(Position);
longCount = 0;
ResetLongState();
return;
}
if (longCount >= PyramidLimit)
{
var tightened = price - stepDistance;
if (!_longStopPrice.HasValue || tightened > _longStopPrice.Value)
_longStopPrice = tightened;
}
}
private void ManageShortPosition(ref int shortCount, decimal price, decimal atrValue)
{
if (_shortEntryHigh is not decimal high || _shortEntryLow is not decimal low)
return;
var stepsDistance = StepsMultiplier * atrValue;
var stepDistance = StepMultiplier * atrValue;
if (_shortStopPrice.HasValue && price >= _shortStopPrice.Value)
{
BuyMarket(Math.Abs(Position));
shortCount = 0;
ResetShortState();
return;
}
if (shortCount < PyramidLimit)
{
if (price <= low - stepsDistance || price >= high + stepsDistance)
{
SellMarket(Volume);
shortCount++;
_shortEntryHigh = Math.Max(high, price);
_shortEntryLow = Math.Min(low, price);
UpdateShortStopAfterEntry(price, atrValue);
return;
}
}
if (price >= high + stepsDistance)
{
BuyMarket(Math.Abs(Position));
shortCount = 0;
ResetShortState();
return;
}
if (shortCount >= PyramidLimit)
{
var tightened = price + stepDistance;
if (!_shortStopPrice.HasValue || tightened < _shortStopPrice.Value)
_shortStopPrice = tightened;
}
}
private void UpdateLongStopAfterEntry(decimal entryPrice, decimal atrValue)
{
var stop = entryPrice - StepMultiplier * StopMultiplier * atrValue;
if (!_longStopPrice.HasValue || stop > _longStopPrice.Value)
_longStopPrice = stop;
}
private void UpdateShortStopAfterEntry(decimal entryPrice, decimal atrValue)
{
var stop = entryPrice + StepMultiplier * StopMultiplier * atrValue;
if (!_shortStopPrice.HasValue || stop < _shortStopPrice.Value)
_shortStopPrice = stop;
}
private void ResetLongState()
{
_longEntryHigh = null;
_longEntryLow = null;
_longStopPrice = null;
_bullishStreak = 0;
}
private void ResetShortState()
{
_shortEntryHigh = null;
_shortEntryLow = null;
_shortStopPrice = null;
_bearishStreak = 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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class atr_step_trader_strategy(Strategy):
def __init__(self):
super(atr_step_trader_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 70) \
.SetGreaterThanZero() \
.SetDisplay("Fast MA Period", "Length of the fast moving average", "Trend Filter")
self._slow_period = self.Param("SlowPeriod", 180) \
.SetGreaterThanZero() \
.SetDisplay("Slow MA Period", "Length of the slow moving average", "Trend Filter")
self._atr_period = self.Param("AtrPeriod", 100) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "ATR window used for distance calculations", "Volatility")
self._momentum_period = self.Param("MomentumPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Momentum Bars", "Number of consecutive bars required for trend confirmation", "Trend Filter")
self._pyramid_limit = self.Param("PyramidLimit", 3) \
.SetGreaterThanZero() \
.SetDisplay("Pyramid Limit", "Maximum number of entries per direction", "Position Sizing")
self._step_multiplier = self.Param("StepMultiplier", 4.0) \
.SetGreaterThanZero() \
.SetDisplay("Step Multiplier", "ATR multiple for breakout validation", "Entry Logic")
self._steps_multiplier = self.Param("StepsMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Steps Multiplier", "ATR multiple for add-on spacing", "Entry Logic")
self._stop_multiplier = self.Param("StopMultiplier", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Multiplier", "Extra multiplier applied on top of the step distance", "Risk Management")
self._trade_volume = self.Param("TradeVolume", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Trade Volume", "Base order size for market entries", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Time frame used for processing", "General")
self._bullish_streak = 0
self._bearish_streak = 0
self._previous_slow = None
self._long_entry_high = None
self._long_entry_low = None
self._short_entry_high = None
self._short_entry_low = None
self._long_stop_price = None
self._short_stop_price = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def AtrPeriod(self):
return self._atr_period.Value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
@property
def PyramidLimit(self):
return self._pyramid_limit.Value
@property
def StepMultiplier(self):
return self._step_multiplier.Value
@property
def StepsMultiplier(self):
return self._steps_multiplier.Value
@property
def StopMultiplier(self):
return self._stop_multiplier.Value
@property
def TradeVolume(self):
return self._trade_volume.Value
def OnReseted(self):
super(atr_step_trader_strategy, self).OnReseted()
self._bullish_streak = 0
self._bearish_streak = 0
self._previous_slow = None
self._long_entry_high = None
self._long_entry_low = None
self._short_entry_high = None
self._short_entry_low = None
self._long_stop_price = None
self._short_stop_price = None
def OnStarted2(self, time):
super(atr_step_trader_strategy, self).OnStarted2(time)
self.Volume = self.TradeVolume
fast_ma = SimpleMovingAverage()
fast_ma.Length = self.FastPeriod
slow_ma = SimpleMovingAverage()
slow_ma.Length = self.SlowPeriod
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
highest = Highest()
highest.Length = self.MomentumPeriod
lowest = Lowest()
lowest.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ma, slow_ma, atr, highest, lowest, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value, atr_value, highest_val, lowest_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
av = float(atr_value)
if av <= 0:
return
self._update_momentum(fv, sv)
price = float(candle.ClosePrice)
previous_slow = self._previous_slow
self._previous_slow = sv
volume = float(self.Volume) if self.Volume > 0 else 1.0
net_pos = float(self.Position)
long_count = int(round(net_pos / volume)) if net_pos > 0 else 0
short_count = int(round(-net_pos / volume)) if net_pos < 0 else 0
if long_count == 0 and short_count == 0:
if previous_slow is not None and sv > 0:
bullish_ready = self._bullish_streak >= self.MomentumPeriod and price > previous_slow
if bullish_ready:
self.BuyMarket(self.Volume)
long_count = 1
self._long_entry_high = price
self._long_entry_low = price
self._long_stop_price = price - float(self.StepMultiplier) * float(self.StopMultiplier) * av
if long_count == 0 and previous_slow is not None and sv > 0:
bearish_ready = self._bearish_streak >= self.MomentumPeriod and price < previous_slow
if bearish_ready:
self.SellMarket(self.Volume)
short_count = 1
self._short_entry_high = price
self._short_entry_low = price
self._short_stop_price = price + float(self.StepMultiplier) * float(self.StopMultiplier) * av
elif long_count > 0 and short_count == 0:
self._manage_long(long_count, price, av, volume)
elif short_count > 0 and long_count == 0:
self._manage_short(short_count, price, av, volume)
def _update_momentum(self, fast_value, slow_value):
if fast_value > slow_value:
self._bullish_streak += 1
self._bearish_streak = 0
elif fast_value < slow_value:
self._bearish_streak += 1
self._bullish_streak = 0
else:
self._bullish_streak += 1
self._bearish_streak += 1
def _manage_long(self, long_count, price, atr_value, volume):
if self._long_entry_high is None or self._long_entry_low is None:
return
high = self._long_entry_high
low = self._long_entry_low
steps_dist = float(self.StepsMultiplier) * atr_value
step_dist = float(self.StepMultiplier) * atr_value
if self._long_stop_price is not None and price <= self._long_stop_price:
self.SellMarket(self.Position)
self._reset_long_state()
return
if long_count < self.PyramidLimit:
if price >= high + steps_dist or price <= low - steps_dist:
self.BuyMarket(self.Volume)
long_count += 1
self._long_entry_high = max(high, price)
self._long_entry_low = min(low, price)
stop = price - float(self.StepMultiplier) * float(self.StopMultiplier) * atr_value
if self._long_stop_price is None or stop > self._long_stop_price:
self._long_stop_price = stop
return
if price <= low - steps_dist:
self.SellMarket(self.Position)
self._reset_long_state()
return
if long_count >= self.PyramidLimit:
tightened = price - step_dist
if self._long_stop_price is None or tightened > self._long_stop_price:
self._long_stop_price = tightened
def _manage_short(self, short_count, price, atr_value, volume):
if self._short_entry_high is None or self._short_entry_low is None:
return
high = self._short_entry_high
low = self._short_entry_low
steps_dist = float(self.StepsMultiplier) * atr_value
step_dist = float(self.StepMultiplier) * atr_value
if self._short_stop_price is not None and price >= self._short_stop_price:
self.BuyMarket(abs(self.Position))
self._reset_short_state()
return
if short_count < self.PyramidLimit:
if price <= low - steps_dist or price >= high + steps_dist:
self.SellMarket(self.Volume)
short_count += 1
self._short_entry_high = max(high, price)
self._short_entry_low = min(low, price)
stop = price + float(self.StepMultiplier) * float(self.StopMultiplier) * atr_value
if self._short_stop_price is None or stop < self._short_stop_price:
self._short_stop_price = stop
return
if price >= high + steps_dist:
self.BuyMarket(abs(self.Position))
self._reset_short_state()
return
if short_count >= self.PyramidLimit:
tightened = price + step_dist
if self._short_stop_price is None or tightened < self._short_stop_price:
self._short_stop_price = tightened
def _reset_long_state(self):
self._long_entry_high = None
self._long_entry_low = None
self._long_stop_price = None
self._bullish_streak = 0
def _reset_short_state(self):
self._short_entry_high = None
self._short_entry_low = None
self._short_stop_price = None
self._bearish_streak = 0
def CreateClone(self):
return atr_step_trader_strategy()