Conversión del asesor experto de MetaTrader 5 RndTrade.mq5 a la API de estrategia de alto nivel de StockSharp.
Cierra cualquier posición existente en un intervalo de tiempo fijo y abre inmediatamente una nueva posición de mercado en una dirección seleccionada aleatoriamente.
Usa suscripciones de velas basadas en tiempo como reemplazo determinista de las callbacks de temporizador originales.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
IntervalMinutes
int
60
Número de minutos entre el cierre de la posición actual y la apertura de una nueva posición aleatoria. Debe ser mayor que cero.
Volume
decimal
1
Tamaño de posición usado para entradas a mercado. Derivado de la clase base Strategy.
Suscripciones de datos
Se suscribe a velas de marco temporal cuya longitud coincide con IntervalMinutes (p. ej., 60 → velas de 60 minutos).
El evento de cierre de vela (CandleStates.Finished) se usa para activar la lógica exactamente una vez por intervalo.
Lógica de trading
Esperar la finalización de cada vela de intervalo.
Omitir el procesamiento hasta que la estrategia esté formada, en línea y se permita el trading.
Cerrar cualquier posición abierta creada durante el intervalo anterior.
Generar un valor aleatorio para decidir entre una entrada larga o corta.
Enviar una orden de mercado (BuyMarket o SellMarket) con el volumen configurado en la dirección seleccionada.
Notas de implementación
Se basa en SubscribeCandles().Bind(ProcessCandle) para evitar el sondeo manual de valores de indicadores o colecciones.
Llama a StartProtection() durante el inicio para que el módulo de riesgo integrado esté activo, aunque no se configure ningún stop-loss o take-profit explícito.
Usa Random de la biblioteca estándar para imitar el comportamiento MathRand() encontrado en la estrategia MQL original.
El código contiene comentarios en inglés que explican cómo cada paso de conversión se mapea a las características de StockSharp.
Diferencias con la estrategia MQL original
Los eventos de temporizador (OnTimer) se emulan mediante suscripciones de velas en lugar de la API de temporizador de MetaTrader.
El cierre de posición se maneja con ClosePosition() en lugar de iterar sobre listas de posiciones y llamar PositionClose para cada ticket.
La versión de StockSharp se basa en la propiedad integrada Volume para el dimensionamiento de posiciones en lugar de la consulta del lote mínimo del símbolo.
Las reglas de llenado de órdenes y la configuración de deslizamiento son gestionadas por el bróker o simulador conectado, por lo que no se configuran explícitamente en la estrategia.
Uso
Adjuntar la estrategia a una cartera y un instrumento dentro del entorno StockSharp.
Configurar IntervalMinutes y Volume según la frecuencia de trading y el tamaño deseados.
Iniciar la estrategia. Aplanará y reabrirá posiciones automáticamente en cada intervalo sin ninguna entrada adicional.
No se proporciona implementación en Python en este momento; solo está disponible la versión en C#.
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>
/// Random direction trading strategy converted from the MetaTrader RndTrade EA.
/// The strategy closes any open position on each interval and immediately opens a new random position.
/// </summary>
public class RndTradeStrategy : Strategy
{
private readonly StrategyParam<int> _intervalMinutes;
/// <summary>
/// Interval in minutes between closing the current position and opening a new random one.
/// </summary>
public int IntervalMinutes
{
get => _intervalMinutes.Value;
set => _intervalMinutes.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="RndTradeStrategy"/> class.
/// </summary>
public RndTradeStrategy()
{
_intervalMinutes = Param(nameof(IntervalMinutes), 360)
.SetGreaterThanZero()
.SetDisplay("Interval Minutes", "Minutes between closing and opening positions", "General");
Volume = 1;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, TimeSpan.FromMinutes(IntervalMinutes).TimeFrame())];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Use time-based candles as a deterministic timer replacement.
var timeFrame = TimeSpan.FromMinutes(IntervalMinutes).TimeFrame();
var subscription = SubscribeCandles(timeFrame);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
// Process only final candles to execute logic exactly once per interval.
if (candle.State != CandleStates.Finished)
return;
// Always close the existing position before selecting a new random direction.
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
// Derive a deterministic pseudo-random direction from the candle data.
if (ShouldBuy(candle))
{
// Enter long after flattening the previous position.
if (Position <= 0)
BuyMarket(Volume);
}
else
{
// Enter short after flattening the previous position.
if (Position >= 0)
SellMarket(Volume);
}
}
private static bool ShouldBuy(ICandleMessage candle)
{
var hash = HashCode.Combine(candle.OpenTime.Ticks, candle.ClosePrice, candle.TotalVolume);
return (hash & 1) == 0;
}
}
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
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class rnd_trade_strategy(Strategy):
def __init__(self):
super(rnd_trade_strategy, self).__init__()
self._interval_minutes = self.Param("IntervalMinutes", 360).SetGreaterThanZero().SetDisplay("Interval Minutes", "Minutes between trades", "General")
def OnStarted2(self, time):
super(rnd_trade_strategy, self).OnStarted2(time)
tf = DataType.TimeFrame(TimeSpan.FromMinutes(self._interval_minutes.Value))
sub = self.SubscribeCandles(tf)
sub.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
h = int(float(candle.ClosePrice) * 1000) ^ int(candle.TotalVolume)
if (h & 1) == 0:
if self.Position <= 0:
self.BuyMarket()
else:
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return rnd_trade_strategy()