Estrategia de Price Flip
La estrategia Price Flip refleja el precio alrededor de máximos y mínimos recientes y opera cruces de medias móviles cuando el cierre anterior se encuentra en el lado opuesto de este precio invertido. Se puede aplicar un filtro de tendencia basado en la media móvil lenta.
Detalles
- Criterios de entrada:
- El cierre anterior está por encima del precio invertido.
- La MA rápida cruza por encima de la MA lenta.
- Opcional: el precio está por encima de la MA lenta cuando el filtro de tendencia está habilitado.
- Largo/Corto: Ambos.
- Criterios de salida:
- La señal opuesta activa una reversión.
- Stops: Ninguno.
- Valores predeterminados:
TickerMaxLookback= 100TickerMinLookback= 100FastMaLength= 12SlowMaLength= 14UseTrendFilter= true
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: SMA, Highest/Lowest
- Stops: No
- Complejidad: Intermedio
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()