Diese Strategie ist eine direkte StockSharp-Umsetzung des MetaTrader 4-Expertenberaters Pinball_machine.mq4. Der ursprüngliche Roboter zeichnete
Bei jedem eingehenden Tick wurden zufällige ganze Zahlen eingegeben und eine Market-Order eröffnet, wenn zwei dieser Ziehungen übereinstimmten. Die StockSharp-Version
behält das gleiche Verhalten im Lotteriestil bei: Bei jeder fertigen Kerze des ausgewählten Zeitrahmens führt der Algorithmus zwei Paare aus
Zufallsziehungen und Einstieg in eine Long- oder Short-Marktposition, wenn das entsprechende Paar gleiche Werte enthält. Stop-Loss und Take-Profit
Die Abstände werden bei jeder Auswertung ebenfalls zufällig ausgewählt, wodurch das Gefühl der ursprünglichen „Flipper“-Routine reproduziert wird, bei der Trades ein- und aussteigen
unvorhersehbar ausgeht.
Handelslogik
Abonnieren Sie Kerzen, die durch den Parameter CandleType definiert sind, und warten Sie, bis sich die Balken vollständig gebildet haben.
Generieren Sie für jede fertige Kerze vier gleichmäßig verteilte Ganzzahlen in [0, RandomMaxValue]. Das erste Paar gehört zum
potenzieller Long-Einstieg, das zweite Paar gehört zum potenziellen Short-Einstieg.
Zeichnen Sie zwei zusätzliche Ganzzahlen zwischen MinStopLossPoints/MaxStopLossPoints und MinTakeProfitPoints/MaxTakeProfitPoints
Bestimmen Sie die Schutzabstände (ausgedrückt in Preisschritten), die beiden Seiten der Bewertung gemeinsam sind.
Wenn die erste und die zweite zufällige Ganzzahl übereinstimmen, senden Sie eine Marktkauforder mit dem Volumen TradeVolume. Wenn der dritte und vierte
Wenn die Werte übereinstimmen, erteilen Sie einen Marktverkaufsauftrag mit dem gleichen Volumen. Beide Bedingungen können innerhalb derselben Kerze ausgelöst werden, genau wie in
die MQL-Version, in der Kauf- und Verkaufsaufträge unabhängige Ereignisse waren.
Fügen Sie sofort eine Stop-Loss- und eine Take-Profit-Order hinzu (wenn die gezogene Distanz größer als Null ist). Die Abstände werden interpretiert
als Vielfaches des PriceStep des Instruments, was den in MetaTrader verwendeten Multiplikator Point widerspiegelt.
Auftragsverwaltung und Risikokontrolle
StartProtection() wird aufgerufen, wenn die Strategie startet, damit StockSharp Schutzanordnungen im Namen der Strategie verwaltet.
Jeder Eintrag misst die resultierende Position (Position ± TradeVolume) und übergibt sie an SetStopLoss und SetTakeProfit, die
ermöglicht es der Plattform, Schutzaufträge zu konsolidieren, selbst wenn mehrere Geschäfte gleichzeitig ausgeführt werden.
Wenn entweder der minimale oder der maximale Abstandsparameter auf Null oder eine negative Zahl eingestellt sind, gilt der entsprechende Schutz
für diesen Zyklus übersprungen.
Parameter
Parameter
Beschreibung
TradeVolume
Auftragsgröße in Losen/Verträgen, die für jeden zufälligen Eintrag eingereicht werden.
CandleType
Zeitrahmen der Kerzen, die die Zufallsziehungen auslösen. Kürzere Zeiträume emulieren das ursprüngliche Tick-basierte EA genauer.
RandomMaxValue
Inklusive Obergrenze für die ganzzahligen Ziehungen. Ein größerer Wert verringert die Wahrscheinlichkeit übereinstimmender Zahlen und verringert daher die Handelshäufigkeit.
MinStopLossPoints
Untergrenze (in Preisschritten) für die zufällig generierte Stop-Loss-Distanz.
MaxStopLossPoints
Obergrenze (in Preisschritten) für die Stop-Loss-Distanz.
MinTakeProfitPoints
Untergrenze (in Preisschritten) für die zufällig generierte Take-Profit-Distanz.
MaxTakeProfitPoints
Obergrenze (in Preisschritten) für die Take-Profit-Distanz.
RandomSeed
Startwert des Pseudozufallszahlengenerators. Null hält das Verhalten zeitbasiert, jeder andere Wert macht die Sequenz reproduzierbar.
Hinweise zur Implementierung
Das MetaTrader-Skript war tickgesteuert; Der Port StockSharp verwendet Kerzenvervollständigungen, da der Port API auf hoher Ebene mit Zeitreihenereignissen arbeitet. Durch das Setzen eines sehr kurzen CandleType (z. B. eine Sekunde oder Tick-Kerzen) wird die Schnelligkeit des Originals wiederhergestellt.
Stop-Loss- und Take-Profit-Werte werden einmal pro Auswertung generiert und sowohl für den Long- als auch den Short-Zweig wiederverwendet, genau wie in der Quelle EA.
Stellen Sie sicher, dass das gehandelte Instrument einen gültigen PriceStep aufweist, andernfalls müssen die in Punkten ausgedrückten Schutzabstände möglicherweise manuell angepasst werden.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Pinball Machine: Pseudo-random entry with ATR-based risk management.
/// Uses candle hash to generate deterministic random signals.
/// </summary>
public class PinballMachineRandomDrawStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private int _candleCount;
public PinballMachineRandomDrawStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_candleCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_candleCount = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
_candleCount++;
var close = candle.ClosePrice;
// Exit management
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
// Pseudo-random entry based on candle price hash
if (Position == 0)
{
var hash = (int)(close * 100m) ^ _candleCount;
var mod = Math.Abs(hash) % 10;
if (mod < 3)
{
_entryPrice = close;
BuyMarket();
}
else if (mod > 6)
{
_entryPrice = close;
SellMarket();
}
}
}
}
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.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class pinball_machine_random_draw_strategy(Strategy):
def __init__(self):
super(pinball_machine_random_draw_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period for stops", "Indicators")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(pinball_machine_random_draw_strategy, self).OnReseted()
self._entry_price = 0
self._candle_count = 0
def OnStarted2(self, time):
super(pinball_machine_random_draw_strategy, self).OnStarted2(time)
self._entry_price = 0
self._candle_count = 0
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0:
return
self._candle_count += 1
close = candle.ClosePrice
if self.Position > 0:
if close >= self._entry_price + atr_val * 2 or close <= self._entry_price - atr_val * 1.5:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if close <= self._entry_price - atr_val * 2 or close >= self._entry_price + atr_val * 1.5:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
h = int(float(close) * 100) ^ self._candle_count
mod = abs(h) % 10
if mod < 3:
self._entry_price = close
self.BuyMarket()
elif mod > 6:
self._entry_price = close
self.SellMarket()
def CreateClone(self):
return pinball_machine_random_draw_strategy()