Strategy · Pine Script v4 · On the chart

Backtesting 3commas DCA Bot v2

by hasnocool· code credited to rouxam

This strategy simulates a 3Commas dollar-cost averaging (DCA) bot directly on the price chart.

View on GitHub

GitHub stars106on the repository
Pine versionv4older, still runs
Lines of code514
Settings18inputs
LicenceGPL-3.0
Last update2 years ago18 Sep 2024

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

514 lines of Pine Script v4 from Backtesting 3commas DCA Bot v2.pine, published under GPL-3.0. Keep the author's credit and licence lines when you reuse it.

Backtesting 3commas DCA Bot v2.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © rouxam
//@version=4
// Author: rouxam
strategy("Backtesting 3commas DCA Bot v2", overlay=true, pyramiding=99, process_orders_on_close=true, commission_type=strategy.commission.percent, commission_value=0.1, initial_capital=3009)
// Important Note : Initial Capital above is written to match the default value based on bot parameters.
//                  It is expected the user will update the initial_capital via the GUI Parameters > Properties tab
//                  A warning will displayed in case initial capital and max amount for bot usage do not match.
// ----------------------------------
// Strategy Inputs
// ----------------------------------
take_profit             = input(4.5, type=input.float,  title="Take Profit (%)", minval=0.0, step=0.1)/100
take_profit_type        = input(defval="% from total volume", title="Take Profit Type", options=["% from total volume", "% from base order"])
ttp                     = input(0.0, type=input.float,  title="Trailing Take Profit (%) [0 = Disabled]", minval=0.0, step=0.1)/100
stop_loss               = input(0.0, type=input.float,  title="Stop Loss (%) [0 = Disabled]", minval=0.0, step=0.1)/100
order_size_type         = input(defval="Fixed",         title="Order Size Type", options=["Fixed", "% of equity"])
base_order              = input(100.0, type=input.float,title="Base Order") 
safe_order              = input(140.0, type=input.float,title="Safety Order") 
max_safe_order          = input(6,                      title="Max Safety Trades Count", minval=0, maxval=99, step=1) 
price_deviation         = input(1.5, type=input.float,  title="Price deviation to open safety order (%)", minval=0.0, step=0.1)/100
safe_order_volume_scale = input(1.5, type=input.float,  title="Safety Order Volume Scale", step=0.1) 
safe_order_step_scale   = input(1.6, type=input.float,  title="Safety Order Step Scale", step=0.1) 
dsc                     = input(defval="RSI-7",         title="Deal Start Condition", options=["Start ASAP", "RSI-7", "TV presets"])
dsc_rsi_threshold       = input(defval=30,              title="[RSI-7 only] RSI Threshold", minval=1, maxval=99)
dsc_technicals          = input(defval="Strong",        title="[TV presets only] Strength", options=["Strong", "Weak"])
dsc_res                 = input("",                     title="[RSI-7 and TV presets Only] Indicator Timeframe", type=input.resolution)
bot_direction           = input(defval="Long",          title="Bot Direction", options=["Long", "Short"])
start_time              = input(defval=timestamp("15 June 2021 06:00"),   title="Start Time",     type=input.time)
end_time                = input(defval=timestamp("31 Dec 2021 20:00"),   title="End Time",       type=input.time)
// ----------------------------------
// Declarations
// ----------------------------------
var bo_level = 0.0
var last_so_level = 0.0
var so_level = 0.0
var ttp_active = false
var ttp_extremum = 0.0
var ttp_level = 0.0
var stop_level = 0.0
var take_profit_level = 0.0
var deal_counter = 0
var stop_loss_counter = 0
var stop_loss_on_bar = false
var latest_price = close
var deal_start_condition = false
var start_time_actual = start_time
var end_time_actual = start_time
// CONSTANTS
var float init_price = na
var IS_LONG = bot_direction == "Long"
// ----------------------------------
// Utilities functions
// ----------------------------------
pretty_date(t) => tostring(dayofmonth(t)) + "/" + tostring(month(t)) + "/" + tostring(year(t))
within_window() => time >= start_time and time <= end_time
base_order_size() => order_size_type == "Fixed" ? base_order : base_order/100 * strategy.equity
safe_order_size() => order_size_type == "Fixed" ? safe_order : safe_order/100 * strategy.equity
safety_order_deviation(index) => price_deviation * pow(safe_order_step_scale,  index - 1)
safety_order_price(index, last_safety_order_price) => 
    if IS_LONG
        last_safety_order_price * (1 - safety_order_deviation(index))
    else
        last_safety_order_price * (1 + safety_order_deviation(index))
safety_order_qty(index) => safe_order_size() * pow(safe_order_volume_scale, index - 1)
max_amount_for_bot_usage() =>
    var total_qty = 0.0
    var last_order_qty = 0.0
    total_qty := base_order_size()
    last_order_qty := safe_order_size()
    if max_safe_order > 0
        for index = 1 to max_safe_order
            total_qty := total_qty + safety_order_qty(index)
    total_qty // returned value
max_deviation() =>
    var total_deviation = 0.0
    total_deviation := 0.0
    for index = 1 to max_safe_order
        total_deviation := total_deviation + safety_order_deviation(index)
currency_format() =>
    if syminfo.currency == "USDT" or syminfo.currency == "USD" or syminfo.currency == "TUSD" or syminfo.currency == "BUSD" or syminfo.currency == "USDC" or syminfo.currency == "EUR" or syminfo.currency == "AUD"
        "#.##"
    else if syminfo.currency == "BTC"
        "#.########"
    else
        // TODO (rouxam) list more options
        "#.####"
// ***********************************
// Deal Start Condition Strategies
// ***********************************
// RSI-7 
// ***********************************
rsi_signal() =>
    // Regular strat would be crossover but not for 3C DCA Bot
    rsi7 = rsi(close, 7)
    if IS_LONG
        [rsi7 < dsc_rsi_threshold, close]
    else
        [rsi7 > dsc_rsi_threshold, close]
// TV presets
// ***********************************
// This whole section is from the TradingView "Technical Ratings" code.
// Adding the Technical Ratings "indicator" as input to this "strategy" is not sufficient for our purpose,
// Therefore the code is copy-pasted.
// ***********************************
// Awesome Oscillator
AO() => 
    sma(hl2, 5) - sma(hl2, 34)
// Stochastic RSI
StochRSI() =>
    rsi1 = rsi(close, 14)
    K = sma(stoch(rsi1, rsi1, rsi1, 14), 3)
    D = sma(K, 3)
    [K, D]
// Ultimate Oscillator
tl() => close[1] < low ? close[1]: low
uo(ShortLen, MiddlLen, LongLen) =>
    Value1 = sum(tr, ShortLen)
    Value2 = sum(tr, MiddlLen)
    Value3 = sum(tr, LongLen)
    Value4 = sum(close - tl(), ShortLen)
    Value5 = sum(close - tl(), MiddlLen)
    Value6 = sum(close - tl(), LongLen)
    float UO = na
    if Value1 != 0 and Value2 != 0 and Value3 != 0
        var0 = LongLen / ShortLen
        var1 = LongLen / MiddlLen
        Value7 = (Value4 / Value1) * (var0)
        Value8 = (Value5 / Value2) * (var1)
        Value9 = (Value6 / Value3)
        UO := (Value7 + Value8 + Value9) / (var0 + var1 + 1)
    UO
// Ichimoku Cloud
donchian(len) => avg(lowest(len), highest(len))
ichimoku_cloud() =>
    conversionLine = donchian(9)
    baseLine = donchian(26)
    leadLine1 = avg(conversionLine, baseLine)
    leadLine2 = donchian(52)
    [conversionLine, baseLine, leadLine1, leadLine2]
calcRatingMA(ma, src) => na(ma) or na(src) ? na : (ma == src ? 0 : ( ma < src ? 1 : -1 ))
calcRating(buy, sell) => buy ? 1 : ( sell ? -1 : 0 )
ta_presets_signal() =>
    //============== MA =================
    SMA10 = sma(close, 10)
    SMA20 = sma(close, 20)
    SMA30 = sma(close, 30)
    SMA50 = sma(close, 50)
    SMA100 = sma(close, 100)
    SMA200 = sma(close, 200)
    
    EMA10 = ema(close, 10)
    EMA20 = ema(close, 20)
    EMA30 = ema(close, 30)
    EMA50 = ema(close, 50)
    EMA100 = ema(close, 100)
    EMA200 = ema(close, 200)
    
    HullMA9 = hma(close, 9)
    
    // Volume Weighted Moving Average (VWMA)
    VWMA = vwma(close, 20)
    
    [IC_CLine, IC_BLine, IC_Lead1, IC_Lead2] = ichimoku_cloud()
    
    // ======= Other =============
    // Relative Strength Index, RSI
    RSI = rsi(close,14)
    
    // Stochastic
    lengthStoch = 14
    smoothKStoch = 3
    smoothDStoch = 3
    kStoch = sma(stoch(close, high, low, lengthStoch), smoothKStoch)
    dStoch = sma(kStoch, smoothDStoch)
    
    // Commodity Channel Index, CCI
    CCI = cci(close, 20)
    
    // Average Directional Index
    float adxValue = na, float adxPlus = na, float adxMinus = na
    [P, M, V] = dmi(14, 14)
    adxValue := V
    adxPlus := P
    adxMinus := M
    // Awesome Oscillator
    ao = AO()
    
    // Momentum
    Mom = mom(close, 10)
    // Moving Average Convergence/Divergence, MACD
    [macdMACD, signalMACD, _] = macd(close, 12, 26, 9)
    // Stochastic RSI
    [Stoch_RSI_K, Stoch_RSI_D] = StochRSI()
    // Williams Percent Range
    WR = wpr(14)
    
    // Bull / Bear Power
    BullPower = high - ema(close, 13)
    BearPower = low - ema(close, 13)
    // Ultimate Oscillator
    UO = uo(7,14,28)
    if not na(UO)
        UO := UO * 100
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    PriceAvg = ema(close, 50)
    DownTrend = close < PriceAvg
    UpTrend = close > PriceAvg
    // calculate trading recommendation based on SMA/EMA
    float ratingMA = 0
    float ratingMAC = 0
    
    if not na(SMA10)
        ratingMA := ratingMA + calcRatingMA(SMA10, close)
        ratingMAC := ratingMAC + 1
    if not na(SMA20)
        ratingMA := ratingMA + calcRatingMA(SMA20, close)
        ratingMAC := ratingMAC + 1
    if not na(SMA30)
        ratingMA := ratingMA + calcRatingMA(SMA30, close)
        ratingMAC := ratingMAC + 1
    if not na(SMA50)
        ratingMA := ratingMA + calcRatingMA(SMA50, close)
        ratingMAC := ratingMAC + 1
    if not na(SMA100)
        ratingMA := ratingMA + calcRatingMA(SMA100, close)
        ratingMAC := ratingMAC + 1
    if not na(SMA200)
        ratingMA := ratingMA + calcRatingMA(SMA200, close)
        ratingMAC := ratingMAC + 1
    if not na(EMA10)
        ratingMA := ratingMA + calcRatingMA(EMA10, close)
        ratingMAC := ratingMAC + 1
    if not na(EMA20)
        ratingMA := ratingMA + calcRatingMA(EMA20, close)
        ratingMAC := ratingMAC + 1
    if not na(EMA30)
        ratingMA := ratingMA + calcRatingMA(EMA30, close)
        ratingMAC := ratingMAC + 1
    if not na(EMA50)
        ratingMA := ratingMA + calcRatingMA(EMA50, close)
        ratingMAC := ratingMAC + 1
    if not na(EMA100)
        ratingMA := ratingMA + calcRatingMA(EMA100, close)
        ratingMAC := ratingMAC + 1
    if not na(EMA200)
        ratingMA := ratingMA + calcRatingMA(EMA200, close)
        ratingMAC := ratingMAC + 1
    
    if not na(HullMA9)
        ratingHullMA9 = calcRatingMA(HullMA9, close)
        ratingMA := ratingMA + ratingHullMA9
        ratingMAC := ratingMAC + 1
    
    if not na(VWMA)
        ratingVWMA = calcRatingMA(VWMA, close)
        ratingMA := ratingMA + ratingVWMA
        ratingMAC := ratingMAC + 1
    
    float ratingIC = na
    if not (na(IC_Lead1) or na(IC_Lead2) or na(close) or na(close[1]) or na(IC_BLine) or na(IC_CLine))
        ratingIC := calcRating(
         IC_Lead1 > IC_Lead2 and close > IC_Lead1 and close < IC_BLine and close[1] < IC_CLine and close > IC_CLine,
         IC_Lead2 > IC_Lead1 and close < IC_Lead2 and close > IC_BLine and close[1] > IC_CLine and close < IC_CLine)
    if not na(ratingIC)
        ratingMA := ratingMA + ratingIC
        ratingMAC := ratingMAC + 1
    
    ratingMA := ratingMAC > 0 ? ratingMA / ratingMAC : na
    
    float ratingOther = 0
    float ratingOtherC = 0
    
    ratingRSI = RSI
    if not(na(ratingRSI) or na(ratingRSI[1]))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(ratingRSI < 30 and ratingRSI[1] < ratingRSI, ratingRSI > 70 and ratingRSI[1] > ratingRSI)
    
    if not(na(kStoch) or na(dStoch) or na(kStoch[1]) or na(dStoch[1]))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(kStoch < 20 and dStoch < 20 and kStoch > dStoch and kStoch[1] < dStoch[1], kStoch > 80 and dStoch > 80 and kStoch < dStoch and kStoch[1] > dStoch[1])
    
    ratingCCI = CCI
    if not(na(ratingCCI) or na(ratingCCI[1]))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(ratingCCI < -100 and ratingCCI > ratingCCI[1], ratingCCI > 100 and ratingCCI < ratingCCI[1])
    
    if not(na(adxValue) or na(adxPlus[1]) or na(adxMinus[1]) or na(adxPlus) or na(adxMinus))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(adxValue > 20 and adxPlus[1] < adxMinus[1] and adxPlus > adxMinus, adxValue > 20 and adxPlus[1] > adxMinus[1] and adxPlus < adxMinus)
    
    if not(na(ao) or na(ao[1]))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(crossover(ao,0) or (ao > 0 and ao[1] > 0 and ao > ao[1] and ao[2] > ao[1]), crossunder(ao,0) or (ao < 0 and ao[1] < 0 and ao < ao[1] and ao[2] < ao[1]))
    
    if not(na(Mom) or na(Mom[1]))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(Mom > Mom[1], Mom < Mom[1])
    
    if not(na(macdMACD) or na(signalMACD))
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + calcRating(macdMACD > signalMACD, macdMACD < signalMACD)
    
    float ratingStoch_RSI = na
    if not(na(DownTrend) or na(UpTrend) or na(Stoch_RSI_K) or na(Stoch_RSI_D) or na(Stoch_RSI_K[1]) or na(Stoch_RSI_D[1]))
        ratingStoch_RSI := calcRating(
         DownTrend and Stoch_RSI_K < 20 and Stoch_RSI_D < 20 and Stoch_RSI_K > Stoch_RSI_D and Stoch_RSI_K[1] < Stoch_RSI_D[1],
         UpTrend and Stoch_RSI_K > 80 and Stoch_RSI_D > 80 and Stoch_RSI_K < Stoch_RSI_D and Stoch_RSI_K[1] > Stoch_RSI_D[1])
    if not na(ratingStoch_RSI)
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + ratingStoch_RSI
    
    float ratingWR = na
    if not(na(WR) or na(WR[1]))
        ratingWR := calcRating(WR < -80 and WR > WR[1], WR > -20 and WR < WR[1])
    if not na(ratingWR)
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + ratingWR
    
    float ratingBBPower = na
    if not(na(UpTrend) or na(DownTrend) or na(BearPower) or na(BearPower[1]) or na(BullPower) or na(BullPower[1]))
        ratingBBPower := calcRating(
         UpTrend and BearPower < 0 and BearPower > BearPower[1],
         DownTrend and BullPower > 0 and BullPower < BullPower[1])
    if not na(ratingBBPower)
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + ratingBBPower
    
    float ratingUO = na
    if not(na(UO))
        ratingUO := calcRating(UO > 70, UO < 30)
    if not na(ratingUO)
        ratingOtherC := ratingOtherC + 1
        ratingOther := ratingOther + ratingUO
    
    ratingOther := ratingOtherC > 0 ? ratingOther / ratingOtherC : na
    
    float ratingTotal = 0
    float ratingTotalC = 0
    if not na(ratingMA)
        ratingTotal := ratingTotal + ratingMA
        ratingTotalC := ratingTotalC + 1
    if not na(ratingOther)
        ratingTotal := ratingTotal + ratingOther
        ratingTotalC := ratingTotalC + 1
    ratingTotal := ratingTotalC > 0 ? ratingTotal / ratingTotalC : na
    
    // little piece of @rouxam logic
    var bool start_condition = false
    float bound = dsc_technicals == "Strong" ? 0.5 : 0.1
    if IS_LONG
        start_condition := ratingTotal > bound
    else
        start_condition := ratingTotal < -bound
    [start_condition, close]
    
// ----------------------------------
// (Re-)Initialize
// ----------------------------------
var max_amount = max_amount_for_bot_usage()
var max_dev = max_deviation()
init_price := na(init_price) and within_window() ? open : init_price
latest_price := within_window() ? close : latest_price
// Actualize the start and end time of the backtesting window because number of bars is limited.
// NOTE: limits on number of available bars:
//      TradingView FREE account: 5000 bars available,
//      TradingView PRO/PRO+ account: 10000 bars available,
//      TradingView PREMIUM account: 20000 bars available.
start_time_actual := barstate.isfirst and time > start_time_actual ?  time : start_time_actual
end_time_actual := time > end_time_actual and time <= end_time ? time : end_time_actual
if strategy.position_size == 0.0
    ttp_extremum := 0.0
    ttp_active := false
    deal_start_condition := false
// ----------------------------------
// Open deal with Base Order on Deal Start Condition
// ----------------------------------
[dsc_rsi, bo_level_rsi] = security(syminfo.tickerid, dsc_res, rsi_signal())
[dsc_ta, bo_level_ta] = security(syminfo.tickerid, dsc_res, ta_presets_signal())
if(strategy.opentrades == 0 and within_window() and close > 0 and strategy.equity > 0.0)
    // Compute deal start condition
    if dsc == "Start ASAP"
        deal_start_condition := true
        bo_level := close
    if dsc == "RSI-7"
        deal_start_condition := dsc_rsi
        bo_level := bo_level_rsi
    if dsc == "TV presets"
        deal_start_condition := dsc_ta
        bo_level := bo_level_ta
    // Place Buy Order
    if deal_start_condition
        deal_counter := deal_counter + 1
        if IS_LONG
            strategy.entry("BO", limit=bo_level, long=strategy.long, qty=base_order_size()/bo_level)
        else
            strategy.entry("BO", limit=bo_level, long=strategy.short, qty=base_order_size()/bo_level)
        last_so_level := bo_level
        // Place Safety Orders
        if max_safe_order > 0
            for index = 1 to max_safe_order
                so_level := safety_order_price(index, last_so_level)
                so_name = "SO" + tostring(index) 
                if IS_LONG
                    strategy.entry(so_name, long=strategy.long, limit=so_level, qty=safety_order_qty(index)/so_level)
                else
                    strategy.entry(so_name, long=strategy.short, limit=so_level, qty=safety_order_qty(index)/so_level)
                last_so_level := so_level
// ----------------------------------
// Close Deal on SL, TP or TTP
// ----------------------------------
if abs(strategy.position_size) > 0
    take_profit_factor = IS_LONG ? (1 + take_profit) : (1 - take_profit)
    stop_loss_factor = IS_LONG ? (1 - stop_loss) : (1 + stop_loss)
    ttp_factor = IS_LONG ? (1 - ttp) : (1 + ttp)
    stop_level := bo_level * stop_loss_factor
    if take_profit_type == "% from total volume"
        take_profit_level := strategy.position_avg_price * take_profit_factor
    else
        take_profit_level := bo_level * take_profit_factor
    // Stop Loss
    stop_loss_on_bar := false
    if stop_loss > max_dev and not ttp_active
        if IS_LONG and low < stop_level
            stop_loss_counter := stop_loss_counter + 1
            strategy.exit(id="x", stop=stop_level, comment="SL")
            stop_loss_on_bar := true
        else if not IS_LONG and high > stop_level
            stop_loss_counter := stop_loss_counter + 1
            strategy.exit(id="x", stop=stop_level, comment="SL")
            stop_loss_on_bar := true
    if not stop_loss_on_bar
        if ttp == 0.0
            // Simple take profit
            strategy.exit(id="x", limit=take_profit_level, comment="TP")
        else
            // Trailing take profit
            if IS_LONG and high >= take_profit_level 
                ttp_extremum := max(high, ttp_extremum)
                ttp_active := true
            if not IS_LONG and low <= take_profit_level
                ttp_extremum := min(low, ttp_extremum)
                ttp_active := true
            if ttp_active 
                ttp_level := ttp_extremum * ttp_factor
                strategy.exit(id="x", stop=ttp_level, comment="TTP")
// Cleanup
if (crossunder(strategy.opentrades, 0.5))
    strategy.close_all()
    strategy.cancel_all()
// ----------------------------------
// Results
// ----------------------------------
profit = max(strategy.equity, 0.0) - strategy.initial_capital
profit_pct = profit / strategy.initial_capital
add_line(the_table, the_row, the_label, the_value, is_warning) =>
    // if providing a value, the row is shown as: the_label | the_value
    // else: the_label is considered to be a title
    is_even     = (the_row % 2) == 1
    is_title    = na(the_value) ? true : false
    text        = is_title ? "" : tostring(the_value)
    bg_color    = is_title ? color.black : is_warning ? color.red : is_even ? color.silver : color.white
    text_color  = is_title ? color.white : color.black
    left_cell_align = is_title ? text.align_right : text.align_left
    table.cell(the_table, 0, the_row, the_label, bgcolor=bg_color, text_color=text_color,  text_size=size.auto, text_halign=left_cell_align)
    table.cell(the_table, 1, the_row, text,      bgcolor=bg_color, text_color=text_color,  text_size=size.auto, text_halign=text.align_right) 
var string warnings_text = ""
var warnings = array.new_string(0, "")
if (max_amount / strategy.initial_capital > 1.005 or max_amount / strategy.initial_capital < 0.995)
    if order_size_type == "Fixed"
        array.push(warnings, "Strategy Initial Capital (currently " + tostring(strategy.initial_capital, currency_format()) + " " + syminfo.currency
                           + ") must match Max Amount For Bot Usage (" + tostring(max_amount, currency_format()) + " " + syminfo.currency + ")")
    else
        array.push(warnings, "Please adjust Base Order and Safe Order percentage to reach 100% of usage of capital. Currently using " + tostring(max_amount / strategy.initial_capital, "#.##%"))
if (max_dev >= 1.0)
    array.push(warnings, "Max Price Deviation (" + tostring(max_dev, "#.##%") + ") cannot be >= 100.0%")
if (stop_loss > 0.0 and stop_loss <= max_dev)
    array.push(warnings, "Stop Loss (" + tostring(stop_loss, "#.##%") + ") should be greater than Max Price Deviation (" + tostring(max_dev, "#.##%") + ")")
if ((timeframe.isminutes and not (tonumber(timeframe.period) < 30)) or timeframe.isdwm)
    array.push(warnings, "Backtesting may be inaccurate. Recommended to use timeframe of 15m or less")
if (ttp >= take_profit)
    array.push(warnings, "Trailing Take Profit (" + tostring(ttp, "#.##%") + ") cannot be greater than Take Profit (" + tostring(take_profit, "#.##%") + ")")
// Making the results table
var results_table  = table.new(position.top_right, 2, 20, frame_color=color.black)
curr_row = -1
None = int(na)
curr_row += 1, add_line(results_table, curr_row, "Bot setup", None, false)
if (array.size(warnings) > 0)
    for index = 1 to array.size(warnings)
        curr_row += 1
        add_line(results_table, curr_row, "Please review your strategy settings", array.pop(warnings), true)
curr_row += 1, add_line(results_table, curr_row, "Dates", pretty_date(start_time_actual) + " to " + pretty_date(end_time_actual), false)
curr_row += 1, add_line(results_table, curr_row, "Deal Start Condition", dsc, false)
curr_row += 1, add_line(results_table, curr_row, "Base and Safety Orders", "BO: " + tostring(base_order, currency_format()) + " " + (order_size_type == "Fixed" ? "" : "% ") + syminfo.currency + " / " + "SO: " + tostring(safe_order, currency_format()) + " " + (order_size_type == "Fixed" ? "" : "% ") + syminfo.currency, false)
curr_row += 1, add_line(results_table, curr_row, "Price Deviation", tostring(price_deviation, "#.##%"), false)
curr_row += 1, add_line(results_table, curr_row, "Safe Order Scale", "Volume: " + tostring(safe_order_volume_scale) + " / Scale: " + tostring(safe_order_step_scale), false)
curr_row += 1, add_line(results_table, curr_row, "Take Profit / Stop Loss", tostring(take_profit, "#.##%") + (ttp > 0.0 ?  "( Trailing: " + tostring(ttp, "#.##%") + ")" : "") + "/ " + (stop_loss > 0.0 ? tostring(stop_loss, "#.##%") : "No Stop Loss"), false)
curr_row += 1, add_line(results_table, curr_row, "Max Amount For Bot Usage", tostring(max_amount, currency_format()) + " " + syminfo.currency, false)
curr_row += 1, add_line(results_table, curr_row, "Max Coverage", tostring(max_safe_order) + " Safety Orders / " + tostring(max_dev, "#.##%"), false)
curr_row += 1, add_line(results_table, curr_row, "Summary", None, false)
curr_row += 1, add_line(results_table, curr_row, "Initial Capital", tostring(strategy.initial_capital, currency_format()) + " " + syminfo.currency, false)
curr_row += 1, add_line(results_table, curr_row, "Final Capital",   tostring(max(strategy.equity, 0.0), currency_format()) + " " + syminfo.currency, false)
curr_row += 1, add_line(results_table, curr_row, "Net Result",      tostring(profit, currency_format()) + " " + syminfo.currency + " (" +  tostring(profit_pct, "#.##%") + ")", false)
curr_row += 1, add_line(results_table, curr_row, "Total Deals",     tostring(deal_counter), false)
if stop_loss > 0.0
    curr_row += 1, add_line(results_table, curr_row, "Deals Closed on Stop Loss", tostring(stop_loss_counter), false)
if IS_LONG
    buy_hold_profit = (latest_price - init_price) / init_price * strategy.initial_capital
    buy_hold_profit_pct = buy_hold_profit / strategy.initial_capital
    ratio = profit_pct / buy_hold_profit_pct
    curr_row += 1, add_line(results_table, curr_row, "Comparison to Buy-And-Hold",                      None, false)
    curr_row += 1, add_line(results_table, curr_row, "Net Result for Buy-and-hold",                     tostring(buy_hold_profit, currency_format()) + " " + syminfo.currency + " (" +  tostring(buy_hold_profit_pct, "#.##%") + ")", false)
    curr_row += 1, add_line(results_table, curr_row, "(Bot Performance) / (Buy-and-hold Performance)",  tostring(ratio, "#.###"), false)
Expand (564 lines)

Overview

Backtesting 3commas DCA Bot v2 opens long or short deals based on configurable conditions such as immediate execution, RSI-7 thresholds, or TradingView technical presets. As price moves against the position, it places scaled safety orders according to defined volume and step multipliers. Deals are closed using fixed or percentage-based take profit, optional trailing take profit, or stop loss. By default, the stop loss is set to zero (disabled).

Details

Published by

hasnocool on GitHub

Code credit

rouxam, as written in the script's header

Type

Strategy, Pine Script v4

Draws

On the price chart

Category

Oscillators & momentum, Mean reversion

Markets

Crypto

Timeframes

Any timeframe

Risk Notes

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

Topics

analysisbtcusdtcryptocurrencydatapandaspythonsimulationtradingtradingview

Settings

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

SettingTypeDefaultVariable
Take Profit (%)float4.5take_profit
Take Profit Typestring% from total volumetake_profit_type
Trailing Take Profit (%) [0 = Disabled]float0.0ttp
Stop Loss (%) [0 = Disabled]float0.0stop_loss
Order Size TypestringFixedorder_size_type
Base Orderfloat100.0base_order
Safety Orderfloat140.0safe_order
Max Safety Trades Countint6max_safe_order
Price deviation to open safety order (%)float1.5price_deviation
Safety Order Volume Scalefloat1.5safe_order_volume_scale
Safety Order Step Scalefloat1.6safe_order_step_scale
Deal Start ConditionstringRSI-7dsc
Show all 18 settings
SettingTypeDefaultVariable
[RSI-7 only] RSI Thresholdint30dsc_rsi_threshold
[TV presets only] StrengthstringStrongdsc_technicals
[RSI-7 and TV presets Only] Indicator Timeframetimeframedsc_res
Bot DirectionstringLongbot_direction
Start Timetimetimestamp("15 June 2021 06:00")start_time
End Timetimetimestamp("31 Dec 2021 20:00")end_time
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
104 stars

This strategy combines Bollinger Bands with a short RSI to fade moves that leave the bands and then return. A long requires the prior bar below the lower band and oversold, then a close back inside the band while RSI is between 30 and 50. Shorts mirror that at

Strategy v5 Oscillators & momentumVolatility & bands
Updated 3 years ago · MIT GitHub repository of Bollinger Bands and RSI Strategy
53 stars

This strategy implements Larry Connors' 2-period RSI mean-reversion algorithm overlaid on the price chart. It filters trades using a long-term Simple Moving Average (SMA), entering long positions when the price trades above the SMA and the short-term RSI drops

Strategy v6 Oscillators & momentumMean reversion
Updated 2 months ago · MPL-2.0 GitHub repository of Larry Connors 2-Period RSI Strategy

FVG RSI Strategy (Optimized)

by skywalker0803r
42 stars

This strategy plots on the price chart and trades fair value gaps filtered by RSI and two EMAs. It opens a long when a bullish gap, a rising candle, RSI below the oversold level, and price under both EMAs align, and a short on the opposite conditions. Each tra

Strategy v5 SMC / ICT / price actionOscillators & momentum
Updated 2 months ago · None stated GitHub repository of FVG RSI Strategy (Optimized)

More strategys

Trade Your Charts With a Regulated Broker

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

Compare brokers