Estrategia BTCUSD con SLTP Ajustable
La estrategia opera BTCUSD usando un cruce entre SMA(10) y SMA(25) con un filtro EMA(150). Las entradas largas esperan un retroceso: tras el cruce se rastrea un porcentaje de retroceso y se abre una posición larga cuando el precio cruza de nuevo por encima de ese nivel. Las entradas cortas se activan inmediatamente ante un cruce bajista mientras el precio está por debajo de la EMA.
Las salidas usan distancias ajustables de take-profit, stop-loss y break-even. Una posición larga también se cierra si SMA(10) cruza por debajo de SMA(25) mientras el precio está por debajo de EMA(150).
Detalles
- Criterios de entrada:
- Largo: SMA(10) cruza por encima de SMA(25), luego el precio retrocede un porcentaje fijo y cruza por encima del nivel de retroceso.
- Corto: SMA(10) cruza por debajo de SMA(25) mientras el precio está por debajo de EMA(150).
- Largo/Corto: Largo y corto.
- Criterios de salida:
- Distancias configurables de take-profit, stop-loss y break-even.
- Salida larga cuando SMA(10) cruza por debajo de SMA(25) bajo EMA(150).
- Stops: Sí, ajustables en puntos.
- Valores predeterminados:
FastSmaLength= 10SlowSmaLength= 25EmaFilterLength= 150TakeProfitDistance= 1000StopLossDistance= 250BreakEvenTrigger= 500RetracementPercentage= 0.01
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Largo y Corto
- Indicadores: SMA, EMA
- Stops: Sí
- Complejidad: Moderado
- Marco temporal: Cualquiera
- 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>
/// BTCUSD Adjustable SLTP strategy.
/// A bullish SMA(FastSmaLength)/SMA(SlowSmaLength) crossover arms a long setup at close * (1 - RetracementPercentage); the long opens
/// when the close crosses back above that level. A bearish crossover cancels the setup, closes a long while the close is below
/// EMA(EmaFilterLength) and in that case also opens a short. Take profit, stop loss and break-even trigger are distances in price
/// steps from the entry close; once the trigger is reached the stop moves to the entry price.
/// </summary>
public class BtcusdAdjustableSltpStrategy : Strategy
{
private readonly StrategyParam<int> _fastSmaLength;
private readonly StrategyParam<int> _slowSmaLength;
private readonly StrategyParam<int> _emaFilterLength;
private readonly StrategyParam<decimal> _takeProfitDistance;
private readonly StrategyParam<decimal> _stopLossDistance;
private readonly StrategyParam<decimal> _breakEvenTrigger;
private readonly StrategyParam<decimal> _retracementPercentage;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal? _prevClose;
private decimal? _retracementLevel;
private decimal _entryPrice;
private bool _breakEven;
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastSmaLength
{
get => _fastSmaLength.Value;
set => _fastSmaLength.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowSmaLength
{
get => _slowSmaLength.Value;
set => _slowSmaLength.Value = value;
}
/// <summary>
/// EMA filter period.
/// </summary>
public int EmaFilterLength
{
get => _emaFilterLength.Value;
set => _emaFilterLength.Value = value;
}
/// <summary>
/// Take profit distance in price steps.
/// </summary>
public decimal TakeProfitDistance
{
get => _takeProfitDistance.Value;
set => _takeProfitDistance.Value = value;
}
/// <summary>
/// Stop loss distance in price steps.
/// </summary>
public decimal StopLossDistance
{
get => _stopLossDistance.Value;
set => _stopLossDistance.Value = value;
}
/// <summary>
/// Profit in price steps that moves the stop to the entry price.
/// </summary>
public decimal BreakEvenTrigger
{
get => _breakEvenTrigger.Value;
set => _breakEvenTrigger.Value = value;
}
/// <summary>
/// Retracement below the crossover close as a fraction (0.01 = 1%).
/// </summary>
public decimal RetracementPercentage
{
get => _retracementPercentage.Value;
set => _retracementPercentage.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BtcusdAdjustableSltpStrategy()
{
_fastSmaLength = Param(nameof(FastSmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast SMA Length", "Fast SMA period", "Indicators");
_slowSmaLength = Param(nameof(SlowSmaLength), 25)
.SetGreaterThanZero()
.SetDisplay("Slow SMA Length", "Slow SMA period", "Indicators");
_emaFilterLength = Param(nameof(EmaFilterLength), 150)
.SetGreaterThanZero()
.SetDisplay("EMA Filter Length", "EMA filter period", "Indicators");
_takeProfitDistance = Param(nameof(TakeProfitDistance), 1000m)
.SetNotNegative()
.SetDisplay("Take Profit Distance", "Take profit distance in price steps", "Risk");
_stopLossDistance = Param(nameof(StopLossDistance), 250m)
.SetNotNegative()
.SetDisplay("Stop Loss Distance", "Stop loss distance in price steps", "Risk");
_breakEvenTrigger = Param(nameof(BreakEvenTrigger), 500m)
.SetNotNegative()
.SetDisplay("Break Even Trigger", "Profit in price steps that moves the stop to the entry price", "Risk");
_retracementPercentage = Param(nameof(RetracementPercentage), 0.01m)
.SetNotNegative()
.SetDisplay("Retracement Percentage", "Retracement below the crossover close as a fraction", "Entry");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fast = new SimpleMovingAverage { Length = FastSmaLength };
var slow = new SimpleMovingAverage { Length = SlowSmaLength };
var ema = new ExponentialMovingAverage { Length = EmaFilterLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ResetState()
{
_prevFast = null;
_prevSlow = null;
_prevClose = null;
_retracementLevel = null;
_entryPrice = 0;
_breakEven = false;
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
var prevClose = _prevClose;
_prevFast = fast;
_prevSlow = slow;
_prevClose = close;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (CheckStops(candle))
return;
if (prevFast is not decimal pf || prevSlow is not decimal ps || prevClose is not decimal pc)
return;
var bullCross = pf <= ps && fast > slow;
var bearCross = pf >= ps && fast < slow;
if (bullCross)
{
_retracementLevel = close * (1m - RetracementPercentage);
}
else if (bearCross)
{
_retracementLevel = null;
if (close < ema)
{
if (Position >= 0)
Enter(false, close);
return;
}
}
if (_retracementLevel is decimal level && pc <= level && close > level && Position <= 0)
{
_retracementLevel = null;
Enter(true, close);
}
}
private void Enter(bool isLong, decimal price)
{
var volume = Volume + Math.Abs(Position);
if (isLong)
BuyMarket(volume);
else
SellMarket(volume);
_entryPrice = price;
_breakEven = false;
}
private bool CheckStops(ICandleMessage candle)
{
if (Position == 0 || _entryPrice <= 0)
return false;
var step = Security?.PriceStep ?? 1m;
var take = TakeProfitDistance * step;
var stopDistance = StopLossDistance * step;
var trigger = BreakEvenTrigger * step;
if (Position > 0)
{
decimal? stop = _breakEven ? _entryPrice : StopLossDistance > 0 ? _entryPrice - stopDistance : null;
if ((stop is decimal s && candle.LowPrice <= s) || (TakeProfitDistance > 0 && candle.HighPrice >= _entryPrice + take))
{
SellMarket(Position);
_entryPrice = 0;
return true;
}
if (BreakEvenTrigger > 0 && candle.HighPrice - _entryPrice >= trigger)
_breakEven = true;
}
else
{
decimal? stop = _breakEven ? _entryPrice : StopLossDistance > 0 ? _entryPrice + stopDistance : null;
if ((stop is decimal s && candle.HighPrice >= s) || (TakeProfitDistance > 0 && candle.LowPrice <= _entryPrice - take))
{
BuyMarket(-Position);
_entryPrice = 0;
return true;
}
if (BreakEvenTrigger > 0 && _entryPrice - candle.LowPrice >= trigger)
_breakEven = true;
}
return false;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class btcusd_adjustable_sltp_strategy(Strategy):
"""
BTCUSD Adjustable SLTP strategy.
A bullish SMA(FastSmaLength)/SMA(SlowSmaLength) crossover arms a long setup at close * (1 - RetracementPercentage); the long opens
when the close crosses back above that level. A bearish crossover cancels the setup, closes a long while the close is below
EMA(EmaFilterLength) and in that case also opens a short. Take profit, stop loss and break-even trigger are distances in price
steps from the entry close; once the trigger is reached the stop moves to the entry price.
"""
def __init__(self):
super(btcusd_adjustable_sltp_strategy, self).__init__()
self._fast_sma_length = self.Param("FastSmaLength", 10).SetGreaterThanZero().SetDisplay("Fast SMA Length", "Fast SMA period", "Indicators")
self._slow_sma_length = self.Param("SlowSmaLength", 25).SetGreaterThanZero().SetDisplay("Slow SMA Length", "Slow SMA period", "Indicators")
self._ema_filter_length = self.Param("EmaFilterLength", 150).SetGreaterThanZero().SetDisplay("EMA Filter Length", "EMA filter period", "Indicators")
self._take_profit_distance = self.Param("TakeProfitDistance", 1000.0).SetNotNegative().SetDisplay("Take Profit Distance", "Take profit distance in price steps", "Risk")
self._stop_loss_distance = self.Param("StopLossDistance", 250.0).SetNotNegative().SetDisplay("Stop Loss Distance", "Stop loss distance in price steps", "Risk")
self._break_even_trigger = self.Param("BreakEvenTrigger", 500.0).SetNotNegative().SetDisplay("Break Even Trigger", "Profit in price steps that moves the stop to the entry price", "Risk")
self._retracement_percentage = self.Param("RetracementPercentage", 0.01).SetNotNegative().SetDisplay("Retracement Percentage", "Retracement below the crossover close as a fraction", "Entry")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = None
self._prev_slow = None
self._prev_close = None
self._retracement_level = None
self._entry_price = Decimal(0)
self._break_even = False
def OnReseted(self):
super(btcusd_adjustable_sltp_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(btcusd_adjustable_sltp_strategy, self).OnStarted2(time)
self._reset_state()
fast = SimpleMovingAverage()
fast.Length = self._fast_sma_length.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_sma_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_filter_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast, slow, ema):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
prev_fast = self._prev_fast
prev_slow = self._prev_slow
prev_close = self._prev_close
self._prev_fast = fast
self._prev_slow = slow
self._prev_close = close
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._check_stops(candle):
return
if prev_fast is None or prev_slow is None or prev_close is None:
return
bull_cross = prev_fast <= prev_slow and fast > slow
bear_cross = prev_fast >= prev_slow and fast < slow
if bull_cross:
self._retracement_level = close * (Decimal(1) - Decimal(self._retracement_percentage.Value))
elif bear_cross:
self._retracement_level = None
if close < ema:
if self.Position >= 0:
self._enter(False, close)
return
level = self._retracement_level
if level is not None and prev_close <= level and close > level and self.Position <= 0:
self._retracement_level = None
self._enter(True, close)
def _enter(self, is_long, price):
volume = self.Volume + abs(self.Position)
if is_long:
self.BuyMarket(volume)
else:
self.SellMarket(volume)
self._entry_price = price
self._break_even = False
def _check_stops(self, candle):
if self.Position == 0 or self._entry_price <= 0:
return False
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
tp_steps = Decimal(self._take_profit_distance.Value)
sl_steps = Decimal(self._stop_loss_distance.Value)
be_steps = Decimal(self._break_even_trigger.Value)
take = tp_steps * step
stop_distance = sl_steps * step
trigger = be_steps * step
if self.Position > 0:
if self._break_even:
stop = self._entry_price
elif sl_steps > 0:
stop = self._entry_price - stop_distance
else:
stop = None
if (stop is not None and candle.LowPrice <= stop) or (tp_steps > 0 and candle.HighPrice >= self._entry_price + take):
self.SellMarket(self.Position)
self._entry_price = Decimal(0)
return True
if be_steps > 0 and candle.HighPrice - self._entry_price >= trigger:
self._break_even = True
else:
if self._break_even:
stop = self._entry_price
elif sl_steps > 0:
stop = self._entry_price + stop_distance
else:
stop = None
if (stop is not None and candle.HighPrice >= stop) or (tp_steps > 0 and candle.LowPrice <= self._entry_price - take):
self.BuyMarket(-self.Position)
self._entry_price = Decimal(0)
return True
if be_steps > 0 and self._entry_price - candle.LowPrice >= trigger:
self._break_even = True
return False
def CreateClone(self):
return btcusd_adjustable_sltp_strategy()