Diese Strategie ist eine Anpassung des ursprünglichen MetaTrader-Experts aus MQL/16214. Sie verwendet den Balance of Power (BOP)-Indikator, um Momentum-Änderungen im Markt zu erkennen.
Logik
Die Strategie berechnet den Balance of Power für jede abgeschlossene Kerze:
\(BOP = \frac{Close - Open}{High - Low}\)
Drei aufeinanderfolgende BOP-Werte werden verglichen.
Wenn der vorherige Wert niedriger als der davor liegende Wert ist und der aktuelle Wert höher als der vorherige, dreht BOP nach oben und die Strategie eröffnet eine Long-Position.
Wenn der vorherige Wert höher als der davor liegende Wert ist und der aktuelle Wert niedriger als der vorherige, dreht BOP nach unten und die Strategie eröffnet eine Short-Position.
Die Position wird nur nach einer abgeschlossenen Kerze geändert, um Fehlsignale zu vermeiden.
Parameter
CandleType – Zeitrahmen der Kerzen für Berechnungen. Standard sind Vier-Stunden-Kerzen.
Hinweise
Dieser Port konzentriert sich auf das Kernverhalten der ursprünglichen Strategie und implementiert nicht die erweiterten Geldmanagement-Optionen der MQL-Version.
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>
/// Balance of Power histogram strategy that trades confirmed zero-line reversals.
/// </summary>
public class BalanceOfPowerHistogramStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _signalLevel;
private readonly StrategyParam<int> _cooldownCandles;
private int _barsSinceSignal;
private decimal? _prevBop;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Minimum Balance of Power value required for a signal.
/// </summary>
public decimal SignalLevel
{
get => _signalLevel.Value;
set => _signalLevel.Value = value;
}
/// <summary>
/// Minimum number of finished candles between entries.
/// </summary>
public int CooldownCandles
{
get => _cooldownCandles.Value;
set => _cooldownCandles.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="BalanceOfPowerHistogramStrategy"/>.
/// </summary>
public BalanceOfPowerHistogramStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_signalLevel = Param(nameof(SignalLevel), 0.30m)
.SetDisplay("Signal Level", "Minimum BOP value for confirmed reversals", "Signal");
_cooldownCandles = Param(nameof(CooldownCandles), 3)
.SetDisplay("Cooldown Candles", "Minimum finished candles between entries", "Signal");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_barsSinceSignal = CooldownCandles;
_prevBop = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_barsSinceSignal = CooldownCandles;
_prevBop = null;
var bop = new BalanceOfPower();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(bop, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal bop)
{
if (candle.State != CandleStates.Finished)
return;
var prevBop = _prevBop;
_prevBop = bop;
_barsSinceSignal++;
if (!IsFormedAndOnlineAndAllowTrading() || prevBop is null)
return;
if (_barsSinceSignal < CooldownCandles)
return;
var turnedUp = prevBop <= -SignalLevel && bop >= SignalLevel;
var turnedDown = prevBop >= SignalLevel && bop <= -SignalLevel;
if (turnedUp && Position <= 0)
{
BuyMarket();
_barsSinceSignal = 0;
}
else if (turnedDown && Position >= 0)
{
SellMarket();
_barsSinceSignal = 0;
}
}
}
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 BalanceOfPower
from StockSharp.Algo.Strategies import Strategy
class balance_of_power_histogram_strategy(Strategy):
def __init__(self):
super(balance_of_power_histogram_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._signal_level = self.Param("SignalLevel", 0.30) \
.SetDisplay("Signal Level", "Minimum BOP value for confirmed reversals", "Signal")
self._cooldown_candles = self.Param("CooldownCandles", 3) \
.SetDisplay("Cooldown Candles", "Minimum finished candles between entries", "Signal")
self._bars_since_signal = 0
self._prev_bop = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def signal_level(self):
return self._signal_level.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
def OnReseted(self):
super(balance_of_power_histogram_strategy, self).OnReseted()
self._bars_since_signal = int(self.cooldown_candles)
self._prev_bop = None
def OnStarted2(self, time):
super(balance_of_power_histogram_strategy, self).OnStarted2(time)
self._bars_since_signal = int(self.cooldown_candles)
self._prev_bop = None
bop = BalanceOfPower()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(bop, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle, bop):
if candle.State != CandleStates.Finished:
return
bop = float(bop)
prev_bop = self._prev_bop
self._prev_bop = bop
self._bars_since_signal += 1
if prev_bop is None:
return
if self._bars_since_signal < int(self.cooldown_candles):
return
sl = float(self.signal_level)
turned_up = prev_bop <= -sl and bop >= sl
turned_down = prev_bop >= sl and bop <= -sl
if turned_up and self.Position <= 0:
self.BuyMarket()
self._bars_since_signal = 0
elif turned_down and self.Position >= 0:
self.SellMarket()
self._bars_since_signal = 0
def CreateClone(self):
return balance_of_power_histogram_strategy()