Esta estratégia reproduz o comportamento central do consultor especialista original Backbone do MQL5 usando a API de alto nível do StockSharp. Ela alterna entre ciclos de trading de alta e baixa, escala em posições de acordo com uma fração de risco e protege as operações abertas com alvos fixos junto com um trailing stop.
Ideia central
Detecção de direção inicial – a estratégia rastreia a máxima mais alta e a mínima mais baixa após o início. Um movimento maior que a distância do trailing stop longe de qualquer extremo define qual lado operará primeiro.
Ciclos direcionais – após um ciclo começar, o algoritmo opera apenas nessa direção até que todas as posições sejam fechadas. Quando a última posição sai, ele imediatamente inverte e se prepara para o ciclo oposto.
Escalonamento baseado em risco – cada entrada adicional usa um volume dinâmico derivado do capital da conta, a fração MaxRisk, o limite configurado MaxTrades e a distância do stop-loss. Isso imita a função de dimensionamento de lotes do EA original.
Saídas protetoras – cada entrada recalcula um nível de stop-loss e take-profit em torno do preço médio ponderado por volume do ciclo atual. Um trailing stop ajusta o stop protetor sempre que o lucro não realizado excede a distância de trailing configurada.
Parâmetros
Parâmetro
Valores padrão
Descrição
MaxRisk
0.5
Fração do capital da conta disponível para todas as posições na direção atual.
MaxTrades
10
Número máximo de entradas sequenciais por ciclo direcional.
TakeProfitPips
170
Distância (em pips) entre a média de entrada e o alvo de take-profit.
StopLossPips
40
Distância (em pips) entre a média de entrada e o stop protetor.
TrailingStopPips
300
Distância (em pips) usada tanto para determinar a direção inicial quanto para seguir os lucros.
CandleType
Período de 5 minutos
Tipo de vela usado para avaliação de sinais.
Definição de pip – a estratégia ajusta automaticamente o tamanho do pip com base no instrumento PriceStep. Símbolos cotados com 3 ou 5 casas decimais usam um multiplicador de 10×, replicando o tratamento de pip original do MetaTrader.
Lógica de trading
Aguardar uma vela finalizada. Pular o processamento enquanto a estratégia está aquecendo ou o trading está desabilitado.
Atualizar os preços extremos enquanto nenhuma direção foi escolhida ainda. Uma vez que a máxima rompe para cima (em mais de TrailingStopPips) o primeiro ciclo será vendido; se a mínima romper para baixo, o primeiro ciclo será comprado.
Enquanto o ciclo é comprado:
Adicionar uma nova entrada comprada quando (a) o ciclo anterior foi vendido e não há posições compradas abertas, ou (b) o ciclo anterior também foi comprado e o número de comprados abertos está abaixo de MaxTrades.
Sair de todo o ciclo comprado quando o take-profit ou stop-loss é atingido, ou quando o trailing stop eleva o nível protetor acima do stop atual.
Enquanto o ciclo é vendido, as mesmas regras se aplicam com condições invertidas.
Após um ciclo fechar, reiniciar seus contadores e aguardar a configuração oposta.
Dimensionamento de posição
O tamanho de posição para cada nova entrada é calculado como:
A quantidade é então alinhada ao passo de volume do instrumento e limitada dentro dos limites mínimo/máximo de volume. Se o tamanho necessário cair abaixo do mínimo permitido, o mínimo é usado. Quando informações de capital não estão disponíveis, o volume de estratégia padrão atua como fallback.
Gestão de saída
Stop-loss / take-profit – recalculado sempre que uma nova ordem é adicionada para que todas as operações no ciclo atual compartilhem os mesmos níveis combinados baseados no preço médio de entrada.
Trailing stop – para um ciclo comprado, o stop se move para Close - TrailingStopPips * pipSize assim que o lucro não realizado excede esse limiar. O trailing do lado vendido é tratado simetricamente.
Notas e limitações
O StockSharp executa operações em um ambiente de netagem, portanto cada ciclo direcional gerencia a posição combinada em vez de tickets individuais. A lógica alternada e a fórmula de risco reproduzem o comportamento original enquanto se adequam ao modelo de API.
A estratégia depende de velas concluídas. Movimentos intrabar menores que o intervalo da vela não são avaliados.
Garantir que o tipo de vela selecionado e o instrumento produzam dados suficientes para construir os extremos iniciais antes de esperar por operações.
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()