Maximaler Gewinn
Max Gain vergleicht den prozentualen Abstand vom tiefsten Tief zum aktuellen Hoch und vom höchsten Hoch zum aktuellen Tief über einen Rückschauzeitraum. Es geht Long, wenn der potenzielle Gewinn den angepassten Verlust übersteigt, andernfalls geht es Short.
Details
- Daten: Preiskerzen.
- Einstiegskriterien:
- Long: Max gain > adjusted max loss.
- Short: Adjusted max loss > max gain.
- Ausstiegskriterien: Umgekehrtes Signal.
- Stops: Keine.
- Standardwerte:
PeriodLength= 30
- Filter:
- Kategorie: Momentum
- Richtung: Long & Short
- Indikatoren: Highest, Lowest
- Komplexität: Niedrig
- Risikolevel: Mittel
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;
/// <summary>
/// Compares upside/downside potential inside a rolling high-low range.
/// </summary>
public class MaxGainStrategy : Strategy
{
private readonly StrategyParam<int> _periodLength;
private readonly StrategyParam<decimal> _edgeMultiplier;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private Lowest _lowest;
private int _barsFromSignal;
/// <summary>
/// High/low lookback length.
/// </summary>
public int PeriodLength
{
get => _periodLength.Value;
set => _periodLength.Value = value;
}
/// <summary>
/// Minimum upside/downside ratio required for position change.
/// </summary>
public decimal EdgeMultiplier
{
get => _edgeMultiplier.Value;
set => _edgeMultiplier.Value = value;
}
/// <summary>
/// Minimum bars between market entries.
/// </summary>
public int SignalCooldownBars
{
get => _signalCooldownBars.Value;
set => _signalCooldownBars.Value = value;
}
/// <summary>
/// Candle timeframe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public MaxGainStrategy()
{
_periodLength = Param(nameof(PeriodLength), 64)
.SetGreaterThanZero()
.SetDisplay("Period Length", "Rolling high-low length", "General");
_edgeMultiplier = Param(nameof(EdgeMultiplier), 1.25m)
.SetGreaterThanZero()
.SetDisplay("Edge Multiplier", "Upside/downside ratio threshold", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 14)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_lowest = null;
_barsFromSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_highest = new() { Length = PeriodLength };
_lowest = new() { Length = PeriodLength };
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_highest, _lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal maxHigh, decimal minLow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_highest.IsFormed || !_lowest.IsFormed)
return;
var close = candle.ClosePrice;
if (close <= 0m || maxHigh <= minLow)
return;
_barsFromSignal++;
if (_barsFromSignal < SignalCooldownBars)
return;
var upside = (maxHigh - close) / close;
var downside = (close - minLow) / close;
if (upside > downside * EdgeMultiplier && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (downside > upside * EdgeMultiplier && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class max_gain_strategy(Strategy):
def __init__(self):
super(max_gain_strategy, self).__init__()
self._period_length = self.Param("PeriodLength", 64) \
.SetGreaterThanZero() \
.SetDisplay("Period Length", "Rolling high-low length", "General")
self._edge_multiplier = self.Param("EdgeMultiplier", 1.25) \
.SetGreaterThanZero() \
.SetDisplay("Edge Multiplier", "Upside/downside ratio threshold", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 14) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(max_gain_strategy, self).OnReseted()
self._bars_from_signal = 0
def OnStarted2(self, time):
super(max_gain_strategy, self).OnStarted2(time)
self._bars_from_signal = self._signal_cooldown_bars.Value
self._highest = Highest()
self._highest.Length = self._period_length.Value
self._lowest = Lowest()
self._lowest.Length = self._period_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._highest, self._lowest, self.OnProcess).Start()
def OnProcess(self, candle, max_high, min_low):
if candle.State != CandleStates.Finished:
return
if not self._highest.IsFormed or not self._lowest.IsFormed:
return
mh = float(max_high)
ml = float(min_low)
close = float(candle.ClosePrice)
if close <= 0.0 or mh <= ml:
return
self._bars_from_signal += 1
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal < cd:
return
upside = (mh - close) / close
downside = (close - ml) / close
em = float(self._edge_multiplier.Value)
if upside > downside * em and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif downside > upside * em and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
def CreateClone(self):
return max_gain_strategy()