Die Williams-AO-+-AC-Strategie wandelt den MetaTrader-4 Expert "Williams_AOAC" in die High-Level-Strategie-API von StockSharp um. Der Ansatz kombiniert mehrere Bill-Williams-Werkzeuge, um Momentum-Schübe auf dem Stundenchart (Standardzeitrahmen) zu finden:
Bollinger-Band-Filter - die Strategie handelt nur, wenn die Bandbreite innerhalb eines konfigurierbaren Punktebereichs liegt, was sowohl flache Märkte als auch übermäßige Volatilität vermeidet.
Relative-Strength-Index-Bestätigung - der RSI muss für Longs über einem bullischen Schwellenwert oder für Shorts unter einem bärischen Schwellenwert liegen.
Awesome-Oscillator-Nulllinienkreuzung - der Oszillator muss die Nullachse in Handelsrichtung kreuzen und damit eine Momentum-Verschiebung signalisieren.
Accelerator-Oscillator-Beschleunigung - die letzten drei Accelerator-Werte müssen auf derselben Seite von null liegen, und die jüngste Bar muss diese Bewegung fortsetzen, um Beschleunigung zu bestätigen.
Handelssitzungsfilter - Einstiege sind nur innerhalb eines konfigurierbaren Zeitfensters erlaubt, das in Tagesstunden ausgedrückt wird.
Auf jeder abgeschlossenen Kerze verarbeitet die Strategie die Indikatorwerte, die von der Bind-Pipeline geliefert werden. Wenn alle Filter übereinstimmen, schließt sie bei Bedarf eine Gegenposition und eröffnet eine neue Marktorder mit der angeforderten Lotgröße. Stop-Loss und Take-Profit werden über Distanzen in Preispunkten angewendet, und ein optionaler Trailing Stop kann den Schutz-Stop enger ziehen, nachdem der Trade profitabel wird.
Einstiegsregeln
Long-Bedingungen
Bollinger-Spread (oberes minus unteres Band, in Punkte umgerechnet) liegt zwischen BollingerSpreadLower und BollingerSpreadUpper.
Der RSI-Wert ist strikt größer als RsiBuyThreshold.
Awesome Oscillator kreuzt auf der aktuellen Bar von negativ nach positiv.
Accelerator-Oscillator-Werte der letzten drei Kerzen sind alle positiv, und der jüngste Wert ist höher als der vorherige, was wachsendes bullisches Momentum signalisiert.
Die Eröffnungszeit der aktuellen Bar liegt innerhalb des Handelsfensters, das bei EntryHour beginnt und sich über TradingWindowHours Stunden erstreckt (bei Bedarf über Mitternacht hinweg).
Die Strategie hält noch keine Long-Position (sie kann flat oder short sein).
Wenn die Logik erfüllt ist, schließt die Strategie jede Short-Exposure, eröffnet eine Long-Marktorder mit TradeVolume und wendet die konfigurierten Stop-Loss-/Take-Profit-Distanzen an. Trailing-Stop-Verfolgung beginnt, nachdem sich der Trade um mindestens TrailingStopPoints zugunsten der Position bewegt hat.
Short-Bedingungen
Bollinger-Spread liegt innerhalb des erlaubten Bereichs.
Der RSI-Wert ist strikt kleiner als RsiSellThreshold.
Awesome Oscillator kreuzt auf der aktuellen Bar von positiv nach negativ.
Accelerator-Oscillator-Werte der letzten drei Kerzen sind alle negativ, und der jüngste Wert ist niedriger als der vorherige, was steigenden bärischen Druck anzeigt.
Die Kerzeneröffnungszeit liegt innerhalb des Handelssitzungsfensters.
Die Strategie hält noch keine Short-Position (sie kann flat oder long sein).
Wenn ausgelöst, schließt das Modul Long-Exposure, steigt mit TradeVolume in eine Short-Marktorder ein und weist die Schutzorders neu zu.
Ausstiegsverwaltung
Take-Profit - wenn TakeProfitPoints größer als null ist, wird an jede neue Position ein Gewinnziel angehängt, das so viele Preispunkte vom Einstiegspreis entfernt liegt.
Stop-Loss - wenn StopLossPoints größer als null ist, wird ein fester Stop relativ zum Einstiegspreis angewendet.
Trailing Stop - wenn TrailingStopPoints größer als null ist, wird der Stop-Loss näher an den Markt verschoben, sobald der Gewinn die Trailing-Distanz überschreitet. Für Long-Trades wird der Stop auf Close - TrailingStopPoints * pip angehoben, für Shorts auf Close + TrailingStopPoints * pip abgesenkt. Trailing ist einseitig: Der Stop bewegt sich nie zurück.
Manuelle Positionsänderungen durch den Benutzer werden respektiert; die Trailing-Logik reagiert auf die aktuelle aggregierte Position, die von der Engine gemeldet wird.
Parameter
Name
Beschreibung
Standard
CandleType
Primäre Kerzenserie für Berechnungen.
1-Stunden-Kerzen
BollingerPeriod
Rückblickperiode für die Bollinger Bands.
20
BollingerDeviation
Standardabweichungsmultiplikator.
2.0
BollingerSpreadLower
Minimale Bandbreite in Punkten, die zum Aktivieren des Handels erforderlich ist.
40
BollingerSpreadUpper
Maximale für den Handel erlaubte Bandbreite in Punkten.
210
AoFastPeriod
Kurze Periode des Awesome Oscillator.
11
AoSlowPeriod
Lange Periode des Awesome Oscillator.
40
RsiPeriod
RSI-Berechnungslänge.
20
RsiBuyThreshold
Minimaler RSI-Wert für Long-Trades.
46
RsiSellThreshold
Maximaler RSI-Wert für Short-Trades.
40
EntryHour
Stunde (0-23), zu der das Handelsfenster beginnt.
0
TradingWindowHours
Dauer des erlaubten Handelsfensters in Stunden (0 behält nur die Startstunde).
20
TradeVolume
Lotgröße für jede neue Position.
0.01
StopLossPoints
Stop-Loss-Distanz in Preispunkten.
60
TakeProfitPoints
Take-Profit-Distanz in Preispunkten.
90
TrailingStopPoints
Trailing-Stop-Distanz in Preispunkten.
30
Zusätzliche Hinweise
Der Accelerator-Oscillator-Wert wird intern abgeleitet, indem ein einfacher gleitender 5-Perioden-Durchschnitt des Awesome Oscillator vom aktuellen AO-Wert abgezogen wird; dies entspricht der MetaTrader-Implementierung des ursprünglichen Expert.
Band-Spread-Berechnungen hängen vom Instrumenten-PriceStep ab. Wenn er nicht verfügbar ist, fällt die Strategie auf rohe Preisdifferenzen zurück.
Das Handelssitzungsfenster läuft über Mitternacht, wenn EntryHour + TradingWindowHours 23 überschreitet, und reproduziert so den MetaTrader-Stundenfilter.
Die Strategie schließt automatisch Gegenexposure, bevor sie eine neue Position eröffnet, und repliziert damit das Ein-Order-Limit des ursprünglichen MQL4-Codes.
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;
public class WilliamsAoAcStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public WilliamsAoAcStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 williams_ao_ac_strategy(Strategy):
def __init__(self):
super(williams_ao_ac_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(williams_ao_ac_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(williams_ao_ac_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return williams_ao_ac_strategy()