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 过滤,冷却期。
- 空头:价格下穿快速 EMA 且出现 UT Bot 卖出信号并满足过滤条件。
- 方向:多/空。
- 出场条件:
- 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、布林带、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>
/// Strategy that combines ALMA filter with UT Bot trailing stop.
/// Enters long when UT Bot gives a buy signal above EMA.
/// Short entries occur on UT Bot sell signals below EMA.
/// Exits are handled by UT Bot trailing stop or ATR-based stop/target.
/// </summary>
public class AlmaUtBotConfluenceStrategy : Strategy
{
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<decimal> _takeProfitAtrMultiplier;
private readonly StrategyParam<int> _utKeyValue;
private readonly StrategyParam<int> _utAtrPeriod;
private readonly StrategyParam<int> _baseCooldownBars;
private readonly StrategyParam<bool> _useUtExit;
private readonly StrategyParam<DataType> _candleType;
private decimal _xAtrTrailingStop;
private decimal _prevSrc;
private decimal _prevStop;
private int _barIndex;
private int? _lastTradeIndex;
private decimal _entryPrice;
/// <summary>
/// Length for long-term EMA (default: 72).
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// ATR length (default: 14).
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// ATR multiplier for stop loss (default: 5.0).
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// ATR multiplier for take profit (default: 4.0).
/// </summary>
public decimal TakeProfitAtrMultiplier
{
get => _takeProfitAtrMultiplier.Value;
set => _takeProfitAtrMultiplier.Value = value;
}
/// <summary>
/// UT Bot key value (default: 2).
/// </summary>
public int UtKeyValue
{
get => _utKeyValue.Value;
set => _utKeyValue.Value = value;
}
/// <summary>
/// ATR period for UT Bot (default: 10).
/// </summary>
public int UtAtrPeriod
{
get => _utAtrPeriod.Value;
set => _utAtrPeriod.Value = value;
}
/// <summary>
/// Cooldown in bars between trades (default: 15).
/// </summary>
public int BaseCooldownBars
{
get => _baseCooldownBars.Value;
set => _baseCooldownBars.Value = value;
}
/// <summary>
/// Use UT Bot trailing stop for exits (default: true).
/// </summary>
public bool UseUtExit
{
get => _useUtExit.Value;
set => _useUtExit.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public AlmaUtBotConfluenceStrategy()
{
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Length for long-term EMA", "Main");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period", "Main");
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 5m)
.SetDisplay("Stop Loss ATR Mult", "ATR multiplier for stop loss", "Risk");
_takeProfitAtrMultiplier = Param(nameof(TakeProfitAtrMultiplier), 4m)
.SetDisplay("Take Profit ATR Mult", "ATR multiplier for take profit", "Risk");
_utKeyValue = Param(nameof(UtKeyValue), 1)
.SetDisplay("UT Key", "UT Bot key value", "UT Bot");
_utAtrPeriod = Param(nameof(UtAtrPeriod), 10)
.SetDisplay("UT ATR Period", "ATR period for UT Bot", "UT Bot");
_baseCooldownBars = Param(nameof(BaseCooldownBars), 30)
.SetDisplay("Base Cooldown", "Cooldown in bars between trades", "Filters");
_useUtExit = Param(nameof(UseUtExit), true)
.SetDisplay("Use UT Exit", "Use UT Bot trailing stop for exits", "Exit");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "Main");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_xAtrTrailingStop = default;
_prevSrc = default;
_prevStop = default;
_barIndex = default;
_lastTradeIndex = default;
_entryPrice = default;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var atrUt = new AverageTrueRange { Length = UtAtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(new IIndicator[] { ema, atr, atrUt }, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal[] values)
{
if (candle.State != CandleStates.Finished)
return;
var emaValue = values[0];
var atrValue = values[1];
var atrUtValue = values[2];
var src = candle.ClosePrice;
var nLoss = UtKeyValue * atrUtValue;
if (_barIndex == 0)
_xAtrTrailingStop = src + nLoss;
if (src > _prevStop && _prevSrc > _prevStop)
_xAtrTrailingStop = Math.Max(_prevStop, src - nLoss);
else if (src < _prevStop && _prevSrc < _prevStop)
_xAtrTrailingStop = Math.Min(_prevStop, src + nLoss);
else
_xAtrTrailingStop = src > _prevStop ? src - nLoss : src + nLoss;
var buyUt = src > _xAtrTrailingStop && _prevSrc <= _prevStop;
var sellUt = src < _xAtrTrailingStop && _prevSrc >= _prevStop;
var cooldownOk = _lastTradeIndex is null || _barIndex - _lastTradeIndex >= BaseCooldownBars;
var buyCondition = buyUt && src > emaValue && cooldownOk;
var sellCondition = sellUt && src < emaValue && cooldownOk;
if (buyCondition && Position <= 0)
{
BuyMarket();
_lastTradeIndex = _barIndex;
_entryPrice = src;
}
else if (sellCondition && Position >= 0)
{
SellMarket();
_lastTradeIndex = _barIndex;
_entryPrice = src;
}
else
{
ManageExit(candle, atrValue, src);
}
_prevSrc = src;
_prevStop = _xAtrTrailingStop;
_barIndex++;
}
private void ManageExit(ICandleMessage candle, decimal atr, decimal src)
{
if (UseUtExit)
{
if (Position > 0 && src < _xAtrTrailingStop && _prevSrc >= _prevStop)
{
SellMarket();
_lastTradeIndex = _barIndex;
}
else if (Position < 0 && src > _xAtrTrailingStop && _prevSrc <= _prevStop)
{
BuyMarket();
_lastTradeIndex = _barIndex;
}
}
else if (Position != 0)
{
var stopLoss = atr * StopLossAtrMultiplier;
var takeProfit = atr * TakeProfitAtrMultiplier;
if (Position > 0)
{
if (candle.ClosePrice <= _entryPrice - stopLoss || candle.ClosePrice >= _entryPrice + takeProfit)
{
SellMarket();
_lastTradeIndex = _barIndex;
}
}
else
{
if (candle.ClosePrice >= _entryPrice + stopLoss || candle.ClosePrice <= _entryPrice - takeProfit)
{
BuyMarket();
_lastTradeIndex = _barIndex;
}
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class alma_ut_bot_confluence_strategy(Strategy):
"""
Strategy that combines ALMA filter with UT Bot trailing stop.
Enters long when UT Bot gives a buy signal above EMA.
Short entries occur on UT Bot sell signals below EMA.
Exits are handled by UT Bot trailing stop or ATR-based stop/target.
"""
def __init__(self):
super(alma_ut_bot_confluence_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Length for long-term EMA", "Main")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Main")
self._stop_loss_atr_mult = self.Param("StopLossAtrMultiplier", 5.0) \
.SetDisplay("Stop Loss ATR Mult", "ATR multiplier for stop loss", "Risk")
self._take_profit_atr_mult = self.Param("TakeProfitAtrMultiplier", 4.0) \
.SetDisplay("Take Profit ATR Mult", "ATR multiplier for take profit", "Risk")
self._ut_key_value = self.Param("UtKeyValue", 1) \
.SetDisplay("UT Key", "UT Bot key value", "UT Bot")
self._ut_atr_period = self.Param("UtAtrPeriod", 10) \
.SetDisplay("UT ATR Period", "ATR period for UT Bot", "UT Bot")
self._base_cooldown_bars = self.Param("BaseCooldownBars", 30) \
.SetDisplay("Base Cooldown", "Cooldown in bars between trades", "Filters")
self._use_ut_exit = self.Param("UseUtExit", True) \
.SetDisplay("Use UT Exit", "Use UT Bot trailing stop for exits", "Exit")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles", "Main")
self._x_atr_trailing_stop = 0.0
self._prev_src = 0.0
self._prev_stop = 0.0
self._bar_index = 0
self._last_trade_index = None
self._entry_price = 0.0
@property
def EmaLength(self): return self._ema_length.Value
@EmaLength.setter
def EmaLength(self, v): self._ema_length.Value = v
@property
def AtrLength(self): return self._atr_length.Value
@AtrLength.setter
def AtrLength(self, v): self._atr_length.Value = v
@property
def StopLossAtrMultiplier(self): return self._stop_loss_atr_mult.Value
@StopLossAtrMultiplier.setter
def StopLossAtrMultiplier(self, v): self._stop_loss_atr_mult.Value = v
@property
def TakeProfitAtrMultiplier(self): return self._take_profit_atr_mult.Value
@TakeProfitAtrMultiplier.setter
def TakeProfitAtrMultiplier(self, v): self._take_profit_atr_mult.Value = v
@property
def UtKeyValue(self): return self._ut_key_value.Value
@UtKeyValue.setter
def UtKeyValue(self, v): self._ut_key_value.Value = v
@property
def UtAtrPeriod(self): return self._ut_atr_period.Value
@UtAtrPeriod.setter
def UtAtrPeriod(self, v): self._ut_atr_period.Value = v
@property
def BaseCooldownBars(self): return self._base_cooldown_bars.Value
@BaseCooldownBars.setter
def BaseCooldownBars(self, v): self._base_cooldown_bars.Value = v
@property
def UseUtExit(self): return self._use_ut_exit.Value
@UseUtExit.setter
def UseUtExit(self, v): self._use_ut_exit.Value = v
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
def OnReseted(self):
super(alma_ut_bot_confluence_strategy, self).OnReseted()
self._x_atr_trailing_stop = 0.0
self._prev_src = 0.0
self._prev_stop = 0.0
self._bar_index = 0
self._last_trade_index = None
self._entry_price = 0.0
def OnStarted2(self, time):
super(alma_ut_bot_confluence_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
atr = AverageTrueRange()
atr.Length = self.AtrLength
atr_ut = AverageTrueRange()
atr_ut.Length = self.UtAtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, atr, atr_ut, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ema_value, atr_value, atr_ut_value):
if candle.State != CandleStates.Finished:
return
src = float(candle.ClosePrice)
n_loss = self.UtKeyValue * atr_ut_value
if self._bar_index == 0:
self._x_atr_trailing_stop = src + n_loss
if src > self._prev_stop and self._prev_src > self._prev_stop:
self._x_atr_trailing_stop = max(self._prev_stop, src - n_loss)
elif src < self._prev_stop and self._prev_src < self._prev_stop:
self._x_atr_trailing_stop = min(self._prev_stop, src + n_loss)
else:
self._x_atr_trailing_stop = src - n_loss if src > self._prev_stop else src + n_loss
buy_ut = src > self._x_atr_trailing_stop and self._prev_src <= self._prev_stop
sell_ut = src < self._x_atr_trailing_stop and self._prev_src >= self._prev_stop
cooldown_ok = self._last_trade_index is None or self._bar_index - self._last_trade_index >= self.BaseCooldownBars
buy_condition = buy_ut and src > ema_value and cooldown_ok
sell_condition = sell_ut and src < ema_value and cooldown_ok
if buy_condition and self.Position <= 0:
self.BuyMarket()
self._last_trade_index = self._bar_index
self._entry_price = src
elif sell_condition and self.Position >= 0:
self.SellMarket()
self._last_trade_index = self._bar_index
self._entry_price = src
else:
self._manage_exit(candle, atr_value, src)
self._prev_src = src
self._prev_stop = self._x_atr_trailing_stop
self._bar_index += 1
def _manage_exit(self, candle, atr, src):
if self.UseUtExit:
if self.Position > 0 and src < self._x_atr_trailing_stop and self._prev_src >= self._prev_stop:
self.SellMarket()
self._last_trade_index = self._bar_index
elif self.Position < 0 and src > self._x_atr_trailing_stop and self._prev_src <= self._prev_stop:
self.BuyMarket()
self._last_trade_index = self._bar_index
elif self.Position != 0:
stop_loss = atr * self.StopLossAtrMultiplier
take_profit = atr * self.TakeProfitAtrMultiplier
close = float(candle.ClosePrice)
if self.Position > 0:
if close <= self._entry_price - stop_loss or close >= self._entry_price + take_profit:
self.SellMarket()
self._last_trade_index = self._bar_index
else:
if close >= self._entry_price + stop_loss or close <= self._entry_price - take_profit:
self.BuyMarket()
self._last_trade_index = self._bar_index
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return alma_ut_bot_confluence_strategy()