Altcoin Index Correlation Strategy
The strategy compares EMA trends on the traded instrument and a reference index. It opens long when both fast EMAs are above their slow EMAs, and short when both are below. Optional inverse logic allows trading against the index trend or skipping the index completely.
Details
- Entry Criteria:
- Fast EMA above slow EMA on both instruments (or opposite if inverse).
- Long/Short: Both.
- Exit Criteria:
- Opposite crossover condition.
- Stops: None.
- Default Values:
FastEmaLength= 47SlowEmaLength= 50IndexFastEmaLength= 47IndexSlowEmaLength= 50SkipIndexReference= falseInverseSignal= false
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: EMA
- Stops: No
- Complexity: Low
- Timeframe: Any
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Altcoin Index Correlation Strategy - trades when EMA trends on the symbol and reference index align.
/// </summary>
public class AltcoinIndexCorrelationStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaLen;
private readonly StrategyParam<int> _slowEmaLen;
private readonly StrategyParam<int> _indexFastEmaLen;
private readonly StrategyParam<int> _indexSlowEmaLen;
private readonly StrategyParam<bool> _skipIndex;
private readonly StrategyParam<bool> _inverseSignal;
private readonly StrategyParam<Security> _indexSecurity;
private readonly StrategyParam<DataType> _candleType;
private decimal _indexFast;
private decimal _indexSlow;
private bool _indexReady;
private decimal _prevFast;
private decimal _prevSlow;
private int _barIndex;
private int _lastTradeBar;
/// <summary>
/// Length of fast EMA for main security.
/// </summary>
public int FastEmaLength
{
get => _fastEmaLen.Value;
set => _fastEmaLen.Value = value;
}
/// <summary>
/// Length of slow EMA for main security.
/// </summary>
public int SlowEmaLength
{
get => _slowEmaLen.Value;
set => _slowEmaLen.Value = value;
}
/// <summary>
/// Length of fast EMA for reference index.
/// </summary>
public int IndexFastEmaLength
{
get => _indexFastEmaLen.Value;
set => _indexFastEmaLen.Value = value;
}
/// <summary>
/// Length of slow EMA for reference index.
/// </summary>
public int IndexSlowEmaLength
{
get => _indexSlowEmaLen.Value;
set => _indexSlowEmaLen.Value = value;
}
/// <summary>
/// Skip using reference index in calculations.
/// </summary>
public bool SkipIndexReference
{
get => _skipIndex.Value;
set => _skipIndex.Value = value;
}
/// <summary>
/// Inverse correlation logic.
/// </summary>
public bool InverseSignal
{
get => _inverseSignal.Value;
set => _inverseSignal.Value = value;
}
/// <summary>
/// Reference index security.
/// </summary>
public Security IndexSecurity
{
get => _indexSecurity.Value;
set => _indexSecurity.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public AltcoinIndexCorrelationStrategy()
{
_fastEmaLen = Param(nameof(FastEmaLength), 7)
.SetDisplay("Fast EMA", "Fast EMA length", "EMA Settings")
.SetOptimize(5, 50, 5);
_slowEmaLen = Param(nameof(SlowEmaLength), 18)
.SetDisplay("Slow EMA", "Slow EMA length", "EMA Settings")
.SetOptimize(10, 100, 5);
_indexFastEmaLen = Param(nameof(IndexFastEmaLength), 47)
.SetDisplay("Index Fast EMA", "Fast EMA length for index", "Index Reference")
.SetOptimize(10, 100, 5);
_indexSlowEmaLen = Param(nameof(IndexSlowEmaLength), 50)
.SetDisplay("Index Slow EMA", "Slow EMA length for index", "Index Reference")
.SetOptimize(10, 100, 5);
_skipIndex = Param(nameof(SkipIndexReference), false)
.SetDisplay("Skip Index", "Ignore index correlation", "Index Reference");
_inverseSignal = Param(nameof(InverseSignal), false)
.SetDisplay("Inverse Signal", "Use inverse correlation logic", "Index Reference");
_indexSecurity = Param<Security>(nameof(IndexSecurity))
.SetDisplay("Index Security", "Reference index security", "Data");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
if (IndexSecurity != null)
yield return (IndexSecurity, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_indexFast = 0m;
_indexSlow = 0m;
_indexReady = false;
_prevFast = 0m;
_prevSlow = 0m;
_barIndex = 0;
_lastTradeBar = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var mainSub = SubscribeCandles(CandleType);
mainSub
.Bind(fastEma, slowEma, ProcessMainCandle)
.Start();
if (IndexSecurity != null)
{
var indexFastEma = new ExponentialMovingAverage { Length = IndexFastEmaLength };
var indexSlowEma = new ExponentialMovingAverage { Length = IndexSlowEmaLength };
var indexSub = SubscribeCandles(CandleType, security: IndexSecurity);
indexSub
.Bind(indexFastEma, indexSlowEma, ProcessIndexCandle)
.Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, mainSub);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessIndexCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
_indexFast = fast;
_indexSlow = slow;
_indexReady = true;
}
private void ProcessMainCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
if (_prevFast == 0 || _prevSlow == 0)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var cooldownOk = _barIndex - _lastTradeBar > 5;
// Cross-over detection
var crossOver = _prevFast <= _prevSlow && fast > slow;
var crossUnder = _prevFast >= _prevSlow && fast < slow;
bool goLong;
bool goShort;
if (SkipIndexReference || !_indexReady)
{
goLong = crossOver;
goShort = crossUnder;
}
else
{
goLong = crossOver && _indexFast > _indexSlow;
goShort = crossUnder && _indexFast < _indexSlow;
if (InverseSignal)
{
goLong = crossOver && _indexFast < _indexSlow;
goShort = crossUnder && _indexFast > _indexSlow;
}
}
if (goLong && Position <= 0 && cooldownOk)
{
BuyMarket();
_lastTradeBar = _barIndex;
}
else if (goShort && Position >= 0 && cooldownOk)
{
SellMarket();
_lastTradeBar = _barIndex;
}
_prevFast = fast;
_prevSlow = slow;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from StockSharp.BusinessEntities import Security
from datatype_extensions import *
class altcoin_index_correlation_strategy(Strategy):
"""
Altcoin Index Correlation Strategy - trades when EMA trends on the symbol
and reference index align.
"""
def __init__(self):
super(altcoin_index_correlation_strategy, self).__init__()
self._fast_ema_len = self.Param("FastEmaLength", 7) \
.SetDisplay("Fast EMA", "Fast EMA length", "EMA Settings")
self._slow_ema_len = self.Param("SlowEmaLength", 18) \
.SetDisplay("Slow EMA", "Slow EMA length", "EMA Settings")
self._index_fast_ema_len = self.Param("IndexFastEmaLength", 47) \
.SetDisplay("Index Fast EMA", "Fast EMA length for index", "Index Reference")
self._index_slow_ema_len = self.Param("IndexSlowEmaLength", 50) \
.SetDisplay("Index Slow EMA", "Slow EMA length for index", "Index Reference")
self._skip_index = self.Param("SkipIndexReference", False) \
.SetDisplay("Skip Index", "Ignore index correlation", "Index Reference")
self._inverse_signal = self.Param("InverseSignal", False) \
.SetDisplay("Inverse Signal", "Use inverse correlation logic", "Index Reference")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._index_security_param = self.Param[Security]("IndexSecurity") \
.SetDisplay("Index Security", "Reference index security", "Data")
self._index_fast = 0.0
self._index_slow = 0.0
self._index_ready = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._bar_index = 0
self._last_trade_bar = 0
@property
def FastEmaLength(self): return self._fast_ema_len.Value
@FastEmaLength.setter
def FastEmaLength(self, v): self._fast_ema_len.Value = v
@property
def SlowEmaLength(self): return self._slow_ema_len.Value
@SlowEmaLength.setter
def SlowEmaLength(self, v): self._slow_ema_len.Value = v
@property
def IndexFastEmaLength(self): return self._index_fast_ema_len.Value
@IndexFastEmaLength.setter
def IndexFastEmaLength(self, v): self._index_fast_ema_len.Value = v
@property
def IndexSlowEmaLength(self): return self._index_slow_ema_len.Value
@IndexSlowEmaLength.setter
def IndexSlowEmaLength(self, v): self._index_slow_ema_len.Value = v
@property
def SkipIndexReference(self): return self._skip_index.Value
@SkipIndexReference.setter
def SkipIndexReference(self, v): self._skip_index.Value = v
@property
def InverseSignal(self): return self._inverse_signal.Value
@InverseSignal.setter
def InverseSignal(self, v): self._inverse_signal.Value = v
@property
def IndexSecurity(self): return self._index_security_param.Value
@IndexSecurity.setter
def IndexSecurity(self, v): self._index_security_param.Value = v
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
def OnReseted(self):
super(altcoin_index_correlation_strategy, self).OnReseted()
self._index_fast = 0.0
self._index_slow = 0.0
self._index_ready = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._bar_index = 0
self._last_trade_bar = 0
def OnStarted2(self, time):
super(altcoin_index_correlation_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastEmaLength
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowEmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self.ProcessMainCandle).Start()
if self.IndexSecurity is not None:
index_fast_ema = ExponentialMovingAverage()
index_fast_ema.Length = self.IndexFastEmaLength
index_slow_ema = ExponentialMovingAverage()
index_slow_ema.Length = self.IndexSlowEmaLength
index_sub = self.SubscribeCandles(self.CandleType, security=self._index_security)
index_sub.Bind(index_fast_ema, index_slow_ema, self.ProcessIndexCandle).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 ProcessIndexCandle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
self._index_fast = float(fast)
self._index_slow = float(slow)
self._index_ready = True
def ProcessMainCandle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast = float(fast)
slow = float(slow)
self._bar_index += 1
if self._prev_fast == 0 or self._prev_slow == 0:
self._prev_fast = fast
self._prev_slow = slow
return
cooldown_ok = self._bar_index - self._last_trade_bar > 5
cross_over = self._prev_fast <= self._prev_slow and fast > slow
cross_under = self._prev_fast >= self._prev_slow and fast < slow
if self.SkipIndexReference or not self._index_ready:
go_long = cross_over
go_short = cross_under
else:
go_long = cross_over and self._index_fast > self._index_slow
go_short = cross_under and self._index_fast < self._index_slow
if self.InverseSignal:
go_long = cross_over and self._index_fast < self._index_slow
go_short = cross_under and self._index_fast > self._index_slow
if go_long and self.Position <= 0 and cooldown_ok:
self.BuyMarket()
self._last_trade_bar = self._bar_index
elif go_short and self.Position >= 0 and cooldown_ok:
self.SellMarket()
self._last_trade_bar = self._bar_index
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return altcoin_index_correlation_strategy()