GitHub で見る

Panel Joke Strategy

This strategy converts the original MetaTrader panel-joke system into StockSharp. It compares the current candle with the previous one across seven price metrics (open, high, low, average of high and low, close, average of high/low/close, and weighted average of high/low/close). Each metric that increased counts toward a potential long setup; each decrease counts toward a short setup.

When the Enable Autopilot parameter is true, the strategy automatically opens or reverses positions based on which side has more points. No additional indicators or stop rules are used.

Details

  • Entry Criteria:
    • Long: Buy counter > Sell counter.
    • Short: Sell counter > Buy counter.
  • Exit Criteria: Reverse when the opposite signal appears.
  • Stops: None.
  • Default Values:
    • Enable Autopilot = true.
    • Candle Type = 5-minute time frame.
  • Filters:
    • Category: Price action
    • Direction: Both
    • Indicators: None
    • Stops: No
    • Complexity: Basic
    • Timeframe: Intraday
    • Seasonality: No
    • Neural networks: No
    • Divergence: No
    • Risk level: High
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>
/// Simple heuristic strategy comparing current and previous candles.
/// Counts how many price components moved up or down and trades on majority.
/// </summary>
public class PanelJokeStrategy : Strategy
{
	private readonly StrategyParam<bool> _enableAutopilot;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevOpen;
	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _prevClose;
	private bool _hasPrev;

	/// <summary>
	/// Enables automatic order execution.
	/// </summary>
	public bool EnableAutopilot
	{
		get => _enableAutopilot.Value;
		set => _enableAutopilot.Value = value;
	}

	/// <summary>
	/// Candle type used for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes parameters.
	/// </summary>
	public PanelJokeStrategy()
	{
		_enableAutopilot = Param(nameof(EnableAutopilot), true)
			.SetDisplay("Enable Autopilot", "Automatically trade based on signals", "General");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for incoming candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevOpen = 0;
		_prevHigh = 0;
		_prevLow = 0;
		_prevClose = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var warmup = new ExponentialMovingAverage { Length = 5 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(warmup, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal _warmupVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevOpen = candle.OpenPrice;
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_prevClose = candle.ClosePrice;
			_hasPrev = true;
			return;
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevOpen = candle.OpenPrice;
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_prevClose = candle.ClosePrice;
			return;
		}

		var buy = 0;
		var sell = 0;

		// Compare each OHLC component
		if (candle.OpenPrice > _prevOpen)
			buy++;
		else
			sell++;

		if (candle.HighPrice > _prevHigh)
			buy++;
		else
			sell++;

		if (candle.LowPrice > _prevLow)
			buy++;
		else
			sell++;

		var avgHL = (candle.HighPrice + candle.LowPrice) / 2m;
		var prevAvgHL = (_prevHigh + _prevLow) / 2m;
		if (avgHL > prevAvgHL)
			buy++;
		else
			sell++;

		if (candle.ClosePrice > _prevClose)
			buy++;
		else
			sell++;

		var avgHLC = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
		var prevAvgHLC = (_prevHigh + _prevLow + _prevClose) / 3m;
		if (avgHLC > prevAvgHLC)
			buy++;
		else
			sell++;

		var avgHLCC = (candle.HighPrice + candle.LowPrice + 2m * candle.ClosePrice) / 4m;
		var prevAvgHLCC = (_prevHigh + _prevLow + 2m * _prevClose) / 4m;
		if (avgHLCC > prevAvgHLCC)
			buy++;
		else
			sell++;

		if (buy > sell && Position <= 0)
			BuyMarket();
		else if (sell > buy && Position >= 0)
			SellMarket();

		_prevOpen = candle.OpenPrice;
		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevClose = candle.ClosePrice;
	}
}