Estrategia Momentum Alligator 4h Bitcoin
La estrategia Momentum Alligator 4h Bitcoin combina el Awesome Oscillator con el Alligator de Bill Williams en el marco temporal diario. Se abre una posición larga cuando el oscilador cruza por encima de su SMA de 5 períodos y el precio opera por encima de las tres líneas diarias del Alligator. Un stop loss dinámico utiliza el mayor valor entre una caída porcentual desde la entrada y la línea de mandíbula del Alligator. Tras una salida rentable, la estrategia omite las dos señales siguientes.
Detalles
- Criterios de entrada: AO cruza por encima de su SMA de 5 períodos y el cierre está por encima de las líneas diarias del Alligator.
- Largo/Corto: Solo largos.
- Criterios de salida: Stop loss dinámico en el máximo entre el stop porcentual y la mandíbula del Alligator.
- Stops: Sí.
- Valores predeterminados:
StopLossPercent= 0.02mCandleType= TimeSpan.FromHours(4)TradeStart= 2023-01-01TradeStop= 2025-01-01
- Filtros:
- Categoría: Momentum
- Dirección: Solo largos
- Indicadores: Awesome Oscillator, Alligator
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Medio plazo
- 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>
/// Momentum Alligator 4h Bitcoin strategy.
/// Goes long when the Awesome Oscillator crosses above its 5-period SMA and the close is above the jaw, teeth and lips
/// of the daily Alligator, only between TradeStart and TradeStop. The position is closed by a stop at the higher of the
/// percent stop below entry and the daily jaw. After a profitable exit the next two entry signals are skipped.
/// </summary>
public class MomentumAlligator4hBitcoinStrategy : Strategy
{
private const int _signalsToSkip = 2;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DateTimeOffset> _tradeStart;
private readonly StrategyParam<DateTimeOffset> _tradeStop;
private SimpleMovingAverage _aoSma;
private SmoothedMovingAverage _jaw;
private SmoothedMovingAverage _teeth;
private SmoothedMovingAverage _lips;
private decimal? _prevAo;
private decimal? _prevAoSma;
private decimal? _dailyJaw;
private decimal? _dailyTeeth;
private decimal? _dailyLips;
private decimal _entryPrice;
private int _skipCount;
/// <summary>
/// Percent stop below the entry price, as a fraction (0.02 = 2%).
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type of the trading timeframe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Entries are allowed from this time.
/// </summary>
public DateTimeOffset TradeStart
{
get => _tradeStart.Value;
set => _tradeStart.Value = value;
}
/// <summary>
/// Entries are allowed until this time.
/// </summary>
public DateTimeOffset TradeStop
{
get => _tradeStop.Value;
set => _tradeStop.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MomentumAlligator4hBitcoinStrategy()
{
_stopLossPercent = Param(nameof(StopLossPercent), 0.02m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Percent stop below entry as a fraction", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_tradeStart = Param(nameof(TradeStart), new DateTimeOffset(2023, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Trade Start", "Entries are allowed from this time", "General");
_tradeStop = Param(nameof(TradeStop), new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Trade Stop", "Entries are allowed until this time", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType), (Security, TimeSpan.FromDays(1).TimeFrame())];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevAo = null;
_prevAoSma = null;
_dailyJaw = null;
_dailyTeeth = null;
_dailyLips = null;
_entryPrice = 0m;
_skipCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ao = new AwesomeOscillator();
_aoSma = new SimpleMovingAverage { Length = 5 };
_jaw = new SmoothedMovingAverage { Length = 13 };
_teeth = new SmoothedMovingAverage { Length = 8 };
_lips = new SmoothedMovingAverage { Length = 5 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ao, ProcessCandle)
.Start();
SubscribeCandles(TimeSpan.FromDays(1).TimeFrame())
.Bind(ProcessDailyCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, ao);
DrawIndicator(oscillators, _aoSma);
}
}
}
private void ProcessDailyCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// The Alligator lines are smoothed averages of the median price.
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var jaw = _jaw.Process(new DecimalIndicatorValue(_jaw, median, candle.OpenTime) { IsFinal = true }).ToDecimal();
var teeth = _teeth.Process(new DecimalIndicatorValue(_teeth, median, candle.OpenTime) { IsFinal = true }).ToDecimal();
var lips = _lips.Process(new DecimalIndicatorValue(_lips, median, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (_jaw.IsFormed)
_dailyJaw = jaw;
if (_teeth.IsFormed)
_dailyTeeth = teeth;
if (_lips.IsFormed)
_dailyLips = lips;
}
private void ProcessCandle(ICandleMessage candle, decimal ao)
{
if (candle.State != CandleStates.Finished)
return;
var aoSma = _aoSma.Process(new DecimalIndicatorValue(_aoSma, ao, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_aoSma.IsFormed)
{
_prevAo = ao;
return;
}
var prevAo = _prevAo;
var prevAoSma = _prevAoSma;
_prevAo = ao;
_prevAoSma = aoSma;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
var stop = _entryPrice * (1m - StopLossPercent);
if (_dailyJaw is decimal jawStop && jawStop > stop)
stop = jawStop;
if (candle.LowPrice <= stop)
{
var exitPrice = Math.Min(stop, candle.OpenPrice);
if (exitPrice > _entryPrice)
_skipCount = _signalsToSkip;
SellMarket(Position);
}
return;
}
if (prevAo is not decimal pa || prevAoSma is not decimal ps)
return;
if (_dailyJaw is not decimal jaw || _dailyTeeth is not decimal teeth || _dailyLips is not decimal lips)
return;
var crossUp = pa <= ps && ao > aoSma;
if (!crossUp)
return;
var close = candle.ClosePrice;
if (close <= jaw || close <= teeth || close <= lips)
return;
var openTime = candle.OpenTime;
if (openTime < TradeStart.UtcDateTime || openTime >= TradeStop.UtcDateTime)
return;
if (_skipCount > 0)
{
_skipCount--;
return;
}
_entryPrice = close;
BuyMarket(Volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DateTimeOffset
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AwesomeOscillator, SimpleMovingAverage, SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
SIGNALS_TO_SKIP = 2
class momentum_alligator_4h_bitcoin_strategy(Strategy):
"""
Momentum Alligator 4h Bitcoin strategy.
Goes long when the Awesome Oscillator crosses above its 5-period SMA and the close is above the jaw, teeth and lips
of the daily Alligator, only between TradeStart and TradeStop. The position is closed by a stop at the higher of the
percent stop below entry and the daily jaw. After a profitable exit the next two entry signals are skipped.
"""
def __init__(self):
super(momentum_alligator_4h_bitcoin_strategy, self).__init__()
self._stop_loss_percent = self.Param("StopLossPercent", 0.02).SetNotNegative().SetDisplay("Stop Loss %", "Percent stop below entry as a fraction", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._trade_start = self.Param("TradeStart", DateTimeOffset(2023, 1, 1, 0, 0, 0, TimeSpan.Zero)).SetDisplay("Trade Start", "Entries are allowed from this time", "General")
self._trade_stop = self.Param("TradeStop", DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero)).SetDisplay("Trade Stop", "Entries are allowed until this time", "General")
self._ao_sma = None
self._jaw = None
self._teeth = None
self._lips = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.TimeFrame(TimeSpan.FromDays(1)))]
def _reset_state(self):
self._prev_ao = None
self._prev_ao_sma = None
self._daily_jaw = None
self._daily_teeth = None
self._daily_lips = None
self._entry_price = 0.0
self._skip_count = 0
def OnReseted(self):
super(momentum_alligator_4h_bitcoin_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(momentum_alligator_4h_bitcoin_strategy, self).OnStarted2(time)
self._reset_state()
ao = AwesomeOscillator()
self._ao_sma = SimpleMovingAverage()
self._ao_sma.Length = 5
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._teeth = SmoothedMovingAverage()
self._teeth.Length = 8
self._lips = SmoothedMovingAverage()
self._lips.Length = 5
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ao, self._process_candle).Start()
self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromDays(1))).Bind(self._process_daily_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, ao)
self.DrawIndicator(oscillators, self._ao_sma)
def _process_daily_candle(self, candle):
if candle.State != CandleStates.Finished:
return
# The Alligator lines are smoothed averages of the median price.
median = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
jaw = float(process_float(self._jaw, median, candle.OpenTime, True))
teeth = float(process_float(self._teeth, median, candle.OpenTime, True))
lips = float(process_float(self._lips, median, candle.OpenTime, True))
if self._jaw.IsFormed:
self._daily_jaw = jaw
if self._teeth.IsFormed:
self._daily_teeth = teeth
if self._lips.IsFormed:
self._daily_lips = lips
def _process_candle(self, candle, ao_value):
if candle.State != CandleStates.Finished:
return
ao = float(ao_value)
ao_sma = float(process_float(self._ao_sma, ao, candle.OpenTime, True))
if not self._ao_sma.IsFormed:
self._prev_ao = ao
return
prev_ao = self._prev_ao
prev_ao_sma = self._prev_ao_sma
self._prev_ao = ao
self._prev_ao_sma = ao_sma
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
stop = self._entry_price * (1.0 - float(self._stop_loss_percent.Value))
if self._daily_jaw is not None and self._daily_jaw > stop:
stop = self._daily_jaw
if float(candle.LowPrice) <= stop:
exit_price = min(stop, float(candle.OpenPrice))
if exit_price > self._entry_price:
self._skip_count = SIGNALS_TO_SKIP
self.SellMarket(self.Position)
return
if prev_ao is None or prev_ao_sma is None:
return
if self._daily_jaw is None or self._daily_teeth is None or self._daily_lips is None:
return
cross_up = prev_ao <= prev_ao_sma and ao > ao_sma
if not cross_up:
return
close = float(candle.ClosePrice)
if close <= self._daily_jaw or close <= self._daily_teeth or close <= self._daily_lips:
return
open_time = candle.OpenTime
if open_time < self._trade_start.Value.UtcDateTime or open_time >= self._trade_stop.Value.UtcDateTime:
return
if self._skip_count > 0:
self._skip_count -= 1
return
self._entry_price = close
self.BuyMarket(self.Volume)
def CreateClone(self):
return momentum_alligator_4h_bitcoin_strategy()