Anomalous Holonomy Field Theory Strategy
This strategy combines EMA, RSI, MACD, ATR, rate of change and VWAP distance into a composite signal. Long positions are opened when the signal exceeds a user-defined threshold, while short positions are opened when it falls below the negative threshold. An ATR-based stop protects open trades.
Details
- Entry Criteria:
- Long: signal ≥ threshold.
- Short: signal ≤ −threshold.
- Long/Short: Both.
- Exit Criteria: ATR stop.
- Stops: Yes, ATR-based.
- Default Values:
SignalThreshold= 2CandleType= 5 minute
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: Multiple
- Stops: Yes
- Complexity: Advanced
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: High
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>
/// Anomalous Holonomy Field Theory strategy.
/// Combines EMA trend with RSI extremes and VWAP distance for composite signal.
/// </summary>
public class AnomalousHolonomyFieldTheoryStrategy : Strategy
{
private readonly StrategyParam<decimal> _signalThreshold;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private int _barIndex;
private int _lastTradeBar;
/// <summary>
/// Absolute signal level required for trades.
/// </summary>
public decimal SignalThreshold
{
get => _signalThreshold.Value;
set => _signalThreshold.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="AnomalousHolonomyFieldTheoryStrategy"/>.
/// </summary>
public AnomalousHolonomyFieldTheoryStrategy()
{
_signalThreshold = Param(nameof(SignalThreshold), 0.1m)
.SetDisplay("Signal Threshold", "Absolute signal level required for trades", "Parameters")
.SetRange(0.1m, 10m);
_cooldownBars = Param(nameof(CooldownBars), 50)
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");
_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();
_barIndex = 0;
_lastTradeBar = -200;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema20 = new ExponentialMovingAverage { Length = 20 };
var ema50 = new ExponentialMovingAverage { Length = 50 };
var rsi = new RelativeStrengthIndex { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema20, ema50, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema20);
DrawIndicator(area, ema50);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal ema20, decimal ema50, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
var close = candle.ClosePrice;
var cooldownOk = _barIndex - _lastTradeBar > CooldownBars;
var signal = 0m;
// EMA trend component
if (close > ema20)
signal += close > ema50 ? 1.5m : 1m;
else if (close < ema20)
signal -= close < ema50 ? 1.5m : 1m;
// VWAP distance component
var vwap = candle.TotalVolume != 0 ? candle.TotalPrice / candle.TotalVolume : close;
var vwapDist = close != 0 ? (close - vwap) / close * 100m : 0m;
vwapDist = Math.Max(-2m, Math.Min(2m, vwapDist));
signal += vwapDist;
// RSI extremes component
if (rsiValue < 30m && signal > 0m)
signal += 1.5m;
else if (rsiValue < 40m && signal > 0m)
signal += 0.5m;
else if (rsiValue > 70m && signal < 0m)
signal -= 1.5m;
else if (rsiValue > 60m && signal < 0m)
signal -= 0.5m;
if (signal >= SignalThreshold && Position <= 0 && cooldownOk)
{
BuyMarket();
_lastTradeBar = _barIndex;
}
else if (signal <= -SignalThreshold && Position >= 0 && cooldownOk)
{
SellMarket();
_lastTradeBar = _barIndex;
}
}
}
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
from StockSharp.Algo.Strategies import Strategy
class anomalous_holonomy_field_theory_strategy(Strategy):
def __init__(self):
super(anomalous_holonomy_field_theory_strategy, self).__init__()
self._signal_threshold = self.Param("SignalThreshold", 0.1) \
.SetDisplay("Signal Threshold", "Absolute signal level required for trades", "Parameters")
self._cooldown_bars = self.Param("CooldownBars", 50) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bar_index = 0
self._last_trade_bar = -200
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@cooldown_bars.setter
def cooldown_bars(self, value):
self._cooldown_bars.Value = value
def OnReseted(self):
super(anomalous_holonomy_field_theory_strategy, self).OnReseted()
self._bar_index = 0
self._last_trade_bar = -200
def OnStarted2(self, time):
super(anomalous_holonomy_field_theory_strategy, self).OnStarted2(time)
ema20 = ExponentialMovingAverage()
ema20.Length = 20
ema50 = ExponentialMovingAverage()
ema50.Length = 50
rsi = RelativeStrengthIndex()
rsi.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema20, ema50, rsi, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema20)
self.DrawIndicator(area, ema50)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema20_val, ema50_val, rsi_val):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
close = float(candle.ClosePrice)
e20 = float(ema20_val)
e50 = float(ema50_val)
rsi_v = float(rsi_val)
cooldown_ok = self._bar_index - self._last_trade_bar > self.cooldown_bars
signal = 0.0
if close > e20:
signal += 1.5 if close > e50 else 1.0
elif close < e20:
signal -= 1.5 if close < e50 else 1.0
vol = float(candle.TotalVolume) if candle.TotalVolume != 0 else 0.0
tp = float(candle.TotalPrice) if hasattr(candle, 'TotalPrice') else 0.0
vwap = tp / vol if vol != 0 else close
vwap_dist = (close - vwap) / close * 100.0 if close != 0 else 0.0
vwap_dist = max(-2.0, min(2.0, vwap_dist))
signal += vwap_dist
if rsi_v < 30.0 and signal > 0:
signal += 1.5
elif rsi_v < 40.0 and signal > 0:
signal += 0.5
elif rsi_v > 70.0 and signal < 0:
signal -= 1.5
elif rsi_v > 60.0 and signal < 0:
signal -= 0.5
threshold = float(self._signal_threshold.Value)
if signal >= threshold and self.Position <= 0 and cooldown_ok:
self.BuyMarket()
self._last_trade_bar = self._bar_index
elif signal <= -threshold and self.Position >= 0 and cooldown_ok:
self.SellMarket()
self._last_trade_bar = self._bar_index
def CreateClone(self):
return anomalous_holonomy_field_theory_strategy()