GitHub で見る

KPrmSt Cross Strategy

Overview

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

  1. The strategy listens for finished candles only.
  2. The stochastic oscillator values are stored to detect crossovers.
  3. When %K falls below %D after being above it, a long position is opened or the short position is closed.
  4. When %K rises above %D after being below it, a short position is opened or the long position is closed.
  5. 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;
	}
}