StockSharp puerto del asesor experto MetaTrader 4 SimpleTrade.mq4 (también conocido como "neroTrade").
Diseñado para operar con un solo símbolo en el período configurado mediante el parámetro CandleType.
Siempre mantiene como máximo una posición abierta y cambia de dirección al abrir cada nueva barra.
Lógica de trading
Cada vez que se activa una nueva vela, la estrategia compara el precio de apertura de la vela con el precio de apertura de la vela que tiene LookbackBars períodos más antigua.
Si la nueva apertura es estrictamente superior a la referencia histórica, todas las posiciones existentes se cierran y se envía una nueva orden larga de mercado con TradeVolume lotes.
De lo contrario (la apertura es igual o inferior), la estrategia cierra cualquier posición existente y abre una posición corta en el mercado del mismo tamaño.
El parámetro StopLossPoints refleja la configuración stop original de EA. Cuando tanto el PriceStep como el StopLossPoints del valor están disponibles, la estrategia convierte el valor en una distancia absoluta y lo reenvía a StartProtection, permitiendo que StockSharp mantenga las órdenes protectoras de stop-loss automáticamente.
Las aperturas de velas se rastrean mediante la suscripción de velas de alto nivel API. Las velas terminadas llenan la lista del historial, mientras que la vela activa activa la decisión una vez por barra.
Parámetros
Parámetro
Descripción
Predeterminado
TradeVolume
Tamaño base del pedido expresado en lotes. Debe ser positivo.
1
StopLossPoints
Distancia de parada de protección en los puntos del instrumento. Establezca en 0 para desactivar el stop-loss automático.
120
LookbackBars
Número de barras utilizadas para la comparación de precios abiertos. Un valor de 3 reproduce Open[0] frente a Open[3] del código original.
3
CandleType
Periodo de tiempo (como DataType) a partir del cual se solicitan velas. Controla cuando aparecen nuevas señales.
1 hour timeframe
Notas de implementación
Utiliza el flujo de trabajo de alto nivel SubscribeCandles(...).Bind(...), por lo que la estrategia sigue siendo liviana y reacciona tanto a velas históricas como a velas activas.
StartProtection se invoca una vez durante OnStarted. Asegúrese de que la seguridad conectada proporcione PriceStep; de lo contrario, la distancia del stop-loss no se puede traducir a precios absolutos.
Debido a que todas las operaciones se ingresan con órdenes de mercado al comienzo de cada barra, el manejo del deslizamiento se delega al centro de negociación y no hay ningún parámetro slippage adicional.
El búfer abierto histórico mantiene solo una pequeña ventana móvil (valores LookbackBars + 5) para evitar el uso innecesario de memoria.
No se proporciona ningún puerto Python; el directorio CS/ contiene la única implementación.
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>
/// StockSharp port of the MetaTrader "SimpleTrade" expert advisor.
/// Compares the opening price of the current bar with the bar from several periods ago and flips the position accordingly.
/// </summary>
public class SimpleTradeFlipStrategy : Strategy
{
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<int> _lookbackBars;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _openHistory = new();
private int _cooldown;
/// <summary>
/// Initializes a new instance of the <see cref="SimpleTradeFlipStrategy"/> class.
/// </summary>
public SimpleTradeFlipStrategy()
{
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Order size in lots", "Trading");
_stopLossPoints = Param(nameof(StopLossPoints), 120m)
.SetNotNegative()
.SetDisplay("Stop-Loss Points", "Protective stop distance expressed in instrument points", "Risk");
_lookbackBars = Param(nameof(LookbackBars), 10)
.SetGreaterThanZero()
.SetDisplay("Lookback Bars", "Number of bars used for the open price comparison", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General");
}
/// <summary>
/// Order size submitted with each entry.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Stop-loss distance in instrument points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = Math.Max(0m, value);
}
/// <summary>
/// Number of historical bars used for the open price comparison.
/// </summary>
public int LookbackBars
{
get => _lookbackBars.Value;
set => _lookbackBars.Value = Math.Max(1, value);
}
/// <summary>
/// Candle type that defines the working timeframe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_openHistory.Clear();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var step = Security?.PriceStep ?? 0m;
Unit stopLossUnit = null;
if (StopLossPoints > 0m && step > 0m)
stopLossUnit = new Unit(StopLossPoints * step, UnitTypes.Absolute);
StartProtection(null, stopLossUnit);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Store the open price for future comparisons.
_openHistory.Add(candle.OpenPrice);
var maxHistory = Math.Max(LookbackBars + 5, 5);
if (_openHistory.Count > maxHistory)
_openHistory.RemoveRange(0, _openHistory.Count - maxHistory);
var lookback = LookbackBars;
if (_openHistory.Count <= lookback)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var volume = TradeVolume;
if (volume <= 0m)
return;
var currentOpen = candle.OpenPrice;
var referenceOpen = _openHistory[^(lookback + 1)];
// Only trade on clear directional difference
var diff = currentOpen - referenceOpen;
if (Math.Abs(diff) < currentOpen * 0.001m)
return;
if (diff > 0 && Position <= 0)
{
if (Position < 0m)
BuyMarket(Math.Abs(Position));
BuyMarket(volume);
_cooldown = 5;
}
else if (diff < 0 && Position >= 0)
{
if (Position > 0m)
SellMarket(Position);
SellMarket(volume);
_cooldown = 5;
}
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
class simple_trade_flip_strategy(Strategy):
def __init__(self):
super(simple_trade_flip_strategy, self).__init__()
self._trade_volume = self.Param("TradeVolume", 1.0) \
.SetDisplay("Trade Volume", "Order size in lots", "Trading")
self._stop_loss_points = self.Param("StopLossPoints", 120.0) \
.SetDisplay("Stop-Loss Points", "Protective stop distance in instrument points", "Risk")
self._lookback_bars = self.Param("LookbackBars", 10) \
.SetDisplay("Lookback Bars", "Number of bars used for open price comparison", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General")
self._open_history = []
self._cooldown = 0
@property
def TradeVolume(self):
return self._trade_volume.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def LookbackBars(self):
return self._lookback_bars.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(simple_trade_flip_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
ps = self.Security.PriceStep if self.Security is not None else None
step = float(ps) if ps is not None and float(ps) > 0 else 0.0
sl_pts = float(self.StopLossPoints)
sl = Unit(sl_pts * step, UnitTypes.Absolute) if sl_pts > 0 and step > 0 else None
self.StartProtection(None, sl)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
self._open_history.append(float(candle.OpenPrice))
max_history = max(self.LookbackBars + 5, 5)
if len(self._open_history) > max_history:
self._open_history = self._open_history[-max_history:]
lookback = int(self.LookbackBars)
if len(self._open_history) <= lookback:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown > 0:
self._cooldown -= 1
return
volume = float(self.TradeVolume)
if volume <= 0:
return
current_open = float(candle.OpenPrice)
reference_open = self._open_history[-(lookback + 1)]
diff = current_open - reference_open
if abs(diff) < current_open * 0.001:
return
if diff > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(volume)
self._cooldown = 5
elif diff < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
self.SellMarket(volume)
self._cooldown = 5
def OnReseted(self):
super(simple_trade_flip_strategy, self).OnReseted()
self._open_history = []
self._cooldown = 0
def CreateClone(self):
return simple_trade_flip_strategy()