Estrategia Forex Fraus Slogger
Esta estrategia replica el sistema de reversión por envoltura de MetaTrader.
Lógica
- Calcula una SMA de 1 período como precio base.
- Las envolturas superior e inferior se establecen a
EnvelopePercentpor ciento desde la base. - Cuando el precio cierra por encima de la banda superior y luego regresa por debajo, se entra en posición corta.
- Cuando el precio cierra por debajo de la banda inferior y luego regresa por encima, se entra en posición larga.
- Las posiciones están protegidas por un stop trailing.
Parámetros
EnvelopePercent– desplazamiento porcentual para las envolturas (predeterminado 0.1).TrailingStop– distancia del stop trailing en unidades de precio (predeterminado 0.001).TrailingStep– movimiento mínimo de precio requerido para avanzar el stop trailing (predeterminado 0.0001).ProfitTrailing– habilitar el trailing solo cuando la posición sea rentable.UseTimeFilter– operar solo durante las horas especificadas.StartHour– inicio de la ventana de operaciones.StopHour– fin de la ventana de operaciones.CandleType– marco temporal de velas utilizado para los cálculos.
Notas
- La estrategia utiliza órdenes de mercado mediante
BuyMarketySellMarket. - El stop trailing cierra la posición cuando el precio cruza el nivel de stop.
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>
/// Envelope reversion strategy with trailing stop.
/// </summary>
public class ForexFrausSloggerStrategy : Strategy
{
private readonly StrategyParam<decimal> _envelopePercent;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<decimal> _trailingStep;
private readonly StrategyParam<bool> _profitTrailing;
private readonly StrategyParam<bool> _useTimeFilter;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _stopHour;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _sma = null!;
private bool _wasAboveUpper;
private bool _wasBelowLower;
private decimal _entryPrice;
private decimal _peakPrice;
private decimal _troughPrice;
private decimal _currentStop;
/// <summary>
/// Envelope percent.
/// </summary>
public decimal EnvelopePercent
{
get => _envelopePercent.Value;
set => _envelopePercent.Value = value;
}
/// <summary>
/// Trailing stop distance.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Trailing step for stop adjustment.
/// </summary>
public decimal TrailingStep
{
get => _trailingStep.Value;
set => _trailingStep.Value = value;
}
/// <summary>
/// Enable trailing only after profit.
/// </summary>
public bool ProfitTrailing
{
get => _profitTrailing.Value;
set => _profitTrailing.Value = value;
}
/// <summary>
/// Use time filter.
/// </summary>
public bool UseTimeFilter
{
get => _useTimeFilter.Value;
set => _useTimeFilter.Value = value;
}
/// <summary>
/// Trading start hour.
/// </summary>
public int StartHour
{
get => _startHour.Value;
set => _startHour.Value = value;
}
/// <summary>
/// Trading stop hour.
/// </summary>
public int StopHour
{
get => _stopHour.Value;
set => _stopHour.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="ForexFrausSloggerStrategy"/>.
/// </summary>
public ForexFrausSloggerStrategy()
{
_envelopePercent = Param(nameof(EnvelopePercent), 1.0m)
.SetGreaterThanZero()
.SetDisplay("Envelope %", "Envelope percent", "Parameters");
_trailingStop = Param(nameof(TrailingStop), 500m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk");
_trailingStep = Param(nameof(TrailingStep), 100m)
.SetGreaterThanZero()
.SetDisplay("Trailing Step", "Minimum stop move", "Risk");
_profitTrailing = Param(nameof(ProfitTrailing), true)
.SetDisplay("Profit Trailing", "Trail only after profit", "Risk");
_useTimeFilter = Param(nameof(UseTimeFilter), false)
.SetDisplay("Use Time Filter", "Enable trading hours filter", "Parameters");
_startHour = Param(nameof(StartHour), 7)
.SetRange(0, 23)
.SetDisplay("Start Hour", "Trading start hour", "Parameters");
_stopHour = Param(nameof(StopHour), 17)
.SetRange(0, 23)
.SetDisplay("Stop Hour", "Trading stop hour", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null!;
_wasAboveUpper = false;
_wasBelowLower = false;
_entryPrice = 0m;
_peakPrice = 0m;
_troughPrice = 0m;
_currentStop = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new ExponentialMovingAverage { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal basis)
{
if (candle.State != CandleStates.Finished)
return;
if (!_sma.IsFormed || !IsFormedAndOnlineAndAllowTrading())
return;
if (UseTimeFilter && !IsWithinTradingHours(candle.OpenTime))
return;
var percent = EnvelopePercent / 100m;
var upper = basis * (1m + percent);
var lower = basis * (1m - percent);
var close = candle.ClosePrice;
if (close > upper)
_wasAboveUpper = true;
if (close < lower)
_wasBelowLower = true;
if (_wasAboveUpper && close <= upper)
{
// Sell signal - go short
if (Position >= 0)
SellMarket();
_entryPrice = close;
_troughPrice = close;
_currentStop = close + TrailingStop;
_wasAboveUpper = false;
return;
}
if (_wasBelowLower && close >= lower)
{
// Buy signal - go long
if (Position <= 0)
BuyMarket();
_entryPrice = close;
_peakPrice = close;
_currentStop = close - TrailingStop;
_wasBelowLower = false;
return;
}
if (Position > 0)
{
if (candle.HighPrice >= _peakPrice + TrailingStep)
{
_peakPrice = candle.HighPrice;
var newStop = _peakPrice - TrailingStop;
if (!ProfitTrailing || newStop > _entryPrice)
_currentStop = Math.Max(_currentStop, newStop);
}
if (close <= _currentStop)
SellMarket();
}
else if (Position < 0)
{
if (_troughPrice == 0m || candle.LowPrice <= _troughPrice - TrailingStep)
{
_troughPrice = _troughPrice == 0m ? candle.LowPrice : Math.Min(_troughPrice, candle.LowPrice);
var newStop = _troughPrice + TrailingStop;
if (!ProfitTrailing || newStop < _entryPrice)
_currentStop = _currentStop == 0m ? newStop : Math.Min(_currentStop, newStop);
}
if (_currentStop != 0m && close >= _currentStop)
BuyMarket();
}
}
private bool IsWithinTradingHours(DateTimeOffset time)
{
var hour = time.Hour;
if (StartHour < StopHour)
return hour >= StartHour && hour < StopHour;
return hour >= StartHour || hour < StopHour;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class forex_fraus_slogger_strategy(Strategy):
def __init__(self):
super(forex_fraus_slogger_strategy, self).__init__()
self._envelope_percent = self.Param("EnvelopePercent", 1.0) \
.SetDisplay("Envelope %", "Envelope percent", "Parameters")
self._trailing_stop = self.Param("TrailingStop", 500.0) \
.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk")
self._trailing_step = self.Param("TrailingStep", 100.0) \
.SetDisplay("Trailing Step", "Minimum stop move", "Risk")
self._profit_trailing = self.Param("ProfitTrailing", True) \
.SetDisplay("Profit Trailing", "Trail only after profit", "Risk")
self._use_time_filter = self.Param("UseTimeFilter", False) \
.SetDisplay("Use Time Filter", "Enable trading hours filter", "Parameters")
self._start_hour = self.Param("StartHour", 7) \
.SetDisplay("Start Hour", "Trading start hour", "Parameters")
self._stop_hour = self.Param("StopHour", 17) \
.SetDisplay("Stop Hour", "Trading stop hour", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Working candle timeframe", "Parameters")
self._sma = None
self._was_above_upper = False
self._was_below_lower = False
self._entry_price = 0.0
self._peak_price = 0.0
self._trough_price = 0.0
self._current_stop = 0.0
@property
def envelope_percent(self):
return self._envelope_percent.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def trailing_step(self):
return self._trailing_step.Value
@property
def profit_trailing(self):
return self._profit_trailing.Value
@property
def use_time_filter(self):
return self._use_time_filter.Value
@property
def start_hour(self):
return self._start_hour.Value
@property
def stop_hour(self):
return self._stop_hour.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(forex_fraus_slogger_strategy, self).OnReseted()
self._sma = None
self._was_above_upper = False
self._was_below_lower = False
self._entry_price = 0.0
self._peak_price = 0.0
self._trough_price = 0.0
self._current_stop = 0.0
def OnStarted2(self, time):
super(forex_fraus_slogger_strategy, self).OnStarted2(time)
self._sma = ExponentialMovingAverage()
self._sma.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._sma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._sma)
self.DrawOwnTrades(area)
def process_candle(self, candle, basis):
if candle.State != CandleStates.Finished:
return
if not self._sma.IsFormed or not self.IsFormedAndOnlineAndAllowTrading():
return
if self.use_time_filter and not self._is_within_trading_hours(candle.OpenTime):
return
basis = float(basis)
pct = float(self.envelope_percent) / 100.0
upper = basis * (1.0 + pct)
lower = basis * (1.0 - pct)
close = float(candle.ClosePrice)
trailing_stop = float(self.trailing_stop)
trailing_step = float(self.trailing_step)
if close > upper:
self._was_above_upper = True
if close < lower:
self._was_below_lower = True
if self._was_above_upper and close <= upper:
if self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._trough_price = close
self._current_stop = close + trailing_stop
self._was_above_upper = False
return
if self._was_below_lower and close >= lower:
if self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._peak_price = close
self._current_stop = close - trailing_stop
self._was_below_lower = False
return
if self.Position > 0:
high = float(candle.HighPrice)
if high >= self._peak_price + trailing_step:
self._peak_price = high
new_stop = self._peak_price - trailing_stop
if not self.profit_trailing or new_stop > self._entry_price:
self._current_stop = max(self._current_stop, new_stop)
if close <= self._current_stop:
self.SellMarket()
elif self.Position < 0:
low = float(candle.LowPrice)
if self._trough_price == 0.0 or low <= self._trough_price - trailing_step:
self._trough_price = low if self._trough_price == 0.0 else min(self._trough_price, low)
new_stop = self._trough_price + trailing_stop
if not self.profit_trailing or new_stop < self._entry_price:
self._current_stop = new_stop if self._current_stop == 0.0 else min(self._current_stop, new_stop)
if self._current_stop != 0.0 and close >= self._current_stop:
self.BuyMarket()
def _is_within_trading_hours(self, time):
hour = time.Hour
sh = int(self.start_hour)
eh = int(self.stop_hour)
if sh < eh:
return hour >= sh and hour < eh
return hour >= sh or hour < eh
def CreateClone(self):
return forex_fraus_slogger_strategy()