Esta estrategia replica el asesor experto MetaTrader "Freeman" usando la API de alto nivel de StockSharp. Acumula múltiples posiciones de mercado mientras una tendencia medida por la pendiente de una media móvil se mantiene alineada con una confirmación RSI. Cada distancia de entrada y salida se define en pips y se convierte en unidades de precio usando el tamaño de tick del instrumento, de modo que el comportamiento coincida con la implementación forex original.
Lógica de trading
Suscribirse a una única serie de velas (marco temporal configurable) y actualizar los indicadores ATR, media móvil y RSI en cada vela finalizada.
Generar una señal direccional cuando:
La pendiente de la media móvil es positiva o negativa comparando el valor más reciente con la barra anterior (filtro de tendencia opcional).
El precio está suficientemente lejos de la media móvil para evitar entradas directamente en la línea.
El RSI cruza el umbral superior o inferior si el filtro RSI está habilitado. La lógica de MetaTrader se mantiene intacta, incluyendo la particularidad donde una confirmación de venta RSI devuelve -11, por lo que activar ambos filtros favorece solo las operaciones largas.
Respetar el número máximo de posiciones abiertas simultáneamente. Las entradas adicionales en la misma dirección solo se permiten cuando el precio se ha movido contra el último llenado por la distancia de pip configurada, construyendo efectivamente una cuadrícula.
Cada entrada usa niveles de stop-loss y take-profit basados en ATR. Los trailing stops ajustan el stop protector una vez que el precio se mueve por el paso de trailing más la distancia del trailing stop.
Las salidas se ejecutan mediante órdenes de mercado opuestas cuando el rango de la vela alcanza el nivel de stop, objetivo o trailing.
Gestión de riesgo
Los multiplicadores ATR controlan las distancias fijas de stop-loss y take-profit. Establecer un multiplicador en cero deshabilita esa protección.
Los trailing stops son opcionales y se definen por dos parámetros de pip: la distancia de trailing real y el paso adicional requerido antes de mover el stop nuevamente.
La estrategia depende de la propiedad base Volume para el dimensionamiento; no se aplica gestión monetaria automatizada más allá del límite de posición.
Parámetros
Nombre
Descripción
CandleType
Marco temporal utilizado para cálculos de indicadores.
MaxPositions
Número máximo de posiciones abiertas simultáneamente (suma de largas y cortas).
DistancePips
Distancia mínima en pips entre entradas consecutivas en la misma dirección.
AtrPeriod
Período de promediado para el indicador ATR.
AtrStopLossMultiplier
Multiplicador ATR para el stop protector. 0 deshabilita el stop.
AtrTakeProfitMultiplier
Multiplicador ATR para el objetivo de beneficio. 0 deshabilita el objetivo.
UseTrendFilter
Habilita el filtro de pendiente de la media móvil.
DistanceFromMaPips
Distancia mínima en pips entre precio y la media móvil cuando el filtro de tendencia está activo.
MaPeriod, MaShift, MaMethod, MaPriceType
Parámetros de media móvil que reflejan las entradas de MetaTrader.
UseRsiFilter
Habilita el filtro de confirmación RSI.
RsiLevelUp, RsiLevelDown, RsiPeriod, RsiPriceType
Configuración RSI con selección de precio aplicado.
TrailingStopPips, TrailingStepPips
Distancia y paso del trailing stop medidos en pips.
CurrentBarOffset
Desplazamiento aplicado al leer valores del indicador, emulando la entrada CurrentBar del asesor experto.
Notas
La conversión de pips multiplica el PriceStep del instrumento por 10 cuando el instrumento tiene 3 o 5 decimales para reproducir el ajuste punto-a-pip de MetaTrader.
La estrategia usa un modelo de posición de compensación; las señales opuestas cierran las posiciones existentes antes de abrir operaciones en la nueva dirección.
La protección de inicio se habilita al lanzar para proteger contra reconexiones inesperadas antes de que se coloquen operaciones.
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()