Estrategia Mutanabby AI Algo Pro
La estrategia Mutanabby AI Algo Pro entra largo cuando un patrón de vela envolvente alcista se alinea con una lectura de RSI por debajo de un umbral y una caída del precio durante un número determinado de barras. Los cierres ocurren en un patrón envolvente bajista o cuando se alcanza el stop loss.
Detalles
- Criterios de entrada: Envolvente alcista, vela estable, RSI por debajo del umbral, precio por debajo del valor de N barras atrás.
- Largo/Corto: Solo largos.
- Criterios de salida: Envolvente bajista o stop loss.
- Stops: Opcional.
- Valores predeterminados:
CandleStabilityIndex= 0.5RsiIndex= 50CandleDeltaLength= 5DisableRepeatingSignals= falseEnableStopLoss= trueStopLossMethod= EntryPriceBasedEntryStopLossPercent= 2.0LookbackPeriod= 10StopLossBufferPercent= 0.5CandleType= TimeSpan.FromMinutes(1)
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Solo largos
- Indicadores: RSI
- Stops: Sí
- Complejidad: Básico
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Mutanabby AI Algo Pro strategy.
/// Enters long on bullish engulfing with RSI and price filters.
/// </summary>
public class MutanabbyAiAlgoProStrategy : Strategy
{
/// <summary>
/// Defines stop loss calculation modes.
/// </summary>
public enum StopLossModes
{
/// <summary>
/// Stop loss is calculated from entry price.
/// </summary>
EntryPriceBased,
/// <summary>
/// Stop loss is based on the lowest low over a period.
/// </summary>
LowestLowBased
}
private readonly StrategyParam<decimal> _candleStabilityIndex;
private readonly StrategyParam<int> _rsiIndex;
private readonly StrategyParam<int> _candleDeltaLength;
private readonly StrategyParam<bool> _disableRepeatingSignals;
private readonly StrategyParam<bool> _enableStopLoss;
private readonly StrategyParam<StopLossModes> _stopLossMethod;
private readonly StrategyParam<decimal> _entryStopLossPercent;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<decimal> _stopLossBufferPercent;
private readonly StrategyParam<DataType> _candleType;
private static readonly object _sync = new();
private decimal _prevOpen;
private decimal _prevClose;
private readonly Queue<decimal> _closeQueue = new();
private readonly Queue<decimal> _lowQueue = new();
private decimal _lowestLow = decimal.MaxValue;
private string _lastSignal = string.Empty;
private decimal _stopLossPrice;
private decimal _entryPrice;
/// <summary>
/// Minimum ratio between candle body and true range.
/// </summary>
public decimal CandleStabilityIndex
{
get => _candleStabilityIndex.Value;
set => _candleStabilityIndex.Value = value;
}
/// <summary>
/// RSI threshold.
/// </summary>
public int RsiIndex
{
get => _rsiIndex.Value;
set => _rsiIndex.Value = value;
}
/// <summary>
/// Bars for price comparison.
/// </summary>
public int CandleDeltaLength
{
get => _candleDeltaLength.Value;
set => _candleDeltaLength.Value = value;
}
/// <summary>
/// Prevent consecutive identical signals.
/// </summary>
public bool DisableRepeatingSignals
{
get => _disableRepeatingSignals.Value;
set => _disableRepeatingSignals.Value = value;
}
/// <summary>
/// Enable stop loss.
/// </summary>
public bool EnableStopLoss
{
get => _enableStopLoss.Value;
set => _enableStopLoss.Value = value;
}
/// <summary>
/// Stop loss calculation method.
/// </summary>
public StopLossModes StopLossMethod
{
get => _stopLossMethod.Value;
set => _stopLossMethod.Value = value;
}
/// <summary>
/// Entry based stop loss percent.
/// </summary>
public decimal EntryStopLossPercent
{
get => _entryStopLossPercent.Value;
set => _entryStopLossPercent.Value = value;
}
/// <summary>
/// Lookback period for lowest low stop.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Buffer percent below lowest low.
/// </summary>
public decimal StopLossBufferPercent
{
get => _stopLossBufferPercent.Value;
set => _stopLossBufferPercent.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="MutanabbyAiAlgoProStrategy"/> class.
/// </summary>
public MutanabbyAiAlgoProStrategy()
{
_candleStabilityIndex = Param(nameof(CandleStabilityIndex), 0.5m)
.SetDisplay("Candle Stability Index", "Minimum body/true range ratio", "Technical")
.SetRange(0m, 1m)
.SetOptimize(0.1m, 1m, 0.1m);
_rsiIndex = Param(nameof(RsiIndex), 50)
.SetDisplay("RSI Index", "RSI threshold for entries", "Technical")
.SetRange(0, 100)
;
_candleDeltaLength = Param(nameof(CandleDeltaLength), 5)
.SetDisplay("Candle Delta Length", "Bars for price comparison", "Technical")
.SetRange(3, 50)
;
_disableRepeatingSignals = Param(nameof(DisableRepeatingSignals), false)
.SetDisplay("Disable Repeating Signals", "Avoid consecutive identical signals", "Technical");
_enableStopLoss = Param(nameof(EnableStopLoss), true)
.SetDisplay("Enable Stop Loss", "Activate stop loss", "Risk Management");
_stopLossMethod = Param(nameof(StopLossMethod), StopLossModes.EntryPriceBased)
.SetDisplay("Stop Loss Method", "Entry price or lowest low based", "Risk Management");
_entryStopLossPercent = Param(nameof(EntryStopLossPercent), 2.0m)
.SetDisplay("Entry Stop Loss %", "Stop loss percent from entry", "Risk Management")
.SetGreaterThanZero();
_lookbackPeriod = Param(nameof(LookbackPeriod), 10)
.SetDisplay("Lookback Period", "Bars for lowest low stop", "Risk Management")
.SetGreaterThanZero();
_stopLossBufferPercent = Param(nameof(StopLossBufferPercent), 0.5m)
.SetDisplay("Stop Loss Buffer %", "Additional buffer below lowest low", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_prevOpen = 0;
_prevClose = 0;
_closeQueue.Clear();
_lowQueue.Clear();
_lowestLow = decimal.MaxValue;
_lastSignal = string.Empty;
_stopLossPrice = 0;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RSI { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(candle => ProcessCandle(candle, rsi))
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, RelativeStrengthIndex rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
decimal rsiValue;
lock (_sync)
{
var rsiResult = rsi.Process(new DecimalIndicatorValue(rsi, candle.ClosePrice, candle.OpenTime) { IsFinal = true });
if (!rsiResult.IsFinal || !rsi.IsFormed)
return;
rsiValue = rsiResult.ToDecimal();
}
// update low queue
if (_lowQueue.Count == LookbackPeriod)
{
var removed = _lowQueue.Dequeue();
if (removed <= _lowestLow)
{
_lowestLow = decimal.MaxValue;
foreach (var v in _lowQueue)
{
if (v < _lowestLow)
_lowestLow = v;
}
}
}
_lowQueue.Enqueue(candle.LowPrice);
if (candle.LowPrice < _lowestLow)
_lowestLow = candle.LowPrice;
var priceN = _closeQueue.Count == CandleDeltaLength ? _closeQueue.Peek() : (decimal?)null;
var trueRange = candle.HighPrice - candle.LowPrice;
var stableCandle = trueRange > 0 && Math.Abs(candle.ClosePrice - candle.OpenPrice) / trueRange > CandleStabilityIndex;
var bullishEngulfing = _prevClose < _prevOpen && candle.ClosePrice > candle.OpenPrice && candle.ClosePrice > _prevOpen;
var rsiBelow = rsiValue < RsiIndex;
var decreaseOver = priceN != null && candle.ClosePrice < priceN;
var entrySignal = bullishEngulfing && stableCandle && rsiBelow && decreaseOver;
var bearishEngulfing = _prevClose > _prevOpen && candle.ClosePrice < candle.OpenPrice && candle.ClosePrice < _prevOpen;
var rsiAbove = rsiValue > 100 - RsiIndex;
var increaseOver = priceN != null && candle.ClosePrice > priceN;
var exitSignal = bearishEngulfing && stableCandle && rsiAbove && increaseOver;
if (entrySignal && Position <= 0 && (!DisableRepeatingSignals || _lastSignal != "buy"))
{
BuyMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
if (EnableStopLoss)
{
_stopLossPrice = StopLossMethod == StopLossModes.EntryPriceBased
? _entryPrice * (1 - EntryStopLossPercent / 100m)
: _lowestLow * (1 - StopLossBufferPercent / 100m);
}
_lastSignal = "buy";
}
if (exitSignal && Position > 0 && (!DisableRepeatingSignals || _lastSignal != "sell"))
{
SellMarket(Position);
_lastSignal = "sell";
}
if (EnableStopLoss && Position > 0 && _stopLossPrice > 0 && candle.ClosePrice <= _stopLossPrice)
{
SellMarket(Position);
_lastSignal = "sell";
}
_closeQueue.Enqueue(candle.ClosePrice);
if (_closeQueue.Count > CandleDeltaLength)
_closeQueue.Dequeue();
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class mutanabby_ai_algo_pro_strategy(Strategy):
def __init__(self):
super(mutanabby_ai_algo_pro_strategy, self).__init__()
self._candle_stability_index = self.Param("CandleStabilityIndex", 0.5) \
.SetDisplay("Candle Stability Index", "Minimum body/true range ratio", "Technical")
self._rsi_index = self.Param("RsiIndex", 50) \
.SetDisplay("RSI Index", "RSI threshold for entries", "Technical")
self._candle_delta_length = self.Param("CandleDeltaLength", 5) \
.SetDisplay("Candle Delta Length", "Bars for price comparison", "Technical")
self._disable_repeating_signals = self.Param("DisableRepeatingSignals", False) \
.SetDisplay("Disable Repeating Signals", "Avoid consecutive identical signals", "Technical")
self._enable_stop_loss = self.Param("EnableStopLoss", True) \
.SetDisplay("Enable Stop Loss", "Activate stop loss", "Risk Management")
self._entry_stop_loss_percent = self.Param("EntryStopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Entry Stop Loss %", "Stop loss percent from entry", "Risk Management")
self._lookback_period = self.Param("LookbackPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("Lookback Period", "Bars for lowest low stop", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_open = 0.0
self._prev_close = 0.0
self._close_queue = []
self._low_queue = []
self._lowest_low = 999999999.0
self._last_signal = ""
self._stop_loss_price = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(mutanabby_ai_algo_pro_strategy, self).OnReseted()
self._prev_open = 0.0
self._prev_close = 0.0
self._close_queue = []
self._low_queue = []
self._lowest_low = 999999999.0
self._last_signal = ""
self._stop_loss_price = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(mutanabby_ai_algo_pro_strategy, self).OnStarted2(time)
self._prev_open = 0.0
self._prev_close = 0.0
self._close_queue = []
self._low_queue = []
self._lowest_low = 999999999.0
self._last_signal = ""
self._stop_loss_price = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
opn = float(candle.OpenPrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
rsi_result = process_float(self._rsi, candle.ClosePrice, candle.OpenTime, True)
if not self._rsi.IsFormed:
self._prev_open = opn
self._prev_close = close
self._close_queue.append(close)
cdl = self._candle_delta_length.Value
if len(self._close_queue) > cdl:
self._close_queue.pop(0)
self._low_queue.append(low)
lp = self._lookback_period.Value
if len(self._low_queue) > lp:
self._low_queue.pop(0)
self._lowest_low = min(self._low_queue) if self._low_queue else 999999999.0
return
rsi_value = float(rsi_result)
lp = self._lookback_period.Value
if len(self._low_queue) >= lp:
self._low_queue.pop(0)
self._low_queue.append(low)
self._lowest_low = min(self._low_queue) if self._low_queue else 999999999.0
cdl = self._candle_delta_length.Value
price_n = self._close_queue[0] if len(self._close_queue) == cdl else None
true_range = high - low
csi = float(self._candle_stability_index.Value)
stable_candle = true_range > 0.0 and abs(close - opn) / true_range > csi
bullish_engulfing = self._prev_close < self._prev_open and close > opn and close > self._prev_open
rsi_idx = self._rsi_index.Value
rsi_below = rsi_value < rsi_idx
decrease_over = price_n is not None and close < price_n
entry_signal = bullish_engulfing and stable_candle and rsi_below and decrease_over
bearish_engulfing = self._prev_close > self._prev_open and close < opn and close < self._prev_open
rsi_above = rsi_value > (100 - rsi_idx)
increase_over = price_n is not None and close > price_n
exit_signal = bearish_engulfing and stable_candle and rsi_above and increase_over
disable_rep = self._disable_repeating_signals.Value
if entry_signal and self.Position <= 0 and (not disable_rep or self._last_signal != "buy"):
self.BuyMarket()
self._entry_price = close
if self._enable_stop_loss.Value:
sl_pct = float(self._entry_stop_loss_percent.Value)
self._stop_loss_price = self._entry_price * (1.0 - sl_pct / 100.0)
self._last_signal = "buy"
if exit_signal and self.Position > 0 and (not disable_rep or self._last_signal != "sell"):
self.SellMarket()
self._last_signal = "sell"
if self._enable_stop_loss.Value and self.Position > 0 and self._stop_loss_price > 0.0 and close <= self._stop_loss_price:
self.SellMarket()
self._last_signal = "sell"
self._close_queue.append(close)
if len(self._close_queue) > cdl:
self._close_queue.pop(0)
self._prev_open = opn
self._prev_close = close
def CreateClone(self):
return mutanabby_ai_algo_pro_strategy()