Diese Strategie ist eine Umsetzung des MetaTrader 5 Expert Advisors „AK-47 Scalper EA“ (Build 44883). Es stellt das ursprüngliche Verhalten innerhalb des übergeordneten Strategie-Frameworks StockSharp wieder her.
Der Algorithmus hält eine einzelne Verkaufsstopp-Order während der zulässigen Handelszeiten aktiv. Sobald die Order ausgelöst wird, fügt die Strategie sofort schützende Stop-Loss- und Take-Profit-Orders hinzu. Sowohl der Pending-Order-Preis als auch der Schutzstopp werden je nach Marktbewegung dynamisch verschärft.
Kernlogik
Berechnen Sie die Pip-Größe anhand der Tick-Größe des Instruments (5-stellige Symbole verwenden 0,1-Pip-Schritte, genau wie in MetaTrader).
Bestimmen Sie das Handelsfenster. Bei aktiviertem Zeitfilter sind Eingaben nur zwischen den konfigurierten Start- und Endzeiten (einschließlich Start, exklusive Ende) zulässig. Nachtsitzungen werden durch einen Abschluss um Mitternacht unterstützt.
Stellen Sie sicher, dass der aktuelle Spread in Punkten das konfigurierte Limit nicht überschreitet, bevor Sie neue Bestellungen aufgeben.
Größe der Position:
Verwenden Sie entweder die feste Menge (Parameter Base Lot) oder
Konvertieren Sie den konfigurierten Risk Percent des Portfoliowerts in Lots (nachahmen Sie die MT5-Formel) und richten Sie ihn an den Beschränkungen des Börsenvolumens aus.
Platzieren Sie eine Verkaufsstopp-Order SL/2 Pips unter dem Gebot. Der Schutzstopp liegt voraussichtlich SL/2 Pips über dem Briefkurs und der Take-Profit liegt TP Pips unter dem Einstiegspunkt.
Während die Order aussteht, wird sie von der Strategie kontinuierlich neu registriert, um die SL/2-Pip-Lücke zum Gebot beizubehalten und die geplanten Schutzpreise zu aktualisieren.
Nach der Ausführung:
Registrieren Sie eine Buy-Stop-Stop-Loss-Order und eine Buy-Limit-Take-Profit-Order zu den geplanten Preisen.
Bei jedem Kerzenschluss folgt die Strategie dem Stop, indem sie ihn genau SL Pips über dem aktuellen Gebot hält (ohne ihn zu lockern).
Der Take-Profit-Preis bleibt fest, sobald er festgelegt wurde.
Wenn die Position flach ist, werden alle Schutzanweisungen aufgehoben und ein neuer Zyklus kann beginnen.
Parameter
Parameter
Beschreibung
Risikoprozentsatz verwenden
Wechseln Sie zwischen festen Losgrößen und eigenkapitalbasierter Größenbestimmung.
Risikoprozentsatz
Prozentsatz, der bei der Berechnung des Handelsvolumens auf den Portfoliowert angewendet wird.
Grundgrundstück
Die Losgröße und der Rundungsschritt für die Positionsgröße wurden korrigiert.
Stop-Loss (Pips)
Abstand zwischen dem Einstiegspreis und dem Schutzstopp. Der Pending-Order-Offset verwendet die Hälfte dieser Distanz.
Gewinnmitnahme (Pips)
Gewinnzielentfernung. Auf Null setzen, um das Ziel zu deaktivieren.
Maximaler Spread (Punkte)
Maximal zulässiger Spread (in MetaTrader Punkten) für den Markteintritt.
Zeitfilter verwenden
Aktivieren oder deaktivieren Sie die Handelsfensterbeschränkung.
Startstunde/Minute
Beginn des Handelsfensters.
Stunde/Minute beenden
Ende des Handelsfensters.
Kerzentyp
Candle-Abonnement, das für Zeit- und Preisaktualisierungen verwendet wird.
Notizen
Die Strategie verwendet nur kurze Einträge, genau wie das Original EA.
Das Trailing wird bei Kerzenschluss durchgeführt, um mit dem StockSharp-High-Level API synchron zu bleiben.
Schutzaufträge werden über ReRegisterOrder-Aufrufe ersetzt, daher muss die Börse oder der Simulator die Auftragsersetzung unterstützen.
Die ursprünglichen grafischen Kommentare von MetaTrader werden nicht reproduziert, da die Strategien von StockSharp auf Protokollierung statt auf Terminalkommentaren basieren.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "AK-47 Scalper" MetaTrader expert.
/// Sells when price breaks below the low of the previous N candles (breakout scalp),
/// buys when price breaks above the high. Uses ATR for stop distance management.
/// </summary>
public class Ak47ScalperStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrStopMultiplier;
private AverageTrueRange _atr;
private decimal _highestHigh;
private decimal _lowestLow;
private int _barsCollected;
private decimal? _entryPrice;
private Sides? _entrySide;
private decimal _stopDistance;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public decimal AtrStopMultiplier
{
get => _atrStopMultiplier.Value;
set => _atrStopMultiplier.Value = value;
}
public Ak47ScalperStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_lookbackPeriod = Param(nameof(LookbackPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Number of bars for high/low channel", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for stop distance", "Indicators");
_atrStopMultiplier = Param(nameof(AtrStopMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Stop Mult", "ATR multiplier for stop distance", "Risk");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr = new AverageTrueRange { Length = AtrPeriod };
_highestHigh = 0;
_lowestLow = decimal.MaxValue;
_barsCollected = 0;
_entryPrice = null;
_entrySide = null;
_stopDistance = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!atrValue.IsFinal)
return;
var atrDecimal = atrValue.IsEmpty ? 0m : atrValue.GetValue<decimal>();
// Build lookback channel
if (_barsCollected < LookbackPeriod)
{
if (candle.HighPrice > _highestHigh)
_highestHigh = candle.HighPrice;
if (candle.LowPrice < _lowestLow)
_lowestLow = candle.LowPrice;
_barsCollected++;
return;
}
if (!_atr.IsFormed)
{
// Keep updating channel
UpdateChannel(candle);
return;
}
var close = candle.ClosePrice;
var volume = Volume;
if (volume <= 0)
volume = 1;
_stopDistance = atrDecimal * AtrStopMultiplier;
// Check stop loss on existing position
if (_entryPrice != null && _entrySide != null)
{
if (_entrySide == Sides.Buy && close <= _entryPrice.Value - _stopDistance)
{
SellMarket(Math.Abs(Position));
_entryPrice = null;
_entrySide = null;
}
else if (_entrySide == Sides.Sell && close >= _entryPrice.Value + _stopDistance)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_entrySide = null;
}
// Take profit at 2x ATR
else if (_entrySide == Sides.Buy && close >= _entryPrice.Value + _stopDistance * 1.5m)
{
SellMarket(Math.Abs(Position));
_entryPrice = null;
_entrySide = null;
}
else if (_entrySide == Sides.Sell && close <= _entryPrice.Value - _stopDistance * 1.5m)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_entrySide = null;
}
}
// Entry signals: breakout
if (Position == 0)
{
if (close > _highestHigh)
{
BuyMarket(volume);
_entryPrice = close;
_entrySide = Sides.Buy;
}
else if (close < _lowestLow)
{
SellMarket(volume);
_entryPrice = close;
_entrySide = Sides.Sell;
}
}
UpdateChannel(candle);
}
private void UpdateChannel(ICandleMessage candle)
{
// Simple rolling update - reset and let it rebuild
// For simplicity, just use last candle's high/low as reference shifted
if (candle.HighPrice > _highestHigh)
_highestHigh = candle.HighPrice;
else
_highestHigh = _highestHigh * 0.999m + candle.HighPrice * 0.001m; // slow decay
if (candle.LowPrice < _lowestLow)
_lowestLow = candle.LowPrice;
else
_lowestLow = _lowestLow * 0.999m + candle.LowPrice * 0.001m; // slow decay
}
/// <inheritdoc />
protected override void OnReseted()
{
_atr = null;
_highestHigh = 0;
_lowestLow = decimal.MaxValue;
_barsCollected = 0;
_entryPrice = null;
_entrySide = null;
_stopDistance = 0;
base.OnReseted();
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ak47_scalper_strategy(Strategy):
def __init__(self):
super(ak47_scalper_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._lookback_period = self.Param("LookbackPeriod", 5)
self._atr_period = self.Param("AtrPeriod", 14)
self._atr_stop_multiplier = self.Param("AtrStopMultiplier", 1.5)
self._highest_high = 0.0
self._lowest_low = float('inf')
self._bars_collected = 0
self._entry_price = None
self._entry_side = None
self._stop_distance = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def LookbackPeriod(self):
return self._lookback_period.Value
@LookbackPeriod.setter
def LookbackPeriod(self, value):
self._lookback_period.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def AtrStopMultiplier(self):
return self._atr_stop_multiplier.Value
@AtrStopMultiplier.setter
def AtrStopMultiplier(self, value):
self._atr_stop_multiplier.Value = value
def OnReseted(self):
super(ak47_scalper_strategy, self).OnReseted()
self._highest_high = 0.0
self._lowest_low = float('inf')
self._bars_collected = 0
self._entry_price = None
self._entry_side = None
self._stop_distance = 0.0
def OnStarted2(self, time):
super(ak47_scalper_strategy, self).OnStarted2(time)
self._highest_high = 0.0
self._lowest_low = float('inf')
self._bars_collected = 0
self._entry_price = None
self._entry_side = None
self._stop_distance = 0.0
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr_value)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
lookback = self.LookbackPeriod
# Build lookback channel
if self._bars_collected < lookback:
if high > self._highest_high:
self._highest_high = high
if low < self._lowest_low:
self._lowest_low = low
self._bars_collected += 1
return
atr_stop_mult = float(self.AtrStopMultiplier)
self._stop_distance = atr_val * atr_stop_mult
# Check stop loss / take profit on existing position
if self._entry_price is not None and self._entry_side is not None:
if self._entry_side == "buy" and close <= self._entry_price - self._stop_distance:
self.SellMarket()
self._entry_price = None
self._entry_side = None
elif self._entry_side == "sell" and close >= self._entry_price + self._stop_distance:
self.BuyMarket()
self._entry_price = None
self._entry_side = None
elif self._entry_side == "buy" and close >= self._entry_price + self._stop_distance * 1.5:
self.SellMarket()
self._entry_price = None
self._entry_side = None
elif self._entry_side == "sell" and close <= self._entry_price - self._stop_distance * 1.5:
self.BuyMarket()
self._entry_price = None
self._entry_side = None
# Entry signals: breakout
if self.Position == 0:
if close > self._highest_high:
self.BuyMarket()
self._entry_price = close
self._entry_side = "buy"
elif close < self._lowest_low:
self.SellMarket()
self._entry_price = close
self._entry_side = "sell"
# Update channel with slow decay
if high > self._highest_high:
self._highest_high = high
else:
self._highest_high = self._highest_high * 0.999 + high * 0.001
if low < self._lowest_low:
self._lowest_low = low
else:
self._lowest_low = self._lowest_low * 0.999 + low * 0.001
def CreateClone(self):
return ak47_scalper_strategy()