Bitcoin Bullish Percent Index Strategy
This strategy uses the Relative Strength Index (RSI) to approximate the Bitcoin Bullish Percent Index. It enters long when RSI rises above an oversold threshold and enters short when RSI falls below an overbought threshold.
Details
- Entry Criteria:
- Long: RSI crosses above the oversold level.
- Short: RSI crosses below the overbought level.
- Long/Short: Both.
- Exit Criteria: Opposite signal.
- Stops: No.
- Default Values:
RSI Period= 14Overbought= 70Oversold= 30
- Filters:
- Category: Oscillator
- Direction: Both
- Indicators: RSI
- Stops: No
- Complexity: Low
- Timeframe: Medium-term
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 Bullish Percent Index strategy.
/// RSI approximates the bullish percent index: RSI crossing above Oversold goes long, RSI crossing below Overbought goes short,
/// and the opposite signal reverses the position.
/// </summary>
public class BitcoinBullishPercentIndexStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevRsi;
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI level whose downward cross opens a short.
/// </summary>
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
/// <summary>
/// RSI level whose upward cross opens a long.
/// </summary>
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BitcoinBullishPercentIndexStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_overbought = Param(nameof(Overbought), 70m)
.SetDisplay("Overbought", "RSI level whose downward cross opens a short", "Indicators");
_oversold = Param(nameof(Oversold), 30m)
.SetDisplay("Oversold", "RSI level whose upward cross opens a long", "Indicators");
_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();
_prevRsi = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = null;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
var prev = _prevRsi;
_prevRsi = rsi;
if (prev is not decimal prevRsi)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevRsi <= Oversold && rsi > Oversold && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (prevRsi >= Overbought && rsi < Overbought && Position >= 0)
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class bitcoin_bullish_percent_index_strategy(Strategy):
"""
Bitcoin Bullish Percent Index strategy.
RSI approximates the bullish percent index: RSI crossing above Oversold goes long, RSI crossing below Overbought goes short,
and the opposite signal reverses the position.
"""
def __init__(self):
super(bitcoin_bullish_percent_index_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI period", "Indicators")
self._overbought = self.Param("Overbought", 70.0).SetDisplay("Overbought", "RSI level whose downward cross opens a short", "Indicators")
self._oversold = self.Param("Oversold", 30.0).SetDisplay("Oversold", "RSI level whose upward cross opens a long", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_rsi = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bitcoin_bullish_percent_index_strategy, self).OnReseted()
self._prev_rsi = None
def OnStarted2(self, time):
super(bitcoin_bullish_percent_index_strategy, self).OnStarted2(time)
self._prev_rsi = None
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, rsi):
if candle.State != CandleStates.Finished:
return
prev = self._prev_rsi
self._prev_rsi = rsi
if prev is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
oversold = Decimal(self._oversold.Value)
overbought = Decimal(self._overbought.Value)
if prev <= oversold and rsi > oversold and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif prev >= overbought and rsi < overbought and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return bitcoin_bullish_percent_index_strategy()