ALMA & UT Bot Confluence戦略
ALMA & UT Bot Confluence戦略は、Arnaud Legoux移動平均フィルターとUT Botスタイルのトレーリングストップを組み合わせたものです。価格が長期EMAとALMAの両方を上回り、出来高が平均を超え、RSIがモメンタムを示し、ADXがトレンドの強さを確認し、ローソク足がボリンジャーバンド上限の下にあり、UT Botが買いシグナルを生成したときにロングポジションを開きます。UT Botが弱気に転じ、同じフィルター下で価格が高速EMAを下回ったときにショートエントリーが発生します。出口はUT Botトレーリングストップ、またはATRベースの固定ストップロスおよびテイクプロフィットを使用します。
詳細
- エントリー条件:
- ロング: 価格 > EMA & ALMA、RSI > 30、ADX > 30、価格 < ボリンジャーバンド上限、UT Bot買いシグナル、出来高およびATRフィルター、クールダウン。
- ショート: UT Bot売りシグナルとフィルター下で価格が高速EMAを下穿き。
- ロング/ショート: 両方。
- エグジット条件:
- UT Botトレーリングストップ、またはATRベースのストップロス/テイクプロフィットとオプションの時間出口。
- ストップ: ATRまたはトレーリング。
- デフォルト値:
FastEmaLength= 20EmaLength= 72AtrLength= 14AdxLength= 10RsiLength= 14BbMultiplier= 3.0StopLossAtrMultiplier= 5.0TakeProfitAtrMultiplier= 4.0UtAtrPeriod= 10UtKeyValue= 1VolumeMaLength= 20BaseCooldownBars= 7MinAtr= 0.005
- フィルター:
- カテゴリ: ボラティリティフィルター付きトレンドフォロー
- 方向: ロング/ショート
- インジケーター: EMA、ALMA、ADX、RSI、Bollinger Bands、UT Bot
- ストップ: ATRまたはトレーリング
- 複雑さ: 高
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ALMA and UT Bot confluence strategy.
/// A long opens on a UT Bot buy signal when the close is above the long EMA and ALMA, volume is above its average, RSI is above
/// 30, ADX is above 30, the close is below the upper Bollinger Band and ATR is at least MinAtr. A short opens when the close
/// crosses below the fast EMA while the UT Bot is bearish under the mirrored filters. Entries are spaced by BaseCooldownBars.
/// A position closes when the UT Bot trailing stop flips against it or when price reaches the ATR stop-loss or take-profit
/// fixed at entry.
/// </summary>
public class AlmaUtBotConfluenceStrategy : Strategy
{
private const int _almaLength = 9;
private const decimal _almaOffset = 0.85m;
private const int _almaSigma = 6;
private const int _bbLength = 20;
private const decimal _rsiLongLevel = 30m;
private const decimal _rsiShortLevel = 70m;
private const decimal _adxLevel = 30m;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _adxLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<decimal> _takeProfitAtrMultiplier;
private readonly StrategyParam<int> _utAtrPeriod;
private readonly StrategyParam<decimal> _utKeyValue;
private readonly StrategyParam<int> _volumeMaLength;
private readonly StrategyParam<int> _baseCooldownBars;
private readonly StrategyParam<decimal> _minAtr;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeMa;
private decimal? _utStop;
private decimal _prevClose;
private decimal? _prevFastEma;
private int _barIndex;
private int? _lastEntryBar;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
/// <summary>
/// Long-term EMA period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// ATR period of the stop-loss and take-profit.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// ADX period.
/// </summary>
public int AdxLength
{
get => _adxLength.Value;
set => _adxLength.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Bollinger Bands width multiplier.
/// </summary>
public decimal BbMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Stop-loss distance in ATR multiples.
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// Take-profit distance in ATR multiples.
/// </summary>
public decimal TakeProfitAtrMultiplier
{
get => _takeProfitAtrMultiplier.Value;
set => _takeProfitAtrMultiplier.Value = value;
}
/// <summary>
/// ATR period of the UT Bot.
/// </summary>
public int UtAtrPeriod
{
get => _utAtrPeriod.Value;
set => _utAtrPeriod.Value = value;
}
/// <summary>
/// UT Bot key value (ATR multiplier of the trailing stop).
/// </summary>
public decimal UtKeyValue
{
get => _utKeyValue.Value;
set => _utKeyValue.Value = value;
}
/// <summary>
/// Period of the volume average.
/// </summary>
public int VolumeMaLength
{
get => _volumeMaLength.Value;
set => _volumeMaLength.Value = value;
}
/// <summary>
/// Minimum bars between entries.
/// </summary>
public int BaseCooldownBars
{
get => _baseCooldownBars.Value;
set => _baseCooldownBars.Value = value;
}
/// <summary>
/// Minimum ATR required for entries.
/// </summary>
public decimal MinAtr
{
get => _minAtr.Value;
set => _minAtr.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AlmaUtBotConfluenceStrategy()
{
_fastEmaLength = Param(nameof(FastEmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length", "Fast EMA period", "Trend");
_emaLength = Param(nameof(EmaLength), 72)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Long-term EMA period", "Trend");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period of the stop-loss and take-profit", "Risk");
_adxLength = Param(nameof(AdxLength), 10)
.SetGreaterThanZero()
.SetDisplay("ADX Length", "ADX period", "Filters");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Filters");
_bbMultiplier = Param(nameof(BbMultiplier), 3.0m)
.SetGreaterThanZero()
.SetDisplay("BB Multiplier", "Bollinger Bands width multiplier", "Filters");
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 5.0m)
.SetNotNegative()
.SetDisplay("Stop Loss ATR", "Stop-loss distance in ATR multiples", "Risk");
_takeProfitAtrMultiplier = Param(nameof(TakeProfitAtrMultiplier), 4.0m)
.SetNotNegative()
.SetDisplay("Take Profit ATR", "Take-profit distance in ATR multiples", "Risk");
_utAtrPeriod = Param(nameof(UtAtrPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("UT ATR Period", "ATR period of the UT Bot", "UT Bot");
_utKeyValue = Param(nameof(UtKeyValue), 1m)
.SetGreaterThanZero()
.SetDisplay("UT Key Value", "ATR multiplier of the UT Bot trailing stop", "UT Bot");
_volumeMaLength = Param(nameof(VolumeMaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Volume MA Length", "Period of the volume average", "Filters");
_baseCooldownBars = Param(nameof(BaseCooldownBars), 7)
.SetNotNegative()
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Filters");
_minAtr = Param(nameof(MinAtr), 0.005m)
.SetNotNegative()
.SetDisplay("Min ATR", "Minimum ATR required for entries", "Filters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_volumeMa = null;
ResetState();
}
private void ResetState()
{
_utStop = null;
_prevClose = 0m;
_prevFastEma = null;
_barIndex = 0;
_lastEntryBar = null;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema = new ExponentialMovingAverage { Length = EmaLength };
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var alma = new ArnaudLegouxMovingAverage { Length = _almaLength, Offset = _almaOffset, Sigma = _almaSigma };
var atr = new AverageTrueRange { Length = AtrLength };
var utAtr = new AverageTrueRange { Length = UtAtrPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var adx = new AverageDirectionalIndex { Length = AdxLength };
var bollinger = new BollingerBands { Length = _bbLength, Width = BbMultiplier };
_volumeMa = new SimpleMovingAverage { Length = VolumeMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(new IIndicator[] { ema, fastEma, alma, atr, utAtr, rsi, adx, bollinger }, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawIndicator(area, fastEma);
DrawIndicator(area, alma);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue[] values)
{
if (candle.State != CandleStates.Finished)
return;
var volumeMa = _volumeMa.Process(candle.TotalVolume, candle.ServerTime, true).ToDecimal();
var close = candle.ClosePrice;
var utAtrValue = values[4];
if (!utAtrValue.IsFormed)
{
_prevClose = close;
return;
}
// UT Bot: an ATR trailing stop that ratchets with price and flips when price closes through it.
var nLoss = UtKeyValue * utAtrValue.ToDecimal();
var prevStop = _utStop ?? close - nLoss;
var prevClose = _prevClose;
decimal utStop;
if (close > prevStop && prevClose > prevStop)
utStop = Math.Max(prevStop, close - nLoss);
else if (close < prevStop && prevClose < prevStop)
utStop = Math.Min(prevStop, close + nLoss);
else
utStop = close > prevStop ? close - nLoss : close + nLoss;
var utBuy = _utStop is not null && prevClose <= prevStop && close > utStop;
var utSell = _utStop is not null && prevClose >= prevStop && close < utStop;
var utBearish = close < utStop;
_utStop = utStop;
_prevClose = close;
_barIndex++;
var fastEmaValue = values[1];
var prevFastEma = _prevFastEma;
if (fastEmaValue.IsFormed)
_prevFastEma = fastEmaValue.ToDecimal();
foreach (var value in values)
{
if (!value.IsFormed)
return;
}
if (!_volumeMa.IsFormed || prevFastEma is not decimal prevFast)
return;
if (values[6] is not IAverageDirectionalIndexValue { MovingAverage: decimal adx })
return;
if (values[7] is not BollingerBandsValue { UpBand: decimal upperBand, LowBand: decimal lowerBand })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ema = values[0].ToDecimal();
var fastEma = values[1].ToDecimal();
var alma = values[2].ToDecimal();
var atr = values[3].ToDecimal();
var rsi = values[5].ToDecimal();
var cooldownOk = _lastEntryBar is not int last || _barIndex - last >= BaseCooldownBars;
var commonFilters = cooldownOk && candle.TotalVolume > volumeMa && adx > _adxLevel && atr >= MinAtr;
var longSignal = commonFilters && utBuy && close > ema && close > alma && rsi > _rsiLongLevel && close < upperBand;
var crossBelowFast = prevClose >= prevFast && close < fastEma;
var shortSignal = commonFilters && utBearish && crossBelowFast && close < ema && close < alma && rsi < _rsiShortLevel && close > lowerBand;
if (longSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_lastEntryBar = _barIndex;
_stopPrice = close - StopLossAtrMultiplier * atr;
_takePrice = close + TakeProfitAtrMultiplier * atr;
}
else if (shortSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_lastEntryBar = _barIndex;
_stopPrice = close + StopLossAtrMultiplier * atr;
_takePrice = close - TakeProfitAtrMultiplier * atr;
}
else if (Position > 0)
{
var hitStop = StopLossAtrMultiplier > 0m && candle.LowPrice <= _stopPrice;
var hitTake = TakeProfitAtrMultiplier > 0m && candle.HighPrice >= _takePrice;
if (utSell || hitStop || hitTake)
SellMarket(Position);
}
else if (Position < 0)
{
var hitStop = StopLossAtrMultiplier > 0m && candle.HighPrice >= _stopPrice;
var hitTake = TakeProfitAtrMultiplier > 0m && candle.LowPrice <= _takePrice;
if (utBuy || hitStop || hitTake)
BuyMarket(-Position);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal, Array
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import IIndicator, ExponentialMovingAverage, ArnaudLegouxMovingAverage, AverageTrueRange, RelativeStrengthIndex, AverageDirectionalIndex, BollingerBands, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
ALMA_LENGTH = 9
ALMA_OFFSET = 0.85
ALMA_SIGMA = 6
BB_LENGTH = 20
RSI_LONG_LEVEL = 30.0
RSI_SHORT_LEVEL = 70.0
ADX_LEVEL = 30.0
class alma_ut_bot_confluence_strategy(Strategy):
"""
ALMA and UT Bot confluence strategy.
A long opens on a UT Bot buy signal when the close is above the long EMA and ALMA, volume is above its average, RSI is above
30, ADX is above 30, the close is below the upper Bollinger Band and ATR is at least MinAtr. A short opens when the close
crosses below the fast EMA while the UT Bot is bearish under the mirrored filters. Entries are spaced by BaseCooldownBars.
A position closes when the UT Bot trailing stop flips against it or when price reaches the ATR stop-loss or take-profit
fixed at entry.
"""
def __init__(self):
super(alma_ut_bot_confluence_strategy, self).__init__()
self._fast_ema_length = self.Param("FastEmaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA Length", "Fast EMA period", "Trend")
self._ema_length = self.Param("EmaLength", 72) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "Long-term EMA period", "Trend")
self._atr_length = self.Param("AtrLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Length", "ATR period of the stop-loss and take-profit", "Risk")
self._adx_length = self.Param("AdxLength", 10) \
.SetGreaterThanZero() \
.SetDisplay("ADX Length", "ADX period", "Filters")
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "Filters")
self._bb_multiplier = self.Param("BbMultiplier", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("BB Multiplier", "Bollinger Bands width multiplier", "Filters")
self._stop_loss_atr_multiplier = self.Param("StopLossAtrMultiplier", 5.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss ATR", "Stop-loss distance in ATR multiples", "Risk")
self._take_profit_atr_multiplier = self.Param("TakeProfitAtrMultiplier", 4.0) \
.SetNotNegative() \
.SetDisplay("Take Profit ATR", "Take-profit distance in ATR multiples", "Risk")
self._ut_atr_period = self.Param("UtAtrPeriod", 10) \
.SetGreaterThanZero() \
.SetDisplay("UT ATR Period", "ATR period of the UT Bot", "UT Bot")
self._ut_key_value = self.Param("UtKeyValue", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("UT Key Value", "ATR multiplier of the UT Bot trailing stop", "UT Bot")
self._volume_ma_length = self.Param("VolumeMaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Volume MA Length", "Period of the volume average", "Filters")
self._base_cooldown_bars = self.Param("BaseCooldownBars", 7) \
.SetNotNegative() \
.SetDisplay("Cooldown Bars", "Minimum bars between entries", "Filters")
self._min_atr = self.Param("MinAtr", 0.005) \
.SetNotNegative() \
.SetDisplay("Min ATR", "Minimum ATR required for entries", "Filters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_ma = None
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def _reset_state(self):
self._ut_stop = None
self._prev_close = 0.0
self._prev_fast_ema = None
self._bar_index = 0
self._last_entry_bar = None
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(alma_ut_bot_confluence_strategy, self).OnReseted()
self._volume_ma = None
self._reset_state()
def OnStarted2(self, time):
super(alma_ut_bot_confluence_strategy, self).OnStarted2(time)
self._reset_state()
ema = ExponentialMovingAverage()
ema.Length = self._ema_length.Value
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_ema_length.Value
alma = ArnaudLegouxMovingAverage()
alma.Length = ALMA_LENGTH
alma.Offset = Decimal(ALMA_OFFSET)
alma.Sigma = ALMA_SIGMA
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
ut_atr = AverageTrueRange()
ut_atr.Length = self._ut_atr_period.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
adx = AverageDirectionalIndex()
adx.Length = self._adx_length.Value
bollinger = BollingerBands()
bollinger.Length = BB_LENGTH
bollinger.Width = Decimal(self._bb_multiplier.Value)
self._volume_ma = SimpleMovingAverage()
self._volume_ma.Length = self._volume_ma_length.Value
indicators = Array[IIndicator]([ema, fast_ema, alma, atr, ut_atr, rsi, adx, bollinger])
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(indicators, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, alma)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
def _process_candle(self, candle, values):
if candle.State != CandleStates.Finished:
return
volume_ma = float(to_decimal(process_float(self._volume_ma, candle.TotalVolume, candle.ServerTime, True)))
close = float(candle.ClosePrice)
ut_atr_value = values[4]
if not ut_atr_value.IsFormed:
self._prev_close = close
return
# UT Bot: an ATR trailing stop that ratchets with price and flips when price closes through it.
n_loss = float(self._ut_key_value.Value) * float(to_decimal(ut_atr_value))
prev_stop = self._ut_stop if self._ut_stop is not None else close - n_loss
prev_close = self._prev_close
if close > prev_stop and prev_close > prev_stop:
ut_stop = max(prev_stop, close - n_loss)
elif close < prev_stop and prev_close < prev_stop:
ut_stop = min(prev_stop, close + n_loss)
else:
ut_stop = close - n_loss if close > prev_stop else close + n_loss
ut_buy = self._ut_stop is not None and prev_close <= prev_stop and close > ut_stop
ut_sell = self._ut_stop is not None and prev_close >= prev_stop and close < ut_stop
ut_bearish = close < ut_stop
self._ut_stop = ut_stop
self._prev_close = close
self._bar_index += 1
fast_ema_value = values[1]
prev_fast = self._prev_fast_ema
if fast_ema_value.IsFormed:
self._prev_fast_ema = float(to_decimal(fast_ema_value))
for value in values:
if not value.IsFormed:
return
if not self._volume_ma.IsFormed or prev_fast is None:
return
adx_value = values[6].MovingAverage
upper_band = values[7].UpBand
lower_band = values[7].LowBand
if adx_value is None or upper_band is None or lower_band is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
adx = float(adx_value)
upper_band = float(upper_band)
lower_band = float(lower_band)
ema = float(to_decimal(values[0]))
fast_ema = float(to_decimal(values[1]))
alma = float(to_decimal(values[2]))
atr = float(to_decimal(values[3]))
rsi = float(to_decimal(values[5]))
cooldown_ok = self._last_entry_bar is None or self._bar_index - self._last_entry_bar >= self._base_cooldown_bars.Value
common_filters = cooldown_ok and float(candle.TotalVolume) > volume_ma and adx > ADX_LEVEL and atr >= float(self._min_atr.Value)
long_signal = common_filters and ut_buy and close > ema and close > alma and rsi > RSI_LONG_LEVEL and close < upper_band
cross_below_fast = prev_close >= prev_fast and close < fast_ema
short_signal = common_filters and ut_bearish and cross_below_fast and close < ema and close < alma \
and rsi < RSI_SHORT_LEVEL and close > lower_band
sl_mult = float(self._stop_loss_atr_multiplier.Value)
tp_mult = float(self._take_profit_atr_multiplier.Value)
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._last_entry_bar = self._bar_index
self._stop_price = close - sl_mult * atr
self._take_price = close + tp_mult * atr
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._last_entry_bar = self._bar_index
self._stop_price = close + sl_mult * atr
self._take_price = close - tp_mult * atr
elif self.Position > 0:
hit_stop = sl_mult > 0 and float(candle.LowPrice) <= self._stop_price
hit_take = tp_mult > 0 and float(candle.HighPrice) >= self._take_price
if ut_sell or hit_stop or hit_take:
self.SellMarket(self.Position)
elif self.Position < 0:
hit_stop = sl_mult > 0 and float(candle.HighPrice) >= self._stop_price
hit_take = tp_mult > 0 and float(candle.LowPrice) <= self._take_price
if ut_buy or hit_stop or hit_take:
self.BuyMarket(-self.Position)
def CreateClone(self):
return alma_ut_bot_confluence_strategy()