Estratégia Forex Fraus Slogger
Esta estratégia replica o sistema de reversão por envelope do MetaTrader.
Lógica
- Calcula uma SMA de 1 período como preço base.
- Os envelopes superior e inferior são definidos a
EnvelopePercentpor cento a partir da base. - Quando o preço fecha acima da banda superior e depois retorna abaixo, entra-se em posição vendida.
- Quando o preço fecha abaixo da banda inferior e depois retorna acima, entra-se em posição comprada.
- As posições são protegidas por um stop trailing.
Parâmetros
EnvelopePercent– deslocamento percentual para os envelopes (padrão 0.1).TrailingStop– distância do stop trailing em unidades de preço (padrão 0.001).TrailingStep– movimento mínimo de preço necessário para avançar o stop trailing (padrão 0.0001).ProfitTrailing– habilitar trailing somente após a posição tornar-se lucrativa.UseTimeFilter– operar apenas durante as horas especificadas.StartHour– início da janela de operações.StopHour– fim da janela de operações.CandleType– período de velas utilizado para os cálculos.
Notas
- A estratégia utiliza ordens a mercado via
BuyMarketeSellMarket. - O stop trailing encerra a posição quando o preço cruza o nível 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()