Market EKG
Market EKG strategy compares the previous bar's OHLC values with the averages of two earlier bars. It buys when the average of the two older closing prices is above the latest close and sells when below.
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;
public class MarketEKGStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _deviationThresholdPercent;
private readonly StrategyParam<int> _cooldownBars;
private ICandleMessage _prev1;
private ICandleMessage _prev2;
private ICandleMessage _prev3;
private int _barsFromSignal;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal DeviationThresholdPercent { get => _deviationThresholdPercent.Value; set => _deviationThresholdPercent.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public MarketEKGStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
_deviationThresholdPercent = Param(nameof(DeviationThresholdPercent), 0.15m);
_cooldownBars = Param(nameof(CooldownBars), 16);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prev1 = null;
_prev2 = null;
_prev3 = null;
_barsFromSignal = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prev1 = null;
_prev2 = null;
_prev3 = null;
_barsFromSignal = CooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_prev1 != null && _prev2 != null && _prev3 != null)
{
var avgClose = (_prev3.ClosePrice + _prev2.ClosePrice) / 2m;
var diffClose = avgClose - _prev1.ClosePrice;
var basePrice = _prev1.ClosePrice;
var diffPercent = basePrice == 0m ? 0m : Math.Abs(diffClose) / basePrice * 100m;
_barsFromSignal++;
var canSignal = _barsFromSignal >= CooldownBars;
if (canSignal && diffClose > 0 && diffPercent >= DeviationThresholdPercent && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (canSignal && diffClose < 0 && diffPercent >= DeviationThresholdPercent && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
_prev3 = _prev2;
_prev2 = _prev1;
_prev1 = candle;
}
}
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.Strategies import Strategy
class market_ekg_strategy(Strategy):
"""
Market EKG: price deviation pattern detection on recent candles.
"""
def __init__(self):
super(market_ekg_strategy, self).__init__()
self._deviation_threshold = self.Param("DeviationThresholdPercent", 0.15).SetDisplay("Deviation %", "Min deviation pct", "Signals")
self._cooldown_bars = self.Param("CooldownBars", 16).SetDisplay("Cooldown", "Bars between signals", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Candles", "General")
self._prev1 = None
self._prev2 = None
self._prev3 = None
self._bars_from_signal = 16
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(market_ekg_strategy, self).OnReseted()
self._prev1 = None
self._prev2 = None
self._prev3 = None
self._bars_from_signal = self._cooldown_bars.Value
def OnStarted2(self, time):
super(market_ekg_strategy, self).OnStarted2(time)
self._bars_from_signal = self._cooldown_bars.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._prev1 is not None and self._prev2 is not None and self._prev3 is not None:
avg_close = (float(self._prev3.ClosePrice) + float(self._prev2.ClosePrice)) / 2.0
diff_close = avg_close - float(self._prev1.ClosePrice)
base_price = float(self._prev1.ClosePrice)
diff_pct = abs(diff_close) / base_price * 100.0 if base_price != 0 else 0.0
self._bars_from_signal += 1
can_signal = self._bars_from_signal >= self._cooldown_bars.Value
threshold = float(self._deviation_threshold.Value)
if can_signal and diff_close > 0 and diff_pct >= threshold and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif can_signal and diff_close < 0 and diff_pct >= threshold and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
self._prev3 = self._prev2
self._prev2 = self._prev1
self._prev1 = candle
def CreateClone(self):
return market_ekg_strategy()