Lux Clara EMA + VWAP-Strategie
Die Lux Clara EMA + VWAP-Strategie handelt den Crossover einer schnellen und einer langsamen EMA, gefiltert durch VWAP und ein Zeitfenster. Eine Long-Position wird eröffnet, wenn die schnelle EMA die langsame EMA von unten kreuzt, während die langsame EMA über dem VWAP liegt und die aktuelle Zeit innerhalb der Sitzung liegt. Eine Short-Position wird unter umgekehrten Bedingungen eröffnet. Positionen werden geschlossen, wenn die EMAs in die entgegengesetzte Richtung kreuzen.
Details
- Einstiegskriterien:
- Schnelle EMA kreuzt die langsame EMA von unten, langsame EMA über VWAP und aktuelle Zeit innerhalb der Sitzung.
- Short: Schnelle EMA kreuzt die langsame EMA von oben, langsame EMA unter VWAP und aktuelle Zeit innerhalb der Sitzung.
- Long/Short: Beide.
- Ausstiegskriterien:
- Entgegengesetzter EMA-Crossover.
- Stops: Keine.
- Standardwerte:
FastEmaLength= 8SlowEmaLength= 50StartTime= 07:30EndTime= 14:30CandleType= 5 Minuten
- Filter:
- Kategorie: Trendfolge
- Richtung: Long und Short
- Indikatoren: EMA, VWAP
- Stops: Keine
- Komplexität: Niedrig
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Niedrig
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>
/// Lux Clara EMA + VWAP strategy.
/// Goes long when the fast EMA crosses above the slow EMA while the slow EMA is above the session VWAP, and short on the opposite
/// cross with the slow EMA below VWAP. Entries are taken only between StartTime and EndTime (UTC). A position closes on the opposite
/// EMA cross, which reverses it when the opposite entry conditions are also met.
/// </summary>
public class LuxClaraEmaVwapStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<TimeSpan> _startTime;
private readonly StrategyParam<TimeSpan> _endTime;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private DateTime? _vwapDay;
private decimal _cumPriceVolume;
private decimal _cumVolume;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
/// <summary>
/// Session start time (UTC).
/// </summary>
public TimeSpan StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// Session end time (UTC).
/// </summary>
public TimeSpan EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public LuxClaraEmaVwapStrategy()
{
_fastEmaLength = Param(nameof(FastEmaLength), 8)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length", "Period of the fast EMA", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Length", "Period of the slow EMA", "Indicators");
_startTime = Param(nameof(StartTime), new TimeSpan(7, 30, 0))
.SetDisplay("Start Time", "Session start time (UTC)", "Session");
_endTime = Param(nameof(EndTime), new TimeSpan(14, 30, 0))
.SetDisplay("End Time", "Session end time (UTC)", "Session");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
ResetState();
}
private void ResetState()
{
_prevFast = null;
_prevSlow = null;
_vwapDay = null;
_cumPriceVolume = 0m;
_cumVolume = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastValue, IIndicatorValue slowValue)
{
if (candle.State != CandleStates.Finished)
return;
// VWAP is anchored to the start of each UTC day.
var day = candle.OpenTime.Date;
if (_vwapDay != day)
{
_vwapDay = day;
_cumPriceVolume = 0m;
_cumVolume = 0m;
}
var typical = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
_cumPriceVolume += typical * candle.TotalVolume;
_cumVolume += candle.TotalVolume;
if (!fastValue.IsFormed || !slowValue.IsFormed)
return;
var fast = fastValue.GetValue<decimal>();
var slow = slowValue.GetValue<decimal>();
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (prevFast is not decimal pf || prevSlow is not decimal ps || _cumVolume <= 0m)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var vwap = _cumPriceVolume / _cumVolume;
var crossUp = pf <= ps && fast > slow;
var crossDown = pf >= ps && fast < slow;
var timeOfDay = candle.OpenTime.TimeOfDay;
var inSession = timeOfDay >= StartTime && timeOfDay < EndTime;
if (crossUp)
{
if (inSession && slow > vwap && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (Position < 0)
BuyMarket(-Position);
}
else if (crossDown)
{
if (inSession && slow < vwap && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0)
SellMarket(Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class lux_clara_ema_vwap_strategy(Strategy):
"""
Lux Clara EMA + VWAP strategy.
Goes long when the fast EMA crosses above the slow EMA while the slow EMA is above the session VWAP, and short on the opposite
cross with the slow EMA below VWAP. Entries are taken only between StartTime and EndTime (UTC). A position closes on the opposite
EMA cross, which reverses it when the opposite entry conditions are also met.
"""
def __init__(self):
super(lux_clara_ema_vwap_strategy, self).__init__()
self._fast_ema_length = self.Param("FastEmaLength", 8).SetGreaterThanZero().SetDisplay("Fast EMA Length", "Period of the fast EMA", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 50).SetGreaterThanZero().SetDisplay("Slow EMA Length", "Period of the slow EMA", "Indicators")
self._start_time = self.Param("StartTime", TimeSpan(7, 30, 0)).SetDisplay("Start Time", "Session start time (UTC)", "Session")
self._end_time = self.Param("EndTime", TimeSpan(14, 30, 0)).SetDisplay("End Time", "Session end time (UTC)", "Session")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = None
self._prev_slow = None
self._vwap_day = None
self._cum_price_volume = Decimal(0)
self._cum_volume = Decimal(0)
def OnReseted(self):
super(lux_clara_ema_vwap_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(lux_clara_ema_vwap_strategy, self).OnStarted2(time)
self._reset_state()
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_ema_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(fast_ema, slow_ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
# VWAP is anchored to the start of each UTC day.
day = candle.OpenTime.Date
if self._vwap_day is None or self._vwap_day != day:
self._vwap_day = day
self._cum_price_volume = Decimal(0)
self._cum_volume = Decimal(0)
typical = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / Decimal(3)
self._cum_price_volume += typical * candle.TotalVolume
self._cum_volume += candle.TotalVolume
if not fast_value.IsFormed or not slow_value.IsFormed:
return
fast = fast_value.GetValue[Decimal](None)
slow = slow_value.GetValue[Decimal](None)
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if prev_fast is None or prev_slow is None or self._cum_volume <= Decimal(0):
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
vwap = self._cum_price_volume / self._cum_volume
cross_up = prev_fast <= prev_slow and fast > slow
cross_down = prev_fast >= prev_slow and fast < slow
time_of_day = candle.OpenTime.TimeOfDay
in_session = time_of_day >= self._start_time.Value and time_of_day < self._end_time.Value
if cross_up:
if in_session and slow > vwap and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self.Position < 0:
self.BuyMarket(-self.Position)
elif cross_down:
if in_session and slow < vwap and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0:
self.SellMarket(self.Position)
def CreateClone(self):
return lux_clara_ema_vwap_strategy()