Конвертация советника MetaTrader 5 RndTrade.mq5 на высокоуровневый API StockSharp.
Через фиксированные интервалы времени закрывает текущую позицию и тут же открывает новую в случайно выбранном направлении.
Вместо таймера MetaTrader используется подписка на свечи с соответствующим таймфреймом.
Параметры
Имя
Тип
Значение по умолчанию
Описание
IntervalMinutes
int
60
Количество минут между закрытием текущей позиции и открытием новой случайной позиции. Значение должно быть больше нуля.
Volume
decimal
1
Размер позиции для рыночного входа. Свойство базового класса Strategy.
Подписки на данные
Подписка на свечи с таймфреймом, равным IntervalMinutes (например, 60 → часовые свечи).
Обработка выполняется только при закрытии свечи (CandleStates.Finished), что гарантирует единичное срабатывание за интервал.
Логика торговли
Ожидаем формирования каждой интервалной свечи.
Пропускаем обработку, пока стратегия не сформирована, находится онлайн и ей разрешена торговля.
Закрываем позицию, открытую на предыдущем интервале.
Генерируем случайное число для выбора длинной или короткой позиции.
Отправляем рыночную заявку (BuyMarket или SellMarket) с заданным объёмом в выбранном направлении.
Особенности реализации
Используется цепочка SubscribeCandles().Bind(ProcessCandle), что исключает ручную работу со списками или индикаторами.
В OnStarted вызывается StartProtection(), чтобы активировать модуль защиты позиций (стопы и тейки не задаются явно).
Генератор Random повторяет поведение MathRand() из оригинального MQL-кода.
В код добавлены английские комментарии, объясняющие связь между шагами конверсии и функциональностью StockSharp.
Отличия от оригинальной стратегии MQL
Таймер OnTimer заменён таймфреймовыми свечами, которые задают частоту исполнения логики.
Закрытие позиции выполняется методом ClosePosition() вместо перебора тикетов и вызова PositionClose для каждого из них.
Размер позиций берётся из свойства Volume, а не из минимального лота инструмента.
Параметры проскальзывания и типа исполнения делегируются брокеру/эмулятору и не настраиваются из стратегии.
Использование
Привяжите стратегию к портфелю и инструменту в среде StockSharp.
Настройте IntervalMinutes и Volume под желаемую частоту торговли и размер позиции.
Запустите стратегию — она автоматически будет закрывать и открывать позиции на каждом интервале.
Python-версия отсутствует: доступна только реализация на 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()