using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the Sidus v1 expert advisor using EMA and RSI filters.
/// Buys when the fast EMA is sufficiently below the slow EMA and RSI is oversold.
/// Sells when the fast EMA is sufficiently above the slow EMA and RSI is overbought.
/// </summary>
public class SidusV1Strategy : Strategy
{
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _fastEma2Length;
private readonly StrategyParam<int> _slowEma2Length;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _rsiPeriod2;
private readonly StrategyParam<decimal> _buyDifferenceThreshold;
private readonly StrategyParam<decimal> _buyRsiThreshold;
private readonly StrategyParam<decimal> _sellDifferenceThreshold;
private readonly StrategyParam<decimal> _sellRsiThreshold;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Length of the fast EMA for buy signal calculation.
/// </summary>
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
/// <summary>
/// Length of the slow EMA for buy signal calculation.
/// </summary>
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
/// <summary>
/// Length of the fast EMA for sell signal calculation.
/// </summary>
public int FastEma2Length
{
get => _fastEma2Length.Value;
set => _fastEma2Length.Value = value;
}
/// <summary>
/// Length of the slow EMA for sell signal calculation.
/// </summary>
public int SlowEma2Length
{
get => _slowEma2Length.Value;
set => _slowEma2Length.Value = value;
}
/// <summary>
/// RSI period used for buy signals.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI period used for sell signals.
/// </summary>
public int RsiPeriod2
{
get => _rsiPeriod2.Value;
set => _rsiPeriod2.Value = value;
}
/// <summary>
/// Threshold for EMA difference to allow buy orders (negative means fast below slow).
/// </summary>
public decimal BuyDifferenceThreshold
{
get => _buyDifferenceThreshold.Value;
set => _buyDifferenceThreshold.Value = value;
}
/// <summary>
/// RSI threshold to confirm oversold conditions.
/// </summary>
public decimal BuyRsiThreshold
{
get => _buyRsiThreshold.Value;
set => _buyRsiThreshold.Value = value;
}
/// <summary>
/// Threshold for EMA difference to allow sell orders (positive means fast above slow).
/// </summary>
public decimal SellDifferenceThreshold
{
get => _sellDifferenceThreshold.Value;
set => _sellDifferenceThreshold.Value = value;
}
/// <summary>
/// RSI threshold to confirm overbought conditions.
/// </summary>
public decimal SellRsiThreshold
{
get => _sellRsiThreshold.Value;
set => _sellRsiThreshold.Value = value;
}
/// <summary>
/// Stop loss distance in absolute price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in absolute price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SidusV1Strategy"/> class.
/// </summary>
public SidusV1Strategy()
{
_fastEmaLength = Param(nameof(FastEmaLength), 23)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length", "Length of the fast EMA for buy signals", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 62)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Length", "Length of the slow EMA for buy signals", "Indicators");
_fastEma2Length = Param(nameof(FastEma2Length), 18)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length (Sell)", "Length of the fast EMA for sell signals", "Indicators");
_slowEma2Length = Param(nameof(SlowEma2Length), 54)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Length (Sell)", "Length of the slow EMA for sell signals", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 67)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period used for buy signals", "Indicators");
_rsiPeriod2 = Param(nameof(RsiPeriod2), 97)
.SetGreaterThanZero()
.SetDisplay("RSI Period (Sell)", "RSI period used for sell signals", "Indicators");
_buyDifferenceThreshold = Param(nameof(BuyDifferenceThreshold), -100m)
.SetDisplay("Buy EMA Threshold", "Maximum fast-slow EMA difference to allow buy", "Trading Rules");
_buyRsiThreshold = Param(nameof(BuyRsiThreshold), 45m)
.SetDisplay("Buy RSI Threshold", "Maximum RSI level to allow buy", "Trading Rules");
_sellDifferenceThreshold = Param(nameof(SellDifferenceThreshold), 100m)
.SetDisplay("Sell EMA Threshold", "Minimum fast-slow EMA difference to allow sell", "Trading Rules");
_sellRsiThreshold = Param(nameof(SellRsiThreshold), 55m)
.SetDisplay("Sell RSI Threshold", "Minimum RSI level to allow sell", "Trading Rules");
_stopLoss = Param(nameof(StopLoss), 500m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss distance in absolute price units", "Risk Management");
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit distance in absolute price units", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return new[] { (Security, CandleType) };
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
var fastEma = new EMA { Length = FastEmaLength };
var slowEma = new EMA { Length = SlowEmaLength };
var fastEma2 = new EMA { Length = FastEma2Length };
var slowEma2 = new EMA { Length = SlowEma2Length };
var rsi = new RSI { Length = RsiPeriod };
var rsi2 = new RSI { Length = RsiPeriod2 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, fastEma2, slowEma2, rsi, rsi2, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
// Use StartProtection for SL/TP
var tp = TakeProfit > 0 ? new Unit(TakeProfit, UnitTypes.Absolute) : null;
var sl = StopLoss > 0 ? new Unit(StopLoss, UnitTypes.Absolute) : null;
StartProtection(tp, sl);
base.OnStarted2(time);
}
private void ProcessCandle(ICandleMessage candle,
decimal fastEmaValue,
decimal slowEmaValue,
decimal fastEma2Value,
decimal slowEma2Value,
decimal rsiValue,
decimal rsi2Value)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var diffBuy = fastEmaValue - slowEmaValue;
var diffSell = fastEma2Value - slowEma2Value;
// Buy when fast EMA is sufficiently below slow EMA and RSI is oversold
if (diffBuy < BuyDifferenceThreshold && rsiValue < BuyRsiThreshold && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
}
// Sell when fast EMA is sufficiently above slow EMA and RSI is overbought
else if (diffSell > SellDifferenceThreshold && rsi2Value > SellRsiThreshold && Position >= 0)
{
if (Position > 0)
SellMarket(Position);
SellMarket(Volume);
}
}
}
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.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class sidus_v1_strategy(Strategy):
def __init__(self):
super(sidus_v1_strategy, self).__init__()
self._fast_ema_len = self.Param("FastEmaLength", 23).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA for buy", "Indicators")
self._slow_ema_len = self.Param("SlowEmaLength", 62).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA for buy", "Indicators")
self._fast_ema2_len = self.Param("FastEma2Length", 18).SetGreaterThanZero().SetDisplay("Fast EMA (Sell)", "Fast EMA for sell", "Indicators")
self._slow_ema2_len = self.Param("SlowEma2Length", 54).SetGreaterThanZero().SetDisplay("Slow EMA (Sell)", "Slow EMA for sell", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 67).SetGreaterThanZero().SetDisplay("RSI Period", "RSI for buy", "Indicators")
self._rsi_period2 = self.Param("RsiPeriod2", 97).SetGreaterThanZero().SetDisplay("RSI Period (Sell)", "RSI for sell", "Indicators")
self._buy_diff = self.Param("BuyDifferenceThreshold", -100.0).SetDisplay("Buy EMA Threshold", "Max fast-slow EMA diff for buy", "Trading")
self._buy_rsi_thresh = self.Param("BuyRsiThreshold", 45.0).SetDisplay("Buy RSI Threshold", "Max RSI for buy", "Trading")
self._sell_diff = self.Param("SellDifferenceThreshold", 100.0).SetDisplay("Sell EMA Threshold", "Min fast-slow EMA diff for sell", "Trading")
self._sell_rsi_thresh = self.Param("SellRsiThreshold", 55.0).SetDisplay("Sell RSI Threshold", "Min RSI for sell", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle type", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnStarted2(self, time):
super(sidus_v1_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_ema_len.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_ema_len.Value
fast_ema2 = ExponentialMovingAverage()
fast_ema2.Length = self._fast_ema2_len.Value
slow_ema2 = ExponentialMovingAverage()
slow_ema2.Length = self._slow_ema2_len.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
rsi2 = RelativeStrengthIndex()
rsi2.Length = self._rsi_period2.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_ema, slow_ema, fast_ema2, slow_ema2, rsi, rsi2, self.OnProcess).Start()
tp = Unit(500.0, UnitTypes.Absolute)
sl = Unit(500.0, UnitTypes.Absolute)
self.StartProtection(tp, sl)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val, fast2_val, slow2_val, rsi_val, rsi2_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
diff_buy = fast_val - slow_val
diff_sell = fast2_val - slow2_val
if diff_buy < self._buy_diff.Value and rsi_val < self._buy_rsi_thresh.Value and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(abs(self.Position))
self.BuyMarket(self.Volume)
elif diff_sell > self._sell_diff.Value and rsi2_val > self._sell_rsi_thresh.Value and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
self.SellMarket(self.Volume)
def CreateClone(self):
return sidus_v1_strategy()