Attachments forums

Re: Banzai Stuffs: backtestings, forex notes, etc...

Banzai, Wed Jul 06, 2022 8:20 am


Code: Select all

//PRC_Logistic Oscillators | indicator
//05.03.2021
//Nicolas @ www.prorealcode.com
//Sharing ProRealTime knowledge
//converted from pinescript 
 
// --- settings 
ti         = 0 //0=Awesome Oscillator (AO),1=Rate of Change (ROC),2=Relative Strength Index (RSI),3= Stochastic (STOCH)
length     = 13
lenROC     = 9 //Length : Rate of Change (ROC)
lenRSI     = 14 //Length : Relative Strength Index (RSI)
lenSTO     = 14 //Length : Stochastic (STOCH)
// --- end of settings 
 
source     = customclose
 
if barindex>length then 
 
if ti = 0 then
rr = average[5](medianprice)/average[34](medianprice)-1
elsif ti=1 then 
rr = (source-source[lenROC])/source[lenROC]
elsif ti=2 then 
rr = rsi[lenRSI](source)/100-.5
elsif ti=3 then 
rr = 100*(source-lowest[lensto](low))/(highest[lensto](high)-lowest[lensto](low))/100-.5
endif
 
mapeq = rr*source / highest[length](high)*(1-source/highest[length](high))
lmap = mapeq*1000000
sum = 0
for i = 1 to 30
lmap = (rr * abs(mapeq[i]) * (1 - mapeq[i]))*1000000
sum=sum+lmap
$v[i]=lmap
next
avg = sum/30
 
for j = 1 to 30
if avg>0 then
r=0
g=255
else
r=255
g=0
endif
drawtext("●",barindex,$v[j],dialog,standard,5) coloured(r,g,0,70)
next
 
if avg>0 then 
ar=0
ag=255
else
ar=255
ag=0
endif
endif
 
return 0 coloured(100,100,100,0),lmap coloured(100,100,100,0),avg coloured(ar,ag,0) style(line,3) as "average"
Kewl ! Logistic Oscillators indicator.


https://www.tradingview.com/script/NioH ... AO-by-DGT/

Code: Select all

//@version=6
// ══════════════════════════════════════════════════════════════════════════════════════════════════ //
//# * ══════════════════════════════════════════════════════════════════════════════════════════════
//# *
//# * Study       : Logistic Map Application on RSI, STOCH, ROC, AO, and Custom Logistic Interpretation
//# * Author      : © dgtrd
//# *
//# * Revision History
//# *  Release    : Nov 5 , 2020  : Initial Release
//# *  Update     : Nov 21, 2020  : Backtest Framework Adaptation
//# *  Update     : Mar 13, 2021  : Enchanced Backtest Framework
//# *                               - long/short/stoploss conditions enchaced
//# *                               - early warning ability added (label + alert)
//# *  Update     : Mar 16, 2021  : Alert additions to Logistic and Backtest Framework
//# *
//# * 
//# * ══════════════════════════════════════════════════════════════════════════════════════════════
// ══════════════════════════════════════════════════════════════════════════════════════════════════ //

indicator('Logistic RSI, STOCH, ROC, AO, ... by DGT', 'LOGISTIC ☼☾', max_labels_count = 500) //, resolution="")

// -Inputs ══════════════════════════════════════════════════════════════════════════════════════ //

display = display.all - display.status_line

ti = input.string('Logistic Dominance', 'Select Indicator', display = display,
     options = ['Awesome Oscillator (AO)', 'Logistic Dominance', 'Rate of Change (ROC)', 'Relative Strength Index (RSI)', 'Stochastic (STOCH)'],
     tooltip = 'Select the base oscillator used as the input for the Logistic Map transformation.\n\n' +
             'Available sources:\n' +
             '• AO – momentum derived from Awesome Oscillator\n' +
             '• Logistic Dominance – custom price-based dominance metric\n' +
             '• ROC – Rate of Change momentum\n' +
             '• RSI – Relative Strength Index\n' +
             '• STOCH – Stochastic oscillator\n\n' +
            'The selected oscillator is transformed through a Logistic Map iteration process to highlight nonlinear momentum dynamics.')

source = input.source(close, 'Source',  group = 'Logistic Settings', display = display)
length = input.int(13, 'Logistic Map Length', minval = 1, group = 'Logistic Settings', display = display,
     tooltip = 'Lookback period used when normalizing the input signal before applying the Logistic Map transformation.\n\n' +
               '• Higher values → smoother and more stable behavior\n' +
               '• Lower values → more reactive but noisier output\n\n' +
               'Controls how sensitive the chaotic iterations are to short-term price changes.')

lenLD = input.int(5, 'Length : Logistic Dominance', minval = 1, group = 'Optimization / Customization Metrics', display = display,
     tooltip = 'Lookback period used when the Logistic Dominance mode is selected as the base oscillator.\n\n' +
               'Controls how price dominance and directional pressure are measured before the Logistic Map transformation.')
lenROC = input.int(9, 'Length : Rate of Change (ROC)', minval = 1, group = 'Optimization / Customization Metrics', display = display,
     tooltip = 'Lookback period used for the Rate of Change oscillator.\n\n' +
               'Only applied when ROC is selected as the base indicator.\n\n' +
               'Higher values emphasize broader momentum trends, while lower values respond faster to short-term moves.')
lenRSI = input.int(14, 'Length : Relative Strength Index (RSI)', minval = 1, group = 'Optimization / Customization Metrics', display = display,
     tooltip = 'Lookback period used for the RSI calculation.\n\n' +
               'Only affects the indicator when RSI is selected as the base oscillator for the Logistic Map.')
lenSTO = input.int(14, 'Length : Stochastic (STOCH)', minval = 1, group = 'Optimization / Customization Metrics', display = display,
     tooltip = 'Lookback period used for the Stochastic oscillator.\n\n' +
               'Only applied when STOCH is selected in the "Select Indicator" setting.')

style = input.string('Cross', 'Style', group = 'Display Settings', display = display,
     options = ['Cross', 'Line', 'StepLine'],
     tooltip = 'Visualization style for the Logistic Map iterations.\n\n' +
               '• Cross → discrete markers for each iteration\n' +
               '• Line → continuous line connecting iterations\n' +
               '• StepLine → stepped representation emphasizing\n' +
               '  discrete iteration levels.')

uC = input.color(color.teal, 'Colors : Bullish', inline = 'PLOT', group = 'Display Settings')
dC = input.color(color.red, 'Bearish', inline = 'PLOT', group = 'Display Settings')

d = input.bool(false, 'Standard Deviation Line Alone', group = 'Display Settings',
     tooltip = 'Display only the aggregated standard deviation\n' +
               'of all Logistic Map iterations.\n\n' +
               '• ON → hides individual iterations and shows\n' +
               '  only the summarized volatility/momentum signal\n' +
               '• OFF → displays all Logistic Map iterations.')
               
// -Calculations ════════════════════════════════════════════════════════════════════════════════ //

f_logmap(_s, _r, _l) =>
    _r * _s / ta.highest(_l) * (1 - _s / ta.highest(_l))

f_map(_s, _r, _v) =>
    mapeq = f_logmap(_s, _r, length)
    lmap = mapeq
    for i = 0 to 29 by 1
        array.push(_v, lmap)
        lmap := _r * math.abs(mapeq[i]) * (1 - mapeq[i])
        lmap
    lmap

r = if ti == 'Awesome Oscillator (AO)'
    ta.sma(hl2, 5) / ta.sma(hl2, 34) - 1
else if ti == 'Logistic Dominance'
    -f_logmap(-source, ta.change(source, lenLD) / source[lenLD], lenLD) - f_logmap(source, ta.change(source, lenLD) / source[lenLD], lenLD)
else if ti == 'Rate of Change (ROC)'
    ta.change(source, lenROC) / source[lenROC]
else if ti == 'Relative Strength Index (RSI)'
    ta.rsi(source, lenRSI) / 100 - .5
else if ti == 'Stochastic (STOCH)'
    ta.stoch(source, high, low, lenSTO) / 100 - .5

var v = array.new_float(0)

val = f_map(source, r, v)
s = style == 'Cross' ? plot.style_cross : style == 'Line' ? plot.style_line : plot.style_stepline

// -Plotting ════════════════════════════════════════════════════════════════════════════════════ //

plot(d ? na : val, 'Last Iteration', val >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 1), '2nd Iteration', array.get(v, 1) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 2), '3rd Iteration', array.get(v, 2) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 3), '4th Iteration', array.get(v, 3) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 4), '5th Iteration', array.get(v, 4) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 5), '6th Iteration', array.get(v, 5) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 6), '7th Iteration', array.get(v, 6) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 7), '8th Iteration', array.get(v, 7) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 8), '9th Iteration', array.get(v, 8) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 9), '10th Iteration', array.get(v, 9) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 10), '11th Iteration', array.get(v, 10) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 11), '12th Iteration', array.get(v, 11) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 12), '13th Iteration', array.get(v, 12) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 13), '14th Iteration', array.get(v, 13) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 14), '15th Iteration', array.get(v, 14) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 15), '16th Iteration', array.get(v, 15) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 16), '17th Iteration', array.get(v, 16) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 17), '18th Iteration', array.get(v, 17) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 18), '19th Iteration', array.get(v, 18) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 19), '20th Iteration', array.get(v, 19) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 20), '21st Iteration', array.get(v, 20) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 21), '22nd Iteration', array.get(v, 21) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 22), '23rd Iteration', array.get(v, 22) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 23), '24th Iteration', array.get(v, 23) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 24), '25th Iteration', array.get(v, 24) >= 0 ? uC : dC, 1, s, display = display)
//plot(d ? na : array.get(v, 25), '26th Iteration', array.get(v, 25) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 26), '27th Iteration', array.get(v, 26) >= 0 ? uC : dC, 1, s, display = display)
//plot(d ? na : array.get(v,27), "28th Iteration", array.get(v,27) >= 0 ? uC : dC, 1, s, display = display)
plot(d ? na : array.get(v, 28), '29th Iteration', array.get(v, 28) >= 0 ? uC : dC, 1, s, display = display)
//plot(d ? na : array.get(v,29), "30th Iteration", array.get(v,29) >= 0 ? uC : dC, 1, s, display = display)

array.remove(v, 0)
aStDev = math.sign(array.avg(v)) * array.stdev(v)
plot(aStDev, 'Standard Deviation of Array\'s Elements', array.avg(v) >= 0 ? #26c6da : #ffa726, 2, display = display)
array.clear(v)

// -Alerts ══════════════════════════════════════════════════════════════════════════════════════ //

longAlertCondition = ta.crossover(aStDev, 0)
alertcondition(longAlertCondition, 'Long : Early Warning', 'LOGISTIC - Not Confirmed Probable Long Trade Opportunity\n{{exchange}}:{{ticker}}->\nPrice = {{close}},\nTime = {{time}}')
alertcondition(longAlertCondition[1], 'Long : Trading Opportunity', 'LOGISTIC - Probable Long Trade Opportunity\n{{exchange}}:{{ticker}}->\nPrice = {{close}},\nTime = {{time}}')

shortAlertCondition = ta.crossunder(aStDev, 0)
alertcondition(shortAlertCondition, 'Short : Early Warning', 'LOGISTIC - Not Confirmed Probable Short Trade Opportunity\n{{exchange}}:{{ticker}}->\nPrice = {{close}},\nTime = {{time}}')
alertcondition(shortAlertCondition[1], 'Short : Trading Opportunity', 'LOGISTIC - Probable Short Trade Opportunity\n{{exchange}}:{{ticker}}->\nPrice = {{close}},\nTime = {{time}}')


// ══════════════════════════════════════════════════════════════════════════════════════════════════ //
//# * ══════════════════════════════════════════════════════════════════════════════════════════════
//# *
//# * Study       : Backtest Framework
//# * Author      : © dgtrd
//# * Purpose     : Trade simulation with long, short and reverse trade ability
//# *
//# * Revision History
//# *  Release    : Nov 21, 2020 : Initial Release
//# *  Update     : Mar 13, 2021 : Enhanced framework — long/short/stoploss, early warning
//# *  Update     : Apr 03, 2026 : Short trade support, trade mode selector (Long / Short / Both),
//# *                              reverse on signal, intrabar stop loss, redesigned stats label
//# *
//# * ══════════════════════════════════════════════════════════════════════════════════════════════
// ══════════════════════════════════════════════════════════════════════════════════════════════════ //

bt_yAnchor = close

// ── Inputs ───────────────────────────────────────────────────────────────────── //

btGR = '=============   Backtest Framework   ============='

isBackTest = input.bool(false, 'Enable Backtest', group = btGR,
     tooltip = 'Enable or disable the backtest framework.\n' +
               '• ON  → trades simulated using the rules below\n' +
               '• OFF → only live signals shown')

tradeMode = input.string('Both', 'Trade Mode', options = ['Long Only', 'Short Only', 'Both'], group   = btGR, display = display,
     tooltip = 'Defines which trade directions are simulated.\n\n' +
               '• Long Only  — enters on long signal, exits on short signal or stop loss\n' +
               '• Short Only — enters on short signal, exits on long signal or stop loss\n' +
               '• Both       — enters on either signal, optionally reverses on the opposite')

reverseOnSignal = input.bool(true, 'Reverse on Opposite Signal', group = btGR,
     tooltip = 'Only applies when Trade Mode is Both.\n' +
               'When enabled, an opposite signal immediately closes the current trade\n' +
               'and opens a new one in the other direction — always in trade once started.\n' +
               'When disabled, opposite signal exits only — no new trade until next entry signal.')

dasCapital = input.float(1000., 'Initial Capital', group = btGR, display = display,
     tooltip = 'Starting capital for the simulation.\n' +
               'Used to calculate compounding PnL and final equity.')

lenBckTst = input.float(1., 'Period (Years)', minval = 0., step = .1, group = btGR, display = display,
     tooltip = 'Lookback window for the simulation in years.\n' +
               'Capped automatically if the selected timeframe has insufficient history.')

isStopLoss = input.bool(false, 'Apply Stop Loss', inline = 'sl', group = btGR,
     tooltip = 'Exits the trade if price moves against the position intrabar.\n' +
               'Long SL : low  reaches entry × (1 − %)\n' +
               'Short SL: high reaches entry × (1 + %)')

stopLoss = input.float(1., '  %', step = .1, minval = 0., inline = 'sl', group = btGR, display = display) / 100

useCandleL = input.bool(false, 'Long Candle Confirmation', group = btGR,
     tooltip = 'Long entries only taken on bullish bars (close > open).')
useCandleS = input.bool(false, 'Short Candle Confirmation', group = btGR,
     tooltip = 'Short entries only taken on bearish bars (close < open).')

isSudden = input.bool(true, 'Avoid Same-Bar Conflicts', group = btGR,
     tooltip = 'Skips entries when both long and short signals fire on the same bar.\n' +
               'Reduces false entries from volatile spikes.')

isTest = input.bool(false, '❗ Next Bar Mode (Repaints)', group = btGR,
     tooltip = 'Uses the next bar\'s open for simulation instead of the confirmed bar open.\n' +
               'Produces earlier entries but signals will repaint.\n' +
               'For testing and comparison only — do not use for live alerts.')

lblInOutSL = input.bool(true, 'Show Entry / Exit Labels', group = btGR,
     tooltip = 'Displays L / S / SL / TP labels at entry and exit bars with tooltips showing direction, price, change, and capital.')

lblWarnings = input.bool(true, 'Show Early Warnings', group = btGR,
     tooltip = 'Shows a ⚠️ label when a signal fires but has not yet been confirmed.\n' +
               'Label is deleted on the following bar regardless of outcome.')

lblTrdStat = input.bool(true, 'Show Trade Statistics', group = btGR,
     tooltip = 'Displays a floating label with full trade summary.\n' +
               'Includes win rate, PnL, long/short breakdown, and current trade status.')

// ── Backtest period ──────────────────────────────────────────────────────────── //

startBckTst = time > timenow - lenBckTst * 31556952000

var _dir    = array.new_int  (1, 0)
var _entry  = array.new_float(1, 0.)
var _exit   = array.new_float(1, 0.)
var _cap    = array.new_float(1, dasCapital)
var _trades = array.new_int  (1, 0)
var _winL   = array.new_int  (1, 0)
var _lossL  = array.new_int  (1, 0)
var _winS   = array.new_int  (1, 0)
var _lossS  = array.new_int  (1, 0)

bt_dir    () => array.get(_dir,    0)
bt_entry  () => array.get(_entry,  0)
bt_exit   () => array.get(_exit,   0)
bt_cap    () => array.get(_cap,    0)
bt_trades () => array.get(_trades, 0)
bt_winL   () => array.get(_winL,   0)
bt_lossL  () => array.get(_lossL,  0)
bt_winS   () => array.get(_winS,   0)
bt_lossS  () => array.get(_lossS,  0)

f_open(_d, _lbl) =>
    array.set(_entry,  0, open)
    array.set(_dir,    0, _d)
    array.set(_trades, 0, bt_trades() + 1)

    if _lbl
        label.new(bar_index, _d == 1 ? low : high, text = _d == 1 ? 'L' : 'S',
             tooltip       = 'Direction .... : ' + (_d == 1 ? 'Long' : 'Short') + '\n' +
                             'Entry price .. : ' + str.tostring(open) + '\n' +
                             'Capital ...... : ' + str.tostring(bt_cap(), '#.##'),
             style = _d == 1 ? label.style_label_up   : label.style_label_down, color = _d == 1 ? color.teal : color.red,
             textcolor = color.white, size = size.tiny, force_overlay = true)

f_close(_isSL, _lbl) =>
    _d     = bt_dir()
    _ep    = bt_entry()
    _xp    = _isSL ? close : open

    array.set(_exit, 0, _xp)

    _entrySafe = math.max(_ep,  syminfo.mintick)
    _exitSafe  = math.max(_xp,  syminfo.mintick)

    _pnl    = _d == 1
             ? _exitSafe / _entrySafe
             : _entrySafe / _exitSafe

    _newCap = bt_cap() * _pnl
    _profit = _pnl > 1.

    array.set(_cap, 0, _newCap)

    if _d == 1
        array.set(_winL,  0, bt_winL()  + (_profit ? 1 : 0))
        array.set(_lossL, 0, bt_lossL() + (_profit ? 0 : 1))
    else
        array.set(_winS,  0, bt_winS()  + (_profit ? 1 : 0))
        array.set(_lossS, 0, bt_lossS() + (_profit ? 0 : 1))

    if _lbl
        label.new(bar_index, _d == 1 ? high : low, text = _isSL ? 'SL' : 'TP',
             tooltip       = 'Exit type .... : ' + (_isSL ? 'Stop Loss' : 'Take Profit') + '\n' +
                             'Direction .... : ' + (_d == 1 ? 'Long' : 'Short') + '\n' +
                             'Entry / Exit . : ' + str.tostring(_ep) + ' / ' + str.tostring(_xp) + '\n' +
                             'Change ....... : ' + str.tostring((_pnl - 1) * 100, '#.##') + '%\n' +
                             'Capital ...... : ' + str.tostring(_newCap, '#.##'),
             style = _d == 1 ? label.style_label_down : label.style_label_up, color = _profit ? color.teal : color.red,
             textcolor = color.white, size = size.tiny, force_overlay = true)

    array.set(_dir, 0, 0)


if isBackTest
    sigL = isTest ? longAlertCondition[1]  : longAlertCondition[2]
    sigS = isTest ? shortAlertCondition[1] : shortAlertCondition[2]

    candleRef = isTest ? 0 : 1
    conflict  = longAlertCondition[candleRef] and shortAlertCondition[candleRef]

    okL = (not useCandleL or close[candleRef] > open[candleRef]) and (not isSudden or not conflict)
    okS = (not useCandleS or close[candleRef] < open[candleRef]) and (not isSudden or not conflict)

    longCond  = sigL and okL
    shortCond = sigS and okS

    canLong  = tradeMode != 'Short Only'
    canShort = tradeMode != 'Long Only'

    slL   = isStopLoss and bt_dir() ==  1 and low  <= bt_entry() * (1 - stopLoss)
    slS   = isStopLoss and bt_dir() == -1 and high >= bt_entry() * (1 + stopLoss)
    slHit = slL or slS

    if startBckTst

        var bool _reversed = false
        _reversed := false

        if reverseOnSignal and tradeMode == 'Both'
            if bt_dir() == 1 and shortCond
                f_close(false, lblInOutSL)
                if canShort
                    f_open(-1, lblInOutSL)
                _reversed := true

            else if bt_dir() == -1 and longCond
                f_close(false, lblInOutSL)
                if canLong
                    f_open(1, lblInOutSL)
                _reversed := true

        if not _reversed
            exitL = bt_dir() ==  1 and (shortCond or slHit)
            exitS = bt_dir() == -1 and (longCond  or slHit)

            if exitL or exitS
                f_close(slHit, lblInOutSL)

        if bt_dir() == 0
            if longCond and canLong
                f_open(1, lblInOutSL)
                alert('Long entry — ' + syminfo.tickerid + ' | Price: ' + str.tostring(close), alert.freq_once_per_bar)

            if shortCond and canShort
                f_open(-1, lblInOutSL)
                alert('Short entry — ' + syminfo.tickerid + ' | Price: ' + str.tostring(close), alert.freq_once_per_bar)

        if slHit
            alert('Stop loss — ' + syminfo.tickerid + ' | Price: ' + str.tostring(close), alert.freq_once_per_bar)

    var label wLabel = na

    if lblWarnings
        if canLong and bt_dir() != 1 and longAlertCondition[1] and not shortAlertCondition
            wLabel := label.new(bar_index, low, '⚠️',
                 tooltip       = 'Probable long entry — awaiting confirmation\n' +
                                 'If confirmed, trade executes at next bar open.',
                 color = color.teal, style = label.style_none, textcolor = color.white, size = size.large, force_overlay = true)
            label.delete(wLabel[1])
            alert('Long early warning — ' + syminfo.tickerid + ' | Price: ' + str.tostring(close), alert.freq_once_per_bar)

        if canShort and bt_dir() != -1 and shortAlertCondition[1] and not longAlertCondition
            wLabel := label.new(bar_index, high, '⚠️',
                 tooltip       = 'Probable short entry — awaiting confirmation\n' +
                                 'If confirmed, trade executes at next bar open.',
                 color = color.red, style = label.style_none, textcolor = color.white, size = size.large, force_overlay = true)
            label.delete(wLabel[1])
            alert('Short early warning — ' + syminfo.tickerid + ' | Price: ' + str.tostring(close), alert.freq_once_per_bar)

        if bool(ta.change(time))
            label.delete(wLabel[1])

    if lblTrdStat
        var float  years  = (timenow - time) / 31556952000
        var string perTxt = ''
        var label  statLbl = na

        totalTrades = bt_trades()
        totalWin    = bt_winL() + bt_winS()
        totalLoss   = bt_lossL() + bt_lossS()

        estimated =
             bt_dir() ==  1 ? bt_cap() * (close / bt_entry()) :
             bt_dir() == -1 ? bt_cap() * (bt_entry() / close) :
             bt_cap()

        winRate     = totalTrades > 0 ? totalWin / totalTrades * 100 : 0.
        gainPct     = (estimated / dasCapital - 1) * 100
        tradeDirTxt = bt_dir() ==  1 ? 'Long'  :
                      bt_dir() == -1 ? 'Short' : 'Flat'

        if years < lenBckTst
            perTxt := str.tostring(lenBckTst, '#.##') + ' Yrs *** (' + str.tostring(bar_index) + ' bars max)'
        else
            perTxt := str.tostring(lenBckTst, '#.##') + ' Year(s)'

        _slTxt = isStopLoss ?
             str.tostring(stopLoss * 100, '#.#') + '% — level: ' +
             str.tostring(
                 bt_dir() ==  1 ? bt_entry() * (1 - stopLoss) :
                 bt_dir() == -1 ? bt_entry() * (1 + stopLoss) : 0., '#.##')
             : 'Off'

        _sep = '\n═════════════════════════════════════'

        _txt =
             '☼☾ Trade Statistics | ' + timeframe.period +
             ' | ' + perTxt + ' | Trade Mode : ' + tradeMode +
             _sep +
             '\nWin Rate : ' + str.tostring(winRate, '#.#') + '%' +
             ' | Trades : ' + str.tostring(totalTrades) +
             '\nWin/Loss : ' + str.tostring(totalWin)    + '/' + str.tostring(totalLoss) +
             ' | Long  W/L : ' + str.tostring(bt_winL())   + '/' + str.tostring(bt_lossL()) +
             ' | Short W/L : ' + str.tostring(bt_winS())   + '/' + str.tostring(bt_lossS()) +
             _sep +
             '\nGain/Loss : ' + str.tostring(gainPct,     '#.##') + '%' +
             ' | Initial/Final Capital : ' + str.tostring(dasCapital,  '#.##') + '/' + str.tostring(estimated,   '#.##') +
             _sep +
             '\nStatus : ' + tradeDirTxt +
             (bt_dir() != 0 ?
                 ' | Entry Price : ' + str.tostring(bt_entry()) +
                 ' | Unrealized : ' + str.tostring((estimated / bt_cap() - 1) * 100, '#.##') + '%' +
                 '\nStop Loss : ' + _slTxt
             : '') +
             (years < lenBckTst ? '\n*** max available history for this timeframe' : '')

        _statCol =
             gainPct > 0 ? color.teal : gainPct < 0 ? color.red  : color.gray

        label.delete(statLbl)
        statLbl := label.new(bar_index + 5, bt_yAnchor, _txt, color = _statCol, xloc = xloc.bar_index, style = label.style_label_left,
             textcolor = color.white, textalign = text.align_left, size = size.small, force_overlay = true)

bgcolor(isBackTest and startBckTst ? startBckTst != startBckTst[1] ? color.new(color.gray, 90) :
         bt_dir() ==  1 ? color.new(color.teal,  92) : bt_dir() == -1 ? color.new(color.red,   92) : na : na,
         title = 'Backtest Background', editable = false)

// ── Entry / exit price line ───────────────────────────────────────────────────── //

plot(bt_dir() != 0 ? bt_entry() : bt_exit() > 0 ? bt_exit() : na, 'Entry / Exit Price',
     color = bt_dir() ==  1 ? color.teal : bt_dir() == -1 ? color.red  : color.gray,
     style = plot.style_circles, display = display, editable = false, force_overlay = true)
     
var table logo = table.new(position.bottom_right, 1, 1)
var table logo2 = table.new(position.bottom_right, 1, 1, force_overlay = true)
if barstate.islast
    table.cell(logo, 0, 0, '☼☾  ', text_size=size.normal, text_color=color.teal, tooltip = 'SoleMare Analytics')
    table.cell(logo2, 0, 0, '☼☾  ', text_size=size.normal, text_color=color.teal, tooltip = 'SoleMare Analytics')
    
All files in topic