The KPrmSt Cross strategy is a port of the MetaTrader 5 expert exp_kprmst.mq5. It uses a stochastic-like oscillator known as KPrmSt to capture reversals when the oscillator's main line crosses the signal line.
The strategy subscribes to candles of configurable timeframe and calculates the Stochastic indicator (used as a KPrmSt approximation). When the %K line crosses below the %D line, it opens a long position; when %K crosses above %D, it opens a short position. Existing positions are reversed accordingly.
Parameters
Candle Type – timeframe of candles used for calculations.
K Period – number of bars for calculating the main line.
D Period – period for smoothing the signal line.
Slowing – additional smoothing applied to %K.
Stop Loss – protective loss in price units. Set to 0 to disable.
Take Profit – target profit in price units. Set to 0 to disable.
Trading Logic
The strategy listens for finished candles only.
The stochastic oscillator values are stored to detect crossovers.
When %K falls below %D after being above it, a long position is opened or the short position is closed.
When %K rises above %D after being below it, a short position is opened or the long position is closed.
Optional stop-loss and take-profit levels close the position when reached.
Notes
The KPrmSt indicator from the original expert is approximated by StockSharp's Stochastic indicator.
Money management options from the original script are not implemented.
The strategy requires market data feed and order routing supported by StockSharp.
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>
/// KPrmSt cross strategy based on the stochastic oscillator.
/// Opens long when %K crosses above %D from below.
/// Opens short when %K crosses below %D from above.
/// </summary>
public class KprmStCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private StochasticOscillator _stochastic;
private decimal? _prevK;
private decimal? _prevD;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public KprmStCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for indicator calculation", "General");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stochastic = default;
_prevK = null;
_prevD = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stochastic = new StochasticOscillator();
Indicators.Add(_stochastic);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).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);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var stochResult = _stochastic.Process(candle);
if (!stochResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var stochVal = (StochasticOscillatorValue)stochResult;
if (stochVal.K is not decimal k || stochVal.D is not decimal d)
return;
if (_prevK.HasValue && _prevD.HasValue)
{
var wasBelow = _prevK.Value < _prevD.Value;
var isAbove = k > d;
// K crosses above D -> buy
if (wasBelow && isAbove && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// K crosses below D -> sell
else if (!wasBelow && !isAbove && _prevK.Value > _prevD.Value && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevK = k;
_prevD = d;
}
}
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 StochasticOscillator, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class kprm_st_cross_strategy(Strategy):
"""
KPrmSt cross strategy using Stochastic K/D crossover.
Buys when K crosses above D, sells when K crosses below D.
Uses StartProtection for percentage-based SL/TP.
"""
def __init__(self):
super(kprm_st_cross_strategy, self).__init__()
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame", "General")
self._stochastic = None
self._prev_k = None
self._prev_d = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(kprm_st_cross_strategy, self).OnReseted()
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(kprm_st_cross_strategy, self).OnStarted2(time)
self._stochastic = StochasticOscillator()
self.Indicators.Add(self._stochastic)
self.StartProtection(
Unit(self._take_profit_pct.Value, UnitTypes.Percent),
Unit(self._stop_loss_pct.Value, UnitTypes.Percent),
useMarketOrders=True)
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
cv = CandleIndicatorValue(self._stochastic, candle)
stoch_result = self._stochastic.Process(cv)
if not stoch_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
k = stoch_result.K
d = stoch_result.D
if k is None or d is None:
return
k = float(k)
d = float(d)
if self._prev_k is not None and self._prev_d is not None:
was_below = self._prev_k < self._prev_d
is_above = k > d
if was_below and is_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif not was_below and not is_above and self._prev_k > self._prev_d and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_k = k
self._prev_d = d
def CreateClone(self):
return kprm_st_cross_strategy()