增强型 BarUpDn 策略
该策略结合布林带与趋势确认,寻找向上或向下跳空的 K 线。上涨趋势中出现向上跳空的看涨 K 线时做多;下跌趋势中出现向下跳空的看跌 K 线时做空。退出通过基于 ATR 的止损和止盈实现。
详情
- 入场条件:
- 多头:看涨 K 线向上跳空,收盘价高于趋势均线且高于布林带下轨。
- 空头:看跌 K 线向下跳空,收盘价低于趋势均线且低于布林带上轨。
- 多空方向:双向。
- 离场条件:
- 价格触及基于 ATR 的止损或止盈(1.5× ATR)。
- 止损:使用 ATR 计算的止损和止盈。
- 默认参数:
BbLength= 20BbMultiplier= 2MaLength= 50AtrLength= 14AtrMultiplierSl= 2AtrMultiplierTp= 3
- 筛选器:
- 类别:趋势跟随
- 方向:双向
- 指标:布林带、SMA、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>
/// Enhanced BarUpDn strategy.
/// A bullish candle that opens above the previous close goes long when it closes above the trend SMA and above the lower
/// Bollinger band; a bearish candle that opens below the previous close goes short when it closes below the trend SMA and below
/// the upper band. An opposite signal reverses the position. The stop lies AtrMultiplierSl ATR and the target AtrMultiplierTp ATR
/// from the entry.
/// </summary>
public class EnhancedBarUpDnStrategy : Strategy
{
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrMultiplierSl;
private readonly StrategyParam<decimal> _atrMultiplierTp;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevClose;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Bollinger Bands length.
/// </summary>
public int BbLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands deviation multiplier.
/// </summary>
public decimal BbMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Trend SMA length.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// ATR length.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// Stop loss distance in ATR.
/// </summary>
public decimal AtrMultiplierSl
{
get => _atrMultiplierSl.Value;
set => _atrMultiplierSl.Value = value;
}
/// <summary>
/// Take profit distance in ATR.
/// </summary>
public decimal AtrMultiplierTp
{
get => _atrMultiplierTp.Value;
set => _atrMultiplierTp.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EnhancedBarUpDnStrategy()
{
_bbLength = Param(nameof(BbLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger Bands length", "Indicators");
_bbMultiplier = Param(nameof(BbMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Multiplier", "Bollinger Bands deviation multiplier", "Indicators");
_maLength = Param(nameof(MaLength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Trend SMA length", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR length", "Risk");
_atrMultiplierSl = Param(nameof(AtrMultiplierSl), 2m)
.SetNotNegative()
.SetDisplay("ATR Stop", "Stop loss distance in ATR", "Risk");
_atrMultiplierTp = Param(nameof(AtrMultiplierTp), 3m)
.SetNotNegative()
.SetDisplay("ATR Target", "Take profit distance in ATR", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
_prevClose = null;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
var bollinger = new BollingerBands { Length = BbLength, Width = BbMultiplier };
var ma = new SimpleMovingAverage { Length = MaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue maValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var prevClose = _prevClose;
_prevClose = candle.ClosePrice;
if (!bollingerValue.IsFormed || !maValue.IsFormed || !atrValue.IsFormed)
return;
if (bollingerValue is not IBollingerBandsValue { UpBand: decimal upper, LowBand: decimal lower })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0 && ((AtrMultiplierSl > 0m && candle.LowPrice <= _stopPrice) || (AtrMultiplierTp > 0m && candle.HighPrice >= _takePrice)))
{
SellMarket(Position);
return;
}
if (Position < 0 && ((AtrMultiplierSl > 0m && candle.HighPrice >= _stopPrice) || (AtrMultiplierTp > 0m && candle.LowPrice <= _takePrice)))
{
BuyMarket(-Position);
return;
}
if (prevClose is not decimal pc)
return;
var ma = maValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var open = candle.OpenPrice;
var close = candle.ClosePrice;
var longSignal = close > open && open > pc && close > ma && close > lower;
var shortSignal = close < open && open < pc && close < ma && close < upper;
if (longSignal && Position <= 0)
{
_stopPrice = close - atr * AtrMultiplierSl;
_takePrice = close + atr * AtrMultiplierTp;
BuyMarket(Volume + Math.Abs(Position));
}
else if (shortSignal && Position >= 0)
{
_stopPrice = close + atr * AtrMultiplierSl;
_takePrice = close - atr * AtrMultiplierTp;
SellMarket(Volume + Math.Abs(Position));
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import BollingerBands, SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class enhanced_bar_up_dn_strategy(Strategy):
"""
Enhanced BarUpDn strategy.
A bullish candle that opens above the previous close goes long when it closes above the trend SMA and above the lower
Bollinger band; a bearish candle that opens below the previous close goes short when it closes below the trend SMA and below
the upper band. An opposite signal reverses the position. The stop lies AtrMultiplierSl ATR and the target AtrMultiplierTp ATR
from the entry.
"""
def __init__(self):
super(enhanced_bar_up_dn_strategy, self).__init__()
self._bb_length = self.Param("BbLength", 20).SetGreaterThanZero().SetDisplay("BB Length", "Bollinger Bands length", "Indicators")
self._bb_multiplier = self.Param("BbMultiplier", 2.0).SetGreaterThanZero().SetDisplay("BB Multiplier", "Bollinger Bands deviation multiplier", "Indicators")
self._ma_length = self.Param("MaLength", 50).SetGreaterThanZero().SetDisplay("MA Length", "Trend SMA length", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetGreaterThanZero().SetDisplay("ATR Length", "ATR length", "Risk")
self._atr_multiplier_sl = self.Param("AtrMultiplierSl", 2.0).SetNotNegative().SetDisplay("ATR Stop", "Stop loss distance in ATR", "Risk")
self._atr_multiplier_tp = self.Param("AtrMultiplierTp", 3.0).SetNotNegative().SetDisplay("ATR Target", "Take profit distance in ATR", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_close = None
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(enhanced_bar_up_dn_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(enhanced_bar_up_dn_strategy, self).OnStarted2(time)
self._reset_state()
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(float(self._bb_multiplier.Value))
ma = SimpleMovingAverage()
ma.Length = self._ma_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bollinger, ma, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bollinger_value, ma_value, atr_value):
if candle.State != CandleStates.Finished:
return
prev_close = self._prev_close
close = float(candle.ClosePrice)
self._prev_close = close
if not bollinger_value.IsFormed or not ma_value.IsFormed or not atr_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
sl_mult = float(self._atr_multiplier_sl.Value)
tp_mult = float(self._atr_multiplier_tp.Value)
if self.Position > 0 and ((sl_mult > 0 and low <= self._stop_price) or (tp_mult > 0 and high >= self._take_price)):
self.SellMarket(self.Position)
return
if self.Position < 0 and ((sl_mult > 0 and high >= self._stop_price) or (tp_mult > 0 and low <= self._take_price)):
self.BuyMarket(-self.Position)
return
if prev_close is None:
return
upper = float(bollinger_value.UpBand)
lower = float(bollinger_value.LowBand)
ma = float(ma_value.GetValue[Decimal](None))
atr = float(atr_value.GetValue[Decimal](None))
open_price = float(candle.OpenPrice)
long_signal = close > open_price and open_price > prev_close and close > ma and close > lower
short_signal = close < open_price and open_price < prev_close and close < ma and close < upper
if long_signal and self.Position <= 0:
self._stop_price = close - atr * sl_mult
self._take_price = close + atr * tp_mult
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self._stop_price = close + atr * sl_mult
self._take_price = close - atr * tp_mult
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return enhanced_bar_up_dn_strategy()