Diese Strategie ist eine High-Level-C#-Portierung des MetaTrader 4 Expert Advisors, gespeichert in MQL/7610/Simplepivot_www_forex-instruments_info.mq4. Das ursprüngliche Programm vergleicht den Eröffnungspreis jeder neuen Kerze mit dem vorherigen Kerzenbereich und wechselt zwischen Long- und Short-Marktpositionen. Die StockSharp-Version behält das gleiche Verhalten bei, indem sie sich ausschließlich auf High-Level-Helfer wie SubscribeCandles, Bind, BuyMarket, SellMarket und ClosePosition verlässt.
Die umgewandelte Logik:
Wartet darauf, dass eine fertige Kerze die offenen, hohen und niedrigen Werte erhält.
Verwendet den vorherigen Kerzenbereich, um einen einfachen Pivot in der Mitte zu erstellen.
Eröffnet eine neue Long-Position, wenn die aktuelle Kerze in der unteren Hälfte der Spanne öffnet oder Lücken über dem vorherigen Hoch aufweist.
Eröffnet eine neue Short-Position, wenn die aktuelle Kerze in der oberen Hälfte der Spanne öffnet.
Schließt immer die bestehende Position, bevor sie in die entgegengesetzte Richtung einsteigt, und reproduziert so das Single-Ticket-Verhalten der MQL-Version.
Im ursprünglichen Expert Advisor sind keine Stop-Loss- oder Take-Profit-Levels implementiert, daher wird die Position nur umgekehrt, wenn eine neue Kerze eine andere Richtung vorgibt.
Parameter
Name
Standard
Beschreibung
OrderVolume
1
Market-Order-Volumen, das bei der Eingabe einer Position verwendet wird.
CandleType
Zeitrahmen von 1 Minute
Vom Datenfeed angeforderter Kerzentyp.
Details zur Handelslogik
Die allererste fertige Kerze wird gespeichert und dient als Referenz für die nächste Entscheidung. Es wird kein Auftrag gesendet, bis eine vollständige Kerze zur Analyse vorliegt.
Für jede weitere fertige Kerze:
Berechnen Sie pivot = (previousHigh + previousLow) / 2.
Wenn Open < previousHighundOpen > pivot, bereitet die Strategie einen kurzen Eintrag vor.
Andernfalls bereitet es einen Long-Einstieg vor (dies umfasst Eröffnungen in der unteren Hälfte, Öffnungen in Höhe des Pivots und alle Lücken über dem vorherigen Hoch oder unter dem vorherigen Tief).
Wenn die Strategie bereits eine Position in der gewählten Richtung hält, wird das Signal ignoriert, um eine doppelte Auszahlung des Spreads zu vermeiden – was die frühe Rendite widerspiegelt, die im MQL-Code gefunden wird.
Ändert sich die Richtung, wird die aktuelle Position über ClosePosition() geschlossen und eine neue Market-Order über OrderVolume gesendet.
Der vorherige Hoch-/Tiefpuffer wird mit der zuletzt abgeschlossenen Kerze aktualisiert, um die nächste Entscheidung voranzutreiben.
Risikomanagement
Der umgewandelte Algorithmus berücksichtigt keine Stopps oder Gewinnziele. Die Positionsgröße wird nur durch den Parameter OrderVolume gesteuert, daher sollte das Risiko extern verwaltet werden (z. B. durch Anpassung des Volumens oder durch Kombination der Strategie mit Schutzmaßnahmen auf Kontoebene).
Visualisierung
Wenn ein Diagrammbereich verfügbar ist, zeichnet die Strategie die angeforderten Kerzen und die ausgeführten Trades auf, was dabei hilft, die Pivot-Flips während Backtests zu validieren.
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simple pivot-based strategy converted from the MetaTrader expert advisor in MQL/7610.
/// The strategy compares the current candle open with the previous candle range to decide
/// whether the next trade should be long or short.
/// </summary>
public class SimplePivotFlipStrategy : Strategy
{
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<DataType> _candleType;
private decimal _previousHigh;
private decimal _previousLow;
private bool _hasPreviousCandle;
/// <summary>
/// Order volume used for market entries.
/// </summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>
/// Candle type used for price subscription.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SimplePivotFlipStrategy"/> class.
/// </summary>
public SimplePivotFlipStrategy()
{
_orderVolume = Param(nameof(OrderVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Market order volume used for entries.", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for pivot calculation.", "Data");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousHigh = 0m;
_previousLow = 0m;
_hasPreviousCandle = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPreviousCandle)
{
// Store the first completed candle to build the reference range.
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
_hasPreviousCandle = true;
return;
}
// Calculate the pivot as the midpoint of the previous candle range.
var pivot = (_previousHigh + _previousLow) / 2m;
var desiredSide = Sides.Buy;
// If the new candle opens inside the upper half of the previous range we go short.
if (candle.OpenPrice < _previousHigh && candle.OpenPrice > pivot)
desiredSide = Sides.Sell;
// Skip re-entry if we already hold a position in the desired direction.
if ((desiredSide == Sides.Buy && Position > 0) || (desiredSide == Sides.Sell && Position < 0))
{
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
return;
}
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
if (desiredSide == Sides.Buy)
{
BuyMarket();
}
else
{
SellMarket();
}
// Update reference range for the next candle.
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class simple_pivot_flip_strategy(Strategy):
"""Simple pivot-based strategy. Compares current candle open with the previous
candle range midpoint. If open is in the upper half of the previous range, sell;
otherwise buy. Closes existing position before reversing."""
def __init__(self):
super(simple_pivot_flip_strategy, self).__init__()
self._order_volume = self.Param("OrderVolume", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Order Volume", "Market order volume used for entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1))) \
.SetDisplay("Candle Type", "Type of candles used for pivot calculation", "Data")
self._previous_high = 0.0
self._previous_low = 0.0
self._has_previous_candle = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def OrderVolume(self):
return self._order_volume.Value
def OnReseted(self):
super(simple_pivot_flip_strategy, self).OnReseted()
self._previous_high = 0.0
self._previous_low = 0.0
self._has_previous_candle = False
def OnStarted2(self, time):
super(simple_pivot_flip_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
open_price = float(candle.OpenPrice)
if not self._has_previous_candle:
self._previous_high = high
self._previous_low = low
self._has_previous_candle = True
return
# Calculate the pivot as the midpoint of the previous candle range
pivot = (self._previous_high + self._previous_low) / 2.0
# Default to buy; if open is in upper half of previous range, sell
desired_buy = True
if open_price < self._previous_high and open_price > pivot:
desired_buy = False
# Skip re-entry if already holding position in desired direction
if desired_buy and self.Position > 0:
self._previous_high = high
self._previous_low = low
return
if not desired_buy and self.Position < 0:
self._previous_high = high
self._previous_low = low
return
# Close existing position
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
# Open new position
if desired_buy:
self.BuyMarket()
else:
self.SellMarket()
# Update reference range for next candle
self._previous_high = high
self._previous_low = low
def CreateClone(self):
return simple_pivot_flip_strategy()