El asesor experto FitFul 13 trabaja alrededor de niveles de pivot semanales derivados de la semana de trading anterior. Espera a que la vela H1 actual (marco temporal predeterminado) reaccione a una de las bandas de pivot y confirma el movimiento con dos velas más antiguas de una serie de confirmación M15. Cuando la confirmación está presente, la estrategia abre una posición con niveles precalculados de stop-loss y take-profit derivados de la misma estructura de pivots. Un trailing stop protege las operaciones rentables una vez que el precio avanza lo suficiente.
Lógica original
Calcular el precio típico y la estructura de pivot de la semana anterior: PriceTypical, R1, S1, niveles intermedios (R0.5, S0.5, R1.5, etc.) y las extensiones de segundo/tercer orden.
Observar la vela H1 más reciente. Si cerró alcista, buscar en el cuerpo de la vela precedente un cruce hacia arriba de uno de los niveles de pivot. Si ocurre tal cruce, preparar parámetros largos: stop por debajo del soporte relevante, take-profit por encima de la resistencia emparejada. Para cierres bajistas, la lógica espejo prepara parámetros cortos.
Si el cuerpo de la vela H1 no interactuó con ningún pivot, verificar dos velas M15 anteriores. Dos mínimos consecutivos perforando el mismo nivel confirman configuraciones largas, mientras que dos máximos cayendo a través de un nivel confirman cortos. Cada combinación se mapea a su propio par de stop/take.
Enviar una orden de mercado con el volumen neto configurado. El port de StockSharp trabaja con posiciones netas, por lo tanto la exposición opuesta se aplana antes de abrir la nueva operación. Los precios de stop-loss y take-profit se almacenan internamente y se aplican mediante salidas virtuales en nuevas velas.
Aplicar un trailing stop virtual: una vez que el beneficio abierto supere TrailingStopPips + TrailingStepPips, mover el stop a close - TrailingStopPips (largo) o close + TrailingStopPips (corto). El stop nunca retrocede y solo se ajusta si el precio avanza al menos el paso de trailing.
Ignorar nuevas señales si la posición neta absoluta ya equivale a Volume × MaxPositions.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
CandleType
DataType
H1
Marco temporal principal usado para evaluar reacciones de pivot.
ConfirmationCandleType
DataType
M15
Marco temporal inferior que proporciona la confirmación de dos barras.
Volume
decimal
0.1
Volumen de orden neto para cada entrada.
MaxPositions
int
3
Exposición neta máxima expresada como múltiplos de Volume.
IndentPips
decimal
3
Desplazamiento aplicado a los cálculos de stop-loss y take-profit basados en pivots.
TrailingStopPips
decimal
150
Distancia del trailing stop en pips. Establecer en cero para deshabilitar el trailing.
TrailingStepPips
decimal
5
Progreso de precio adicional mínimo (en pips) requerido antes de ajustar el trailing stop.
Notas sobre el port
StockSharp gestiona una única posición neta. La capacidad de cobertura original se emula aplanando la exposición opuesta cuando se toma una nueva entrada.
La lógica de stop-loss, take-profit y trailing se implementa virtualmente. La estrategia cierra posiciones en actualizaciones de velas cuando el precio cruza los niveles almacenados.
Los pivots semanales se recalculan cada vez que se recibe una nueva vela semanal. La confirmación predeterminada usa H1/M15, pero ambos marcos temporales pueden ajustarse mediante parámetros.
Todos los comentarios en el código fuente están escritos en inglés según lo requerido por las directrices de conversión.
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;
public class FitFul13Strategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public FitFul13Strategy()
{
_fastPeriod = Param(nameof(FastPeriod), 13).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fit_ful13_strategy(Strategy):
def __init__(self):
super(fit_ful13_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 13) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(fit_ful13_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(fit_ful13_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return fit_ful13_strategy()