比特币流动性突破策略
该策略在流动性和波动性较高且短期趋势看涨时建立多头仓位。当成交量高于其移动平均值乘以阈值时视为高流动性;当ATR高于其移动平均值时确认波动性。
详情
- 入场条件:
成交量 > 成交量SMA * LiquidityThreshold价格变动(%) > PriceChangeThreshold快SMA > 慢SMARSI < 65ATR > SMA(ATR,10)
- 多空方向: 仅做多。
- 出场条件: 快SMA下穿慢SMA或RSI > 70。
- 止损: 可选的止损和止盈百分比。
- 默认参数:
LiquidityThreshold= 1.3PriceChangeThreshold= 1.5VolatilityPeriod= 14LiquidityPeriod= 20FastMaPeriod= 9SlowMaPeriod= 21RsiPeriod= 14StopLossPercent= 0.5TakeProfitPercent= 7
- 过滤器:
- 分类: 突破
- 方向: 多头
- 指标: SMA, RSI, ATR
- 止损: 有
- 复杂度: 中等
- 时间框架: 1小时
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>
/// Bitcoin Liquidity Breakout strategy.
/// Long only: buys when volume exceeds LiquidityThreshold times its SMA(LiquidityPeriod), the close has risen more than
/// PriceChangeThreshold percent from the previous close, SMA(FastMaPeriod) is above SMA(SlowMaPeriod), RSI(RsiPeriod) is below 65
/// and ATR(VolatilityPeriod) is above its 10-bar SMA. The long closes when the fast SMA crosses below the slow SMA or RSI rises
/// above 70; percent stop loss and take profit protect it (0 disables either).
/// </summary>
public class BitcoinLiquidityBreakoutStrategy : Strategy
{
private const decimal _rsiEntryMax = 65m;
private const decimal _rsiExit = 70m;
private const int _atrSmaLength = 10;
private readonly StrategyParam<decimal> _liquidityThreshold;
private readonly StrategyParam<decimal> _priceChangeThreshold;
private readonly StrategyParam<int> _volatilityPeriod;
private readonly StrategyParam<int> _liquidityPeriod;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _volumeSma;
private SimpleMovingAverage _atrSma;
private decimal? _prevClose;
private decimal? _prevFast;
private decimal? _prevSlow;
/// <summary>
/// Multiple of the volume SMA that marks high liquidity.
/// </summary>
public decimal LiquidityThreshold { get => _liquidityThreshold.Value; set => _liquidityThreshold.Value = value; }
/// <summary>
/// Minimum close-to-close rise in percent.
/// </summary>
public decimal PriceChangeThreshold { get => _priceChangeThreshold.Value; set => _priceChangeThreshold.Value = value; }
/// <summary>
/// ATR period.
/// </summary>
public int VolatilityPeriod { get => _volatilityPeriod.Value; set => _volatilityPeriod.Value = value; }
/// <summary>
/// Volume SMA period.
/// </summary>
public int LiquidityPeriod { get => _liquidityPeriod.Value; set => _liquidityPeriod.Value = value; }
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
/// <summary>
/// Stop loss in percent.
/// </summary>
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
/// <summary>
/// Take profit in percent.
/// </summary>
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public BitcoinLiquidityBreakoutStrategy()
{
_liquidityThreshold = Param(nameof(LiquidityThreshold), 1.3m)
.SetGreaterThanZero()
.SetDisplay("Liquidity Threshold", "Multiple of the volume SMA that marks high liquidity", "Entry");
_priceChangeThreshold = Param(nameof(PriceChangeThreshold), 1.5m)
.SetDisplay("Price Change Threshold %", "Minimum close-to-close rise", "Entry");
_volatilityPeriod = Param(nameof(VolatilityPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Volatility Period", "ATR period", "Indicators");
_liquidityPeriod = Param(nameof(LiquidityPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Liquidity Period", "Volume SMA period", "Indicators");
_fastMaPeriod = Param(nameof(FastMaPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Fast MA Period", "Fast SMA period", "Indicators");
_slowMaPeriod = Param(nameof(SlowMaPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow MA Period", "Slow SMA period", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 0.5m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss in percent", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 7m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit in percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(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();
_volumeSma = null;
_atrSma = null;
_prevClose = null;
_prevFast = null;
_prevSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = null;
_prevFast = null;
_prevSlow = null;
_volumeSma = new SimpleMovingAverage { Length = LiquidityPeriod };
_atrSma = new SimpleMovingAverage { Length = _atrSmaLength };
var fast = new SimpleMovingAverage { Length = FastMaPeriod };
var slow = new SimpleMovingAverage { Length = SlowMaPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var atr = new AverageTrueRange { Length = VolatilityPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, rsi, atr, ProcessCandle)
.Start();
var take = TakeProfitPercent > 0 ? new Unit(TakeProfitPercent, UnitTypes.Percent) : new Unit();
var stop = StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : new Unit();
StartProtection(take, stop, useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal rsi, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var volumeAverage = _volumeSma.Process(candle.TotalVolume, candle.ServerTime, true).ToDecimal();
var atrAverage = _atrSma.Process(atr, candle.ServerTime, true).ToDecimal();
var close = candle.ClosePrice;
var prevClose = _prevClose;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevClose = close;
_prevFast = fast;
_prevSlow = slow;
if (!_volumeSma.IsFormed || !_atrSma.IsFormed || prevClose is not decimal pc || pc <= 0)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
var crossDown = prevFast is decimal pf && prevSlow is decimal ps && pf >= ps && fast < slow;
if (crossDown || rsi > _rsiExit)
SellMarket(Position);
return;
}
var priceChange = (close - pc) / pc * 100m;
if (Position == 0
&& candle.TotalVolume > volumeAverage * LiquidityThreshold
&& priceChange > PriceChangeThreshold
&& fast > slow
&& rsi < _rsiEntryMax
&& atr > atrAverage)
{
BuyMarket(Volume);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
RSI_ENTRY_MAX = 65
RSI_EXIT = 70
ATR_SMA_LENGTH = 10
class bitcoin_liquidity_breakout_strategy(Strategy):
"""
Bitcoin Liquidity Breakout strategy.
Long only: buys when volume exceeds LiquidityThreshold times its SMA(LiquidityPeriod), the close has risen more than
PriceChangeThreshold percent from the previous close, SMA(FastMaPeriod) is above SMA(SlowMaPeriod), RSI(RsiPeriod) is below 65
and ATR(VolatilityPeriod) is above its 10-bar SMA. The long closes when the fast SMA crosses below the slow SMA or RSI rises
above 70; percent stop loss and take profit protect it (0 disables either).
"""
def __init__(self):
super(bitcoin_liquidity_breakout_strategy, self).__init__()
self._liquidity_threshold = self.Param("LiquidityThreshold", 1.3).SetGreaterThanZero().SetDisplay("Liquidity Threshold", "Multiple of the volume SMA that marks high liquidity", "Entry")
self._price_change_threshold = self.Param("PriceChangeThreshold", 1.5).SetDisplay("Price Change Threshold %", "Minimum close-to-close rise", "Entry")
self._volatility_period = self.Param("VolatilityPeriod", 14).SetGreaterThanZero().SetDisplay("Volatility Period", "ATR period", "Indicators")
self._liquidity_period = self.Param("LiquidityPeriod", 20).SetGreaterThanZero().SetDisplay("Liquidity Period", "Volume SMA period", "Indicators")
self._fast_ma_period = self.Param("FastMaPeriod", 9).SetGreaterThanZero().SetDisplay("Fast MA Period", "Fast SMA period", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 21).SetGreaterThanZero().SetDisplay("Slow MA Period", "Slow SMA period", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI period", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 0.5).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss in percent", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 7.0).SetNotNegative().SetDisplay("Take Profit %", "Take profit in percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(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._volume_sma = None
self._atr_sma = None
self._prev_close = None
self._prev_fast = None
self._prev_slow = None
def OnReseted(self):
super(bitcoin_liquidity_breakout_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(bitcoin_liquidity_breakout_strategy, self).OnStarted2(time)
self._reset_state()
self._volume_sma = SimpleMovingAverage()
self._volume_sma.Length = self._liquidity_period.Value
self._atr_sma = SimpleMovingAverage()
self._atr_sma.Length = ATR_SMA_LENGTH
fast = SimpleMovingAverage()
fast.Length = self._fast_ma_period.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_ma_period.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
atr = AverageTrueRange()
atr.Length = self._volatility_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, rsi, atr, self._process_candle).Start()
tp = self._take_profit_percent.Value
sl = self._stop_loss_percent.Value
take = Unit(Decimal(tp), UnitTypes.Percent) if tp > 0 else Unit()
stop = Unit(Decimal(sl), UnitTypes.Percent) if sl > 0 else Unit()
self.StartProtection(take, stop, useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, fast, slow, rsi, atr):
if candle.State != CandleStates.Finished:
return
volume_average = process_float(self._volume_sma, candle.TotalVolume, candle.ServerTime, True).GetValue[Decimal](None)
atr_average = process_float(self._atr_sma, atr, candle.ServerTime, True).GetValue[Decimal](None)
close = candle.ClosePrice
prev_close = self._prev_close
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_close = close
self._prev_fast = fast
self._prev_slow = slow
if not self._volume_sma.IsFormed or not self._atr_sma.IsFormed or prev_close is None or prev_close <= 0:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
cross_down = prev_fast is not None and prev_slow is not None and prev_fast >= prev_slow and fast < slow
if cross_down or rsi > RSI_EXIT:
self.SellMarket(self.Position)
return
price_change = (close - prev_close) / prev_close * Decimal(100)
if (self.Position == 0
and candle.TotalVolume > volume_average * Decimal(self._liquidity_threshold.Value)
and price_change > Decimal(self._price_change_threshold.Value)
and fast > slow
and rsi < RSI_ENTRY_MAX
and atr > atr_average):
self.BuyMarket(self.Volume)
def CreateClone(self):
return bitcoin_liquidity_breakout_strategy()