0bps

Strategy vault / Machine learning

Dual Moving Average Matching Strategy Based on Bollinger Bands

The dual moving average matching strategy based on Bollinger Bands is a trend-following strategy that runs with price and volume in the market. It uses the crossover of Bollinger Bands and moving averages as trading…

Author
ChaoZhang
Complexity
Intermediate · 88 lines
Parameters
9
Backtest bar
1h · intraday
Tested on
BTC/USDT · futures

IMG

Overview

The dual moving average matching strategy based on Bollinger Bands is a trend-following strategy that runs with price and volume in the market. It uses the crossover of Bollinger Bands and moving averages as trading signals to implement a quantitative strategy that can automatically identify market trends and trade with stop profit and stop loss rules.

Strategy Principle

This strategy is mainly based on the crossover signals of the Bollinger Bands indicator and the moving average indicator for trading. Specifically, it uses the middle rail, upper rail of Bollinger Bands, and 7 moving averages with lengths from 5 to 200 days at the same time. It generates a buy signal when the price breaks through the middle and lower rails of the Bollinger Bands from bottom to top; it generates a sell signal when the price breaks through the upper rail of the Bollinger Bands from top to bottom to achieve trend following.

In addition, the strategy also introduces the moveToFract indicator for judging long and short positions. This indicator determines whether the current market trend is up or down by calculating the order of arrangement of short-term and long-term moving averages, thus avoiding generating wrong signals in range-bound markets. Finally, combined with configurable stop profit and stop loss rules, it forms a more complete trend following trading strategy.

Advantage Analysis

  1. Flexible configuration that can customize parameter combinations to suit different market environments
  2. Combining two different indicators as filters can reduce erroneous signals
  3. Trend judging indicator can avoid reverse operations in volatile markets
  4. Tracking stop loss setting maximizes profits

Risk Analysis

  1. Parameters should be adjusted appropriately to suit cycles of different timeframes to avoid over-trading
  2. Tracking stop loss may expand losses in a fast decline
  3. Must ensure sufficient funds, otherwise unable to withstand the risk of continuous losses

Optimization Directions

  1. Add golden cross and death cross judgments to further optimize
  2. Different varieties have different parameters, consider machine learning for optimal parameters
  3. Combine with volatility index to determine trend volatility and strengthen risk control

Conclusion

In general, this is a very practical trend following strategy. It uses indicator crossover for decision making, and also incorporates a trend judging module to effectively filter out wrong signals. After configuring stop profit and stop loss, it can fully follow trends for trading and obtain good returns. By adjusting parameter combinations and adding more filters, this strategy can be further optimized to adapt to more market environments, and has great room for improvement and application prospects.

Parameters

NameDefaultWhat it controls
v_input_10Moving Average Type: sma | ema | hma | rma | vwma | wma
v_input_20Exit Strategy: Signal | TrailingStop | Both
v_input_330LookbackPeriod
v_input_42BBStdDev
v_input_560BBLength
v_input_622atrLength
v_input_76atrMult
v_input_80Trade Direction: strategy.direction.all | strategy.direction.long | strategy.direction.short
v_input_910backtestYears

Source Pine Script

/*backtest
start: 2023-10-24 00:00:00
end: 2023-11-23 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed

//@version=4
strategy("BuyTheDip", overlay=true, initial_capital = 100000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.percent, pyramiding = 1, commission_value = 0.01, calc_on_order_fills = true)

MAType = input(title="Moving Average Type", defval="sma", options=["ema", "sma", "hma", "rma", "vwma", "wma"])
exitType = input(title="Exit Strategy", defval="Signal", options=["Signal", "TrailingStop", "Both"])
LookbackPeriod = input(30, minval=10,step=10)

BBStdDev = input(2, minval=1, maxval=10, step=0.5)
BBLength = input(60, minval=5, step=5)

atrLength = input(22)
atrMult = input(6)

tradeDirection = input(title="Trade Direction", defval=strategy.direction.all, options=[strategy.direction.all, strategy.direction.long, strategy.direction.short])
backtestYears = input(10, minval=1, step=1)
includePartiallyAligned = true
f_getMovingAverage(source, MAType, length)=>
    ma = sma(source, length)
    if(MAType == "ema")
        ma := ema(source,length)
    if(MAType == "hma")
        ma := hma(source,length)
    if(MAType == "rma")
        ma := rma(source,length)
    if(MAType == "vwma")
        ma := vwma(source,length)
    if(MAType == "wma")
        ma := wma(source,length)
    ma

f_getTrailingStop(atr, atrMult)=>
    stop = close - atrMult*atr
    stop := strategy.position_size > 0 ? max(stop, stop[1]) : stop
    stop

f_getMaAlignment(MAType, includePartiallyAligned)=>
    ma5 = f_getMovingAverage(close,MAType,5)
    ma10 = f_getMovingAverage(close,MAType,10)
    ma20 = f_getMovingAverage(close,MAType,20)
    ma30 = f_getMovingAverage(close,MAType,30)
    ma50 = f_getMovingAverage(close,MAType,50)
    ma100 = f_getMovingAverage(close,MAType,100)
    ma200 = f_getMovingAverage(close,MAType,200)

    upwardScore = 0
    upwardScore := close > ma5? upwardScore+1:upwardScore
    upwardScore := ma5 > ma10? upwardScore+1:upwardScore
    upwardScore := ma10 > ma20? upwardScore+1:upwardScore
    upwardScore := ma20 > ma30? upwardScore+1:upwardScore
    upwardScore := ma30 > ma50? upwardScore+1:upwardScore
    upwardScore := ma50 > ma100? upwardScore+1:upwardScore
    upwardScore := ma100 > ma200? upwardScore+1:upwardScore
    
    upwards = close > ma5 and ma5 > ma10 and ma10 > ma20 and ma20 > ma30 and ma30 > ma50 and ma50 > ma100 and ma100 > ma200
    downwards = close < ma5 and ma5 < ma10 and ma10 < ma20 and ma20 < ma30 and ma30 < ma50 and ma50 < ma100 and ma100 < ma200
    upwards?1:downwards?-1:includePartiallyAligned ? (upwardScore > 5? 0.5: upwardScore < 2?-0.5:upwardScore>3?0.25:-0.25) : 0
    
inDateRange = time >= timestamp(syminfo.timezone, year(timenow) - backtestYears, 01, 01, 0, 0)

exitBySignal = exitType == "Signal" or exitType == "Both"
exitByTrailingStop = exitType == "TrailingStop" or exitType == "Both"
maAlignment = f_getMaAlignment(MAType,includePartiallyAligned)
atr = atr(atrLength)

trailingStop = f_getTrailingStop(atr, atrMult)
maAligned = highest(maAlignment,LookbackPeriod)
[middle, upper, lower] = bb(close, BBLength, BBStdDev)

buyCondition = maAligned == 1 and (crossover(close, lower) or crossover(close, middle))
buyExitCondition = crossunder(close, upper)

strategy.entry("Buy", strategy.long, when=buyCondition and inDateRange, oca_name="oca_buy")
strategy.close("Buy", when=buyExitCondition and exitBySignal)
strategy.exit("ExitBuy", "Buy", stop = trailingStop, when=exitByTrailingStop )


Published by ChaoZhang on the FMZ strategy square · last modified 2023-11-24. 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.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Dual-Moving-Average-Matching-Strategy-Based-on-Bollinger-Bands.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. 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

esc

Try , , or .

↑↓ move↵ open