XKRI Histogram Strategy
Strategy based on the Kairi Relative Index (KRI) smoothed by an exponential moving average. The system looks for local minima and maxima of the smoothed oscillator and enters long or short positions when a reversal pattern appears.
Details
- Entry Criteria:
- Long:
Kri[1] < Kri[2] && Kri[0] > Kri[1] - Short:
Kri[1] > Kri[2] && Kri[0] < Kri[1]
- Long:
- Long/Short: Both
- Stops: Point take profit and stop loss
- Default Values:
KriPeriod= 20SmoothPeriod= 7TakeProfit= 2000StopLoss= 1000CandleType= TimeSpan.FromHours(4).TimeFrame()
- Filters:
- Category: Reversal
- Direction: Both
- Indicators: Kairi, EMA
- Stops: Yes
- Complexity: Beginner
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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 XkriHistogramStrategy : Strategy
{
private readonly StrategyParam<int> _kriPeriod;
private readonly StrategyParam<int> _smoothPeriod;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _smooth;
private decimal _last;
private decimal _prev;
private decimal _prev2;
private int _valueCount;
public int KriPeriod { get => _kriPeriod.Value; set => _kriPeriod.Value = value; }
public int SmoothPeriod { get => _smoothPeriod.Value; set => _smoothPeriod.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public XkriHistogramStrategy()
{
_kriPeriod = Param(nameof(KriPeriod), 20)
.SetDisplay("KRI Period", "Base moving average period", "Indicators");
_smoothPeriod = Param(nameof(SmoothPeriod), 7)
.SetDisplay("Smooth Period", "EMA smoothing period", "Indicators");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Protection");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Protection");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_smooth = default;
_last = 0;
_prev = 0;
_prev2 = 0;
_valueCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma = new ExponentialMovingAverage { Length = KriPeriod };
_smooth = new ExponentialMovingAverage { Length = SmoothPeriod };
Indicators.Add(_smooth);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ma, (candle, maValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (maValue == 0) return;
var kri = 100m * (candle.ClosePrice - maValue) / maValue;
var smoothResult = _smooth.Process(kri, candle.OpenTime, true);
if (!smoothResult.IsFormed)
return;
var smooth = smoothResult.ToDecimal();
_prev2 = _prev;
_prev = _last;
_last = smooth;
_valueCount++;
if (_valueCount < 3)
return;
var longSignal = _prev < _prev2 && _last > _prev && Position <= 0;
var shortSignal = _prev > _prev2 && _last < _prev && Position >= 0;
if (longSignal)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (shortSignal)
{
if (Position > 0) SellMarket();
SellMarket();
}
}).Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _smooth);
DrawOwnTrades(area);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class xkri_histogram_strategy(Strategy):
def __init__(self):
super(xkri_histogram_strategy, self).__init__()
self._kri_period = self.Param("KriPeriod", 20) \
.SetDisplay("KRI Period", "Base moving average period", "Indicators")
self._smooth_period = self.Param("SmoothPeriod", 7) \
.SetDisplay("Smooth Period", "EMA smoothing period", "Indicators")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Protection")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for candles", "General")
self._smooth = None
self._last = 0.0
self._prev = 0.0
self._prev2 = 0.0
self._value_count = 0
@property
def kri_period(self):
return self._kri_period.Value
@property
def smooth_period(self):
return self._smooth_period.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(xkri_histogram_strategy, self).OnReseted()
self._smooth = None
self._last = 0.0
self._prev = 0.0
self._prev2 = 0.0
self._value_count = 0
def OnStarted2(self, time):
super(xkri_histogram_strategy, self).OnStarted2(time)
ma = ExponentialMovingAverage()
ma.Length = self.kri_period
self._smooth = ExponentialMovingAverage()
self._smooth.Length = self.smooth_period
self.Indicators.Add(self._smooth)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self.on_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._smooth)
self.DrawOwnTrades(area)
def on_candle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
if ma_value == 0:
return
kri = 100 * (candle.ClosePrice - ma_value) / ma_value
smooth_result = process_float(self._smooth, kri, candle.OpenTime, True)
if not smooth_result.IsFormed:
return
smooth = float(smooth_result)
self._prev2 = self._prev
self._prev = self._last
self._last = smooth
self._value_count += 1
if self._value_count < 3:
return
long_signal = self._prev < self._prev2 and self._last > self._prev and self.Position <= 0
short_signal = self._prev > self._prev2 and self._last < self._prev and self.Position >= 0
if long_signal:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif short_signal:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return xkri_histogram_strategy()