Estrategia E-Skoch-Open (Port de StockSharp)
Descripción general
La estrategia E-Skoch-Open replica el asesor experto original de MetaTrader 5 que opera un patrón simple de tres velas. La implementación de StockSharp procesa velas completadas, evalúa reversiones de momentum en los cierres recientes y abre una nueva posición cuando aparece la configuración requerida. El riesgo se controla con offsets de stop-loss/take-profit medidos en puntos ajustados (pips) y un objetivo de crecimiento del capital que puede aplanar todas las posiciones abiertas. El dimensionamiento de posición sigue un esquema de martingala: después de una operación perdedora el siguiente tamaño de orden se multiplica por 1.6, mientras que las operaciones rentables reinician el volumen al valor inicial.
Lógica de trading
- Trabaja con el marco temporal definido por el parámetro
CandleType(por defecto: 1 hora). - Espera hasta que haya al menos tres velas completadas disponibles.
- Configuración de compra: si
Close[n-3] > Close[n-2]yClose[n-1] < Close[n-2], y las operaciones largas están habilitadas. - Configuración de venta: si
Close[n-3] > Close[n-2]yClose[n-2] < Close[n-1], y las operaciones cortas están habilitadas. - Si
CloseOnOppositeSignalestá habilitado, recibir una señal opuesta cierra la posición existente inmediatamente y omite nuevas entradas para la barra actual. - Para cada nueva posición la estrategia adjunta niveles estáticos de stop-loss y take-profit calculados desde el cierre actual y la distancia configurada en puntos ajustados. Cuando el máximo/mínimo de una vela completada alcanza uno de estos niveles la posición se cierra.
- La estrategia verifica continuamente el capital de la cuenta. Cuando el crecimiento del capital relativo al último momento plano supera
TargetProfitPercent, se cierran todas las posiciones. - Después de que una operación cierra con pérdida, el siguiente volumen de orden se multiplica por 1.6. Después de una operación rentable el volumen vuelve al tamaño inicial. Los volúmenes se normalizan usando las restricciones del instrumento (
VolumeStep,VolumeMin,VolumeMax).
Parámetros
| Parámetro | Descripción |
|---|---|
CandleType |
Marco temporal usado para la detección de patrones. Funciona con cualquier vela soportada por StockSharp. |
InitialOrderVolume |
Tamaño de lote base para la primera operación en una secuencia (por defecto: 0.01). |
StopLossPoints |
Distancia del stop-loss expresada en puntos ajustados. Para instrumentos de 5 o 3 dígitos el valor del punto es PriceStep * 10, de lo contrario PriceStep. |
TakeProfitPoints |
Distancia del take-profit usando la misma convención de punto ajustado. |
EnableBuySignals / EnableSellSignals |
Activar o desactivar entradas largas o cortas. |
MaxBuyTrades / MaxSellTrades |
Número máximo de operaciones consecutivas permitidas por dirección (-1 elimina el límite). El port mantiene como máximo una posición por dirección por defecto. |
TargetProfitPercent |
Ganancia porcentual del capital que desencadena el cierre de todas las posiciones (por defecto: 1.2%). |
CloseOnOppositeSignal |
Si está habilitado, una señal en la dirección opuesta fuerza una posición plana antes de considerar nuevas operaciones. |
Notas de gestión de riesgo
- Los niveles de stop-loss y take-profit se simulan desde los extremos de las velas. En trading en vivo la ejecución intrabar puede diferir de MetaTrader donde las órdenes protectoras están registradas en el servidor.
- El multiplicador de martingala (1.6) puede hacer crecer los volúmenes rápidamente durante drawdowns. Asegurarse de que los límites del instrumento (
VolumeMax) y el capital del portafolio puedan soportar la posición más grande esperada. - El bloqueo de ganancias basado en capital funciona solo cuando la información del portafolio está disponible a través de
Portfolio.CurrentValue.
Consejos de uso
- Ajustar
CandleTypepara que coincida con el marco temporal usado en el asesor experto original. - Ajustar
StopLossPoints/TakeProfitPointsa la volatilidad del instrumento; son basados en pips gracias al cálculo del punto ajustado. - Deshabilitar una dirección si el hedging no está permitido por el broker o política de riesgo.
- Vigilar el objetivo de capital y la configuración de martingala al ejecutar pruebas largas para evitar liquidaciones inesperadas.
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>
/// Port of the "E-Skoch-Open" MetaTrader strategy using StockSharp high level API.
/// The strategy reacts to a three-candle closing price pattern and applies
/// martingale position sizing together with equity based stops.
/// </summary>
public class ESkochOpenStrategy : Strategy
{
private readonly StrategyParam<decimal> _martingaleMultiplier;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<bool> _enableBuySignals;
private readonly StrategyParam<bool> _enableSellSignals;
private readonly StrategyParam<decimal> _targetProfitPercent;
private readonly StrategyParam<bool> _closeOnOppositeSignal;
private readonly StrategyParam<int> _maxBuyTrades;
private readonly StrategyParam<int> _maxSellTrades;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _initialOrderVolume;
private decimal _pointValue;
private decimal _currentVolume;
private decimal _entryEquity;
private decimal _baselineEquity;
private bool _positionTracked;
private decimal? _closeMinus1;
private decimal? _closeMinus2;
private decimal? _closeMinus3;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
private int _activeLongEntries;
private int _activeShortEntries;
private int _previousPatternSignal;
/// <summary>
/// Stop loss distance expressed in adjusted points (default: 130).
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit distance expressed in adjusted points (default: 200).
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Enables long entries created by the pattern.
/// </summary>
public bool EnableBuySignals
{
get => _enableBuySignals.Value;
set => _enableBuySignals.Value = value;
}
/// <summary>
/// Enables short entries created by the pattern.
/// </summary>
public bool EnableSellSignals
{
get => _enableSellSignals.Value;
set => _enableSellSignals.Value = value;
}
/// <summary>
/// Equity percentage gain that triggers closing every open position.
/// </summary>
public decimal TargetProfitPercent
{
get => _targetProfitPercent.Value;
set => _targetProfitPercent.Value = value;
}
/// <summary>
/// When true, opposite trades immediately flatten the existing position.
/// </summary>
public bool CloseOnOppositeSignal
{
get => _closeOnOppositeSignal.Value;
set => _closeOnOppositeSignal.Value = value;
}
/// <summary>
/// Maximum number of consecutive long entries (-1 disables the limit).
/// </summary>
public int MaxBuyTrades
{
get => _maxBuyTrades.Value;
set => _maxBuyTrades.Value = value;
}
/// <summary>
/// Maximum number of consecutive short entries (-1 disables the limit).
/// </summary>
public int MaxSellTrades
{
get => _maxSellTrades.Value;
set => _maxSellTrades.Value = value;
}
/// <summary>
/// Candle type used for pattern detection.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Base order volume used for the first trade in a sequence.
/// </summary>
public decimal InitialOrderVolume
{
get => _initialOrderVolume.Value;
set => _initialOrderVolume.Value = value;
}
/// <summary>
/// Volume multiplier after losses (martingale).
/// </summary>
public decimal MartingaleMultiplier
{
get => _martingaleMultiplier.Value;
set => _martingaleMultiplier.Value = value;
}
/// <summary>
/// Creates the strategy parameters with defaults similar to the MQL version.
/// </summary>
public ESkochOpenStrategy()
{
_martingaleMultiplier = Param(nameof(MartingaleMultiplier), 1.6m)
.SetGreaterThanZero()
.SetDisplay("Martingale Mult", "Volume multiplier after losses", "Risk");
_stopLossPoints = Param(nameof(StopLossPoints), 130m)
.SetDisplay("Stop Loss Points", "Loss distance measured in adjusted points", "Risk")
.SetNotNegative();
_takeProfitPoints = Param(nameof(TakeProfitPoints), 200m)
.SetDisplay("Take Profit Points", "Profit distance measured in adjusted points", "Risk")
.SetNotNegative();
_enableBuySignals = Param(nameof(EnableBuySignals), true)
.SetDisplay("Enable Buy", "Allow opening long positions", "Trading");
_enableSellSignals = Param(nameof(EnableSellSignals), true)
.SetDisplay("Enable Sell", "Allow opening short positions", "Trading");
_targetProfitPercent = Param(nameof(TargetProfitPercent), 1.2m)
.SetDisplay("Target Profit %", "Close all positions after reaching this equity growth", "Risk")
.SetNotNegative();
_closeOnOppositeSignal = Param(nameof(CloseOnOppositeSignal), false)
.SetDisplay("Close On Opposite", "Close open positions when an opposite signal appears", "Trading");
_maxBuyTrades = Param(nameof(MaxBuyTrades), 1)
.SetDisplay("Max Long Trades", "Maximum concurrent long trades", "Risk");
_maxSellTrades = Param(nameof(MaxSellTrades), 1)
.SetDisplay("Max Short Trades", "Maximum concurrent short trades", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for pattern recognition", "Data");
_initialOrderVolume = Param(nameof(InitialOrderVolume), 0.01m)
.SetDisplay("Initial Volume", "Volume of the first trade", "Trading")
.SetGreaterThanZero();
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closeMinus1 = null;
_closeMinus2 = null;
_closeMinus3 = null;
_longStop = null;
_longTake = null;
_shortStop = null;
_shortTake = null;
_activeLongEntries = 0;
_activeShortEntries = 0;
_positionTracked = false;
_pointValue = 0m;
_currentVolume = 0m;
_entryEquity = 0m;
_baselineEquity = 0m;
_previousPatternSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = InitialOrderVolume;
_pointValue = CalculatePointValue();
_currentVolume = NormalizeVolume(InitialOrderVolume);
var equity = Portfolio?.CurrentValue ?? 0m;
_baselineEquity = equity;
_entryEquity = equity;
_positionTracked = Position != 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
{
return;
}
CheckEquityTarget();
if (CheckProtection(candle))
{
// Skip new entries if a protection exit already triggered on this bar.
UpdateCloses(candle.ClosePrice);
return;
}
// removed IsFormedAndOnlineAndAllowTrading check for backtesting
if (_closeMinus1.HasValue && _closeMinus2.HasValue && _closeMinus3.HasValue)
{
var close1 = _closeMinus1.Value;
var close2 = _closeMinus2.Value;
var close3 = _closeMinus3.Value;
var buySignal = close3 > close2 && close1 < close2;
var sellSignal = close3 > close2 && close2 < close1;
var patternSignal = buySignal ? 1 : sellSignal ? -1 : 0;
if (buySignal && patternSignal != _previousPatternSignal)
{
HandleBuySignal(candle);
}
if (sellSignal && patternSignal != _previousPatternSignal)
{
HandleSellSignal(candle);
}
_previousPatternSignal = patternSignal;
}
else
{
_previousPatternSignal = 0;
}
UpdateCloses(candle.ClosePrice);
}
private void HandleBuySignal(ICandleMessage candle)
{
if (!EnableBuySignals)
{
return;
}
if (CloseOnOppositeSignal && Position < 0)
{
BuyMarket(Math.Abs(Position));
return;
}
if (Position > 0)
{
return;
}
if (MaxBuyTrades != -1 && _activeLongEntries >= MaxBuyTrades)
{
return;
}
var volume = NormalizeVolume(_currentVolume);
if (volume <= 0m)
{
return;
}
BuyMarket(volume);
_activeLongEntries++;
_positionTracked = true;
_entryEquity = Portfolio?.CurrentValue ?? _entryEquity;
SetupProtection(true, candle.ClosePrice);
}
private void HandleSellSignal(ICandleMessage candle)
{
if (!EnableSellSignals)
{
return;
}
if (CloseOnOppositeSignal && Position > 0)
{
SellMarket(Math.Abs(Position));
return;
}
if (Position < 0)
{
return;
}
if (MaxSellTrades != -1 && _activeShortEntries >= MaxSellTrades)
{
return;
}
var volume = NormalizeVolume(_currentVolume);
if (volume <= 0m)
{
return;
}
SellMarket(volume);
_activeShortEntries++;
_positionTracked = true;
_entryEquity = Portfolio?.CurrentValue ?? _entryEquity;
SetupProtection(false, candle.ClosePrice);
}
private bool CheckProtection(ICandleMessage candle)
{
if (Position > 0)
{
if (_longStop.HasValue && candle.LowPrice <= _longStop.Value)
{
SellMarket(Math.Abs(Position));
ResetProtection();
return true;
}
if (_longTake.HasValue && candle.HighPrice >= _longTake.Value)
{
SellMarket(Math.Abs(Position));
ResetProtection();
return true;
}
}
else if (Position < 0)
{
if (_shortStop.HasValue && candle.HighPrice >= _shortStop.Value)
{
BuyMarket(Math.Abs(Position));
ResetProtection();
return true;
}
if (_shortTake.HasValue && candle.LowPrice <= _shortTake.Value)
{
BuyMarket(Math.Abs(Position));
ResetProtection();
return true;
}
}
return false;
}
private void SetupProtection(bool isLong, decimal referencePrice)
{
var point = _pointValue;
if (point <= 0m)
{
point = Security?.PriceStep ?? 0m;
}
if (isLong)
{
_longStop = StopLossPoints > 0m ? referencePrice - StopLossPoints * point : null;
_longTake = TakeProfitPoints > 0m ? referencePrice + TakeProfitPoints * point : null;
_shortStop = null;
_shortTake = null;
}
else
{
_shortStop = StopLossPoints > 0m ? referencePrice + StopLossPoints * point : null;
_shortTake = TakeProfitPoints > 0m ? referencePrice - TakeProfitPoints * point : null;
_longStop = null;
_longTake = null;
}
}
private void ResetProtection()
{
_longStop = null;
_longTake = null;
_shortStop = null;
_shortTake = null;
}
private void UpdateCloses(decimal close)
{
_closeMinus3 = _closeMinus2;
_closeMinus2 = _closeMinus1;
_closeMinus1 = close;
}
private void CheckEquityTarget()
{
if (TargetProfitPercent <= 0m)
{
return;
}
if (_baselineEquity <= 0m)
{
return;
}
var equity = Portfolio?.CurrentValue ?? 0m;
var growthPercent = (equity - _baselineEquity) / _baselineEquity * 100m;
if (growthPercent >= TargetProfitPercent)
{
CloseAllPositions();
}
}
private void CloseAllPositions()
{
if (Position > 0)
{
SellMarket(Math.Abs(Position));
}
else if (Position < 0)
{
BuyMarket(Math.Abs(Position));
}
}
/// <inheritdoc />
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
if (Position == 0)
{
if (_positionTracked)
{
var equity = Portfolio?.CurrentValue ?? _baselineEquity;
if (equity >= _entryEquity)
{
_currentVolume = NormalizeVolume(InitialOrderVolume);
}
else
{
_currentVolume = NormalizeVolume(_currentVolume * MartingaleMultiplier);
}
_baselineEquity = equity;
_positionTracked = false;
ResetProtection();
_activeLongEntries = 0;
_activeShortEntries = 0;
}
else
{
_baselineEquity = Portfolio?.CurrentValue ?? _baselineEquity;
}
}
else
{
_positionTracked = true;
_entryEquity = Portfolio?.CurrentValue ?? _entryEquity;
}
}
private decimal NormalizeVolume(decimal volume)
{
if (volume <= 0m)
{
return 0m;
}
var sec = Security;
if (sec != null)
{
var step = sec.VolumeStep ?? 0m;
if (step > 0m)
{
volume = Math.Floor(volume / step) * step;
}
var min = sec.MinVolume ?? 0m;
if (min > 0m && volume < min)
{
volume = min;
}
var max = sec.MaxVolume ?? 0m;
if (max > 0m && volume > max)
{
volume = max;
}
}
return volume;
}
private decimal CalculatePointValue()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
{
return 0m;
}
var decimals = CountDecimals(step);
return decimals == 3 || decimals == 5 ? step * 10m : step;
}
private static int CountDecimals(decimal value)
{
value = Math.Abs(value);
var decimals = 0;
while (value != Math.Truncate(value) && decimals < 10)
{
value *= 10m;
decimals++;
}
return decimals;
}
}
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.Strategies import Strategy
class e_skoch_open_strategy(Strategy):
"""
Port of the E-Skoch-Open MetaTrader strategy.
Reacts to a three-candle closing price pattern and applies
martingale position sizing together with equity-based stops.
"""
def __init__(self):
super(e_skoch_open_strategy, self).__init__()
self._martingale_multiplier = self.Param("MartingaleMultiplier", 1.6) \
.SetDisplay("Martingale Mult", "Volume multiplier after losses", "Risk")
self._stop_loss_points = self.Param("StopLossPoints", 130.0) \
.SetDisplay("Stop Loss Points", "Loss distance in adjusted points", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 200.0) \
.SetDisplay("Take Profit Points", "Profit distance in adjusted points", "Risk")
self._enable_buy = self.Param("EnableBuySignals", True) \
.SetDisplay("Enable Buy", "Allow opening long positions", "Trading")
self._enable_sell = self.Param("EnableSellSignals", True) \
.SetDisplay("Enable Sell", "Allow opening short positions", "Trading")
self._target_profit_pct = self.Param("TargetProfitPercent", 1.2) \
.SetDisplay("Target Profit %", "Close all after equity growth", "Risk")
self._close_on_opposite = self.Param("CloseOnOppositeSignal", False) \
.SetDisplay("Close On Opposite", "Close positions on opposite signal", "Trading")
self._max_buy_trades = self.Param("MaxBuyTrades", 1) \
.SetDisplay("Max Long Trades", "Maximum concurrent long trades", "Risk")
self._max_sell_trades = self.Param("MaxSellTrades", 1) \
.SetDisplay("Max Short Trades", "Maximum concurrent short trades", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for pattern recognition", "Data")
self._initial_volume = self.Param("InitialOrderVolume", 0.01) \
.SetDisplay("Initial Volume", "Volume of the first trade", "Trading")
self._point_value = 0.0
self._current_volume = 0.0
self._entry_equity = 0.0
self._baseline_equity = 0.0
self._position_tracked = False
self._close_m1 = None
self._close_m2 = None
self._close_m3 = None
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._active_long_entries = 0
self._active_short_entries = 0
self._prev_pattern_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(e_skoch_open_strategy, self).OnReseted()
self._close_m1 = None
self._close_m2 = None
self._close_m3 = None
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._active_long_entries = 0
self._active_short_entries = 0
self._position_tracked = False
self._point_value = 0.0
self._current_volume = 0.0
self._entry_equity = 0.0
self._baseline_equity = 0.0
self._prev_pattern_signal = 0
def OnStarted2(self, time):
super(e_skoch_open_strategy, self).OnStarted2(time)
self.Volume = self._initial_volume.Value
self._point_value = self._calculate_point_value()
self._current_volume = self._initial_volume.Value
equity = 0.0
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
equity = float(self.Portfolio.CurrentValue)
self._baseline_equity = equity
self._entry_equity = equity
self._position_tracked = self.Position != 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._check_equity_target()
if self._check_protection(candle):
self._update_closes(float(candle.ClosePrice))
return
if self._close_m1 is not None and self._close_m2 is not None and self._close_m3 is not None:
c1 = self._close_m1
c2 = self._close_m2
c3 = self._close_m3
buy_signal = c3 > c2 and c1 < c2
sell_signal = c3 > c2 and c2 < c1
pattern_signal = 1 if buy_signal else (-1 if sell_signal else 0)
if buy_signal and pattern_signal != self._prev_pattern_signal:
self._handle_buy_signal(candle)
if sell_signal and pattern_signal != self._prev_pattern_signal:
self._handle_sell_signal(candle)
self._prev_pattern_signal = pattern_signal
else:
self._prev_pattern_signal = 0
self._update_closes(float(candle.ClosePrice))
def _handle_buy_signal(self, candle):
if not self._enable_buy.Value:
return
if self._close_on_opposite.Value and self.Position < 0:
self.BuyMarket(abs(float(self.Position)))
return
if self.Position > 0:
return
max_buy = self._max_buy_trades.Value
if max_buy != -1 and self._active_long_entries >= max_buy:
return
volume = self._current_volume
if volume <= 0:
return
self.BuyMarket(volume)
self._active_long_entries += 1
self._position_tracked = True
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
self._entry_equity = float(self.Portfolio.CurrentValue)
self._setup_protection(True, float(candle.ClosePrice))
def _handle_sell_signal(self, candle):
if not self._enable_sell.Value:
return
if self._close_on_opposite.Value and self.Position > 0:
self.SellMarket(abs(float(self.Position)))
return
if self.Position < 0:
return
max_sell = self._max_sell_trades.Value
if max_sell != -1 and self._active_short_entries >= max_sell:
return
volume = self._current_volume
if volume <= 0:
return
self.SellMarket(volume)
self._active_short_entries += 1
self._position_tracked = True
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
self._entry_equity = float(self.Portfolio.CurrentValue)
self._setup_protection(False, float(candle.ClosePrice))
def _check_protection(self, candle):
if self.Position > 0:
if self._long_stop is not None and float(candle.LowPrice) <= self._long_stop:
self.SellMarket(abs(float(self.Position)))
self._reset_protection()
return True
if self._long_take is not None and float(candle.HighPrice) >= self._long_take:
self.SellMarket(abs(float(self.Position)))
self._reset_protection()
return True
elif self.Position < 0:
if self._short_stop is not None and float(candle.HighPrice) >= self._short_stop:
self.BuyMarket(abs(float(self.Position)))
self._reset_protection()
return True
if self._short_take is not None and float(candle.LowPrice) <= self._short_take:
self.BuyMarket(abs(float(self.Position)))
self._reset_protection()
return True
return False
def _setup_protection(self, is_long, ref_price):
point = self._point_value
if point <= 0.0:
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
point = step if step > 0 else 1.0
sl = self._stop_loss_points.Value
tp = self._take_profit_points.Value
if is_long:
self._long_stop = ref_price - sl * point if sl > 0 else None
self._long_take = ref_price + tp * point if tp > 0 else None
self._short_stop = None
self._short_take = None
else:
self._short_stop = ref_price + sl * point if sl > 0 else None
self._short_take = ref_price - tp * point if tp > 0 else None
self._long_stop = None
self._long_take = None
def _reset_protection(self):
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
def _update_closes(self, close):
self._close_m3 = self._close_m2
self._close_m2 = self._close_m1
self._close_m1 = close
def _check_equity_target(self):
if self._target_profit_pct.Value <= 0.0:
return
if self._baseline_equity <= 0.0:
return
equity = 0.0
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
equity = float(self.Portfolio.CurrentValue)
growth = (equity - self._baseline_equity) / self._baseline_equity * 100.0
if growth >= self._target_profit_pct.Value:
if self.Position > 0:
self.SellMarket(abs(float(self.Position)))
elif self.Position < 0:
self.BuyMarket(abs(float(self.Position)))
def OnPositionReceived(self, position):
super(e_skoch_open_strategy, self).OnPositionReceived(position)
if self.Position == 0:
if self._position_tracked:
equity = self._baseline_equity
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
equity = float(self.Portfolio.CurrentValue)
if equity >= self._entry_equity:
self._current_volume = self._initial_volume.Value
else:
self._current_volume = self._current_volume * self._martingale_multiplier.Value
self._baseline_equity = equity
self._position_tracked = False
self._reset_protection()
self._active_long_entries = 0
self._active_short_entries = 0
else:
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
self._baseline_equity = float(self.Portfolio.CurrentValue)
else:
self._position_tracked = True
if self.Portfolio is not None and self.Portfolio.CurrentValue is not None:
self._entry_equity = float(self.Portfolio.CurrentValue)
def _calculate_point_value(self):
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0.0:
return 0.0
decimals = self._count_decimals(step)
return step * 10.0 if decimals == 3 or decimals == 5 else step
@staticmethod
def _count_decimals(value):
value = abs(value)
decimals = 0
while value != int(value) and decimals < 10:
value *= 10.0
decimals += 1
return decimals
def CreateClone(self):
return e_skoch_open_strategy()