Intraday-Momentum-Strategie
Handelt innerhalb einer festgelegten Sitzung mit EMA-Kreuzung, RSI-Filter und VWAP-Bestätigung. Geht long, wenn die schnelle EMA über die langsame EMA kreuzt, der RSI unter dem überkauften Niveau liegt und der Kurs über dem VWAP notiert. Shorts bei entgegengesetzten Bedingungen. Wendet feste Stop-Loss- und Take-Profit-Prozentsätze an und schließt alle Positionen am Ende der Sitzung.
Parameter
- EmaFastLength: Länge der schnellen EMA.
- EmaSlowLength: Länge der langsamen EMA.
- RsiLength: RSI-Periode.
- RsiOverbought: RSI-Überkauft-Niveau.
- RsiOversold: RSI-Überverkauft-Niveau.
- StopLossPerc: Stop-Loss-Prozentsatz.
- TakeProfitPerc: Take-Profit-Prozentsatz.
- StartHour: Startstunde der Sitzung.
- StartMinute: Startminute der Sitzung.
- EndHour: Endstunde der Sitzung.
- EndMinute: Endminute der Sitzung.
- CandleType: Kerzentyp.
using System;
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>
/// Intraday momentum strategy using EMA crossover, RSI and VWAP.
/// </summary>
public class IntradayMomentumStrategy : Strategy
{
private readonly StrategyParam<int> _emaFastLength;
private readonly StrategyParam<int> _emaSlowLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _rsiOverbought;
private readonly StrategyParam<int> _rsiOversold;
private readonly StrategyParam<decimal> _stopLossPerc;
private readonly StrategyParam<decimal> _takeProfitPerc;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _startMinute;
private readonly StrategyParam<int> _endHour;
private readonly StrategyParam<int> _endMinute;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _prevSet;
private decimal _entryPrice;
private decimal _stopLoss;
private decimal _takeProfit;
/// <summary>
/// Fast EMA period length.
/// </summary>
public int EmaFastLength { get => _emaFastLength.Value; set => _emaFastLength.Value = value; }
/// <summary>
/// Slow EMA period length.
/// </summary>
public int EmaSlowLength { get => _emaSlowLength.Value; set => _emaSlowLength.Value = value; }
/// <summary>
/// RSI period length.
/// </summary>
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
/// <summary>
/// RSI overbought level.
/// </summary>
public int RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }
/// <summary>
/// RSI oversold level.
/// </summary>
public int RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPerc { get => _stopLossPerc.Value; set => _stopLossPerc.Value = value; }
/// <summary>
/// Take-profit percentage.
/// </summary>
public decimal TakeProfitPerc { get => _takeProfitPerc.Value; set => _takeProfitPerc.Value = value; }
/// <summary>
/// Session start hour.
/// </summary>
public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }
/// <summary>
/// Session start minute.
/// </summary>
public int StartMinute { get => _startMinute.Value; set => _startMinute.Value = value; }
/// <summary>
/// Session end hour.
/// </summary>
public int EndHour { get => _endHour.Value; set => _endHour.Value = value; }
/// <summary>
/// Session end minute.
/// </summary>
public int EndMinute { get => _endMinute.Value; set => _endMinute.Value = value; }
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public IntradayMomentumStrategy()
{
_emaFastLength = Param(nameof(EmaFastLength), 9)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length", "Period for fast EMA", "Indicators")
.SetOptimize(5, 20, 1);
_emaSlowLength = Param(nameof(EmaSlowLength), 21)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Length", "Period for slow EMA", "Indicators")
.SetOptimize(10, 50, 1);
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators")
.SetOptimize(7, 28, 1);
_rsiOverbought = Param(nameof(RsiOverbought), 70)
.SetRange(0, 100)
.SetDisplay("RSI Overbought", "Overbought level", "Indicators")
.SetOptimize(60, 90, 5);
_rsiOversold = Param(nameof(RsiOversold), 30)
.SetRange(0, 100)
.SetDisplay("RSI Oversold", "Oversold level", "Indicators")
.SetOptimize(10, 40, 5);
_stopLossPerc = Param(nameof(StopLossPerc), 1m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
.SetOptimize(0.5m, 5m, 0.5m);
_takeProfitPerc = Param(nameof(TakeProfitPerc), 2m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk Management")
.SetOptimize(1m, 10m, 0.5m);
_startHour = Param(nameof(StartHour), 0)
.SetRange(0, 23)
.SetDisplay("Session Start Hour", "Trading session start hour", "Session");
_startMinute = Param(nameof(StartMinute), 0)
.SetRange(0, 59)
.SetDisplay("Session Start Minute", "Trading session start minute", "Session");
_endHour = Param(nameof(EndHour), 23)
.SetRange(0, 23)
.SetDisplay("Session End Hour", "Trading session end hour", "Session");
_endMinute = Param(nameof(EndMinute), 59)
.SetRange(0, 59)
.SetDisplay("Session End Minute", "Trading session end minute", "Session");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_prevSet = false;
_entryPrice = 0m;
_stopLoss = 0m;
_takeProfit = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFastInd = new ExponentialMovingAverage { Length = EmaFastLength };
var emaSlowInd = new ExponentialMovingAverage { Length = EmaSlowLength };
var rsiInd = new RelativeStrengthIndex { Length = RsiLength };
var vwapInd = new VolumeWeightedAveragePrice();
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(emaFastInd, emaSlowInd, rsiInd, vwapInd, ProcessCandle)
.Start();
StartProtection(null, null);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaFastVal, IIndicatorValue emaSlowVal, IIndicatorValue rsiVal, IIndicatorValue vwapVal)
{
if (candle.State != CandleStates.Finished)
return;
if (emaFastVal.IsEmpty || emaSlowVal.IsEmpty || rsiVal.IsEmpty || vwapVal.IsEmpty)
return;
var emaFast = emaFastVal.ToDecimal();
var emaSlow = emaSlowVal.ToDecimal();
var rsi = rsiVal.ToDecimal();
var vwap = vwapVal.ToDecimal();
var time = candle.OpenTime.TimeOfDay;
var start = new TimeSpan(StartHour, StartMinute, 0);
var end = new TimeSpan(EndHour, EndMinute, 0);
var inSession = time >= start && time <= end;
if (!inSession)
{
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
return;
}
if (!_prevSet)
{
_prevFast = emaFast;
_prevSlow = emaSlow;
_prevSet = true;
return;
}
var crossover = _prevFast <= _prevSlow && emaFast > emaSlow;
var crossunder = _prevFast >= _prevSlow && emaFast < emaSlow;
_prevFast = emaFast;
_prevSlow = emaSlow;
var longCondition = crossover && rsi < RsiOverbought && candle.ClosePrice > vwap;
var shortCondition = crossunder && rsi > RsiOversold && candle.ClosePrice < vwap;
if (longCondition && Position <= 0)
{
_entryPrice = candle.ClosePrice;
_stopLoss = _entryPrice * (1 - StopLossPerc / 100m);
_takeProfit = _entryPrice * (1 + TakeProfitPerc / 100m);
BuyMarket();
}
else if (shortCondition && Position >= 0)
{
_entryPrice = candle.ClosePrice;
_stopLoss = _entryPrice * (1 + StopLossPerc / 100m);
_takeProfit = _entryPrice * (1 - TakeProfitPerc / 100m);
SellMarket();
}
if (Position > 0)
{
if (candle.LowPrice <= _stopLoss || candle.HighPrice >= _takeProfit)
SellMarket();
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopLoss || candle.LowPrice <= _takeProfit)
BuyMarket();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, VolumeWeightedAveragePrice, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class intraday_momentum_strategy(Strategy):
"""
Intraday momentum using EMA crossover, RSI and VWAP filters.
Trades within session hours with percentage-based SL/TP.
"""
def __init__(self):
super(intraday_momentum_strategy, self).__init__()
self._ema_fast_length = self.Param("EmaFastLength", 9) \
.SetDisplay("Fast EMA Length", "Period for fast EMA", "Indicators")
self._ema_slow_length = self.Param("EmaSlowLength", 21) \
.SetDisplay("Slow EMA Length", "Period for slow EMA", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 70) \
.SetDisplay("RSI Overbought", "Overbought level", "Indicators")
self._rsi_oversold = self.Param("RsiOversold", 30) \
.SetDisplay("RSI Oversold", "Oversold level", "Indicators")
self._stop_loss_perc = self.Param("StopLossPerc", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk Management")
self._take_profit_perc = self.Param("TakeProfitPerc", 2.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk Management")
self._start_hour = self.Param("StartHour", 0) \
.SetDisplay("Session Start Hour", "Trading session start hour", "Session")
self._start_minute = self.Param("StartMinute", 0) \
.SetDisplay("Session Start Minute", "Trading session start minute", "Session")
self._end_hour = self.Param("EndHour", 23) \
.SetDisplay("Session End Hour", "Trading session end hour", "Session")
self._end_minute = self.Param("EndMinute", 59) \
.SetDisplay("Session End Minute", "Trading session end minute", "Session")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_set = False
self._entry_price = 0.0
self._stop_loss = 0.0
self._take_profit = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(intraday_momentum_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_set = False
self._entry_price = 0.0
self._stop_loss = 0.0
self._take_profit = 0.0
def OnStarted2(self, time):
super(intraday_momentum_strategy, self).OnStarted2(time)
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self._ema_fast_length.Value
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self._ema_slow_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
vwap = VolumeWeightedAveragePrice()
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ema_fast, ema_slow, rsi, vwap, self._process_candle).Start()
def _process_candle(self, candle, ema_fast_val, ema_slow_val, rsi_val, vwap_val):
if candle.State != CandleStates.Finished:
return
if ema_fast_val.IsEmpty or ema_slow_val.IsEmpty or rsi_val.IsEmpty or vwap_val.IsEmpty:
return
ema_fast = float(IndicatorHelper.ToDecimal(ema_fast_val))
ema_slow = float(IndicatorHelper.ToDecimal(ema_slow_val))
rsi = float(IndicatorHelper.ToDecimal(rsi_val))
vwap = float(IndicatorHelper.ToDecimal(vwap_val))
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
time_of_day = candle.OpenTime.TimeOfDay
start = TimeSpan(self._start_hour.Value, self._start_minute.Value, 0)
end = TimeSpan(self._end_hour.Value, self._end_minute.Value, 0)
in_session = time_of_day >= start and time_of_day <= end
if not in_session:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
return
if not self._prev_set:
self._prev_fast = ema_fast
self._prev_slow = ema_slow
self._prev_set = True
return
crossover = self._prev_fast <= self._prev_slow and ema_fast > ema_slow
crossunder = self._prev_fast >= self._prev_slow and ema_fast < ema_slow
self._prev_fast = ema_fast
self._prev_slow = ema_slow
sl_pct = self._stop_loss_perc.Value
tp_pct = self._take_profit_perc.Value
long_cond = crossover and rsi < self._rsi_overbought.Value and close > vwap
short_cond = crossunder and rsi > self._rsi_oversold.Value and close < vwap
if long_cond and self.Position <= 0:
self._entry_price = close
self._stop_loss = close * (1.0 - sl_pct / 100.0)
self._take_profit = close * (1.0 + tp_pct / 100.0)
self.BuyMarket()
elif short_cond and self.Position >= 0:
self._entry_price = close
self._stop_loss = close * (1.0 + sl_pct / 100.0)
self._take_profit = close * (1.0 - tp_pct / 100.0)
self.SellMarket()
if self.Position > 0:
if low <= self._stop_loss or high >= self._take_profit:
self.SellMarket()
elif self.Position < 0:
if high >= self._stop_loss or low <= self._take_profit:
self.BuyMarket()
def CreateClone(self):
return intraday_momentum_strategy()