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;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class panel_joke_strategy(Strategy):
def __init__(self):
super(panel_joke_strategy, self).__init__()
self._enable_autopilot = self.Param("EnableAutopilot", True) \
.SetDisplay("Enable Autopilot", "Automatically trade based on signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for incoming candles", "General")
self._prev_open = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._has_prev = False
@property
def enable_autopilot(self):
return self._enable_autopilot.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(panel_joke_strategy, self).OnReseted()
self._prev_open = 0.0
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(panel_joke_strategy, self).OnStarted2(time)
warmup = ExponentialMovingAverage()
warmup.Length = 5
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(warmup, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle, _warmup_val):
if candle.State != CandleStates.Finished:
return
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
if not self._has_prev:
self._prev_open = o
self._prev_high = h
self._prev_low = l
self._prev_close = c
self._has_prev = True
return
buy = 0
sell = 0
if o > self._prev_open:
buy += 1
else:
sell += 1
if h > self._prev_high:
buy += 1
else:
sell += 1
if l > self._prev_low:
buy += 1
else:
sell += 1
avg_hl = (h + l) / 2.0
prev_avg_hl = (self._prev_high + self._prev_low) / 2.0
if avg_hl > prev_avg_hl:
buy += 1
else:
sell += 1
if c > self._prev_close:
buy += 1
else:
sell += 1
avg_hlc = (h + l + c) / 3.0
prev_avg_hlc = (self._prev_high + self._prev_low + self._prev_close) / 3.0
if avg_hlc > prev_avg_hlc:
buy += 1
else:
sell += 1
avg_hlcc = (h + l + 2.0 * c) / 4.0
prev_avg_hlcc = (self._prev_high + self._prev_low + 2.0 * self._prev_close) / 4.0
if avg_hlcc > prev_avg_hlcc:
buy += 1
else:
sell += 1
if buy > sell and self.Position <= 0:
self.BuyMarket()
elif sell > buy and self.Position >= 0:
self.SellMarket()
self._prev_open = o
self._prev_high = h
self._prev_low = l
self._prev_close = c
def CreateClone(self):
return panel_joke_strategy()