Scalping de Cuadrícula Fractal Adaptativa
El Scalping de Cuadrícula Fractal Adaptativa coloca órdenes limitadas alrededor de pivotes fractales recientes utilizando el ATR para la distancia. La tendencia se define mediante una media móvil simple. Cuando la volatilidad supera un umbral, se colocan límites de compra por debajo de los mínimos fractales en tendencias alcistas y límites de venta por encima de los máximos fractales en tendencias bajistas. Las salidas se producen en el nivel de cuadrícula opuesto o con un stop trailing basado en ATR.
Detalles
- Criterios de entrada: ATR por encima del umbral con el precio relativo a la SMA; límite de compra en el mínimo fractal menos el multiplicador ATR o límite de venta en el máximo fractal más el multiplicador ATR.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Nivel de cuadrícula opuesto o stop basado en fractales.
- Stops: Sí.
- Valores predeterminados:
AtrLength= 14SmaLength= 50GridMultiplierHigh= 2.0mGridMultiplierLow= 0.5mTrailStopMultiplier= 0.5mVolatilityThreshold= 1.0mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Scalping
- Dirección: Ambos
- Indicadores: Fractal, ATR, SMA
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Strategy that trades based on fractal pivots, ATR volatility, and SMA trend filter.
/// Buys when price breaks above fractal high in uptrend, sells when breaks below fractal low in downtrend.
/// </summary>
public class AdaptiveFractalGridScalpingStrategy : Strategy
{
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<decimal> _stopMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _h1, _h2, _h3, _h4, _h5;
private decimal _l1, _l2, _l3, _l4, _l5;
private decimal? _fractalHigh;
private decimal? _fractalLow;
private decimal _entryPrice;
private int _cooldownRemaining;
private int _barCount;
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
public int SmaLength { get => _smaLength.Value; set => _smaLength.Value = value; }
public decimal StopMultiplier { get => _stopMultiplier.Value; set => _stopMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public AdaptiveFractalGridScalpingStrategy()
{
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period", "Parameters")
.SetOptimize(7, 28, 7);
_smaLength = Param(nameof(SmaLength), 50)
.SetDisplay("SMA Length", "SMA period", "Parameters")
.SetOptimize(20, 100, 10);
_stopMultiplier = Param(nameof(StopMultiplier), 2m)
.SetDisplay("Stop Multiplier", "ATR multiplier for stop/TP", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "Data");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_h1 = _h2 = _h3 = _h4 = _h5 = 0;
_l1 = _l2 = _l3 = _l4 = _l5 = 0;
_fractalHigh = null;
_fractalLow = null;
_entryPrice = 0;
_cooldownRemaining = 0;
_barCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrLength };
var sma = new SimpleMovingAverage { Length = SmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_barCount++;
// Update fractal buffers
_h1 = _h2; _h2 = _h3; _h3 = _h4; _h4 = _h5; _h5 = candle.HighPrice;
_l1 = _l2; _l2 = _l3; _l3 = _l4; _l4 = _l5; _l5 = candle.LowPrice;
if (_barCount < 5)
return;
// Detect fractals
if (_h3 > _h1 && _h3 > _h2 && _h3 > _h4 && _h3 > _h5)
_fractalHigh = _h3;
if (_l3 < _l1 && _l3 < _l2 && _l3 < _l4 && _l3 < _l5)
_fractalLow = _l3;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Exit on ATR-based stop/TP
if (Position > 0 && _entryPrice > 0)
{
var stopLoss = _entryPrice - atrValue * StopMultiplier;
var takeProfit = _entryPrice + atrValue * StopMultiplier * 2;
if (candle.ClosePrice <= stopLoss || candle.ClosePrice >= takeProfit)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
_entryPrice = 0;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
var stopLoss = _entryPrice + atrValue * StopMultiplier;
var takeProfit = _entryPrice - atrValue * StopMultiplier * 2;
if (candle.ClosePrice >= stopLoss || candle.ClosePrice <= takeProfit)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
_entryPrice = 0;
return;
}
}
// Entry signals
var isBullish = candle.ClosePrice > smaValue;
var isBearish = candle.ClosePrice < smaValue;
if (isBullish && _fractalHigh is decimal fh && candle.ClosePrice > fh && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
else if (isBearish && _fractalLow is decimal fl && candle.ClosePrice < fl && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
}
}
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, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class adaptive_fractal_grid_scalping_strategy(Strategy):
"""Adaptive Fractal Grid Scalping Strategy."""
def __init__(self):
super(adaptive_fractal_grid_scalping_strategy, self).__init__()
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Parameters")
self._sma_length = self.Param("SmaLength", 50) \
.SetDisplay("SMA Length", "SMA period", "Parameters")
self._stop_multiplier = self.Param("StopMultiplier", 2.0) \
.SetDisplay("Stop Multiplier", "ATR multiplier for stop/TP", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles", "Data")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._h1 = 0.0
self._h2 = 0.0
self._h3 = 0.0
self._h4 = 0.0
self._h5 = 0.0
self._l1 = 0.0
self._l2 = 0.0
self._l3 = 0.0
self._l4 = 0.0
self._l5 = 0.0
self._fractal_high = None
self._fractal_low = None
self._entry_price = 0.0
self._cooldown_remaining = 0
self._bar_count = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_fractal_grid_scalping_strategy, self).OnReseted()
self._h1 = self._h2 = self._h3 = self._h4 = self._h5 = 0.0
self._l1 = self._l2 = self._l3 = self._l4 = self._l5 = 0.0
self._fractal_high = None
self._fractal_low = None
self._entry_price = 0.0
self._cooldown_remaining = 0
self._bar_count = 0
def OnStarted2(self, time):
super(adaptive_fractal_grid_scalping_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = int(self._atr_length.Value)
sma = SimpleMovingAverage()
sma.Length = int(self._sma_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(atr, sma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, atr_value, sma_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
self._bar_count += 1
# Update fractal buffers
self._h1 = self._h2
self._h2 = self._h3
self._h3 = self._h4
self._h4 = self._h5
self._h5 = float(candle.HighPrice)
self._l1 = self._l2
self._l2 = self._l3
self._l3 = self._l4
self._l4 = self._l5
self._l5 = float(candle.LowPrice)
if self._bar_count < 5:
return
# Detect fractals
if self._h3 > self._h1 and self._h3 > self._h2 and self._h3 > self._h4 and self._h3 > self._h5:
self._fractal_high = self._h3
if self._l3 < self._l1 and self._l3 < self._l2 and self._l3 < self._l4 and self._l3 < self._l5:
self._fractal_low = self._l3
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
atr_v = float(atr_value)
sma_v = float(sma_value)
close = float(candle.ClosePrice)
stop_mult = float(self._stop_multiplier.Value)
cooldown = int(self._cooldown_bars.Value)
# Exit on ATR-based stop/TP
if self.Position > 0 and self._entry_price > 0:
stop_loss = self._entry_price - atr_v * stop_mult
take_profit = self._entry_price + atr_v * stop_mult * 2
if close <= stop_loss or close >= take_profit:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._entry_price = 0.0
return
elif self.Position < 0 and self._entry_price > 0:
stop_loss = self._entry_price + atr_v * stop_mult
take_profit = self._entry_price - atr_v * stop_mult * 2
if close >= stop_loss or close <= take_profit:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._entry_price = 0.0
return
# Entry signals
is_bullish = close > sma_v
is_bearish = close < sma_v
if is_bullish and self._fractal_high is not None and close > self._fractal_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
elif is_bearish and self._fractal_low is not None and close < self._fractal_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
def CreateClone(self):
return adaptive_fractal_grid_scalping_strategy()