Стратегия Backbone
Данная реализация переносит эксперта Backbone с платформы MetaTrader 5 на высокоуровневый API StockSharp. Алгоритм поочерёдно формирует длинные и короткие серии сделок, наращивает позицию по доле риска и защищает её фиксированными целями, дополняя сопровождением по трейлинг-стопу.
Основная идея
- Определение стартового направления. После запуска стратегия отслеживает максимумы и минимумы. Импульс, превышающий дистанцию
TrailingStopPips, задаёт первое рабочее направление. - Циклы по направлениям. Пока серия активна, торгуется только одна сторона. После полного закрытия всех позиций счётчик сбрасывается и логика готовится к сделкам в противоположную сторону.
- Масштабирование по риску. Каждое добавление позиций рассчитывает новый объём на основе капитала счёта, параметра
MaxRisk, лимитаMaxTradesи величины стоп-лосса — аналогично функцииVolв оригинальном советнике. - Защита позиции. Стоп-лосс и тейк-профит пересчитываются для усреднённой цены текущей серии. Трейлинг-стоп подтягивает защитный уровень, как только прибыль превышает заданный порог.
Параметры
| Параметр | Значение по умолчанию | Назначение |
|---|---|---|
MaxRisk |
0.5 | Доля капитала, доступная для одной направленной серии. |
MaxTrades |
10 | Максимальное количество усреднений в рамках цикла. |
TakeProfitPips |
170 | Расстояние до тейк-профита в пипсах. |
StopLossPips |
40 | Расстояние до защитного стоп-ордера в пипсах. |
TrailingStopPips |
300 | Порог для определения направления и для трейлинга. |
CandleType |
5-минутные свечи | Тип свечей, по которым выполняются расчёты. |
Пояснение по пипсу. Размер шага автоматически подстраивается по
PriceStep. Для инструментов с 3 или 5 десятичными знаками применяется коэффициент ×10, как в MetaTrader.
Логика торговли
- Обрабатываются только закрытые свечи, при этом стратегия должна быть готова к торгам (
IsFormedAndOnlineAndAllowTrading). - Пока направление не выбрано, обновляются экстремумы и проверяется пробой на величину
TrailingStopPips. - Для длинной серии:
- Открывается первая покупка, если прошлый цикл был коротким и позиция отсутствует.
- Дополнительные покупки разрешены, если текущая серия уже длинная и количество сделок меньше
MaxTrades. - Выход осуществляется по тейк-профиту, стоп-лоссу либо после подтяжки трейлинга.
- Для короткой серии выполняются зеркальные условия.
- После закрытия серии счётчики сбрасываются, и стратегия готовится к обратному движению.
Расчёт объёма
Объём каждой новой сделки вычисляется по формуле:
qty = equity * fraction / (pipSize * stopLoss)
где fraction = 1 / (MaxTrades / MaxRisk - openTrades)
Полученный объём округляется по шагу лота и ограничивается минимальным/максимальным значением. Если расчёт меньше допустимого минимума, используется минимальный объём. При отсутствии данных по капиталу применяется стандартный объём стратегии.
Управление выходом
- Стоп-лосс и тейк-профит. После каждого усреднения пересчитываются относительно средневзвешенной цены текущей серии.
- Трейлинг-стоп. Для длинных позиций стоп переносится на
Close - TrailingStopPips * pipSize, когда плавающая прибыль превышает заданную дистанцию. Для коротких позиций используется зеркальная логика.
Особенности
- StockSharp работает в модели неттинга, поэтому управление ведётся по совокупной позиции, а не по отдельным ордерам. Алгоритм повторяет оригинальную смену направлений, адаптированную под неттинговый учёт.
- Расчёты выполняются на закрытии свечи. Внутридневные движения меньше диапазона свечи не анализируются.
- Для корректной работы необходимо выбрать таймфрейм и инструмент с достаточным объёмом исторических данных, чтобы сформировать стартовые экстремумы.
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;
using System.Globalization;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Backbone strategy converted from the MQL5 expert advisor.
/// Alternates long and short series with risk-based scaling, stop-loss, take-profit, and trailing stop management.
/// </summary>
public class BackboneStrategy : Strategy
{
private readonly StrategyParam<decimal> _maxRisk;
private readonly StrategyParam<int> _maxTrades;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<DataType> _candleType;
private decimal _bidMax;
private decimal _askMin;
private int _lastDirection;
private int _currentDirection;
private int _longCount;
private int _shortCount;
private decimal _longAveragePrice;
private decimal _shortAveragePrice;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
private decimal _adjustedPoint;
/// <summary>
/// Maximum total risk fraction shared across all positions.
/// </summary>
public decimal MaxRisk
{
get => _maxRisk.Value;
set => _maxRisk.Value = value;
}
/// <summary>
/// Maximum number of stacked entries in one direction.
/// </summary>
public int MaxTrades
{
get => _maxTrades.Value;
set => _maxTrades.Value = value;
}
/// <summary>
/// Take-profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Trailing stop activation distance in pips.
/// </summary>
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Candle type used for the calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BackboneStrategy"/> class.
/// </summary>
public BackboneStrategy()
{
_maxRisk = Param(nameof(MaxRisk), 0.5m)
.SetGreaterThanZero()
.SetDisplay("Max Risk", "Maximum risk fraction shared across trades", "Risk");
_maxTrades = Param(nameof(MaxTrades), 1)
.SetGreaterThanZero()
.SetDisplay("Max Trades", "Maximum number of layered entries", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 170m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Distance for the take-profit target (pips)", "Risk");
_stopLossPips = Param(nameof(StopLossPips), 40m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Distance for the protective stop (pips)", "Risk");
_trailingStopPips = Param(nameof(TrailingStopPips), 300m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Distance for the trailing stop activation (pips)", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
_adjustedPoint = GetAdjustedPoint();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
// Wait for completed candles only.
if (candle.State != CandleStates.Finished)
return;
// Trade only when the strategy is fully operational.
// removed IsFormedAndOnlineAndAllowTrading for backtesting
if (_adjustedPoint <= 0m)
_adjustedPoint = GetAdjustedPoint();
UpdateExtremeLevels(candle);
if (_currentDirection == 1)
{
if (HandleLongExit(candle))
return;
}
else if (_currentDirection == -1)
{
if (HandleShortExit(candle))
return;
}
else
{
// Reset counters when all positions are closed.
ResetLongState();
ResetShortState();
}
if (ShouldEnterLong())
{
EnterLong(candle);
}
else if (ShouldEnterShort())
{
EnterShort(candle);
}
}
private void EnterLong(ICandleMessage candle)
{
var openPositions = _currentDirection == 1 ? _longCount : 0;
var qty = CalculateOrderVolume(openPositions);
if (qty <= 0m)
return;
if (_currentDirection == -1)
{
// Close the short series before switching sides.
if (Position > 0) SellMarket(Math.Abs(Position)); else if (Position < 0) BuyMarket(Math.Abs(Position));
ResetShortState();
_currentDirection = 0;
openPositions = 0;
}
BuyMarket(qty);
openPositions = Math.Max(0, openPositions) + 1;
_longCount = openPositions;
_currentDirection = 1;
var average = _longCount == 1
? candle.ClosePrice
: (_longAveragePrice * (_longCount - 1) + candle.ClosePrice) / _longCount;
_longAveragePrice = average;
if (StopLossPips > 0m && _adjustedPoint > 0m)
_longStop = average - StopLossPips * _adjustedPoint;
else
_longStop = null;
if (TakeProfitPips > 0m && _adjustedPoint > 0m)
_longTake = average + TakeProfitPips * _adjustedPoint;
else
_longTake = null;
_lastDirection = 1;
}
private void EnterShort(ICandleMessage candle)
{
var openPositions = _currentDirection == -1 ? _shortCount : 0;
var qty = CalculateOrderVolume(openPositions);
if (qty <= 0m)
return;
if (_currentDirection == 1)
{
// Close the long series before switching sides.
if (Position > 0) SellMarket(Math.Abs(Position)); else if (Position < 0) BuyMarket(Math.Abs(Position));
ResetLongState();
_currentDirection = 0;
openPositions = 0;
}
SellMarket(qty);
openPositions = Math.Max(0, openPositions) + 1;
_shortCount = openPositions;
_currentDirection = -1;
var average = _shortCount == 1
? candle.ClosePrice
: (_shortAveragePrice * (_shortCount - 1) + candle.ClosePrice) / _shortCount;
_shortAveragePrice = average;
if (StopLossPips > 0m && _adjustedPoint > 0m)
_shortStop = average + StopLossPips * _adjustedPoint;
else
_shortStop = null;
if (TakeProfitPips > 0m && _adjustedPoint > 0m)
_shortTake = average - TakeProfitPips * _adjustedPoint;
else
_shortTake = null;
_lastDirection = -1;
}
private bool HandleLongExit(ICandleMessage candle)
{
var exitTriggered = false;
if (_longTake.HasValue && candle.HighPrice >= _longTake.Value)
{
// Take-profit reached for the long series.
SellMarket(Math.Abs(Position));
exitTriggered = true;
}
else if (_longStop.HasValue && candle.LowPrice <= _longStop.Value)
{
// Stop-loss touched for the long series.
SellMarket(Math.Abs(Position));
exitTriggered = true;
}
else if (TrailingStopPips > 0m && StopLossPips > 0m && _longCount > 0 && _adjustedPoint > 0m)
{
var trailingDistance = TrailingStopPips * _adjustedPoint;
var profit = candle.ClosePrice - _longAveragePrice;
if (trailingDistance > 0m && profit > trailingDistance)
{
var newStop = candle.ClosePrice - trailingDistance;
if (!_longStop.HasValue || _longStop.Value < newStop)
_longStop = newStop;
}
}
if (exitTriggered)
{
ResetLongState();
_currentDirection = 0;
return true;
}
return false;
}
private bool HandleShortExit(ICandleMessage candle)
{
var exitTriggered = false;
if (_shortTake.HasValue && candle.LowPrice <= _shortTake.Value)
{
// Take-profit reached for the short series.
BuyMarket(Math.Abs(Position));
exitTriggered = true;
}
else if (_shortStop.HasValue && candle.HighPrice >= _shortStop.Value)
{
// Stop-loss touched for the short series.
BuyMarket(Math.Abs(Position));
exitTriggered = true;
}
else if (TrailingStopPips > 0m && StopLossPips > 0m && _shortCount > 0 && _adjustedPoint > 0m)
{
var trailingDistance = TrailingStopPips * _adjustedPoint;
var profit = _shortAveragePrice - candle.ClosePrice;
if (trailingDistance > 0m && profit > trailingDistance)
{
var newStop = candle.ClosePrice + trailingDistance;
if (!_shortStop.HasValue || _shortStop.Value > newStop)
_shortStop = newStop;
}
}
if (exitTriggered)
{
ResetShortState();
_currentDirection = 0;
return true;
}
return false;
}
private bool ShouldEnterLong()
{
var openPositions = _currentDirection == 1 ? _longCount : 0;
if (MaxTrades <= 0)
return false;
var firstEntry = _lastDirection == -1 && openPositions == 0;
var addEntry = _lastDirection == 1 && openPositions > 0 && openPositions < MaxTrades;
return firstEntry || addEntry;
}
private bool ShouldEnterShort()
{
var openPositions = _currentDirection == -1 ? _shortCount : 0;
if (MaxTrades <= 0)
return false;
var firstEntry = _lastDirection == 1 && openPositions == 0;
var addEntry = _lastDirection == -1 && openPositions > 0 && openPositions < MaxTrades;
return firstEntry || addEntry;
}
private decimal CalculateOrderVolume(int openPositions)
{
var defaultVolume = Volume > 0m ? Volume : 1m;
var minVolume = Security?.MinVolume ?? defaultVolume;
var volumeStep = Security?.VolumeStep ?? 0m;
var maxVolume = Security?.MaxVolume;
if (minVolume <= 0m)
minVolume = defaultVolume;
if (MaxTrades <= 0 || MaxRisk <= 0m)
return minVolume;
var denominatorBase = (decimal)MaxTrades / MaxRisk;
var denominator = denominatorBase - openPositions;
if (denominator <= 0m)
return 0m;
var fraction = 1m / denominator;
var equity = Portfolio?.CurrentValue ?? 0m;
if (equity <= 0m)
return 0m;
var pip = _adjustedPoint;
if (pip <= 0m)
{
var priceStep = Security?.PriceStep ?? 0m;
pip = priceStep > 0m ? priceStep : 1m;
}
var stopLoss = StopLossPips > 0m ? StopLossPips : 1m;
var riskPerUnit = stopLoss * pip;
if (riskPerUnit <= 0m)
return minVolume;
var qty = equity * fraction / riskPerUnit;
if (volumeStep > 0m)
qty = Math.Floor(qty / volumeStep) * volumeStep;
if (qty < minVolume)
qty = minVolume;
if (maxVolume.HasValue && maxVolume.Value > 0m && qty > maxVolume.Value)
qty = maxVolume.Value;
return qty;
}
private void UpdateExtremeLevels(ICandleMessage candle)
{
if (_lastDirection != 0)
return;
var trailingDistance = TrailingStopPips * _adjustedPoint;
if (trailingDistance <= 0m)
return;
if (candle.HighPrice > _bidMax)
_bidMax = candle.HighPrice;
if (candle.LowPrice < _askMin)
_askMin = candle.LowPrice;
if (_bidMax != decimal.MinValue && candle.LowPrice < _bidMax - trailingDistance)
{
_lastDirection = -1;
return;
}
if (_askMin != decimal.MaxValue && candle.HighPrice > _askMin + trailingDistance)
_lastDirection = 1;
}
private decimal GetAdjustedPoint()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 1m;
var decimals = CountDecimals(step);
if (decimals == 3 || decimals == 5)
return step * 10m;
return step;
}
private static int CountDecimals(decimal value)
{
value = Math.Abs(value);
var text = value.ToString(CultureInfo.InvariantCulture);
var index = text.IndexOf('.');
return index < 0 ? 0 : text.Length - index - 1;
}
private void ResetState()
{
_bidMax = decimal.MinValue;
_askMin = decimal.MaxValue;
_lastDirection = 0;
_currentDirection = 0;
ResetLongState();
ResetShortState();
_adjustedPoint = 0m;
}
private void ResetLongState()
{
_longCount = 0;
_longAveragePrice = 0m;
_longStop = null;
_longTake = null;
}
private void ResetShortState()
{
_shortCount = 0;
_shortAveragePrice = 0m;
_shortStop = null;
_shortTake = null;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class backbone_strategy(Strategy):
def __init__(self):
super(backbone_strategy, self).__init__()
self._max_risk = self.Param("MaxRisk", 0.5)
self._max_trades = self.Param("MaxTrades", 1)
self._take_profit_pips = self.Param("TakeProfitPips", 170.0)
self._stop_loss_pips = self.Param("StopLossPips", 40.0)
self._trailing_stop_pips = self.Param("TrailingStopPips", 300.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1)))
self._bid_max = -1e18
self._ask_min = 1e18
self._last_direction = 0
self._current_direction = 0
self._long_count = 0
self._short_count = 0
self._long_avg_price = 0.0
self._short_avg_price = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._adjusted_point = 0.0
@property
def MaxRisk(self):
return self._max_risk.Value
@MaxRisk.setter
def MaxRisk(self, value):
self._max_risk.Value = value
@property
def MaxTrades(self):
return self._max_trades.Value
@MaxTrades.setter
def MaxTrades(self, value):
self._max_trades.Value = value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@TakeProfitPips.setter
def TakeProfitPips(self, value):
self._take_profit_pips.Value = value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@StopLossPips.setter
def StopLossPips(self, value):
self._stop_loss_pips.Value = value
@property
def TrailingStopPips(self):
return self._trailing_stop_pips.Value
@TrailingStopPips.setter
def TrailingStopPips(self, value):
self._trailing_stop_pips.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(backbone_strategy, self).OnStarted2(time)
self._reset_state()
self._adjusted_point = self._get_adjusted_point()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
# protection handled manually via SL/TP/trailing
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._adjusted_point <= 0.0:
self._adjusted_point = self._get_adjusted_point()
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
self._update_extreme_levels(candle)
if self._current_direction == 1:
if self._handle_long_exit(candle):
return
elif self._current_direction == -1:
if self._handle_short_exit(candle):
return
else:
self._reset_long_state()
self._reset_short_state()
if self._should_enter_long():
self._enter_long(candle)
elif self._should_enter_short():
self._enter_short(candle)
def _enter_long(self, candle):
close = float(candle.ClosePrice)
if self._current_direction == -1:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._reset_short_state()
self._current_direction = 0
self.BuyMarket()
self._long_count += 1
self._current_direction = 1
if self._long_count == 1:
self._long_avg_price = close
else:
self._long_avg_price = (self._long_avg_price * (self._long_count - 1) + close) / self._long_count
sl_pips = float(self.StopLossPips)
tp_pips = float(self.TakeProfitPips)
if sl_pips > 0.0 and self._adjusted_point > 0.0:
self._long_stop = self._long_avg_price - sl_pips * self._adjusted_point
else:
self._long_stop = None
if tp_pips > 0.0 and self._adjusted_point > 0.0:
self._long_take = self._long_avg_price + tp_pips * self._adjusted_point
else:
self._long_take = None
self._last_direction = 1
def _enter_short(self, candle):
close = float(candle.ClosePrice)
if self._current_direction == 1:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._reset_long_state()
self._current_direction = 0
self.SellMarket()
self._short_count += 1
self._current_direction = -1
if self._short_count == 1:
self._short_avg_price = close
else:
self._short_avg_price = (self._short_avg_price * (self._short_count - 1) + close) / self._short_count
sl_pips = float(self.StopLossPips)
tp_pips = float(self.TakeProfitPips)
if sl_pips > 0.0 and self._adjusted_point > 0.0:
self._short_stop = self._short_avg_price + sl_pips * self._adjusted_point
else:
self._short_stop = None
if tp_pips > 0.0 and self._adjusted_point > 0.0:
self._short_take = self._short_avg_price - tp_pips * self._adjusted_point
else:
self._short_take = None
self._last_direction = -1
def _handle_long_exit(self, candle):
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
exit_triggered = False
if self._long_take is not None and high >= self._long_take:
self.SellMarket()
exit_triggered = True
elif self._long_stop is not None and low <= self._long_stop:
self.SellMarket()
exit_triggered = True
else:
trail_pips = float(self.TrailingStopPips)
sl_pips = float(self.StopLossPips)
if trail_pips > 0.0 and sl_pips > 0.0 and self._long_count > 0 and self._adjusted_point > 0.0:
trail_distance = trail_pips * self._adjusted_point
profit = close - self._long_avg_price
if trail_distance > 0.0 and profit > trail_distance:
new_stop = close - trail_distance
if self._long_stop is None or self._long_stop < new_stop:
self._long_stop = new_stop
if exit_triggered:
self._reset_long_state()
self._current_direction = 0
return True
return False
def _handle_short_exit(self, candle):
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
exit_triggered = False
if self._short_take is not None and low <= self._short_take:
self.BuyMarket()
exit_triggered = True
elif self._short_stop is not None and high >= self._short_stop:
self.BuyMarket()
exit_triggered = True
else:
trail_pips = float(self.TrailingStopPips)
sl_pips = float(self.StopLossPips)
if trail_pips > 0.0 and sl_pips > 0.0 and self._short_count > 0 and self._adjusted_point > 0.0:
trail_distance = trail_pips * self._adjusted_point
profit = self._short_avg_price - close
if trail_distance > 0.0 and profit > trail_distance:
new_stop = close + trail_distance
if self._short_stop is None or self._short_stop > new_stop:
self._short_stop = new_stop
if exit_triggered:
self._reset_short_state()
self._current_direction = 0
return True
return False
def _should_enter_long(self):
open_positions = self._long_count if self._current_direction == 1 else 0
max_trades = int(self.MaxTrades)
if max_trades <= 0:
return False
first_entry = self._last_direction == -1 and open_positions == 0
add_entry = self._last_direction == 1 and open_positions > 0 and open_positions < max_trades
return first_entry or add_entry
def _should_enter_short(self):
open_positions = self._short_count if self._current_direction == -1 else 0
max_trades = int(self.MaxTrades)
if max_trades <= 0:
return False
first_entry = self._last_direction == 1 and open_positions == 0
add_entry = self._last_direction == -1 and open_positions > 0 and open_positions < max_trades
return first_entry or add_entry
def _update_extreme_levels(self, candle):
if self._last_direction != 0:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
trail_pips = float(self.TrailingStopPips)
trail_distance = trail_pips * self._adjusted_point
if trail_distance <= 0.0:
return
if high > self._bid_max:
self._bid_max = high
if low < self._ask_min:
self._ask_min = low
if self._bid_max > -1e17 and low < self._bid_max - trail_distance:
self._last_direction = -1
return
if self._ask_min < 1e17 and high > self._ask_min + trail_distance:
self._last_direction = 1
def _get_adjusted_point(self):
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
if step <= 0.0:
return 1.0
return step
def _reset_state(self):
self._bid_max = -1e18
self._ask_min = 1e18
self._last_direction = 0
self._current_direction = 0
self._reset_long_state()
self._reset_short_state()
self._adjusted_point = 0.0
def _reset_long_state(self):
self._long_count = 0
self._long_avg_price = 0.0
self._long_stop = None
self._long_take = None
def _reset_short_state(self):
self._short_count = 0
self._short_avg_price = 0.0
self._short_stop = None
self._short_take = None
def OnReseted(self):
super(backbone_strategy, self).OnReseted()
self._reset_state()
def CreateClone(self):
return backbone_strategy()