Esta estrategia es una conversión en C# del asesor experto de MetaTrader 5 "SimpleTrade (edición de barabashkakvn)". Compara el precio de apertura de la barra actual con el precio de apertura de tres barras atrás. Si la apertura actual es mayor, la estrategia va larga; de lo contrario va corta. Cada posición se mantiene durante solo una vela completada y está asegurada con una distancia de stop-loss fija expresada en pips.
La implementación en StockSharp se suscribe a la serie de velas seleccionada a través de la API de alto nivel y reacciona solo a las barras terminadas, asegurando que las decisiones se basan en datos de precio completos. Las posiciones se cierran en la siguiente transición de barra o antes si el nivel de stop es tocado dentro del rango de la barra.
Lógica de trading
Entrada
En cada barra completada, almacenar su precio de apertura y mantener un historial deslizante de las últimas cuatro aperturas.
Cuando no hay posición abierta y hay al menos cuatro precios de apertura disponibles, comparar la apertura más reciente con la registrada tres barras atrás.
Entrar largo si la apertura actual está por encima de la apertura de tres barras atrás; de lo contrario entrar corto.
Salida
Cada operación está protegida por un nivel de stop calculado como StopLossPips × tamaño de pip desde el precio de apertura de entrada.
En la siguiente barra la posición se cierra independientemente del resultado, replicando el asesor experto original que nunca mantiene una operación más de una vela.
Si el máximo de la barra (para cortos) o el mínimo (para largos) penetra el nivel de stop, la estrategia intenta cerrar la posición inmediatamente a mercado.
Parámetros
Parámetro
Predeterminado
Descripción
StopLossPips
120
Distancia desde el precio de apertura de entrada al stop protector, medida en pips. El código reproduce el comportamiento de MetaTrader multiplicando el paso de precio por 10 para símbolos cotizados con 3 o 5 decimales.
TradeVolume
1
Volumen de orden usado para entradas de mercado. Ajústalo para alinearlo con el tamaño del contrato del instrumento negociado.
CandleType
Marco temporal de 1 hora
Especifica la serie de velas a la que se suscribe la estrategia. Selecciona el marco temporal que corresponde al gráfico usado en MetaTrader.
Todos los parámetros están expuestos como objetos StrategyParam<T> para que puedan optimizarse o cambiarse a través de la interfaz gráfica.
Notas de implementación
El historial deslizante de cuatro precios de apertura se mantiene sin colecciones para cumplir con las directrices del repositorio.
Los stops no se envían como órdenes separadas; en cambio, la lógica verifica los rangos de velas y emite una salida a mercado cuando el nivel de stop habría sido activado.
Dado que StockSharp procesa posiciones de forma asíncrona, la estrategia sale de una operación existente antes de evaluar una nueva señal de entrada. En trading en vivo, esto refleja la secuencia original de "cerrar y luego reabrir" al tiempo que evita órdenes superpuestas.
El tamaño de pip se deriva de Security.PriceStep. Para símbolos de 5 o 3 dígitos, el paso se multiplica por diez para que un pip coincida con la definición de MetaTrader.
Consejos de uso
Ejecuta la estrategia en instrumentos con tamaños de tick consistentes donde los stops basados en pips sean significativos (por ejemplo, pares de Forex principales).
Optimiza el valor de StopLossPips por instrumento; valores grandes amplían el buffer protector, mientras que valores más pequeños hacen la estrategia más sensible al ruido intrabarra.
Asegúrate de que la conexión con el broker envíe actualizaciones de velas con estados finales para que la estrategia reciba los precios de apertura correctos.
Riesgos y limitaciones
Mantener operaciones durante solo una barra significa que la estrategia depende en gran medida del marco temporal elegido. Es esencial hacer backtesting con diferentes duraciones de velas.
Usar los extremos de la vela para emular la ejecución de stops introduce deslizamiento en mercados volátiles en comparación con las órdenes stop nativas.
La estrategia siempre permanece en el mercado (ya sea larga o corta) después de las primeras cuatro barras de datos, lo que puede generar operaciones frecuentes en mercados laterales.
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simple trade strategy converted from MetaTrader that compares the current open with the open three bars ago.
/// The logic holds positions for a single bar and protects the trade with a fixed stop distance.
/// </summary>
public class SimpleTradeStrategy : Strategy
{
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private decimal _openCurrent;
private decimal _openMinus1;
private decimal _openMinus2;
private decimal _openMinus3;
private int _historyCount;
private decimal? _stopPrice;
/// <summary>
/// Stop loss distance expressed in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Trade volume used for market orders.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="SimpleTradeStrategy"/> parameters.
/// </summary>
public SimpleTradeStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 120)
.SetGreaterThanZero()
.SetDisplay("Stop Loss (pips)", "Fixed protective distance in pips", "Risk Management")
;
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Order volume in lots", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle source for decisions", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_openCurrent = 0m;
_openMinus1 = 0m;
_openMinus2 = 0m;
_openMinus3 = 0m;
_historyCount = 0;
_stopPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Exit existing trades before evaluating new entries to mimic the original MQL behaviour.
if (TryCloseExistingPosition(candle))
return;
UpdateHistory(candle.OpenPrice);
// Need at least four opens to compare with the value three bars ago.
if (_historyCount < 4)
return;
ExecuteEntry(candle);
}
private bool TryCloseExistingPosition(ICandleMessage candle)
{
if (Position > 0)
{
var volume = Position;
// Close long trades at the protective stop or at the bar change.
if (_stopPrice is decimal stop && candle.LowPrice <= stop)
{
SellMarket();
}
else
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
}
_stopPrice = null;
return true;
}
if (Position < 0)
{
var volume = Math.Abs(Position);
// Close short trades at the protective stop or at the bar change.
if (_stopPrice is decimal stop && candle.HighPrice >= stop)
{
BuyMarket();
}
else
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
}
_stopPrice = null;
return true;
}
return false;
}
private void UpdateHistory(decimal currentOpen)
{
// Shift the stored opens so that _openMinus3 keeps the value three candles back.
_openMinus3 = _openMinus2;
_openMinus2 = _openMinus1;
_openMinus1 = _openCurrent;
_openCurrent = currentOpen;
if (_historyCount < 4)
_historyCount++;
}
private void ExecuteEntry(ICandleMessage candle)
{
var pipSize = CalculatePipSize();
var stopOffset = pipSize * StopLossPips;
// Enter long when the current open is above the open three bars ago, otherwise enter short.
if (_openCurrent > _openMinus3)
{
BuyMarket();
_stopPrice = candle.OpenPrice - stopOffset;
}
else
{
SellMarket();
_stopPrice = candle.OpenPrice + stopOffset;
}
}
private decimal CalculatePipSize()
{
// Reproduce the MetaTrader adjustment: multiply by ten for symbols with 3 or 5 decimals.
var step = Security?.PriceStep ?? 1m;
var decimals = Security?.Decimals;
if (decimals == 3 || decimals == 5)
step *= 10m;
return step;
}
}
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
class simple_trade_strategy(Strategy):
def __init__(self):
super(simple_trade_strategy, self).__init__()
self._sl_pips = self.Param("StopLossPips", 120)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(simple_trade_strategy, self).OnReseted()
self._opens = []
self._stop_price = None
def OnStarted2(self, time):
super(simple_trade_strategy, self).OnStarted2(time)
self._opens = []
self._stop_price = None
self._pip_size = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
self._pip_size = float(self.Security.PriceStep)
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
# Close existing position first (hold only 1 bar)
if self.Position > 0:
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
else:
self.SellMarket()
self._stop_price = None
self._update_opens(float(candle.OpenPrice))
return
elif self.Position < 0:
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
else:
self.BuyMarket()
self._stop_price = None
self._update_opens(float(candle.OpenPrice))
return
self._update_opens(float(candle.OpenPrice))
if len(self._opens) < 4:
return
open_current = self._opens[-1]
open_minus3 = self._opens[-4]
stop_offset = self._pip_size * self._sl_pips.Value
if open_current > open_minus3:
self.BuyMarket()
self._stop_price = float(candle.OpenPrice) - stop_offset
else:
self.SellMarket()
self._stop_price = float(candle.OpenPrice) + stop_offset
def _update_opens(self, val):
self._opens.append(val)
if len(self._opens) > 5:
self._opens.pop(0)
def CreateClone(self):
return simple_trade_strategy()