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()