Strategy · Pine Script v4 · On the chart

DCA Bot Backtest

by Metzenas

A dollar-cost averaging strategy template designed to simulate algorithmic DCA trading bots.

View on GitHub

GitHub stars2on the repository
Pine versionv4older, still runs
Lines of code197
Settings13inputs
LicenceNone statedfile: MPL-2.0
Last update5 years ago8 Apr 2021

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

197 lines of Pine Script v4 from utils/dca_strategy_template.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

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

// @version=4

////////////////////////////////
// STRATEGY TO TEST
///////////////////////////////

// Paste your strategy to test in this section and assign the deal start and stop condition variables


// Set dealStartCondition to your buy condition and dealStopCondition to your sell condition
bool dealStartCondition = true
bool dealStopCondition = na


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////
// DCA STRATEGY CODE
///////////////////////////////
strategy("DCA Bot Backtest", shorttitle="DCA Bot Backtest", 
    format=format.price, 
    overlay=true, 
    default_qty_type=strategy.cash, 
    default_qty_value=100, 
    commission_type=strategy.commission.percent, 
    commission_value=.1, 
    slippage=0, 
    calc_on_order_fills=false, 
    initial_capital=1000, 
    currency=currency.NONE)

// Input variables
botType = input(title="Bot Type", defval="Long", options=["Long", "Short"])
baseOrder = input(title="Base Order Size", type=input.float, minval=0, defval=100)
safetyOrder = input(title="Safety Order Size", type=input.float, minval=0, defval=200)
maxSafetyOrders = input(title="Max safety order count", type=input.float, minval=1, defval=5)
priceDeviation = input(title="Price deviation in % to open safety orders", type=input.float, step=0.1, minval=0.1, defval=3) // the less the more profit
volumeScaleInput = input(title="Safety Order volume scale", type=input.float, step=0.1, minval=0.1, defval=1.1) // the more the more profit
safetyOrderStepScale = input(title="Safety Order step scale", type=input.float, step=0.1, minval=0.1, defval=1.2) // the more the less profit
targetProfit = input(title="Target profit (%)", type=input.float, step=0.1, minval=0.1, defval=2)
startTime = input(title="Start Time", defval=timestamp("1 Jan 2021"), type=input.time)
endTime = input(title="End Time", defval=timestamp("1 Jan 2022"), type=input.time)

dealStartType = input(title="Deal Start Condition", defval="ASAP", options=["ASAP", "Script", "Indicator"])
sourceDealStart = input(title="Deal Start Source", defval=close, type=input.source)
sourceDealStartValue = input(title="Deal Start Value", defval=0, type=input.float)
// Sadly you can't have multiple source inputs, so I can't get the deal close condition from another indicator :/


if (dealStartType == "ASAP")
    dealStartCondition := true
else if (dealStartType == "Indicator")
    dealStartCondition := not na(sourceDealStart) and sourceDealStart >= sourceDealStartValue

bool isLongBot = botType == "Long"

window()  => time >= startTime and time <= endTime ? true : false

// Declaring key DCA variables
var float entry_price = 0.0
entry_price := na(entry_price[1]) ? na : entry_price[1]
var float baseOrderPrice = 0.0

//main_stop = strategy.position_avg_price - strategy.position_avg_price*(D_sl/100)
float main_takeprofit = 0
if (isLongBot)
    main_takeprofit := strategy.position_avg_price + strategy.position_avg_price*(targetProfit/100)
else
    main_takeprofit := strategy.position_avg_price - strategy.position_avg_price*(targetProfit/100)
slCondition = false//close <= main_stop
exitConditions = dealStopCondition or slCondition
var float firstBaseOrderPrice = na



///////////////////////////
// UTILITY FUNCTIONS
///////////////////////////
// safety order calculation functions
calcSafetySize(i) => safetyOrder*(pow(volumeScaleInput, i-1))
calcPriceDeviation(index) =>
    var float totalDeviation = 0.0 
    totalDeviation := 0
    for i = 0 to (index-1)
        totalDeviation := totalDeviation + priceDeviation*pow(safetyOrderStepScale,i)
calcSafetyPrice(index) =>
    if (isLongBot)
        baseOrderPrice - (baseOrderPrice * (calcPriceDeviation(index)/100))
    else
        baseOrderPrice + (baseOrderPrice * (calcPriceDeviation(index)/100))




///////////////////////////
// ORDER PLACING
//////////////////////////
// place base order
if(dealStartCondition and strategy.opentrades == 0 and window()) 
    entry_price := close
    if (isLongBot)
        strategy.order('DCA Order', true, qty=baseOrder/entry_price, limit=entry_price, when = window(), comment="Base Order")
    else
        strategy.order('DCA Order', false, qty=baseOrder, limit=entry_price, when = window(), comment="Base Order")
    baseOrderPrice := entry_price
    if (na(firstBaseOrderPrice))
        firstBaseOrderPrice := close

    for i = 1 to maxSafetyOrders
        nextEntryPrice = calcSafetyPrice(i)
        if (isLongBot)
            strategy.order('DCA Order'+tostring(i), true, qty=calcSafetySize(i)/nextEntryPrice, limit=nextEntryPrice, when = window(), comment="Safety Order")
        else
            strategy.order('DCA Order'+tostring(i), false, qty=calcSafetySize(i), limit=nextEntryPrice, when = window(), comment="Safety Order")
        entry_price := nextEntryPrice


if (strategy.opentrades >= 1)
    if (isLongBot)
        // add sell order for whole position and update target price
        strategy.order('DCA Sell', false, qty=strategy.position_size, limit=main_takeprofit, comment="DCA Sell")
    else 
        strategy.order('DCA Buy', true, qty=abs(strategy.position_size), limit=main_takeprofit, comment="DCA Buy")
        
      
// cancel all open orders if we just closed all our open trades
if ((strategy.opentrades[1] > 0 and strategy.opentrades == 0) or exitConditions)
    strategy.close_all()
    strategy.cancel_all()
    baseOrderPrice := 0.0
    entry_price := 0
        

    

/////////////////////////////////
// DCA PLOTTING
/////////////////////////////////
shouldPlot = strategy.opentrades > 0 and not exitConditions
col = (shouldPlot[1] and shouldPlot) ? color.white : color.new(color.white, 100)
avePlot = plot(strategy.position_avg_price, title="Average Buy Price", color=col, linewidth=2)

desiredSafetyCol = strategy.opentrades[1] > maxSafetyOrders ? color.orange : color.red
safetyCol = (shouldPlot[1] and shouldPlot) ? desiredSafetyCol : color.new(color.white, 100)
nextSafetyPrice = calcSafetyPrice(min(strategy.opentrades, maxSafetyOrders))
safetyPlot = plot(nextSafetyPrice, title="Next Safety Order", color=safetyCol, linewidth=2)

tpCol = (shouldPlot[1] and shouldPlot) ? color.green : color.new(color.white, 100)
tpPlot = plot(main_takeprofit, title="Take Profit", color=tpCol, linewidth=2)

fill(tpPlot, avePlot, color=color.green, transp=80) 
fill(avePlot, safetyPlot, color=color.red, transp=80)



//////////////////////////////
// RESULTS LABEL 
//////////////////////////////
// label to hold the results of the backtest
var label  _resultsLabel    = na
var label  _instructionsLabel = na
var string _labelText      = na
var string _title = " RESULTS\n------------\n\n"
var string _maxUsageText = na
var float  _maxUsage = baseOrder

// -- build our label text
if (na(_labelText))
    for i = 1 to maxSafetyOrders
        _maxUsage := _maxUsage + calcSafetySize(i)
    _maxUsageText := "Max amount for bot usage: " + tostring(_maxUsage, "###,###.########")
   
    

requiredInitialCapital = _maxUsage
if (not isLongBot)
    requiredInitialCapital := requiredInitialCapital * firstBaseOrderPrice


_labelText := _title 
     + "Profit Percentage: " + tostring(strategy.netprofit / requiredInitialCapital, "#.##%") + "\n"
     + "Total Profit: " + tostring(strategy.netprofit, "###,###.########") + "\n"
     + _maxUsageText + "\n"
     + "Covered Deviation: " + tostring(calcPriceDeviation(maxSafetyOrders)/100, "#.##%") + "\n"
     + "Required Initial Capital: " + tostring(requiredInitialCapital, "###,###.########") + "\n"
     + "\n--------------------------------\n"
     + "Please adjust your initial capital amount to match the \n'Required Initial Capital' amount to get accurate tester results"

f_offsetTime(offset) => time + (time - time[1]) * offset

if (na(_resultsLabel))
    _resultsLabel := label.new(time,close,text=_labelText,xloc=xloc.bar_time,color=color.black, style=label.style_label_left,textcolor=color.white, textalign=text.align_left, size=size.normal)
label.set_xy(_resultsLabel,f_offsetTime(4), high)
label.set_text(_resultsLabel, _labelText)

Overview

DCA Bot Backtest initiates a base order immediately or based on indicator inputs, then scales into positions with safety orders as price deviates against the entry, applying configurable volume and step scale multipliers. Exits occur when the position reaches a defined target profit percentage calculated from the average position price. It plots directly on the price chart. Stop-loss logic is disabled by default, meaning deals rely on price recovery to exit.

Details

Published by

Metzenas on GitHub

Type

Strategy, Pine Script v4

Draws

On the price chart

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • The strategy can add several entries in the same direction
  • The strategy enters trades without a stop loss

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Bot TypestringLongbotType
Base Order Sizefloat100baseOrder
Safety Order Sizefloat200safetyOrder
Max safety order countfloat5maxSafetyOrders
Price deviation in % to open safety ordersfloat3priceDeviation
Safety Order volume scalefloat1.1volumeScaleInput
Safety Order step scalefloat1.2safetyOrderStepScale
Target profit (%)float2targetProfit
Start Timetimetimestamp("1 Jan 2021")startTime
End Timetimetimestamp("1 Jan 2022")endTime
Deal Start ConditionstringASAPdealStartType
Deal Start SourcesourceclosesourceDealStart
Show all 13 settings
SettingTypeDefaultVariable
Deal Start Valuefloat0sourceDealStartValue
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

330 stars

Alert BB + RSI [Alorse] is a multi-symbol mean-reversion strategy designed for cryptocurrency trading. It evaluates Bollinger Bands and the Relative Strength Index (RSI) across up to 20 user-defined ticker symbols simultaneously. The strategy enters long posit

Strategy v4 Oscillators & momentumVolatility & bands
Updated 2 months ago · None stated GitHub repository of Alert BB + RSI [Alorse]
285 stars

Mean Reversion Strategy is an overlay strategy that identifies pullbacks using a long-term EMA, RSI, and candlestick patterns. It generates long signals when a bullish engulfing candle forms below the EMA while RSI is oversold, and short signals when a bearish

Strategy v4 Mean reversionCandlestick patterns
Updated 3 years ago · None stated GitHub repository of Mean Reversion Strategy

STRG One Bar Pursuit

by harryguiacorn
220 stars

The STRG One Bar Pursuit strategy identifies single-bar momentum reversals by monitoring shifts between bullish and bearish candlesticks starting from the year 2024. It enters a long position when a bullish candle follows a bearish candle and enters a short po

Strategy v5 Candlestick patternsMean reversion
Updated 1 year ago · None stated GitHub repository of STRG One Bar Pursuit

This strategy draws on the price chart and combines Stochastic RSI, the Money Flow Index, and an exponential moving average to open long or short trades. Longs use the lower oscillator thresholds and shorts use the upper ones, with direction limited to longs,

Strategy v5 Oscillators & momentumMoving averages Repaints
Updated 2 years ago · GPL-3.0 GitHub repository of 3 Indicator Strategy (StochRSI, MFI & EMA) With Safety Orders

Bollinger Bands Strategy

by cklose2000
105 stars

This strategy generates buy and sell signals based on Bollinger Band crossovers combined with a simple moving average trend filter. It enters a long position when price crosses above the lower band while trading above the SMA filter line, and enters a short po

Strategy v5 Volatility & bandsMoving averages
Updated 1 year ago · None stated GitHub repository of Bollinger Bands Strategy

More Mean reversion scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers