Laptrend_1 Strategy
Overview
Laptrend_1 reproduces the logic of the MetaTrader expert advisor Laptrend_1.mq4. The strategy blends a multi-timeframe LabTrend channel filter, Fisher Transform momentum confirmation and an ADX trend strength check on 15-minute candles. Orders are opened only when the higher-timeframe (H1) and signal timeframe (M15) LabTrend directions agree, the Fisher transform confirms the move and the ADX shows a strengthening trend. Positions are closed when the momentum reverses, the LabTrend direction changes, or the market transitions into a flat regime where ADX and the DI components converge.
Trading Logic
- Primary data – 15-minute candles drive entries/exits while 1-hour candles feed the long-term LabTrend filter.
- LabTrend channel – The code recreates the
LabTrend1_v2.1indicator by building Donchian-style channels over the lastChannelLengthbars and narrowing them with theRiskFactor. A close above the upper band marks a bullish trend; a close below the lower band marks a bearish trend. The M15 and H1 trends must align to open trades. - Fisher Transform – A custom Fisher Transform (
Fisher_Yur4ik) tracks momentum on the M15 timeframe. Crosses through zero flip the bullish/bearish bias, while traversing ±0.25 produces exit signals. - ADX filter – The 15-minute Average Directional Index must rise and the dominant DI component has to agree with the proposed trade. When ADX, +DI and –DI fall within
Deltapoints of each other, the strategy treats the market as flat, resets the momentum flags and liquidates open positions. - Position management – New positions close any opposite exposure and trade a configurable volume. Exits are triggered by LabTrend reversals, Fisher exits or a flat market condition.
Risk Management
- Stop Loss / Take Profit – Configurable in instrument points (MetaTrader “pips”). They are evaluated against candle highs/lows to mimic protective orders from the original EA.
- Trailing Stop – Once the price moves in the trade’s favour, a trailing stop tracks the close at a distance equal to
TrailingStopPoints. Crossing the trailing level triggers an immediate market exit. - Volume – All orders use the fixed
Volumeparameter (lots).
Parameters
Volume– Order size in lots. Default 1.AdxPeriod– ADX smoothing period. Default 14.FisherLength– Window for the Fisher transform. Default 10.ChannelLength– Bars used for the LabTrend channel. Default 9.RiskFactor– LabTrend channel narrowing factor (original indicator range 1..10). Default 3.Delta– Maximum difference between ADX and DI values before the market is labelled flat. Default 7.StopLossPoints– Stop loss distance in points. Default 100.TakeProfitPoints– Take profit distance in points. Default 40.TrailingStopPoints– Trailing stop distance in points. Default 100.SignalCandleType– Candle series for signal calculations (default M15).TrendCandleType– Candle series for the higher-timeframe LabTrend filter (default H1).
Notes
- The original MQL implementation worked on every incoming tick; this port processes completed M15 candles, which keeps the logic deterministic while still respecting the indicator calculations.
- Stop loss, take profit and trailing exits are executed with market orders when the candle’s high/low breaches the configured thresholds. This mirrors the behaviour of MetaTrader protective orders without maintaining explicit stop/limit orders.
- Ensure that the data source supplies both the 15-minute and 1-hour candle series defined in the parameters before starting the strategy.
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>
/// Conversion of the Laptrend_1 MetaTrader expert advisor.
/// Combines LabTrend channel direction, Fisher transform momentum and ADX filter on multiple timeframes.
/// </summary>
public class Laptrend1Strategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _fisherLength;
private readonly StrategyParam<int> _channelLength;
private readonly StrategyParam<decimal> _risk;
private readonly StrategyParam<decimal> _delta;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<DataType> _signalCandleType;
private readonly StrategyParam<DataType> _trendCandleType;
private AverageDirectionalIndex _adx = null!;
private FisherYur4ikIndicator _fisher = null!;
private readonly LabTrendState _signalTrend = new();
private readonly LabTrendState _trendTrend = new();
private readonly Queue<decimal> _fisherHistory = new();
private bool _fisherBullish;
private bool _fisherBearish;
private bool _fisherExitLong;
private bool _fisherExitShort;
private decimal? _previousAdx;
private decimal _pointValue;
private decimal? _longEntryPrice;
private decimal? _shortEntryPrice;
private decimal? _longTrailingStop;
private decimal? _shortTrailingStop;
private int _lastPositionSign;
private bool _pendingClose;
private int _candleIndex;
private int _lastCloseCandle;
/// <summary>
/// ADX calculation period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Fisher transform length.
/// </summary>
public int FisherLength
{
get => _fisherLength.Value;
set => _fisherLength.Value = value;
}
/// <summary>
/// LabTrend channel lookback.
/// </summary>
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
/// <summary>
/// LabTrend risk factor (1..10 in the original code).
/// </summary>
public decimal RiskFactor
{
get => _risk.Value;
set => _risk.Value = value;
}
/// <summary>
/// Maximum distance between ADX and DI values before the market is considered flat.
/// </summary>
public decimal Delta
{
get => _delta.Value;
set => _delta.Value = value;
}
/// <summary>
/// Stop loss in points (MetaTrader style).
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit in points (MetaTrader style).
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Trailing stop in points (MetaTrader style).
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Candle type used for signal calculations (default 15 minutes).
/// </summary>
public DataType SignalCandleType
{
get => _signalCandleType.Value;
set => _signalCandleType.Value = value;
}
/// <summary>
/// Higher timeframe candle type for the LabTrend filter (default 1 hour).
/// </summary>
public DataType TrendCandleType
{
get => _trendCandleType.Value;
set => _trendCandleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="Laptrend1Strategy"/>.
/// </summary>
public Laptrend1Strategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Average Directional Index length", "Indicators");
_fisherLength = Param(nameof(FisherLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fisher Length", "Fisher transform window", "Indicators");
_channelLength = Param(nameof(ChannelLength), 9)
.SetGreaterThanZero()
.SetDisplay("Channel Length", "LabTrend channel lookback", "Indicators");
_risk = Param(nameof(RiskFactor), 3m)
.SetGreaterThanZero()
.SetDisplay("Risk Factor", "LabTrend risk factor", "Indicators");
_delta = Param(nameof(Delta), 7m)
.SetGreaterThanZero()
.SetDisplay("ADX Delta", "Maximum spread between ADX and DI before flat exit", "Filters");
_stopLossPoints = Param(nameof(StopLossPoints), 100m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss distance in points", "Risk Management");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 40m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit distance in points", "Risk Management");
_trailingStopPoints = Param(nameof(TrailingStopPoints), 100m)
.SetNotNegative()
.SetDisplay("Trailing Stop", "Trailing stop distance in points", "Risk Management");
_signalCandleType = Param(nameof(SignalCandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Signal Candle", "Primary timeframe for signals", "General");
_trendCandleType = Param(nameof(TrendCandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Trend Candle", "Higher timeframe for LabTrend filter", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, SignalCandleType);
yield return (Security, TrendCandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_adx = null!;
_fisher = null!;
_signalTrend.Reset();
_trendTrend.Reset();
_fisherHistory.Clear();
_fisherBullish = false;
_fisherBearish = false;
_fisherExitLong = false;
_fisherExitShort = false;
_previousAdx = null;
_pointValue = 0m;
ResetLongState();
ResetShortState();
_lastPositionSign = 0;
_longEntryPrice = null;
_shortEntryPrice = null;
_longTrailingStop = null;
_shortTrailingStop = null;
_pendingClose = false;
_candleIndex = 0;
_lastCloseCandle = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pointValue = Security?.PriceStep ?? 0m;
if (_pointValue <= 0m)
_pointValue = 1m;
_fisherHistory.Clear();
_fisherBullish = false;
_fisherBearish = false;
_fisherExitLong = false;
_fisherExitShort = false;
_previousAdx = null;
ResetLongState();
ResetShortState();
_lastPositionSign = Math.Sign(Position);
_pendingClose = false;
_candleIndex = 0;
_lastCloseCandle = -20;
_fisher = new FisherYur4ikIndicator
{
Length = FisherLength
};
_adx = new AverageDirectionalIndex
{
Length = AdxPeriod
};
var signalSubscription = SubscribeCandles(SignalCandleType);
signalSubscription.BindEx(_fisher, _adx, ProcessSignalCandle).Start();
var trendSubscription = SubscribeCandles(TrendCandleType);
trendSubscription.Bind(ProcessTrendCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, signalSubscription);
DrawIndicator(area, _fisher);
DrawOwnTrades(area);
}
}
private void ProcessSignalCandle(ICandleMessage candle, IIndicatorValue fisherValue, IIndicatorValue adxValue)
{
if (candle.State != CandleStates.Finished)
return;
_candleIndex++;
// Update LabTrend state on the signal timeframe.
_signalTrend.Process(candle, ChannelLength, RiskFactor);
// Keep Fisher state in sync whenever a final value is available.
if (fisherValue.IsFinal && _fisher.IsFormed)
{
var fisher = fisherValue.GetValue<decimal>();
UpdateFisherFlags(fisher);
}
var canTrade = IsFormedAndOnlineAndAllowTrading();
if (!adxValue.IsFinal || !_adx.IsFormed || adxValue is not AverageDirectionalIndexValue adxData ||
adxData.MovingAverage is not decimal adxCurrent ||
adxData.Dx.Plus is not decimal plusDi ||
adxData.Dx.Minus is not decimal minusDi)
{
return;
}
var previousAdx = _previousAdx;
_previousAdx = adxCurrent;
var adxRising = previousAdx.HasValue && adxCurrent > previousAdx.Value;
var bullDirectional = plusDi > minusDi;
var bearDirectional = minusDi > plusDi;
var flat = Math.Abs(plusDi - minusDi) < Delta &&
Math.Abs(adxCurrent - plusDi) < Delta &&
Math.Abs(adxCurrent - minusDi) < Delta;
if (flat && canTrade)
{
// Reset momentum flags and close any open trades in ranging conditions.
_fisherBullish = false;
_fisherBearish = false;
_fisherExitLong = false;
_fisherExitShort = false;
if (Position > 0)
{
_lastCloseCandle = _candleIndex;
SellMarket(Position);
}
else if (Position < 0)
{
_lastCloseCandle = _candleIndex;
BuyMarket(Math.Abs(Position));
}
return;
}
if (canTrade)
{
const int CooldownCandles = 20;
var cooldownOk = (_candleIndex - _lastCloseCandle) >= CooldownCandles;
if (Position == 0)
{
// Only enter when flat, cooldown has elapsed, and ADX is strong enough
var adxStrong = adxCurrent >= 20m;
if (cooldownOk && adxStrong && _trendTrend.IsUpTrend && _signalTrend.IsUpTrend && _fisherBullish && bullDirectional && adxRising)
{
_fisherBullish = false;
BuyMarket(Volume);
return;
}
else if (cooldownOk && adxStrong && _trendTrend.IsDownTrend && _signalTrend.IsDownTrend && _fisherBearish && bearDirectional && adxRising)
{
_fisherBearish = false;
SellMarket(Volume);
return;
}
}
else if (Position > 0)
{
if (_fisherExitLong)
{
_fisherExitLong = false;
_lastCloseCandle = _candleIndex;
SellMarket(Position);
return;
}
}
else // Position < 0
{
if (_fisherExitShort)
{
_fisherExitShort = false;
_lastCloseCandle = _candleIndex;
BuyMarket(Math.Abs(Position));
return;
}
}
}
ManagePosition(candle, canTrade);
}
private void ProcessTrendCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Track the higher timeframe trend for directional filtering.
_trendTrend.Process(candle, ChannelLength, RiskFactor);
}
private void ManagePosition(ICandleMessage candle, bool canTrade)
{
var position = Position;
var positionSign = Math.Sign(position);
if (!canTrade)
{
_lastPositionSign = positionSign;
return;
}
// If we submitted a close order, wait until position is actually flat.
if (_pendingClose)
{
if (positionSign == 0)
_pendingClose = false;
else
{
_lastPositionSign = positionSign;
return;
}
}
var step = _pointValue > 0m ? _pointValue : 1m;
var stopOffset = StopLossPoints > 0m ? StopLossPoints * step : 0m;
var takeOffset = TakeProfitPoints > 0m ? TakeProfitPoints * step : 0m;
var trailingOffset = TrailingStopPoints > 0m ? TrailingStopPoints * step : 0m;
if (positionSign > 0)
{
// Capture the entry price when switching from short or flat to long.
if (_lastPositionSign <= 0)
{
_longEntryPrice = candle.ClosePrice;
_longTrailingStop = trailingOffset > 0m ? candle.ClosePrice - trailingOffset : null;
ResetShortState();
}
if (_longEntryPrice.HasValue)
{
var entry = _longEntryPrice.Value;
var volume = position;
if (stopOffset > 0m && candle.LowPrice <= entry - stopOffset)
{
SellMarket(volume);
ResetLongState();
_lastPositionSign = 0;
_pendingClose = true;
_lastCloseCandle = _candleIndex;
return;
}
if (takeOffset > 0m && candle.HighPrice >= entry + takeOffset)
{
SellMarket(volume);
ResetLongState();
_lastPositionSign = 0;
_pendingClose = true;
_lastCloseCandle = _candleIndex;
return;
}
if (trailingOffset > 0m)
{
var candidate = candle.ClosePrice - trailingOffset;
if (!_longTrailingStop.HasValue || candidate > _longTrailingStop.Value)
_longTrailingStop = candidate;
if (_longTrailingStop.HasValue && candle.LowPrice <= _longTrailingStop.Value)
{
SellMarket(volume);
ResetLongState();
_lastPositionSign = 0;
_pendingClose = true;
return;
}
}
}
}
else if (positionSign < 0)
{
// Capture the entry price when switching from long or flat to short.
if (_lastPositionSign >= 0)
{
_shortEntryPrice = candle.ClosePrice;
_shortTrailingStop = trailingOffset > 0m ? candle.ClosePrice + trailingOffset : null;
ResetLongState();
}
if (_shortEntryPrice.HasValue)
{
var entry = _shortEntryPrice.Value;
var volume = Math.Abs(position);
if (stopOffset > 0m && candle.HighPrice >= entry + stopOffset)
{
BuyMarket(volume);
ResetShortState();
_lastPositionSign = 0;
_pendingClose = true;
_lastCloseCandle = _candleIndex;
return;
}
if (takeOffset > 0m && candle.LowPrice <= entry - takeOffset)
{
BuyMarket(volume);
ResetShortState();
_lastPositionSign = 0;
_pendingClose = true;
_lastCloseCandle = _candleIndex;
return;
}
if (trailingOffset > 0m)
{
var candidate = candle.ClosePrice + trailingOffset;
if (!_shortTrailingStop.HasValue || candidate < _shortTrailingStop.Value)
_shortTrailingStop = candidate;
if (_shortTrailingStop.HasValue && candle.HighPrice >= _shortTrailingStop.Value)
{
BuyMarket(volume);
ResetShortState();
_lastPositionSign = 0;
_pendingClose = true;
return;
}
}
}
}
else
{
ResetLongState();
ResetShortState();
}
_lastPositionSign = positionSign;
}
private void UpdateFisherFlags(decimal value)
{
_fisherHistory.Enqueue(value);
while (_fisherHistory.Count > 3)
_fisherHistory.Dequeue();
if (_fisherHistory.Count < 3)
return;
var values = _fisherHistory.ToArray();
var fx0n = values[^1];
var fx1n = values[^2];
var fx2n = values[^3];
var fx0 = (fx0n + fx1n) / 2m;
var fx1 = (fx1n + fx2n) / 2m;
if (fx1 < 0m && fx0 > 0m)
{
// Fisher crossed above zero -> bullish momentum.
_fisherBullish = true;
_fisherBearish = false;
}
else if (fx1 > 0m && fx0 < 0m)
{
// Fisher crossed below zero -> bearish momentum.
_fisherBearish = true;
_fisherBullish = false;
}
if (fx1 > 0.25m && fx0 < 0.25m)
{
// Fisher dropped back under +0.25 -> exit long.
_fisherExitLong = true;
_fisherExitShort = false;
}
else if (fx1 < -0.25m && fx0 > -0.25m)
{
// Fisher climbed back above -0.25 -> exit short.
_fisherExitShort = true;
_fisherExitLong = false;
}
}
private void ResetLongState()
{
_longEntryPrice = null;
_longTrailingStop = null;
}
private void ResetShortState()
{
_shortEntryPrice = null;
_shortTrailingStop = null;
}
private sealed class LabTrendState
{
private readonly Queue<decimal> _highs = new();
private readonly Queue<decimal> _lows = new();
private decimal _trend;
public bool IsUpTrend => _trend > 0m;
public bool IsDownTrend => _trend < 0m;
public void Reset()
{
_highs.Clear();
_lows.Clear();
_trend = 0m;
}
public override bool Equals(object obj)
{
if (obj is not LabTrendState other) return false;
return _trend == other._trend
&& _highs.Count == other._highs.Count
&& _lows.Count == other._lows.Count
&& _highs.SequenceEqual(other._highs)
&& _lows.SequenceEqual(other._lows);
}
public override int GetHashCode() => HashCode.Combine(_trend, _highs.Count, _lows.Count);
public void Process(ICandleMessage candle, int length, decimal risk)
{
var lookback = Math.Max(1, length);
_highs.Enqueue(candle.HighPrice);
_lows.Enqueue(candle.LowPrice);
if (_highs.Count > lookback)
{
_highs.Dequeue();
_lows.Dequeue();
}
if (_highs.Count < lookback)
return;
var highest = _highs.Max();
var lowest = _lows.Min();
var range = highest - lowest;
if (range <= 0m)
return;
var safeRisk = risk;
if (safeRisk < 0m)
safeRisk = 0m;
else if (safeRisk > 33m)
safeRisk = 33m;
var coefficient = (33m - safeRisk) / 100m;
var upper = highest - range * coefficient;
var lower = lowest + range * coefficient;
if (_trend <= 0m && candle.ClosePrice > upper)
_trend = 1m;
else if (_trend >= 0m && candle.ClosePrice < lower)
_trend = -1m;
}
}
private sealed class FisherYur4ikIndicator : BaseIndicator
{
public int Length { get; set; } = 10;
private readonly Queue<decimal> _medians = new();
private decimal _previousValue;
private decimal _previousFish;
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var candle = input.GetValue<ICandleMessage>();
if (candle == null)
return new DecimalIndicatorValue(this, 0m, input.Time);
var length = Math.Max(1, Length);
var median = (candle.HighPrice + candle.LowPrice) / 2m;
_medians.Enqueue(median);
if (_medians.Count > length)
{
_medians.Dequeue();
}
if (_medians.Count < length)
{
IsFormed = false;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
var highest = _medians.Max();
var lowest = _medians.Min();
var range = highest - lowest;
decimal fish;
if (range == 0m)
{
fish = _previousFish;
}
else
{
var value = 0.66m * ((median - lowest) / range - 0.5m) + 0.67m * _previousValue;
if (value > 0.999m)
value = 0.999m;
else if (value < -0.999m)
value = -0.999m;
var ratio = (1m + value) / (1m - value);
fish = 0.5m * (decimal)Math.Log((double)ratio) + 0.5m * _previousFish;
_previousValue = value;
_previousFish = fish;
}
IsFormed = _medians.Count >= length;
return new DecimalIndicatorValue(this, fish, input.Time);
}
public override void Reset()
{
base.Reset();
_medians.Clear();
_previousValue = 0m;
_previousFish = 0m;
}
}
}
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 ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class laptrend1_strategy(Strategy):
"""
Laptrend: EMA trend + RSI momentum with ATR-based stops.
Simplified from multi-timeframe custom indicator version.
"""
def __init__(self):
super(laptrend1_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 20).SetDisplay("EMA Length", "Trend EMA", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI Length", "RSI period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
self._sl_mult = self.Param("SlMultiplier", 2.0).SetDisplay("SL Mult", "ATR multiplier for SL", "Risk")
self._tp_mult = self.Param("TpMultiplier", 3.0).SetDisplay("TP Mult", "ATR multiplier for TP", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._prev_ema = 0.0
self._entry_price = 0.0
self._stop = 0.0
self._tp = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(laptrend1_strategy, self).OnReseted()
self._prev_ema = 0.0
self._entry_price = 0.0
self._stop = 0.0
self._tp = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(laptrend1_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, rsi, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
ema = float(ema_val)
rsi = float(rsi_val)
atr = float(atr_val)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self._cooldown > 0:
self._cooldown -= 1
if atr <= 0:
self._prev_ema = ema
return
if self.Position > 0:
if low <= self._stop or high >= self._tp:
self.SellMarket()
self._entry_price = 0
self._cooldown = 20
elif self.Position < 0:
if high >= self._stop or low <= self._tp:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 20
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ema = ema
return
if self._cooldown > 0:
self._prev_ema = ema
return
if self._prev_ema == 0:
self._prev_ema = ema
return
trend_up = close > ema and self._prev_ema < ema
trend_down = close < ema and self._prev_ema > ema
if trend_up and rsi > 50 and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._stop = close - atr * self._sl_mult.Value
self._tp = close + atr * self._tp_mult.Value
self._cooldown = 20
elif trend_down and rsi < 50 and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._stop = close + atr * self._sl_mult.Value
self._tp = close - atr * self._tp_mult.Value
self._cooldown = 20
self._prev_ema = ema
def CreateClone(self):
return laptrend1_strategy()