Revised Self Adaptive EA
Port of the MetaTrader 5 expert advisor revised_self_adaptive_ea.mq5 into the StockSharp high level strategy framework.
Strategy overview
The algorithm scans a configurable candle series and looks for engulfing reversal setups confirmed by momentum and trend filters:
- Pattern detection – evaluates the last closed candle against the previous one. A bullish setup requires a green body that opens below the previous close while the previous candle is bearish. The mirror logic is applied for bearish setups. Candle bodies are compared against a rolling average to filter out weak signals.
- Momentum filter – a classic RSI ensures that bullish trades only trigger from oversold territory and bearish trades from overbought conditions.
- Trend filter – a short simple moving average must agree with the trade direction. This prevents fading strong trends without confirmation.
- Risk management – ATR driven stop-loss and take-profit levels are calculated for every new position. Optional trailing stops keep following profitable moves while never reducing protection. Positions are force-closed when price hits the protective levels.
- Spread and risk guard – trades are skipped whenever the current spread exceeds the configured threshold or when the ATR based stop would risk more than the allowed percentage of price.
Parameters
| Name | Description |
|---|---|
CandleType |
Candle aggregation used for analysis. Defaults to one hour bars. |
AverageBodyPeriod |
Number of candles used to compute the average body size filter. |
MovingAveragePeriod |
Length of the simple moving average that acts as a directional filter. |
RsiPeriod |
RSI length used for oversold/overbought confirmation. |
OversoldLevel |
RSI threshold that must be met before accepting a bullish reversal. |
OverboughtLevel |
RSI threshold that must be met before accepting a bearish reversal. |
AtrPeriod |
ATR length used for volatility based protective distances. |
StopLossAtrMultiplier |
Multiplicative factor applied to ATR for the stop-loss distance. |
TakeProfitAtrMultiplier |
Multiplicative factor applied to ATR for the take-profit distance. |
TrailingStopAtrMultiplier |
ATR distance maintained by the trailing stop logic. |
UseTrailingStop |
Enables the trailing stop supervisor. |
MaxSpreadPoints |
Maximum allowed spread (expressed in price steps/pips). Signals are ignored when the market is wider. |
MaxRiskPercent |
Maximum acceptable percentage risk based on the ATR stop relative to the entry price. |
TradeVolume |
Base lot size used for market orders. |
Behaviour notes
- Positions are flattened before reversing direction to mirror the MetaTrader implementation.
- Protective stop/take levels are recomputed after every fill using the most recent ATR reading.
- The trailing stop only moves in the trade direction and is disabled when ATR data is not yet available.
- If the strategy is running on an instrument without reliable bid/ask quotes the spread filter will stay inactive automatically.
Differences vs. original MQL
The original script only outlined the signal detection routine. In this port the missing elements were reconstructed using the provided parameters:
- Added moving-average confirmation to make use of the MA handle declared in the MQL source.
- Implemented ATR based stop-loss, take-profit, and trailing stop logic using the volatility handle defined in the original expert.
- Added a risk-percentage guard so that oversized ATR stops are skipped instead of blindly executing.
- Visualization elements (chart arrows) were omitted because StockSharp strategies do not draw objects on charts by default.
Usage
- Attach the strategy to a portfolio and security inside Hydra or your custom StockSharp host.
- Ensure the candle subscription matches the intended timeframe (default: one hour).
- Adjust the risk parameters to reflect the instrument’s volatility.
- Start the strategy. It will automatically subscribe to candles, compute indicators, and place market orders when conditions are satisfied.
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()