Esta estratégia replica o expert advisor MetaTrader "Freeman" usando a API de alto nível do StockSharp. Acumula múltiplas posições de mercado enquanto uma tendência medida pela inclinação de uma média móvel permanece alinhada com uma confirmação RSI. Cada distância de entrada e saída é definida em pips e convertida em unidades de preço usando o tamanho do tick do instrumento para que o comportamento corresponda à implementação forex original.
Lógica de negociação
Assinar uma única série de candles (período configurável) e atualizar os indicadores ATR, média móvel e RSI em cada candle finalizado.
Gerar um sinal direcional quando:
A inclinação da média móvel é positiva ou negativa comparando o valor mais recente com a barra anterior (filtro de tendência opcional).
O preço está suficientemente distante da média móvel para evitar entradas diretamente na linha.
O RSI cruza o limiar superior ou inferior se o filtro RSI estiver habilitado. A lógica do MetaTrader é mantida intacta, incluindo a peculiaridade onde uma confirmação de venda RSI retorna -11, portanto, ativar ambos os filtros favorece apenas operações compradas.
Respeitar o número máximo de posições abertas simultaneamente. Entradas adicionais na mesma direção são permitidas somente quando o preço se moveu contra o último preenchimento pela distância de pip configurada, construindo efetivamente uma grade.
Cada entrada usa níveis de stop-loss e take-profit baseados em ATR. Os trailing stops ajustam o stop protetor uma vez que o preço se move pelo passo de trailing mais a distância do trailing stop.
As saídas são executadas via ordens de mercado opostas quando o intervalo do candle atinge o nível de stop, alvo ou trailing.
Gestão de risco
Os multiplicadores ATR controlam as distâncias fixas de stop-loss e take-profit. Definir um multiplicador para zero desabilita essa proteção.
Os trailing stops são opcionais e são definidos por dois parâmetros de pip: a distância de trailing real e o passo adicional necessário antes de mover o stop novamente.
A estratégia depende da propriedade base Volume para dimensionamento; nenhum gerenciamento monetário automatizado é aplicado além do limite de posição.
Parâmetros
Nome
Descrição
CandleType
Período usado para cálculos de indicadores.
MaxPositions
Número máximo de posições abertas simultaneamente (soma de compradas e vendidas).
DistancePips
Distância mínima em pips entre entradas consecutivas na mesma direção.
AtrPeriod
Período de média para o indicador ATR.
AtrStopLossMultiplier
Multiplicador ATR para o stop protetor. 0 desabilita o stop.
AtrTakeProfitMultiplier
Multiplicador ATR para o alvo de lucro. 0 desabilita o alvo.
UseTrendFilter
Habilita o filtro de inclinação da média móvel.
DistanceFromMaPips
Distância mínima em pips entre preço e a média móvel quando o filtro de tendência está ativo.
MaPeriod, MaShift, MaMethod, MaPriceType
Parâmetros de média móvel espelhando as entradas do MetaTrader.
UseRsiFilter
Habilita o filtro de confirmação RSI.
RsiLevelUp, RsiLevelDown, RsiPeriod, RsiPriceType
Configuração RSI com seleção de preço aplicado.
TrailingStopPips, TrailingStepPips
Distância e passo do trailing stop medidos em pips.
CurrentBarOffset
Deslocamento aplicado ao ler valores do indicador, emulando a entrada CurrentBar do expert advisor.
Notas
A conversão de pips multiplica o PriceStep do instrumento por 10 quando o instrumento tem 3 ou 5 casas decimais para reproduzir o ajuste ponto-para-pip do MetaTrader.
A estratégia usa um modelo de posição de compensação; sinais opostos fecham posições existentes antes de abrir operações na nova direção.
A proteção de início é habilitada no lançamento para proteger contra reconexões inesperadas antes que quaisquer operações sejam colocadas.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class FreemanAtrMaRsiGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public FreemanAtrMaRsiGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 8).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class freeman_atr_ma_rsi_grid_strategy(Strategy):
def __init__(self):
super(freeman_atr_ma_rsi_grid_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 8) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(freeman_atr_ma_rsi_grid_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(freeman_atr_ma_rsi_grid_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return freeman_atr_ma_rsi_grid_strategy()