Bitcoin Dollar Cost Averaging Based on BEAM Bands

Author: ChaoZhang, Date: 2024-02-18 15:40:42



This strategy is based on Ben Cowen’s risk level theory and aims to implement a similar approach using BEAM band levels. The upper BEAM level is the 200-week moving average after taking the logarithm, and the lower level is the 200-week moving average itself. This gives us a range from 0 to 1. Buy orders are issued when the price is below the 0.5 level bands, and sell orders are issued when above.

Strategy Logic

The strategy mainly relies on the BEAM band theory proposed by Ben Cowen. According to BTC’s price changes, the price can be divided into 10 areas between 0 and 1, representing 10 different levels of risk. Level 0 represents the lowest risk with price close to the 200-week moving average. Level 5 represents the medium-risk value area. Level 10 represents the highest risk with price approaching the upper rail.

When the price falls to the lows, the strategy will gradually increase the long position. Specifically, if the price is between bands 0 and 0.5, buy orders will be issued on a set day each month. The buy amount will gradually increase as the band number decreases. For example, with band 5 the buy amount is 20% of the monthly DCA total. With band 1, the buy amount rises to 100% of the monthly DCA total.

When prices rise to highs, the strategy will gradually reduce its position. Specifically, if the price exceeds band 0.5, sell orders will be issued in proportion. The sell position will gradually increase as the band number increases. For example with band 6, 6.67% will be sold. With band 10, all positions will be sold.

Advantage Analysis

The biggest advantage of this BEAM band DCA strategy is that it fully utilizes the volatility characteristics of BTC trading by bottom fishing when prices fall to their lowest and profit taking when prices rise to their peaks. This approach will not miss any buying or selling opportunities. The specific advantages can be summarized as follows:

  1. Use BEAM theory to judge asset underestimation and scientifically avoid risks;
  2. Make full use of BTC volatility characteristics to catch the best buying and selling opportunities;
  3. Adopt the cost averaging method to effectively control investment costs and obtain long-term stable returns;
  4. Automatically execute buy and sell transactions without manual intervention to reduce operational risks;
  5. Customizable parameters allow flexible adjustment of strategies to adapt to market changes;

In summary, this is a sophisticated parameter tuning strategy that can generate long-term steady returns in fluctuating BTC market conditions.

Risk Analysis

Although the BEAM band DCA strategy has many advantages, there are still some potential risks to be aware of. The main risk points can be summarized as follows:

  1. BEAM theory and parameter settings rely on subjective judgments, which have some probability of misjudgment;
  2. BTC trends are difficult to predict and there is risk of losses;
  3. Automatic trading can be adversely affected by system failures and parameter hacking;
  4. Excessive fluctuations may lead to expanded losses.

To mitigate risks, the following measures can be taken:

  1. Optimize parameter settings to improve BEAM theory judgment accuracy;
  2. Appropriately reduce position size to decrease single loss amount;
  3. Increase redundancy and fault tolerance capabilities to reduce operational risks for automated trading;
  4. Set stop loss points to avoid excessively large single losses.


In view of the above risks, optimization of this strategy may focus on:

  1. Optimize BEAM theory parameters: adjust log parameters, backtest cycle, etc. to improve model accuracy;
  2. Optimize position control: adjust monthly DCA amount, buy/sell ratios to control single loss amount;
  3. Increase automated trading security: set redundant servers, local processing, etc. to improve fault tolerance;
  4. Add a stop loss module: set reasonable stop loss points based on historical volatility to effectively control losses.

Through these measures, the stability and security of the strategy can be greatly improved.


The BEAM band DCA average cost strategy is a highly practical quantitative trading strategy. It successfully leverages BEAM theory to guide trading decisions, supplemented by a cost averaging model to control buying costs. At the same time, it pays attention to risk management by setting stop loss points to prevent loss expansion. With parameter optimization and modular additions, this strategy can become an important tool for quantitative trading to obtain long-term steady returns from the BTC market. It deserves further research and application by quantitative trading practitioners.

start: 2023-02-11 00:00:00
end: 2024-02-17 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]

// © gjfsdrtytru - BEAM DCA Strategy {
// Based on Ben Cowen's risk level strategy, this aims to copy that method but with BEAM band levels.
// Upper BEAM level is derived from ln(price/200W MA)/2.5, while the 200W MA is the floor price. This is our 0-1 range. 
// Buy limit orders are set at the < 0.5 levels and sell orders are set at the > 0.5 level.
  title                 = "BEAM DCA Strategy Monthly", 
  shorttitle            = "BEAM DCA M",
  overlay               = true,
  pyramiding            = 500,
  default_qty_type      = strategy.percent_of_equity,
  default_qty_value     = 0,
  initial_capital       = 0) //}

// Inputs { ————————————————————————————————————————————————————————————————————
T_ceiling   = input.string("Off", "Diminishing Returns", ["Off","Linear","Parabolic"], "Account for diminishing returns as time increases")
day         =, "DCA Day of Month",1,28,1,"Select day of month for buy orders.")
DCAamount   =,"DCA Amount",400,tooltip="Enter the maximum amount you'd be willing to DCA for any given month.")
T_buy       = input(true,"Buy Orders","Toggle buy orders.")
T_sell      = input(true,"Sell Orders","Toggle sell orders.")

// Time period
testStartYear   =,   title="Backtest Start Year",    minval=2010,maxval=2100,group="Backtest Period")
testStartMonth  =,      title="Backtest Start Month",   minval=1,   maxval=12,  group="Backtest Period")
testStartDay    =,      title="Backtest Start Day",     minval=1,   maxval=31,  group="Backtest Period")
testPeriodLen   =,   title="Backtest Period (days)", minval=1,               group="Backtest Period",tooltip="Days until strategy ends") * 86400000 // convert days into UNIX time
testPeriodStart = timestamp(testStartYear,testStartMonth,testStartDay,0,0)
testPeriodStop  = testPeriodStart + testPeriodLen
testPeriod() => true
// ——————————————————————————————————————————————————————————————————————————— }
// Diminishing Returns { ———————————————————————————————————————————————————————
x = bar_index + 1
assetDivisor= 2.5
    T_ceiling == "Linear"   => assetDivisor:= 3.50542 - 0.000277696 * x
    T_ceiling == "Parabolic"=> assetDivisor:= -0.0000001058992338 * math.pow(x,2) + 0.000120729 * x + 3.1982
// ——————————————————————————————————————————————————————————————————————————— }
// Risk Levels { ———————————————————————————————————————————————————————————————
cycleLen = 1400
getMaLen() =>
    if bar_index < cycleLen
        bar_index + 1

// Define Risk Bands
price       = close
riskLow     = ta.sma(price,getMaLen())
risk1       = riskLow * math.exp((assetDivisor)*0.1)
risk2       = riskLow * math.exp((assetDivisor)*0.2)
risk3       = riskLow * math.exp((assetDivisor)*0.3)
risk4       = riskLow * math.exp((assetDivisor)*0.4)
risk5       = riskLow * math.exp((assetDivisor)*0.5)
risk6       = riskLow * math.exp((assetDivisor)*0.6)
risk7       = riskLow * math.exp((assetDivisor)*0.7)
risk8       = riskLow * math.exp((assetDivisor)*0.8)
risk9       = riskLow * math.exp((assetDivisor)*0.9)
riskHigh    = riskLow * math.exp((assetDivisor))

// Plot Risk Bands
p_low       = plot(riskLow,   "Beam Risk 0.0",,50),3,editable=false)
p_band1     = plot(risk1,     "Beam Risk 0.1",,20),1,editable=false)
p_band2     = plot(risk2,     "Beam Risk 0.2",,20),1,editable=false)
p_band3     = plot(risk3,     "Beam Risk 0.3",,20),1,editable=false)
p_band4     = plot(risk4,     "Beam Risk 0.4",,20),1,editable=false)
p_band5     = plot(risk5,     "Beam Risk 0.5",,50),3,editable=false)
p_band6     = plot(risk6,     "Beam Risk 0.6",,20),1,editable=false)
p_band7     = plot(risk7,     "Beam Risk 0.7",,20),1,editable=false)
p_band8     = plot(risk8,     "Beam Risk 0.8",,20),1,editable=false)
p_band9     = plot(risk9,     "Beam Risk 0.9",,20),1,editable=false)
p_band10    = plot(riskHigh,  "Beam Risk 1.0",,50),3,editable=false)
// ——————————————————————————————————————————————————————————————————————————— }
// Order Execution { ———————————————————————————————————————————————————————————
band5   = price<risk5 and price>risk4
band4   = price<risk4 and price>risk3
band3   = price<risk3 and price>risk2
band2   = price<risk2 and price>risk1
band1   = price<risk1

// DCA buy order weights
y       = DCAamount / 5
    band5 => y:= y * 1
    band4 => y:= y * 2
    band3 => y:= y * 3
    band2 => y:= y * 4
    band1 => y:= y * 5

// Contracts per order
contracts =(y/price)

if testPeriod()
// Buy orders
    if T_buy == true
        if dayofmonth == day
            strategy.entry("Risk Band 5",strategy.long,qty=contracts,when=band5)
            strategy.entry("Risk Band 4",strategy.long,qty=contracts,when=band4)
            strategy.entry("Risk Band 3",strategy.long,qty=contracts,when=band3)
            strategy.entry("Risk Band 2",strategy.long,qty=contracts,when=band2)
            strategy.entry("Risk Band 1",strategy.long,qty=contracts,when=band1)
// Sell orders 
    if T_sell == true
        if strategy.opentrades > 5
            strategy.exit("Risk Band 6",qty_percent=6.67,limit=risk6) 
            strategy.exit("Risk Band 7",qty_percent=14.28,limit=risk7)
            strategy.exit("Risk Band 8",qty_percent=25.00,limit=risk8)
            strategy.exit("Risk Band 9",qty_percent=44.44,limit=risk9)
            strategy.exit("Risk Band 10",qty_percent=100,limit=riskHigh)
// ——————————————————————————————————————————————————————————————————————————— }
// Info { ——————————————————————————————————————————————————————————————————————

// Line plot of avg. entry price
plot(strategy.position_size > 0 ? strategy.position_avg_price : na,"Average Entry",,trackprice=true,editable=false)

// Unrealised PNL
uPNL = price/strategy.position_avg_price

// Realised PNL
realPNL = 0.
for i = 0 to strategy.closedtrades-1
    realPNL += strategy.closedtrades.profit(i)

// Size of open position in ($)
openPosSize = 0.
for i = 0 to strategy.opentrades-1
    openPosSize += strategy.opentrades.size(i) * strategy.position_avg_price

// Size of closed position in ($)
closePosSize = 0.
if strategy.closedtrades > 0
    for i = 0 to strategy.closedtrades-1
        closePosSize += strategy.closedtrades.size(i) * strategy.closedtrades.entry_price(i)

invested    = openPosSize+closePosSize                              // Total capital ($) put into strategy
equity      = openPosSize+closePosSize+strategy.openprofit+realPNL  // Total current equity ($) in strategy (counting realised PNL)
ROI         = (equity-invested) / invested * 100                    // ROI of strategy (compare capital invested to excess return)

// // Info Table
// var table table1 =,2,9,,color.gray,1,color.gray,2)

// table.cell(table1,0,0,"Capital Invested",   text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,1,"Open Position",      text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,2,"Average Entry",      text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,3,"Last Price",         text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,4,"Open PNL (%)",       text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,5,"Open PNL ($)",       text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,6,"Realised PNL ($)",   text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,7,"Total Equity",       text_color=color.white,text_halign=text.align_right)
// table.cell(table1,0,8,"Strategy ROI",       text_color=color.white,text_halign=text.align_right)

// table.cell(table1,1,0,"$" + str.tostring(invested,                      "#,###.00"),      text_halign=text.align_right,text_color = color.white)
// table.cell(table1,1,1,"$" + str.tostring(openPosSize,                   "#,###.00"),      text_halign=text.align_right,text_color = color.white)
// table.cell(table1,1,2,"$" + str.tostring(strategy.position_avg_price,   "#,###.00"),      text_halign=text.align_right,text_color = color.white)
// table.cell(table1,1,3,"$" + str.tostring(price,                         "#,###.00"),      text_halign=text.align_right,text_color = color.white)
// table.cell(table1,1,4,      str.tostring((uPNL-1)*100,                  "#,###.00") + "%",text_halign=text.align_right,text_color = uPNL > 1 ? color.lime :
// table.cell(table1,1,5,"$" + str.tostring(strategy.openprofit,           "#,###.00"),      text_halign=text.align_right,text_color = uPNL > 1 ? color.lime :
// table.cell(table1,1,6,"$" + str.tostring(realPNL,                       "#,###.00"),      text_halign=text.align_right,text_color = color.white)
// table.cell(table1,1,7,"$" + str.tostring(equity,                        "#,###.00"),      text_halign=text.align_right,text_color = color.white)
// table.cell(table1,1,8,      str.tostring(ROI,                           "#,###.00") + "%",text_halign=text.align_right,text_color = ROI > 1 ? color.lime :
// // ——————————————————————————————————————————————————————————————————————————— }