Price Flip-Strategie
Die Price Flip-Strategie spiegelt den Preis um aktuelle Hochs und Tiefs und handelt gleitende Durchschnitt-Crossover, wenn der vorherige Schlusskurs auf der entgegengesetzten Seite dieses invertierten Preises liegt. Ein Trendfilter basierend auf dem langsamen gleitenden Durchschnitt kann angewendet werden.
Details
- Einstiegskriterien:
- Der vorherige Schlusskurs liegt über dem invertierten Preis.
- Schneller MA kreuzt den langsamen MA nach oben.
- Optional: Preis liegt über dem langsamen MA, wenn der Trendfilter aktiviert ist.
- Long/Short: Beide.
- Ausstiegskriterien:
- Das entgegengesetzte Signal löst eine Umkehr aus.
- Stops: Keine.
- Standardwerte:
TickerMaxLookback= 100TickerMinLookback= 100FastMaLength= 12SlowMaLength= 14UseTrendFilter= true
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: SMA, Highest/Lowest
- Stops: Nein
- Komplexität: Mittel
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Price Flip strategy using mirrored range price and SMA crossover confirmation.
/// </summary>
public class PriceFlipStrategy : Strategy
{
private readonly StrategyParam<int> _tickerMaxLookback;
private readonly StrategyParam<int> _tickerMinLookback;
private readonly StrategyParam<int> _fastMaLength;
private readonly StrategyParam<int> _slowMaLength;
private readonly StrategyParam<bool> _useTrendFilter;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _candles = [];
private decimal? _previousClose;
private decimal? _previousInverted;
private decimal? _previousFast;
private decimal? _previousSlow;
public int TickerMaxLookback { get => _tickerMaxLookback.Value; set => _tickerMaxLookback.Value = value; }
public int TickerMinLookback { get => _tickerMinLookback.Value; set => _tickerMinLookback.Value = value; }
public int FastMaLength { get => _fastMaLength.Value; set => _fastMaLength.Value = value; }
public int SlowMaLength { get => _slowMaLength.Value; set => _slowMaLength.Value = value; }
public bool UseTrendFilter { get => _useTrendFilter.Value; set => _useTrendFilter.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public PriceFlipStrategy()
{
_tickerMaxLookback = Param(nameof(TickerMaxLookback), 100).SetGreaterThanZero();
_tickerMinLookback = Param(nameof(TickerMinLookback), 100).SetGreaterThanZero();
_fastMaLength = Param(nameof(FastMaLength), 12).SetGreaterThanZero();
_slowMaLength = Param(nameof(SlowMaLength), 14).SetGreaterThanZero();
_useTrendFilter = Param(nameof(UseTrendFilter), true);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame());
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
_previousClose = null;
_previousInverted = null;
_previousFast = null;
_previousSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_candles.Add(candle);
var keep = Math.Max(Math.Max(TickerMaxLookback, TickerMinLookback), Math.Max(SlowMaLength, FastMaLength));
if (_candles.Count > keep)
_candles.RemoveRange(0, _candles.Count - keep);
if (_candles.Count < keep)
return;
var fast = _candles.Skip(_candles.Count - FastMaLength).Average(c => c.ClosePrice);
var slow = _candles.Skip(_candles.Count - SlowMaLength).Average(c => c.ClosePrice);
if (_previousClose is decimal previousClose &&
_previousInverted is decimal previousInverted &&
_previousFast is decimal previousFast &&
_previousSlow is decimal previousSlow)
{
var signal = GetSignal(
previousClose,
previousInverted,
previousFast,
previousSlow,
fast,
slow,
candle.ClosePrice,
UseTrendFilter);
if (signal > 0 && Position <= 0m)
BuyMarket(Volume + Math.Abs(Position));
else if (signal < 0 && Position >= 0m)
SellMarket(Volume + Math.Abs(Position));
}
var recentHigh = _candles.Skip(_candles.Count - TickerMaxLookback).Max(c => c.HighPrice);
var recentLow = _candles.Skip(_candles.Count - TickerMinLookback).Min(c => c.LowPrice);
_previousClose = candle.ClosePrice;
_previousInverted = CalculateInvertedPrice(recentHigh, recentLow, candle.ClosePrice);
_previousFast = fast;
_previousSlow = slow;
}
internal static decimal CalculateInvertedPrice(decimal recentHigh, decimal recentLow, decimal price)
=> recentHigh + recentLow - price;
internal static int GetSignal(
decimal previousClose,
decimal previousInverted,
decimal previousFast,
decimal previousSlow,
decimal fast,
decimal slow,
decimal currentClose,
bool useTrendFilter)
{
var crossUp = previousFast <= previousSlow && fast > slow;
var crossDown = previousFast >= previousSlow && fast < slow;
if (previousClose > previousInverted &&
crossUp &&
(!useTrendFilter || currentClose > slow))
return 1;
if (previousClose < previousInverted &&
crossDown &&
(!useTrendFilter || currentClose < slow))
return -1;
return 0;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class price_flip_strategy(Strategy):
def __init__(self):
super(price_flip_strategy, self).__init__()
self._ticker_max_lookback = self.Param("TickerMaxLookback", 100).SetGreaterThanZero()
self._ticker_min_lookback = self.Param("TickerMinLookback", 100).SetGreaterThanZero()
self._fast_ma_length = self.Param("FastMaLength", 12).SetGreaterThanZero()
self._slow_ma_length = self.Param("SlowMaLength", 14).SetGreaterThanZero()
self._use_trend_filter = self.Param("UseTrendFilter", True)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._candles = []
self._previous_close = None
self._previous_inverted = None
self._previous_fast = None
self._previous_slow = None
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(price_flip_strategy, self).OnReseted()
self._candles = []
self._previous_close = None
self._previous_inverted = None
self._previous_fast = None
self._previous_slow = None
def OnStarted2(self, time):
super(price_flip_strategy, self).OnStarted2(time)
self.SubscribeCandles(self._candle_type.Value).Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candles.append(candle)
max_lookback = int(self._ticker_max_lookback.Value)
min_lookback = int(self._ticker_min_lookback.Value)
fast_len = int(self._fast_ma_length.Value)
slow_len = int(self._slow_ma_length.Value)
keep = max(max_lookback, min_lookback, slow_len, fast_len)
if len(self._candles) > keep:
del self._candles[:-keep]
if len(self._candles) < keep:
return
fast = sum(float(c.ClosePrice) for c in self._candles[-fast_len:]) / fast_len
slow = sum(float(c.ClosePrice) for c in self._candles[-slow_len:]) / slow_len
if all(v is not None for v in [
self._previous_close, self._previous_inverted,
self._previous_fast, self._previous_slow]):
signal = self.get_signal(
self._previous_close,
self._previous_inverted,
self._previous_fast,
self._previous_slow,
fast,
slow,
float(candle.ClosePrice),
bool(self._use_trend_filter.Value))
if signal > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif signal < 0 and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
recent_high = max(float(c.HighPrice) for c in self._candles[-max_lookback:])
recent_low = min(float(c.LowPrice) for c in self._candles[-min_lookback:])
self._previous_close = float(candle.ClosePrice)
self._previous_inverted = self.calculate_inverted_price(
recent_high, recent_low, self._previous_close)
self._previous_fast = fast
self._previous_slow = slow
@staticmethod
def calculate_inverted_price(recent_high, recent_low, price):
return recent_high + recent_low - price
@staticmethod
def get_signal(previous_close, previous_inverted,
previous_fast, previous_slow,
fast, slow, current_close, use_trend_filter):
cross_up = previous_fast <= previous_slow and fast > slow
cross_down = previous_fast >= previous_slow and fast < slow
if previous_close > previous_inverted and cross_up and (not use_trend_filter or current_close > slow):
return 1
if previous_close < previous_inverted and cross_down and (not use_trend_filter or current_close < slow):
return -1
return 0
def CreateClone(self):
return price_flip_strategy()