MPM-Momentum-Strategie
Diese Strategie ist eine vereinfachte Konvertierung des ursprünglichen MQL-Experten mpm-1_8.mq4.
Sie wartet auf eine Folge progressiver Kerzen und eröffnet dann eine Position in
dieselbe Richtung. Der Average True Range wird verwendet, um die Kerzengröße zu bewerten
und Stops zu verfolgen.
Parameter
| Name | Beschreibung |
|---|---|
ProgressiveCandles |
Anzahl aufeinanderfolgender Kerzen, die zum Auslösen eines Trades erforderlich sind. |
ProgressiveSize |
Minimale Kerzenkörpergröße relativ zum ATR, um als progressiv zu gelten. |
StopRatio |
ATR-Anteil für das Trailing des Stop-Niveaus. |
AtrPeriod |
Periode des Average-True-Range-Indikators. |
CandleType |
Von der Strategie verwendeter Kerzentyp. |
ProfitPerLot |
Gewinnziel pro Lot. |
BreakEvenPerLot |
Gewinn, der zum Ausstieg beim Breakeven erforderlich ist. |
LossPerLot |
Maximal tolerierter Verlust pro Lot. |
Logik
- Bei jeder abgeschlossenen Kerze wird die Körpergröße mit dem ATR verglichen.
- Ein bullischer oder bärischer Zähler wird erhöht, wenn der Körper den
ProgressiveSize-Schwellenwert überschreitet. - Nach
ProgressiveCandlesin eine Richtung wird eine Market-Order gesendet. - Das Stop-Niveau wird um
StopRatiodes ATR nachgezogen. - Positionen werden geschlossen, wenn der Stop erreicht wird oder wenn Gewinn-/Verlustziele erreicht werden.
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MPM momentum strategy converted from MQL.
/// </summary>
public class MpmStrategy : Strategy
{
private readonly StrategyParam<int> _progressiveCandles;
private readonly StrategyParam<decimal> _progressiveSize;
private readonly StrategyParam<decimal> _stopRatio;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _profitPerLot;
private readonly StrategyParam<decimal> _breakEvenPerLot;
private readonly StrategyParam<decimal> _lossPerLot;
private int _bullCount;
private int _bearCount;
private decimal _entryPrice;
private decimal _stopPrice;
public int ProgressiveCandles
{
get => _progressiveCandles.Value;
set => _progressiveCandles.Value = value;
}
public decimal ProgressiveSize
{
get => _progressiveSize.Value;
set => _progressiveSize.Value = value;
}
public decimal StopRatio
{
get => _stopRatio.Value;
set => _stopRatio.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal ProfitPerLot
{
get => _profitPerLot.Value;
set => _profitPerLot.Value = value;
}
public decimal BreakEvenPerLot
{
get => _breakEvenPerLot.Value;
set => _breakEvenPerLot.Value = value;
}
public decimal LossPerLot
{
get => _lossPerLot.Value;
set => _lossPerLot.Value = value;
}
public MpmStrategy()
{
_progressiveCandles = Param(nameof(ProgressiveCandles), 3)
.SetDisplay("Progressive Candles", "Number of consecutive candles", "Signal");
_progressiveSize = Param(nameof(ProgressiveSize), 0.9m)
.SetDisplay("Progressive Size", "Minimal body size relative to ATR", "Signal");
_stopRatio = Param(nameof(StopRatio), 1.5m)
.SetDisplay("Stop Ratio", "Trailing stop ratio", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Average True Range period", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_profitPerLot = Param(nameof(ProfitPerLot), 2000m)
.SetDisplay("Profit Per Lot", "Profit target per lot", "Risk");
_breakEvenPerLot = Param(nameof(BreakEvenPerLot), 800m)
.SetDisplay("BreakEven Per Lot", "Break even profit per lot", "Risk");
_lossPerLot = Param(nameof(LossPerLot), 1200m)
.SetDisplay("Loss Per Lot", "Maximum loss per lot", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bullCount = 0;
_bearCount = 0;
_entryPrice = 0m;
_stopPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
// Count consecutive bullish or bearish candles with sufficient body
if (candle.ClosePrice > candle.OpenPrice && body >= atr * ProgressiveSize)
{
_bullCount++;
_bearCount = 0;
}
else if (candle.ClosePrice < candle.OpenPrice && body >= atr * ProgressiveSize)
{
_bearCount++;
_bullCount = 0;
}
else
{
_bullCount = 0;
_bearCount = 0;
}
// Open long position after sequence of bullish candles
if (Position <= 0 && _bullCount >= ProgressiveCandles)
{
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - atr * StopRatio;
BuyMarket();
return;
}
// Open short position after sequence of bearish candles
if (Position >= 0 && _bearCount >= ProgressiveCandles)
{
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + atr * StopRatio;
SellMarket();
return;
}
if (Position > 0)
{
var profitPerLot = candle.ClosePrice - _entryPrice;
if (profitPerLot >= ProfitPerLot || profitPerLot >= BreakEvenPerLot || profitPerLot <= -LossPerLot)
{
SellMarket();
return;
}
var newStop = candle.ClosePrice - atr * StopRatio;
if (newStop > _stopPrice)
_stopPrice = newStop;
if (candle.ClosePrice <= _stopPrice)
SellMarket();
}
else if (Position < 0)
{
var profitPerLot = _entryPrice - candle.ClosePrice;
if (profitPerLot >= ProfitPerLot || profitPerLot >= BreakEvenPerLot || profitPerLot <= -LossPerLot)
{
BuyMarket();
return;
}
var newStop = candle.ClosePrice + atr * StopRatio;
if (newStop < _stopPrice)
_stopPrice = newStop;
if (candle.ClosePrice >= _stopPrice)
BuyMarket();
}
}
}
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 mpm_strategy(Strategy):
def __init__(self):
super(mpm_strategy, self).__init__()
self._progressive_candles = self.Param("ProgressiveCandles", 3) \
.SetDisplay("Progressive Candles", "Number of consecutive candles", "Signal")
self._progressive_size = self.Param("ProgressiveSize", 0.9) \
.SetDisplay("Progressive Size", "Minimal body size relative to ATR", "Signal")
self._stop_ratio = self.Param("StopRatio", 1.5) \
.SetDisplay("Stop Ratio", "Trailing stop ratio", "Risk")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "Average True Range period", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._profit_per_lot = self.Param("ProfitPerLot", 2000.0) \
.SetDisplay("Profit Per Lot", "Profit target per lot", "Risk")
self._break_even_per_lot = self.Param("BreakEvenPerLot", 800.0) \
.SetDisplay("BreakEven Per Lot", "Break even profit per lot", "Risk")
self._loss_per_lot = self.Param("LossPerLot", 1200.0) \
.SetDisplay("Loss Per Lot", "Maximum loss per lot", "Risk")
self._bull_count = 0
self._bear_count = 0
self._entry_price = 0.0
self._stop_price = 0.0
@property
def ProgressiveCandles(self):
return self._progressive_candles.Value
@ProgressiveCandles.setter
def ProgressiveCandles(self, value):
self._progressive_candles.Value = value
@property
def ProgressiveSize(self):
return self._progressive_size.Value
@ProgressiveSize.setter
def ProgressiveSize(self, value):
self._progressive_size.Value = value
@property
def StopRatio(self):
return self._stop_ratio.Value
@StopRatio.setter
def StopRatio(self, value):
self._stop_ratio.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def ProfitPerLot(self):
return self._profit_per_lot.Value
@ProfitPerLot.setter
def ProfitPerLot(self, value):
self._profit_per_lot.Value = value
@property
def BreakEvenPerLot(self):
return self._break_even_per_lot.Value
@BreakEvenPerLot.setter
def BreakEvenPerLot(self, value):
self._break_even_per_lot.Value = value
@property
def LossPerLot(self):
return self._loss_per_lot.Value
@LossPerLot.setter
def LossPerLot(self, value):
self._loss_per_lot.Value = value
def OnStarted2(self, time):
super(mpm_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr = float(atr_value)
body = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
if candle.ClosePrice > candle.OpenPrice and body >= atr * float(self.ProgressiveSize):
self._bull_count += 1
self._bear_count = 0
elif candle.ClosePrice < candle.OpenPrice and body >= atr * float(self.ProgressiveSize):
self._bear_count += 1
self._bull_count = 0
else:
self._bull_count = 0
self._bear_count = 0
price = float(candle.ClosePrice)
if self.Position <= 0 and self._bull_count >= self.ProgressiveCandles:
self._entry_price = price
self._stop_price = self._entry_price - atr * float(self.StopRatio)
self.BuyMarket()
return
if self.Position >= 0 and self._bear_count >= self.ProgressiveCandles:
self._entry_price = price
self._stop_price = self._entry_price + atr * float(self.StopRatio)
self.SellMarket()
return
if self.Position > 0:
profit_per_lot = price - self._entry_price
if (profit_per_lot >= float(self.ProfitPerLot)
or profit_per_lot >= float(self.BreakEvenPerLot)
or profit_per_lot <= -float(self.LossPerLot)):
self.SellMarket()
return
new_stop = price - atr * float(self.StopRatio)
if new_stop > self._stop_price:
self._stop_price = new_stop
if price <= self._stop_price:
self.SellMarket()
elif self.Position < 0:
profit_per_lot = self._entry_price - price
if (profit_per_lot >= float(self.ProfitPerLot)
or profit_per_lot >= float(self.BreakEvenPerLot)
or profit_per_lot <= -float(self.LossPerLot)):
self.BuyMarket()
return
new_stop = price + atr * float(self.StopRatio)
if new_stop < self._stop_price:
self._stop_price = new_stop
if price >= self._stop_price:
self.BuyMarket()
def OnReseted(self):
super(mpm_strategy, self).OnReseted()
self._bull_count = 0
self._bear_count = 0
self._entry_price = 0.0
self._stop_price = 0.0
def CreateClone(self):
return mpm_strategy()