Sidus v1 策略
概述
Sidus v1 是一套趋势型策略,结合了两组指数移动平均线(EMA)与 RSI 滤波器。原始的 MT4 智能交易系统在快 EMA 明显偏离慢 EMA 且 RSI 显示超卖或超买时入场。本移植版本保留了核心逻辑,仅允许在低成交量的蜡烛上交易,并针对多头与空头分别挂出不同的止盈止损订单。
使用的指标
- 快 EMA(做多):衡量短期动量。
- 慢 EMA(做多):过滤长期趋势方向。
- 快 EMA(做空):衡量做空前的短期动量。
- 慢 EMA(做空):过滤做空前的趋势方向。
- RSI(做多):确认超卖状态。
- RSI(做空):确认超买状态。
交易逻辑
- 订阅所选周期的蜡烛数据(默认 15 分钟)。
- 在每根收盘蜡烛上更新全部 EMA 与 RSI。
- 当蜡烛成交量超过阈值(默认 10)时跳过信号。
- 做多条件:
- 快 EMA 与慢 EMA 的差值低于做多阈值。
- RSI 低于做多阈值。
- 当前净头寸不为正(允许平空并转多)。
- 做空条件:
- 做空 EMA 组合的差值高于做空阈值。
- 做空 RSI 高于做空阈值。
- 当前净头寸不为负(允许平多并转空)。
- 触发信号后取消挂单,按需平仓并建立新的市场单,然后立即挂出相应方向的止盈与止损单。
风险控制
- 多头止盈价:
entry + BuyTakeProfitPips * priceStep;止损价:entry - BuyStopLossPips * priceStep。 - 空头止盈价:
entry - SellTakeProfitPips * priceStep;止损价:entry + SellStopLossPips * priceStep。 - 参数以“点”为单位,通过合约的最小价格变动(priceStep)换算成价格。若标的的 tick 大小不同,请调整相关参数。
参数列表
| 参数 | 说明 | 默认值 |
|---|---|---|
FastEmaLength |
做多信号的快 EMA 长度 | 23 |
SlowEmaLength |
做多信号的慢 EMA 长度 | 62 |
FastEma2Length |
做空信号的快 EMA 长度 | 18 |
SlowEma2Length |
做空信号的慢 EMA 长度 | 54 |
RsiPeriod |
做多 RSI 周期 | 67 |
RsiPeriod2 |
做空 RSI 周期 | 97 |
BuyDifferenceThreshold |
做多时允许的最大 EMA 差值 | 63 |
BuyRsiThreshold |
做多时允许的最大 RSI 值 | 59 |
SellDifferenceThreshold |
做空时需要的最小 EMA 差值 | -57 |
SellRsiThreshold |
做空时需要的最小 RSI 值 | 60 |
BuyTakeProfitPips |
多头止盈点数 | 95 |
BuyStopLossPips |
多头止损点数 | 100 |
SellTakeProfitPips |
空头止盈点数 | 17 |
SellStopLossPips |
空头止损点数 | 69 |
OrderVolume |
下单数量 | 0.5 |
MaxCandleVolume |
允许的最大蜡烛成交量 | 10 |
CandleType |
指标计算所用的蜡烛类型 | 15 分钟蜡烛 |
使用建议
- 确认交易通道支持同时挂出市价单、止损单和限价单,以便策略布设保护订单。
- 如标的价格最小变动与 MT4 的
Point不同,请调整止盈止损点数。 - 策略基于净头寸管理,改变方向时会先平掉已有仓位,再建立新的方向。
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()