Autoadaptable revisado EA
Incorporación del MetaTrader 5 asesores expertos revised_self_adaptive_ea.mq5 al marco estratégico de alto nivel StockSharp.
Descripción general de la estrategia
El algoritmo escanea una serie de velas configurables y busca configuraciones de reversión envolvente confirmadas por filtros de impulso y tendencia:
- Detección de patrones: evalúa la última vela cerrada frente a la anterior. Una configuración alcista requiere un cuerpo verde que se abre por debajo del cierre anterior mientras que la vela anterior es bajista. La lógica del espejo se aplica para configuraciones bajistas. Los cuerpos de las velas se comparan con un promedio móvil para filtrar señales débiles.
- Filtro de impulso: un RSI clásico garantiza que las operaciones alcistas solo se activen en territorio de sobreventa y las operaciones bajistas en condiciones de sobrecompra.
- Filtro de tendencia: una media móvil simple corta debe coincidir con la dirección comercial. Esto evita que las tendencias fuertes se desvanezcan sin confirmación.
- Gestión de riesgos: se calculan niveles de stop-loss y take-profit impulsados por ATR para cada nueva posición. Los trailingstops opcionales siguen movimientos rentables sin reducir nunca la protección. Las posiciones se cierran a la fuerza cuando el precio alcanza los niveles de protección.
- Diferencial y protección contra riesgos: las operaciones se omiten siempre que el diferencial actual supere el umbral configurado o cuando el stop basado en ATR arriesgaría más que el porcentaje permitido del precio.
Parámetros
| Nombre | Descripción |
|---|---|
CandleType |
Agregación de velas utilizada para el análisis. El valor predeterminado es barras de una hora. |
AverageBodyPeriod |
Número de velas utilizadas para calcular el filtro de tamaño corporal promedio. |
MovingAveragePeriod |
Longitud de la media móvil simple que actúa como filtro direccional. |
RsiPeriod |
RSI longitud utilizada para la confirmación de sobreventa/sobrecompra. |
OversoldLevel |
RSI umbral que se debe alcanzar antes de aceptar una reversión alcista. |
OverboughtLevel |
RSI umbral que se debe alcanzar antes de aceptar una reversión bajista. |
AtrPeriod |
ATR longitud utilizada para distancias protectoras basadas en la volatilidad. |
StopLossAtrMultiplier |
Factor multiplicativo aplicado a ATR para la distancia de stop-loss. |
TakeProfitAtrMultiplier |
Factor multiplicativo aplicado a ATR para la distancia de obtención de beneficios. |
TrailingStopAtrMultiplier |
ATR distancia mantenida por la lógica del trailing stop. |
UseTrailingStop |
Habilita el supervisor de trailing stop. |
MaxSpreadPoints |
Spread máximo permitido (expresado en pasos de precio/pips). Las señales se ignoran cuando el mercado es más amplio. |
MaxRiskPercent |
Riesgo porcentual máximo aceptable basado en el stop ATR en relación con el precio de entrada. |
TradeVolume |
Tamaño de lote base utilizado para órdenes de mercado. |
Notas de comportamiento
- Las posiciones se aplanan antes de invertir la dirección para reflejar la implementación de MetaTrader.
- Los niveles de parada/toma de protección se vuelven a calcular después de cada llenado utilizando la lectura ATR más reciente.
- El trailing stop solo se mueve en la dirección comercial y se desactiva cuando los datos ATR aún no están disponibles.
- Si la estrategia se ejecuta en un instrumento sin cotizaciones de oferta/demanda confiables, el filtro de diferencial permanecerá inactivo automáticamente.
Diferencias vs. original MQL
El guión original sólo describía la rutina de detección de señales. En este puerto los elementos faltantes fueron reconstruidos utilizando los parámetros proporcionados:
- Se agregó confirmación de promedio móvil para hacer uso del identificador MA declarado en la fuente MQL.
- Se implementó la lógica de stop-loss, take-profit y trailing stop basada en ATR utilizando el control de volatilidad definido en el experto original.
- Se agregó una protección de porcentaje de riesgo para que las paradas ATR sobredimensionadas se omitan en lugar de ejecutarse a ciegas.
- Se omitieron los elementos de visualización (flechas del gráfico) porque las estrategias StockSharp no dibujan objetos en los gráficos de forma predeterminada.
Uso
- Adjunte la estrategia a una cartera y seguridad dentro de Hydra o su host StockSharp personalizado.
- Asegúrese de que la suscripción a la vela coincida con el plazo previsto (predeterminado: una hora).
- Ajustar los parámetros de riesgo para reflejar la volatilidad del instrumento.
- Inicia la estrategia. Se suscribirá automáticamente a velas, calculará indicadores y realizará órdenes de mercado cuando se cumplan las condiciones.
namespace StockSharp.Samples.Strategies;
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;
using StockSharp.Algo;
/// <summary>
/// Port of the MetaTrader expert revised_self_adaptive_ea.
/// Detects bullish and bearish engulfing patterns confirmed by RSI and a trend moving average.
/// Applies ATR based risk management with optional trailing stop supervision.
/// </summary>
public class RevisedSelfAdaptiveEaStrategy : Strategy
{
private readonly StrategyParam<int> _averageBodyPeriod;
private readonly StrategyParam<int> _movingAveragePeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _volume;
private readonly StrategyParam<decimal> _maxSpreadPoints;
private readonly StrategyParam<decimal> _maxRiskPercent;
private readonly StrategyParam<bool> _useTrailingStop;
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<decimal> _takeProfitAtrMultiplier;
private readonly StrategyParam<decimal> _trailingStopAtrMultiplier;
private readonly StrategyParam<decimal> _oversoldLevel;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi = null!;
private SimpleMovingAverage _movingAverage = null!;
private AverageTrueRange _atr = null!;
private SimpleMovingAverage _bodyAverage = null!;
private ICandleMessage _previousCandle;
private decimal _lastAtrValue;
private decimal _averageBodyValue;
private decimal _pipSize;
private bool _pipSizeInitialized;
private decimal? _longEntryPrice;
private decimal? _longStopPrice;
private decimal? _longTakeProfitPrice;
private decimal? _longTrailingStopPrice;
private decimal? _shortEntryPrice;
private decimal? _shortStopPrice;
private decimal? _shortTakeProfitPrice;
private decimal? _shortTrailingStopPrice;
/// <summary>
/// Initializes a new instance of <see cref="RevisedSelfAdaptiveEaStrategy"/>.
/// </summary>
public RevisedSelfAdaptiveEaStrategy()
{
_averageBodyPeriod = Param(nameof(AverageBodyPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Average body period", "Number of candles used to calculate the average body size filter.", "Pattern")
.SetOptimize(2, 10, 1);
_movingAveragePeriod = Param(nameof(MovingAveragePeriod), 2)
.SetGreaterThanZero()
.SetDisplay("MA period", "Simple moving average period used as a directional filter.", "Trend")
.SetOptimize(2, 30, 1);
_rsiPeriod = Param(nameof(RsiPeriod), 6)
.SetGreaterThanZero()
.SetDisplay("RSI period", "Length of the RSI oscillator applied to candle closes.", "Oscillator")
.SetOptimize(3, 30, 1);
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR period", "Average True Range period that controls stop distances.", "Risk")
.SetOptimize(7, 50, 1);
_volume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade volume", "Base position size expressed in lots.", "Trading")
.SetOptimize(0.01m, 1m, 0.01m);
_maxSpreadPoints = Param(nameof(MaxSpreadPoints), 20m)
.SetNotNegative()
.SetDisplay("Max spread", "Maximum allowed spread expressed in points.", "Risk");
_maxRiskPercent = Param(nameof(MaxRiskPercent), 10m)
.SetNotNegative()
.SetDisplay("Max risk percent", "Maximum percentage of the portfolio equity accepted per trade.", "Risk");
_useTrailingStop = Param(nameof(UseTrailingStop), true)
.SetDisplay("Use trailing stop", "Enable ATR driven trailing stop supervision.", "Risk");
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("Stop loss ATR multiplier", "Number of ATRs used to place the protective stop.", "Risk")
.SetOptimize(0.5m, 5m, 0.5m);
_takeProfitAtrMultiplier = Param(nameof(TakeProfitAtrMultiplier), 4m)
.SetNotNegative()
.SetDisplay("Take profit ATR multiplier", "Number of ATRs used to place the profit target.", "Risk")
.SetOptimize(0.5m, 8m, 0.5m);
_trailingStopAtrMultiplier = Param(nameof(TrailingStopAtrMultiplier), 1.5m)
.SetNotNegative()
.SetDisplay("Trailing stop ATR multiplier", "ATR distance maintained by the trailing stop logic.", "Risk")
.SetOptimize(0.5m, 5m, 0.5m);
_oversoldLevel = Param(nameof(OversoldLevel), 40m)
.SetNotNegative()
.SetDisplay("Oversold level", "RSI threshold that confirms bullish reversals.", "Oscillator")
.SetOptimize(10m, 50m, 5m);
_overboughtLevel = Param(nameof(OverboughtLevel), 60m)
.SetNotNegative()
.SetDisplay("Overbought level", "RSI threshold that confirms bearish reversals.", "Oscillator")
.SetOptimize(50m, 90m, 5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle type", "Time frame used for pattern detection.", "General");
}
/// <summary>
/// Rolling period used to evaluate the average candle body size.
/// </summary>
public int AverageBodyPeriod
{
get => _averageBodyPeriod.Value;
set => _averageBodyPeriod.Value = value;
}
/// <summary>
/// Period applied to the simple moving average filter.
/// </summary>
public int MovingAveragePeriod
{
get => _movingAveragePeriod.Value;
set => _movingAveragePeriod.Value = value;
}
/// <summary>
/// RSI length used to confirm overbought and oversold conditions.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Average True Range period that controls risk distances.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Default trade volume.
/// </summary>
public decimal TradeVolume
{
get => _volume.Value;
set => _volume.Value = value;
}
/// <summary>
/// Maximum allowed spread measured in points.
/// </summary>
public decimal MaxSpreadPoints
{
get => _maxSpreadPoints.Value;
set => _maxSpreadPoints.Value = value;
}
/// <summary>
/// Maximum portion of portfolio equity that can be exposed per trade.
/// </summary>
public decimal MaxRiskPercent
{
get => _maxRiskPercent.Value;
set => _maxRiskPercent.Value = value;
}
/// <summary>
/// Enables the ATR based trailing stop controller.
/// </summary>
public bool UseTrailingStop
{
get => _useTrailingStop.Value;
set => _useTrailingStop.Value = value;
}
/// <summary>
/// ATR multiplier used to position the stop loss.
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// ATR multiplier used to position the take profit target.
/// </summary>
public decimal TakeProfitAtrMultiplier
{
get => _takeProfitAtrMultiplier.Value;
set => _takeProfitAtrMultiplier.Value = value;
}
/// <summary>
/// ATR multiplier that defines the trailing stop distance.
/// </summary>
public decimal TrailingStopAtrMultiplier
{
get => _trailingStopAtrMultiplier.Value;
set => _trailingStopAtrMultiplier.Value = value;
}
/// <summary>
/// RSI threshold that validates bullish signals.
/// </summary>
public decimal OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// RSI threshold that validates bearish signals.
/// </summary>
public decimal OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// Candle type consumed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousCandle = null;
_lastAtrValue = 0m;
_averageBodyValue = 0m;
_pipSize = 0m;
_pipSizeInitialized = false;
ResetLongRiskLevels();
ResetShortRiskLevels();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = TradeVolume;
_rsi = new RelativeStrengthIndex
{
Length = RsiPeriod
};
_movingAverage = new SMA
{
Length = MovingAveragePeriod
};
_atr = new AverageTrueRange
{
Length = AtrPeriod
};
_bodyAverage = new SMA
{
Length = AverageBodyPeriod
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, _movingAverage, _atr, ProcessCandle)
.Start();
StartProtection(null, null);
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
var order = trade.Order;
if (order == null)
return;
if (order.Side == Sides.Buy)
{
if (Position > 0m)
{
// Long exposure established, compute fresh protective levels.
_longEntryPrice = trade.Trade.Price;
InitializeLongRiskLevels(trade.Trade.Price);
}
else if (Position >= 0m)
{
// Short exposure was reduced or closed.
ResetShortRiskLevels();
}
}
else if (order.Side == Sides.Sell)
{
if (Position < 0m)
{
// Short exposure established, compute protective levels.
_shortEntryPrice = trade.Trade.Price;
InitializeShortRiskLevels(trade.Trade.Price);
}
else if (Position <= 0m)
{
// Long exposure was reduced or closed.
ResetLongRiskLevels();
}
}
}
/// <inheritdoc />
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
if (Position == 0m)
{
// Flat state clears pending protective levels.
ResetLongRiskLevels();
ResetShortRiskLevels();
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal maValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_pipSizeInitialized)
InitializePipSize();
_lastAtrValue = atrValue;
UpdateAverageBody(candle);
ManageOpenPositions(candle);
if (!_rsi.IsFormed || !_movingAverage.IsFormed || !_atr.IsFormed)
{
_previousCandle = candle;
return;
}
if (_previousCandle == null)
{
_previousCandle = candle;
return;
}
if (!IsSpreadWithinLimit())
{
_previousCandle = candle;
return;
}
var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var minimumBody = _averageBodyValue > 0m ? _averageBodyValue : 0m;
var bullishEngulfing = candle.ClosePrice > candle.OpenPrice &&
_previousCandle.ClosePrice < _previousCandle.OpenPrice &&
candle.OpenPrice <= _previousCandle.ClosePrice &&
bodySize >= minimumBody;
var bearishEngulfing = candle.ClosePrice < candle.OpenPrice &&
_previousCandle.ClosePrice > _previousCandle.OpenPrice &&
candle.OpenPrice >= _previousCandle.ClosePrice &&
bodySize >= minimumBody;
if (bullishEngulfing && rsiValue <= OversoldLevel && candle.ClosePrice >= maValue)
{
TryOpenLong(candle.ClosePrice);
}
else if (bearishEngulfing && rsiValue >= OverboughtLevel && candle.ClosePrice <= maValue)
{
TryOpenShort(candle.ClosePrice);
}
_previousCandle = candle;
}
private void TryOpenLong(decimal price)
{
if (Position > 0m)
return;
if (ShouldBlockByRisk(price))
return;
if (Position < 0m)
{
// Close the opposing short exposure before flipping direction.
BuyMarket(Math.Abs(Position));
return;
}
var volume = GetTradeVolume();
if (volume <= 0m)
return;
// Market order is used to replicate the MetaTrader execution style.
BuyMarket(volume);
}
private void TryOpenShort(decimal price)
{
if (Position < 0m)
return;
if (ShouldBlockByRisk(price))
return;
if (Position > 0m)
{
// Close the opposing long exposure before flipping direction.
SellMarket(Math.Abs(Position));
return;
}
var volume = GetTradeVolume();
if (volume <= 0m)
return;
SellMarket(volume);
}
private void ManageOpenPositions(ICandleMessage candle)
{
if (Position > 0m)
{
// Manage protective logic for long exposure.
var exitVolume = Position;
if (_longStopPrice.HasValue && candle.LowPrice <= _longStopPrice.Value)
{
SellMarket(exitVolume);
return;
}
if (_longTakeProfitPrice.HasValue && candle.HighPrice >= _longTakeProfitPrice.Value)
{
SellMarket(exitVolume);
return;
}
if (UseTrailingStop && _longTrailingStopPrice.HasValue && _lastAtrValue > 0m)
{
var candidate = candle.ClosePrice - _lastAtrValue * TrailingStopAtrMultiplier;
if (candidate > _longTrailingStopPrice.Value)
_longTrailingStopPrice = candidate;
if (candle.LowPrice <= _longTrailingStopPrice.Value)
{
SellMarket(exitVolume);
return;
}
}
}
else if (Position < 0m)
{
// Manage protective logic for short exposure.
var exitVolume = Math.Abs(Position);
if (_shortStopPrice.HasValue && candle.HighPrice >= _shortStopPrice.Value)
{
BuyMarket(exitVolume);
return;
}
if (_shortTakeProfitPrice.HasValue && candle.LowPrice <= _shortTakeProfitPrice.Value)
{
BuyMarket(exitVolume);
return;
}
if (UseTrailingStop && _shortTrailingStopPrice.HasValue && _lastAtrValue > 0m)
{
var candidate = candle.ClosePrice + _lastAtrValue * TrailingStopAtrMultiplier;
if (candidate < _shortTrailingStopPrice.Value)
_shortTrailingStopPrice = candidate;
if (candle.HighPrice >= _shortTrailingStopPrice.Value)
{
BuyMarket(exitVolume);
return;
}
}
}
}
private decimal GetTradeVolume()
{
var volume = TradeVolume;
var security = Security;
if (security == null)
return volume;
var step = security.VolumeStep;
if (step != null && step.Value > 0m)
{
var steps = Math.Max(1m, Math.Round(volume / step.Value, MidpointRounding.AwayFromZero));
volume = steps * step.Value;
}
var minVolume = security.MinVolume;
if (minVolume.HasValue && volume < minVolume.Value)
volume = minVolume.Value;
var maxVolume = security.MaxVolume;
if (maxVolume.HasValue && volume > maxVolume.Value)
volume = maxVolume.Value;
return volume;
}
private bool ShouldBlockByRisk(decimal price)
{
if (MaxRiskPercent <= 0m)
return false;
if (_lastAtrValue <= 0m || StopLossAtrMultiplier <= 0m || price <= 0m)
return false;
var potentialLoss = _lastAtrValue * StopLossAtrMultiplier;
var riskPercent = potentialLoss / price * 100m;
return riskPercent > MaxRiskPercent;
}
private void UpdateAverageBody(ICandleMessage candle)
{
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var value = _bodyAverage.Process(new DecimalIndicatorValue(_bodyAverage, body, candle.OpenTime));
if (value.IsFinal)
_averageBodyValue = value.GetValue<decimal>();
}
private void InitializePipSize()
{
_pipSize = GetPipSize();
_pipSizeInitialized = _pipSize > 0m;
}
private decimal GetPipSize()
{
var security = Security;
if (security == null)
return 0.0001m;
var step = security.PriceStep;
if (step != null && step.Value > 0m)
return step.Value;
var decimals = security.Decimals;
if (decimals.HasValue)
{
var pow = Math.Pow(10, -decimals.Value);
return Convert.ToDecimal(pow);
}
return 0.0001m;
}
private bool IsSpreadWithinLimit()
{
if (MaxSpreadPoints <= 0m)
return true;
var security = Security;
if (security == null)
return true;
var bestBid = GetSecurityValue<decimal?>(Level1Fields.BestBidPrice);
var bestAsk = GetSecurityValue<decimal?>(Level1Fields.BestAskPrice);
if (!bestBid.HasValue || !bestAsk.HasValue || !_pipSizeInitialized || _pipSize <= 0m)
return true;
var spreadPoints = (bestAsk.Value - bestBid.Value) / _pipSize;
return spreadPoints <= MaxSpreadPoints;
}
private void InitializeLongRiskLevels(decimal entryPrice)
{
if (_lastAtrValue <= 0m)
{
ResetLongRiskLevels();
return;
}
_longStopPrice = StopLossAtrMultiplier > 0m ? entryPrice - _lastAtrValue * StopLossAtrMultiplier : null;
_longTakeProfitPrice = TakeProfitAtrMultiplier > 0m ? entryPrice + _lastAtrValue * TakeProfitAtrMultiplier : null;
_longTrailingStopPrice = UseTrailingStop && TrailingStopAtrMultiplier > 0m ? entryPrice - _lastAtrValue * TrailingStopAtrMultiplier : null;
}
private void InitializeShortRiskLevels(decimal entryPrice)
{
if (_lastAtrValue <= 0m)
{
ResetShortRiskLevels();
return;
}
_shortStopPrice = StopLossAtrMultiplier > 0m ? entryPrice + _lastAtrValue * StopLossAtrMultiplier : null;
_shortTakeProfitPrice = TakeProfitAtrMultiplier > 0m ? entryPrice - _lastAtrValue * TakeProfitAtrMultiplier : null;
_shortTrailingStopPrice = UseTrailingStop && TrailingStopAtrMultiplier > 0m ? entryPrice + _lastAtrValue * TrailingStopAtrMultiplier : null;
}
private void ResetLongRiskLevels()
{
_longEntryPrice = null;
_longStopPrice = null;
_longTakeProfitPrice = null;
_longTrailingStopPrice = null;
}
private void ResetShortRiskLevels()
{
_shortEntryPrice = null;
_shortStopPrice = null;
_shortTakeProfitPrice = null;
_shortTrailingStopPrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import Math, TimeSpan
from StockSharp.Messages import DataType, CandleStates, Sides, Level1Fields
from StockSharp.Algo.Indicators import RelativeStrengthIndex, SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class revised_self_adaptive_ea_strategy(Strategy):
def __init__(self):
super(revised_self_adaptive_ea_strategy, self).__init__()
self._average_body_period = self.Param("AverageBodyPeriod", 3)
self._moving_average_period = self.Param("MovingAveragePeriod", 2)
self._rsi_period = self.Param("RsiPeriod", 6)
self._atr_period = self.Param("AtrPeriod", 14)
self._volume_param = self.Param("TradeVolume", 1.0)
self._max_spread_points = self.Param("MaxSpreadPoints", 20.0)
self._max_risk_percent = self.Param("MaxRiskPercent", 10.0)
self._use_trailing_stop = self.Param("UseTrailingStop", True)
self._stop_loss_atr_multiplier = self.Param("StopLossAtrMultiplier", 2.0)
self._take_profit_atr_multiplier = self.Param("TakeProfitAtrMultiplier", 4.0)
self._trailing_stop_atr_multiplier = self.Param("TrailingStopAtrMultiplier", 1.5)
self._oversold_level = self.Param("OversoldLevel", 40.0)
self._overbought_level = self.Param("OverboughtLevel", 60.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2)))
self._rsi = None
self._ma = None
self._atr = None
self._body_average = None
self._previous_candle = None
self._last_atr_value = 0.0
self._average_body_value = 0.0
self._long_entry_price = None
self._long_stop_price = None
self._long_take_profit_price = None
self._long_trailing_stop_price = None
self._short_entry_price = None
self._short_stop_price = None
self._short_take_profit_price = None
self._short_trailing_stop_price = None
@property
def AverageBodyPeriod(self):
return self._average_body_period.Value
@AverageBodyPeriod.setter
def AverageBodyPeriod(self, value):
self._average_body_period.Value = value
@property
def MovingAveragePeriod(self):
return self._moving_average_period.Value
@MovingAveragePeriod.setter
def MovingAveragePeriod(self, value):
self._moving_average_period.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def TradeVolume(self):
return self._volume_param.Value
@TradeVolume.setter
def TradeVolume(self, value):
self._volume_param.Value = value
@property
def MaxSpreadPoints(self):
return self._max_spread_points.Value
@MaxSpreadPoints.setter
def MaxSpreadPoints(self, value):
self._max_spread_points.Value = value
@property
def MaxRiskPercent(self):
return self._max_risk_percent.Value
@MaxRiskPercent.setter
def MaxRiskPercent(self, value):
self._max_risk_percent.Value = value
@property
def UseTrailingStop(self):
return self._use_trailing_stop.Value
@UseTrailingStop.setter
def UseTrailingStop(self, value):
self._use_trailing_stop.Value = value
@property
def StopLossAtrMultiplier(self):
return self._stop_loss_atr_multiplier.Value
@StopLossAtrMultiplier.setter
def StopLossAtrMultiplier(self, value):
self._stop_loss_atr_multiplier.Value = value
@property
def TakeProfitAtrMultiplier(self):
return self._take_profit_atr_multiplier.Value
@TakeProfitAtrMultiplier.setter
def TakeProfitAtrMultiplier(self, value):
self._take_profit_atr_multiplier.Value = value
@property
def TrailingStopAtrMultiplier(self):
return self._trailing_stop_atr_multiplier.Value
@TrailingStopAtrMultiplier.setter
def TrailingStopAtrMultiplier(self, value):
self._trailing_stop_atr_multiplier.Value = value
@property
def OversoldLevel(self):
return self._oversold_level.Value
@OversoldLevel.setter
def OversoldLevel(self, value):
self._oversold_level.Value = value
@property
def OverboughtLevel(self):
return self._overbought_level.Value
@OverboughtLevel.setter
def OverboughtLevel(self, value):
self._overbought_level.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(revised_self_adaptive_ea_strategy, self).OnReseted()
self._previous_candle = None
self._last_atr_value = 0.0
self._average_body_value = 0.0
self._reset_long_risk_levels()
self._reset_short_risk_levels()
def _reset_long_risk_levels(self):
self._long_entry_price = None
self._long_stop_price = None
self._long_take_profit_price = None
self._long_trailing_stop_price = None
def _reset_short_risk_levels(self):
self._short_entry_price = None
self._short_stop_price = None
self._short_take_profit_price = None
self._short_trailing_stop_price = None
def OnStarted2(self, time):
super(revised_self_adaptive_ea_strategy, self).OnStarted2(time)
self.Volume = float(self.TradeVolume)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
self._ma = SimpleMovingAverage()
self._ma.Length = self.MovingAveragePeriod
self._atr = AverageTrueRange()
self._atr.Length = self.AtrPeriod
self._body_average = SimpleMovingAverage()
self._body_average.Length = self.AverageBodyPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ma, self._atr, self._process_candle).Start()
def _update_average_body(self, candle):
body = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
value = process_float(self._body_average, body, candle.OpenTime, True)
if value.IsFinal:
self._average_body_value = float(to_decimal(value))
def _process_candle(self, candle, rsi_value, ma_value, atr_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
ma_val = float(ma_value)
atr_val = float(atr_value)
self._last_atr_value = atr_val
self._update_average_body(candle)
self._manage_open_positions(candle)
if not self._rsi.IsFormed or not self._ma.IsFormed or not self._atr.IsFormed:
self._previous_candle = candle
return
if self._previous_candle is None:
self._previous_candle = candle
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
prev_close = float(self._previous_candle.ClosePrice)
prev_open = float(self._previous_candle.OpenPrice)
body_size = abs(close - open_p)
minimum_body = self._average_body_value if self._average_body_value > 0 else 0.0
bullish_engulfing = (close > open_p and
prev_close < prev_open and
open_p <= prev_close and
body_size >= minimum_body)
bearish_engulfing = (close < open_p and
prev_close > prev_open and
open_p >= prev_close and
body_size >= minimum_body)
if bullish_engulfing and rsi_val <= float(self.OversoldLevel) and close >= ma_val:
self._try_open_long(close)
elif bearish_engulfing and rsi_val >= float(self.OverboughtLevel) and close <= ma_val:
self._try_open_short(close)
self._previous_candle = candle
def _try_open_long(self, price):
if self.Position > 0:
return
if self.Position < 0:
self.BuyMarket(abs(float(self.Position)))
return
volume = float(self.TradeVolume)
if volume <= 0:
return
self.BuyMarket(volume)
self._initialize_long_risk_levels(price)
def _try_open_short(self, price):
if self.Position < 0:
return
if self.Position > 0:
self.SellMarket(abs(float(self.Position)))
return
volume = float(self.TradeVolume)
if volume <= 0:
return
self.SellMarket(volume)
self._initialize_short_risk_levels(price)
def _manage_open_positions(self, candle):
if self.Position > 0:
exit_volume = float(self.Position)
if self._long_stop_price is not None and float(candle.LowPrice) <= self._long_stop_price:
self.SellMarket(exit_volume)
return
if self._long_take_profit_price is not None and float(candle.HighPrice) >= self._long_take_profit_price:
self.SellMarket(exit_volume)
return
if self.UseTrailingStop and self._long_trailing_stop_price is not None and self._last_atr_value > 0:
candidate = float(candle.ClosePrice) - self._last_atr_value * float(self.TrailingStopAtrMultiplier)
if candidate > self._long_trailing_stop_price:
self._long_trailing_stop_price = candidate
if float(candle.LowPrice) <= self._long_trailing_stop_price:
self.SellMarket(exit_volume)
return
elif self.Position < 0:
exit_volume = abs(float(self.Position))
if self._short_stop_price is not None and float(candle.HighPrice) >= self._short_stop_price:
self.BuyMarket(exit_volume)
return
if self._short_take_profit_price is not None and float(candle.LowPrice) <= self._short_take_profit_price:
self.BuyMarket(exit_volume)
return
if self.UseTrailingStop and self._short_trailing_stop_price is not None and self._last_atr_value > 0:
candidate = float(candle.ClosePrice) + self._last_atr_value * float(self.TrailingStopAtrMultiplier)
if candidate < self._short_trailing_stop_price:
self._short_trailing_stop_price = candidate
if float(candle.HighPrice) >= self._short_trailing_stop_price:
self.BuyMarket(exit_volume)
return
def _initialize_long_risk_levels(self, entry_price):
if self._last_atr_value <= 0:
self._reset_long_risk_levels()
return
sl_mult = float(self.StopLossAtrMultiplier)
tp_mult = float(self.TakeProfitAtrMultiplier)
trail_mult = float(self.TrailingStopAtrMultiplier)
self._long_stop_price = entry_price - self._last_atr_value * sl_mult if sl_mult > 0 else None
self._long_take_profit_price = entry_price + self._last_atr_value * tp_mult if tp_mult > 0 else None
self._long_trailing_stop_price = entry_price - self._last_atr_value * trail_mult if self.UseTrailingStop and trail_mult > 0 else None
def _initialize_short_risk_levels(self, entry_price):
if self._last_atr_value <= 0:
self._reset_short_risk_levels()
return
sl_mult = float(self.StopLossAtrMultiplier)
tp_mult = float(self.TakeProfitAtrMultiplier)
trail_mult = float(self.TrailingStopAtrMultiplier)
self._short_stop_price = entry_price + self._last_atr_value * sl_mult if sl_mult > 0 else None
self._short_take_profit_price = entry_price - self._last_atr_value * tp_mult if tp_mult > 0 else None
self._short_trailing_stop_price = entry_price + self._last_atr_value * trail_mult if self.UseTrailingStop and trail_mult > 0 else None
def CreateClone(self):
return revised_self_adaptive_ea_strategy()