Conversión del asesor experto MetaTrader 5 "Media Móvil con Marcos". El sistema original evalúa la relación entre los precios de apertura/cierre de cada vela y un promedio móvil simple desplazado (SMA) mientras muestra múltiples "marcos" de optimización en los gráficos. Este puerto StockSharp se centra en la lógica comercial: reacciona solo una vez por barra completada, abre una única posición de compensación y refleja las reglas de administración de dinero del código fuente.
Lógica de trading
Fuente de datos: la estrategia se suscribe al período de tiempo configurado (CandleType) y procesa solo velas terminadas, lo que reproduce la restricción MetaTrader if(rt[1].tick_volume>1) return;.
Indicador: una media móvil simple con período MovingPeriod. La salida del indicador avanza MovingShift velas completadas manteniendo un búfer de valores pasados.
Calentamiento: el comercio se suspende hasta que se recolecten al menos 100 velas completas, que coincidan con la guardia Bars(_Symbol,_Period)>100 original.
Condiciones de entrada
Vaya largo cuando la vela se abra por debajo del SMA desplazado y cierre por encima de él.
Vaya corto cuando la vela se abra por encima del SMA desplazado y cierre por debajo de él.
El motor impone una única posición: la exposición opuesta se aplana antes de entrar en la nueva dirección.
Condiciones de salida: una posición larga existente se cierra cuando el precio de apertura está por encima y el precio de cierre está por debajo del SMA desplazado; Los pantalones cortos se cierran en el cruce opuesto. Las nuevas operaciones no se abren en la misma barra después de una salida, como el experto original.
Tamaño de la posición y riesgo
Riesgo Máximo: determina el volumen de pedido bruto como Portfolio.CurrentValue * MaximumRisk / price cuando los datos de la cartera están disponibles. Si el feed del corredor no proporciona información sobre el capital, la estrategia recurre a la propiedad manual Volume.
DecreaseFactor: después de más de una operación perdedora consecutiva, el tamaño de la siguiente posición se reduce en volume * losses / DecreaseFactor, imitando la lógica de reducción de lotes de MetaTrader. Cualquier operación rentable reinicia el contador.
Alineación de volumen: el tamaño calculado se normaliza al VolumeStep del instrumento, se fija entre MinVolume y MaxVolume y se redondea a dos decimales cuando el intercambio no publica un paso.
Notas adicionales
La visualización de "marcos" MetaTrader no se transfiere porque StockSharp ya proporciona paneles de optimización enriquecidos. La lógica comercial, el momento de la señal y el comportamiento del tamaño siguen siendo fieles a la fuente.
Todos los valores del indicador se consumen directamente desde la devolución de llamada Bind; no se utilizan llamadas GetValue manuales.
El seguimiento de pérdidas consecutivas se implementa dentro de OnOwnTradeReceived, lo que permite que la estrategia reaccione correctamente a los llenados parciales y al comportamiento de compensación.
Parámetros
Parámetro
Predeterminado
Descripción
MaximumRisk
0.02
Fracción del patrimonio de la cartera arriesgada en cada entrada.
DecreaseFactor
3
Divisor utilizado para reducir el tamaño de la posición después de dos o más pérdidas consecutivas.
MovingPeriod
12
Longitud de la media móvil simple aplicada a los precios de cierre.
MovingShift
6
Número de velas completadas utilizadas para compensar el avance SMA en el tiempo.
CandleType
1h time frame
Serie de velas primarias procesadas por la estrategia.
Consejos de uso
Adjunte la estrategia a un valor y una cartera en StockSharp Designer o código.
Ajuste el tipo de vela para que coincida con el período del gráfico MetaTrader deseado.
Ajuste MaximumRisk y DecreaseFactor para que coincidan con el tamaño de su cuenta y la tolerancia al riesgo deseada.
Ejecute pruebas retrospectivas para validar que las señales cruzadas se alineen con los resultados originales de MetaTrader.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Moving Average With Frames: SMA crossover with candle body confirmation.
/// Buys when close crosses above shifted SMA, sells when crosses below.
/// </summary>
public class MovingAverageWithFramesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _prevClose;
public MovingAverageWithFramesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 12)
.SetDisplay("SMA Length", "SMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevClose = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
if (_prevClose == 0)
{
_prevClose = close;
return;
}
if (Position > 0)
{
if (close < smaVal && _prevClose >= smaVal)
{
SellMarket();
_entryPrice = 0;
}
else if (close <= _entryPrice - atrVal * 2m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close > smaVal && _prevClose <= smaVal)
{
BuyMarket();
_entryPrice = 0;
}
else if (close >= _entryPrice + atrVal * 2m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > smaVal && _prevClose <= smaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < smaVal && _prevClose >= smaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevClose = close;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange
class moving_average_with_frames_strategy(Strategy):
def __init__(self):
super(moving_average_with_frames_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._sma_length = self.Param("SmaLength", 12) \
.SetDisplay("SMA Length", "SMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._prev_close = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def SmaLength(self):
return self._sma_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(moving_average_with_frames_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._prev_close = 0.0
self._sma = SimpleMovingAverage()
self._sma.Length = self.SmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
sv = float(sma_val)
av = float(atr_val)
if av <= 0:
return
close = float(candle.ClosePrice)
if self._prev_close == 0:
self._prev_close = close
return
if self.Position > 0:
if close < sv and self._prev_close >= sv:
self.SellMarket()
self._entry_price = 0.0
elif close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close > sv and self._prev_close <= sv:
self.BuyMarket()
self._entry_price = 0.0
elif close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = close
return
if self.Position == 0:
if close > sv and self._prev_close <= sv:
self._entry_price = close
self.BuyMarket()
elif close < sv and self._prev_close >= sv:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def OnReseted(self):
super(moving_average_with_frames_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_close = 0.0
def CreateClone(self):
return moving_average_with_frames_strategy()