ATR Step Trader 策略
概览
ATR Step Trader 策略移植自 MetaTrader5 专家顾问 atrTrader.mq5。策略利用快/慢简单移动平均线进行趋势过滤,并以平均真实波幅(ATR)来衡量入场、加仓以及止损距离。StockSharp 版本沿用原始 EA 的思路:仅在蜡烛收盘后运算、要求趋势连续确认若干根蜡烛,并且所有价差都以 ATR 倍数表示,以便适应不同市场的波动性。
指标与数据
- 简单移动平均线(SMA):
FastPeriod与SlowPeriod两个参数定义趋势过滤器,均基于订阅的蜡烛序列计算。 - 平均真实波幅(ATR):
AverageTrueRange指标(周期为AtrPeriod)把波动转化为价格距离,所有突破、加仓和止损都使用 ATR 倍数。 - 最高/最低通道:
Highest与Lowest指标跟踪最近MomentumPeriod根蜡烛的高点和低点,等价于 MQL 中的iHighest/iLowest调用。 - 时间框架:默认订阅 1 小时蜡烛(
TimeSpan.FromHours(1)),对应原 EA 的PERIOD_CURRENT。通过参数CandleType可切换到任意时间框架。
入场规则
- 等待蜡烛收盘,未完成的蜡烛不参与运算,以保持与 MT5 中
OnTick + iTime逻辑一致。 - 更新多头与空头的连续计数器:当快线高于慢线时,多头计数器递增并重置空头计数;当快线低于慢线时,空头计数递增并重置多头计数;持平则两个计数器都递增。
- 多头计数器达到
MomentumPeriod后,确认收盘价仍然低于最近高点至少StepMultiplier * ATR,触发买入。 - 空头计数器达到
MomentumPeriod后,确认收盘价仍然高于最近低点至少StepMultiplier * ATR,触发卖出。 - 首次建仓会记录当前方向的最高/最低建仓价,并设置初始波动止损(
StepMultiplier * StopMultiplier * ATR),以便后续层级继续参考。
持仓管理
- 金字塔加仓:当持仓数量尚未达到
PyramidLimit时,若价格相对参考极值移动了± StepsMultiplier * ATR,则再加一层仓位。这与原 EA 中的 “Steps” 机制一致,既能顺势加仓也能在回撤时摊薄。 - 保护性止损:新仓位的初始止损位于
StepMultiplier * StopMultiplier * ATR的距离处。当仓位数达到上限时,止损会收紧到StepMultiplier * ATR,模拟原 EA 在持有三单时的跟踪止损逻辑。 - 不利退出:若价格突破最近层级的边界
StepsMultiplier * ATR,策略立即以市价平掉该方向的全部仓位。 - 状态重置:全部离场后会清空连续计数器与止损参考,等待新的趋势条件再次成立。
参数
| 分组 | 名称 | 说明 | 默认值 |
|---|---|---|---|
| Trend Filter | FastPeriod |
快速 SMA 周期。 | 70 |
| Trend Filter | SlowPeriod |
慢速 SMA 周期。 | 180 |
| Trend Filter | MomentumPeriod |
需要连续确认的蜡烛数量。 | 50 |
| Volatility | AtrPeriod |
ATR 计算窗口。 | 100 |
| Entry Logic | StepMultiplier |
初次突破的 ATR 倍数阈值。 | 4 |
| Entry Logic | StepsMultiplier |
每层加仓之间的 ATR 间距。 | 2 |
| Risk Management | StopMultiplier |
初始止损相对于步长的额外倍数。 | 3 |
| Position Sizing | PyramidLimit |
单方向允许的最大仓位层数。 | 3 |
| Trading | TradeVolume |
每次下单的数量(使用策略 Volume)。 |
1 |
| General | CandleType |
计算所使用的蜡烛类型。 | TimeFrame(1h) |
实用提示
- 策略通过
TradeVolume设置下单量,等价于 StockSharp 中的Volume属性,使用前请与品种合约乘数匹配。 - 代码使用市价单(等同 MT5 中的
CTrade.Buy/Sell)。若品种流动性不足,可自行改成限价或止损单。 - 内部维护的最高/最低参考值复刻了 MQL 中的
h_price和l_price,用于判断何时加仓或整体退出。 - 原 EA 为每一单独立设置止损。移植版在策略层面统一管理止损,因此所有层级会同时退出,减少了交易通道对止损订单的依赖。
- 在真实账户运行前务必回测或模拟。虽然 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()