Strategy vault / Machine learning
Bitcoin Trading Strategy Based on Quantitative Indicators
This strategy uses multiple quantitative indicators to determine the timing of buying and selling Bitcoin and automate trading. It mainly includes the Hull indicator, Relative Strength Index (RSI), Bollinger Bands…

Overview
This strategy uses multiple quantitative indicators to determine the timing of buying and selling Bitcoin and automate trading. It mainly includes the Hull indicator, Relative Strength Index (RSI), Bollinger Bands (BB) and Volume Oscillator (VO).
Strategy Principle
Use the modified Hull Moving Average to determine the main trend direction of the market, combined with Bollinger Bands to assist in determining breakout buy and sell points.
The RSI indicator combined with an adaptive volatility range determines the overbought and oversold zones to generate trading signals. Two sets of parameters are also set up for duplicate signal verification.
The Volume Oscillator determines the momentum of buying and selling to avoid false breakouts.
Set stop loss/take profit ratios in advance to preset stop loss and take profit levels for risk management.
Advantage Analysis
The Hull curve can capture trend changes faster, and Bollinger Bands can help reduce false signals.
Optimization of RSI parameters and verification of duplicate signals make it more reliable.
Volume Oscillator combined with trends and indicator signals avoids inaccurate trading.
Preset stop loss and take profit methods can automatically control single profit and loss and effectively manage overall risk.
Risk Analysis
Improper parameter settings may result in too high trading frequency or deteriorated signal performance.
Sudden market events may cause prices to fluctuate violently, resulting in stop loss being triggered and greater losses.
When the trading variety is changed to other coins, the parameters need to be retested and optimized.
If volume data is missing, the Volume Oscillator will fail.
Optimization Directions
Test more RSI parameter combinations to find the optimal parameters.
Try combining RSI with other indicators like MACD and KD to improve signal accuracy.
Add model prediction modules and use machine learning to judge market direction.
Test the parameters when applied to other trading varieties.
Optimize the stop loss and take profit algorithms to maximize profits.
Summary
This strategy combines multiple quantitative technical indicators to determine entry and exit timing. Through parameter optimization, risk control and other methods, it has achieved automated Bitcoin trading with good results. But it still requires continuous testing and optimization to adapt to market changes. It can serve as a reference for investors to assist in trading decisions.
Parameters
| Name | Default | What it controls |
|---|---|---|
| v_input_source_1_close | 0 | Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4 |
| v_input_float_1 | 1.5 | Risk/Reward |
| v_input_int_1 | true | (?beginning Backtest)Start Date |
| v_input_int_2 | 5 | Start Month |
| v_input_int_3 | 2022 | Start Year |
| v_input_int_4 | 7 | (?number of past candles)Swing High |
| v_input_int_5 | 7 | Swing Low |
| v_input_string_1 | 0 | (?Hull Suite)Hull Variation: Hma | Thma | Ehma |
| v_input_1 | 60 | Length |
| v_input_2 | 3 | Length multiplier |
| v_input_3 | 6 | (?QQE MOD)RSI Length |
| v_input_4 | 5 | RSI Smoothing |
| v_input_5 | 3 | Fast QQE Factor |
| v_input_6 | 3 | Thresh-hold |
| v_input_7_close | 0 | RSI Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4 |
| v_input_int_6 | 50 | Bollinger Length |
| v_input_float_2 | 0.35 | BB Multiplier |
| v_input_8 | 6 | RSI Length |
| v_input_9 | 5 | RSI Smoothing |
| v_input_10 | 1.61 | Fast QQE2 Factor |
| v_input_11 | 3 | Thresh-hold |
| v_input_12_close | 0 | RSI Source: close | high | low | open | hl2 | hlc3 | hlcc4 | ohlc4 |
| v_input_int_7 | 5 | (?Volume Oscillator)Short Length |
| v_input_int_8 | 10 | Long Length |
Source Pine Script
/*backtest
start: 2023-11-25 00:00:00
end: 2023-12-25 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/
// © maxencetajet
//@version=5
strategy("Strategy Crypto", overlay=true, initial_capital=1000, default_qty_type=strategy.fixed, default_qty_value=0.5, slippage=25)
src1 = input.source(close, title="Source")
target_stop_ratio = input.float(title='Risk/Reward', defval=1.5, minval=0.5, maxval=100)
startDate = input.int(title='Start Date', defval=1, minval=1, maxval=31, group="beginning Backtest")
startMonth = input.int(title='Start Month', defval=5, minval=1, maxval=12, group="beginning Backtest")
startYear = input.int(title='Start Year', defval=2022, minval=2000, maxval=2100, group="beginning Backtest")
inDateRange = time >= timestamp(syminfo.timezone, startYear, startMonth, startDate, 0, 0)
swingHighV = input.int(7, title="Swing High", group="number of past candles")
swingLowV = input.int(7, title="Swing Low", group="number of past candles")
//Hull Suite
modeSwitch = input.string("Hma", title="Hull Variation", options=["Hma", "Thma", "Ehma"], group="Hull Suite")
length = input(60, title="Length", group="Hull Suite")
lengthMult = input(3, title="Length multiplier", group="Hull Suite")
HMA(_src1, _length) =>
ta.wma(2 * ta.wma(_src1, _length / 2) - ta.wma(_src1, _length), math.round(math.sqrt(_length)))
EHMA(_src1, _length) =>
ta.ema(2 * ta.ema(_src1, _length / 2) - ta.ema(_src1, _length), math.round(math.sqrt(_length)))
THMA(_src1, _length) =>
ta.wma(ta.wma(_src1, _length / 3) * 3 - ta.wma(_src1, _length / 2) - ta.wma(_src1, _length), _length)
Mode(modeSwitch, src1, len) =>
modeSwitch == 'Hma' ? HMA(src1, len) : modeSwitch == 'Ehma' ? EHMA(src1, len) : modeSwitch == 'Thma' ? THMA(src1, len / 2) : na
_hull = Mode(modeSwitch, src1, int(length * lengthMult))
HULL = _hull
MHULL = HULL[0]
SHULL = HULL[2]
hullColor = HULL > HULL[2] ? #00ff00 : #ff0000
Fi1 = plot(MHULL, title='MHULL', color=hullColor, linewidth=1, transp=50)
Fi2 = plot(SHULL, title='SHULL', color=hullColor, linewidth=1, transp=50)
fill(Fi1, Fi2, title='Band Filler', color=hullColor, transp=40)
//QQE MOD
RSI_Period = input(6, title='RSI Length', group="QQE MOD")
SF = input(5, title='RSI Smoothing', group="QQE MOD")
QQE = input(3, title='Fast QQE Factor', group="QQE MOD")
ThreshHold = input(3, title='Thresh-hold', group="QQE MOD")
src = input(close, title='RSI Source', group="QQE MOD")
Wilders_Period = RSI_Period * 2 - 1
Rsi = ta.rsi(src, RSI_Period)
RsiMa = ta.ema(Rsi, SF)
AtrRsi = math.abs(RsiMa[1] - RsiMa)
MaAtrRsi = ta.ema(AtrRsi, Wilders_Period)
dar = ta.ema(MaAtrRsi, Wilders_Period) * QQE
longband = 0.0
shortband = 0.0
trend = 0
DeltaFastAtrRsi = dar
RSIndex = RsiMa
newshortband = RSIndex + DeltaFastAtrRsi
newlongband = RSIndex - DeltaFastAtrRsi
longband := RSIndex[1] > longband[1] and RSIndex > longband[1] ? math.max(longband[1], newlongband) : newlongband
shortband := RSIndex[1] < shortband[1] and RSIndex < shortband[1] ? math.min(shortband[1], newshortband) : newshortband
cross_1 = ta.cross(longband[1], RSIndex)
trend := ta.cross(RSIndex, shortband[1]) ? 1 : cross_1 ? -1 : nz(trend[1], 1)
FastAtrRsiTL = trend == 1 ? longband : shortband
length1 = input.int(50, minval=1, title='Bollinger Length', group="QQE MOD")
mult = input.float(0.35, minval=0.001, maxval=5, step=0.1, title='BB Multiplier', group="QQE MOD")
basis = ta.sma(FastAtrRsiTL - 50, length1)
dev = mult * ta.stdev(FastAtrRsiTL - 50, length1)
upper = basis + dev
lower = basis - dev
color_bar = RsiMa - 50 > upper ? #00c3ff : RsiMa - 50 < lower ? #ff0062 : color.gray
QQEzlong = 0
QQEzlong := nz(QQEzlong[1])
QQEzshort = 0
QQEzshort := nz(QQEzshort[1])
QQEzlong := RSIndex >= 50 ? QQEzlong + 1 : 0
QQEzshort := RSIndex < 50 ? QQEzshort + 1 : 0
RSI_Period2 = input(6, title='RSI Length', group="QQE MOD")
SF2 = input(5, title='RSI Smoothing', group="QQE MOD")
QQE2 = input(1.61, title='Fast QQE2 Factor', group="QQE MOD")
ThreshHold2 = input(3, title='Thresh-hold', group="QQE MOD")
src2 = input(close, title='RSI Source', group="QQE MOD")
Wilders_Period2 = RSI_Period2 * 2 - 1
Rsi2 = ta.rsi(src2, RSI_Period2)
RsiMa2 = ta.ema(Rsi2, SF2)
AtrRsi2 = math.abs(RsiMa2[1] - RsiMa2)
MaAtrRsi2 = ta.ema(AtrRsi2, Wilders_Period2)
dar2 = ta.ema(MaAtrRsi2, Wilders_Period2) * QQE2
longband2 = 0.0
shortband2 = 0.0
trend2 = 0
DeltaFastAtrRsi2 = dar2
RSIndex2 = RsiMa2
newshortband2 = RSIndex2 + DeltaFastAtrRsi2
newlongband2 = RSIndex2 - DeltaFastAtrRsi2
longband2 := RSIndex2[1] > longband2[1] and RSIndex2 > longband2[1] ? math.max(longband2[1], newlongband2) : newlongband2
shortband2 := RSIndex2[1] < shortband2[1] and RSIndex2 < shortband2[1] ? math.min(shortband2[1], newshortband2) : newshortband2
cross_2 = ta.cross(longband2[1], RSIndex2)
trend2 := ta.cross(RSIndex2, shortband2[1]) ? 1 : cross_2 ? -1 : nz(trend2[1], 1)
FastAtrRsi2TL = trend2 == 1 ? longband2 : shortband2
QQE2zlong = 0
QQE2zlong := nz(QQE2zlong[1])
QQE2zshort = 0
QQE2zshort := nz(QQE2zshort[1])
QQE2zlong := RSIndex2 >= 50 ? QQE2zlong + 1 : 0
QQE2zshort := RSIndex2 < 50 ? QQE2zshort + 1 : 0
hcolor2 = RsiMa2 - 50 > ThreshHold2 ? color.silver : RsiMa2 - 50 < 0 - ThreshHold2 ? color.silver : na
Greenbar1 = RsiMa2 - 50 > ThreshHold2
Greenbar2 = RsiMa - 50 > upper
Redbar1 = RsiMa2 - 50 < 0 - ThreshHold2
Redbar2 = RsiMa - 50 < lower
//Volume Oscillator
var cumVol = 0.
cumVol += nz(volume)
if barstate.islast and cumVol == 0
runtime.error("No volume is provided by the data vendor.")
shortlen = input.int(5, minval=1, title = "Short Length", group="Volume Oscillator")
longlen = input.int(10, minval=1, title = "Long Length", group="Volume Oscillator")
short = ta.ema(volume, shortlen)
long = ta.ema(volume, longlen)
osc = 100 * (short - long) / long
//strategy
enterLong = ' { "message_type": "bot", "bot_id": 4635591, "email_token": "25byourtefcodeuufyd2-43314-ab98-bjorg224", "delay_seconds": 1} ' //start long deal
ExitLong = ' { "message_type": "bot", "bot_id": 4635591, "email_token": "25byourtefcodeuufyd2-43314-ab98-bjorg224", "delay_seconds": 0, "action": "close_at_market_price"} ' // close long deal market
enterShort = ' { "message_type": "bot", "bot_id": 4635690, "email_token": "25byourtefcodeuufyd2-43314-ab98-bjorg224", "delay_seconds": 1} ' // start short deal
ExitShort = ' { "message_type": "bot", "bot_id": 4635690, "email_token": "25byourtefcodeuufyd2-43314-ab98-bjorg224", "delay_seconds": 0, "action": "close_at_market_price"} ' // close short deal market
longcondition = close > MHULL and HULL > HULL[2] and osc > 0 and Greenbar1 and Greenbar2 and not Greenbar1[1] and not Greenbar2[1]
shortcondition = close < SHULL and HULL < HULL[2] and osc > 0 and Redbar1 and Redbar2 and not Redbar1[1] and not Redbar2[1]
float risk_long = na
float risk_short = na
float stopLoss = na
float takeProfit = na
float entry_price = na
risk_long := risk_long[1]
risk_short := risk_short[1]
swingHigh = ta.highest(high, swingHighV)
swingLow = ta.lowest(low, swingLowV)
if strategy.position_size == 0 and longcondition and inDateRange
risk_long := (close - swingLow) / close
strategy.entry("long", strategy.long, comment="Buy", alert_message=enterLong)
if strategy.position_size == 0 and shortcondition and inDateRange
risk_short := (swingHigh - close) / close
strategy.entry("short", strategy.short, comment="Sell", alert_message=enterShort)
if strategy.position_size > 0
stopLoss := strategy.position_avg_price * (1 - risk_long)
takeProfit := strategy.position_avg_price * (1 + target_stop_ratio * risk_long)
entry_price := strategy.position_avg_price
strategy.exit("long exit", "long", stop = stopLoss, limit = takeProfit, alert_message=ExitLong)
if strategy.position_size < 0
stopLoss := strategy.position_avg_price * (1 + risk_short)
takeProfit := strategy.position_avg_price * (1 - target_stop_ratio * risk_short)
entry_price := strategy.position_avg_price
strategy.exit("short exit", "short", stop = stopLoss, limit = takeProfit, alert_message=ExitShort)
p_ep = plot(entry_price, color=color.new(color.white, 0), linewidth=2, style=plot.style_linebr, title='entry price')
p_sl = plot(stopLoss, color=color.new(color.red, 0), linewidth=2, style=plot.style_linebr, title='stopLoss')
p_tp = plot(takeProfit, color=color.new(color.green, 0), linewidth=2, style=plot.style_linebr, title='takeProfit')
fill(p_sl, p_ep, color.new(color.red, transp=85))
fill(p_tp, p_ep, color.new(color.green, transp=85))
Published by ChaoZhang on the FMZ strategy square · last modified 2023-12-26. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.
Build this with AgenKit
A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Bitcoin-Trading-Strategy-Based-on-Quantitative-Indicators.md- npx agenkit install engineering-kit in your project
- Paste the command into your agent and approve each phase
- Get tests, backtest, risk controls and a paper-trading switch
Understand it first
Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.
Related specs
- EMA and RSI Quantitative Trading StrategyPine Script · Machine learning · 1h
- Triple Moving Average Combined with MACD Quantitative StrategyPine Script · Machine learning · 1d
- Multi indicator Assisted StrategyPine Script · Machine learning · 1d
- ADX EFI 50 Moving Average Channel Pullback StrategyPine Script · Machine learning · 1h
- Bottom Hunter StrategyPine Script · Machine learning · 1d
- MACD Robot Trading StrategyPine Script · Machine learning · 1d