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

23
January 14, 2026

https://www.reddit.com/r/algotrading/
https://github.com/quantium-ai/research ... tegy.ipynb
https://app.testnet.everstrike.io/app/t ... waway=true
https://chatgpt.com/share/6967d9c1-9d54 ... 21cb7ad36f = Gold Check list
https://chatgpt.com/share/6967de28-ccc4 ... 1cd7c23beb = Hidden Markov Model Strategy
These users thanked the author Banzai for the post (total 2):
Jedidiah, Knight

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

24
Yes. You can integrate Hermes Agent from Nous Research with your MQL5 indicators in MetaTrader 5, allowing an AI agent to analyze gold (XAUUSD), evaluate your indicators, and generate trading recommendations.

Hermes has an OpenAI-compatible API server, so you don't need to modify Hermes itself to connect it to MT5. 

[image](https://www.google.com/s2/favicons?doma ... com\&sz=32)

Hermes Agent+1



## 1. Recommended architecture

For your Windows 11 trading setup, I recommend the following structure:

MetaTrader 5 — XAUUSD

Gold prices, candles, indicator buffers

MQL5 indicator signals

MQL5 Expert Advisor

Collects data and sends HTTP requests

Local API connection

Hermes Agent

Analyzes gold, filters signals, explains decisions

AI model inference

Ollama (local) or cloud model

Qwen / Gemini / other supported LLMs

Result returned to MT5: BUY / SELL / WAIT

Important limitation: MQL5 indicators cannot call `WebRequest()` directly. MetaQuotes restricts it to Expert Advisors and scripts. Therefore, your indicators should calculate their normal signals, while a separate EA communicates with Hermes. 

[image](https://www.google.com/s2/favicons?doma ... com\&sz=32)

MQL5 Reference+1



## 2. What Hermes can do for gold trading

| Feature | Example |
| ----------------- | ---------------------------------------- |
| Analyze XAUUSD | Evaluate H1 trend and M15 entry |
| Filter indicators | Half Trend + ADX + Stochastic |
| Detect confluence | Confirm multiple bullish conditions |
| Risk analysis | Suggest stop-loss and take-profit levels |
| Explain signals | Explain why a BUY was rejected |
| Trading journal | Log AI analysis and trading outcomes |
| Code assistance | Review and improve your MQL5 indicators |

For example, your Half Trend indicator might generate a BUY on XAUUSD M15. Instead of automatically accepting that signal, the EA sends the indicator values, current prices, and trend conditions to Hermes.

Hermes could return:

```
{
"symbol": "XAUUSD",
"timeframe": "M15",
"signal": "WAIT",
"confidence": 0.72,
"reason": "Bullish Half Trend, but ADX is weak.",
"risk": "Avoid entry until momentum improves."
}
```

That JSON is an illustrative response format, not Hermes's native response schema. Your integration would need to instruct Hermes to produce it and validate the result.

## 3. Enable Hermes API on your PC

Assuming Hermes Agent is installed and its AI model provider is configured, edit the Hermes `.env` file in your Hermes home directory and add:

```
API_SERVER_ENABLED=true
API_SERVER_HOST=127.0.0.1
API_SERVER_PORT=8642
API_SERVER_KEY=replace-with-a-long-random-secret
```

Start Hermes:

```
hermes gateway
```

The API will normally be available at:

```
http://127.0.0.1:8642/v1/chat/completions
```

Hermes supports local Ollama models as well as cloud providers. If you want to run everything locally, select Ollama as a custom endpoint using `hermes model` and configure an appropriate model with sufficient context capacity. 

[image](https://www.google.com/s2/favicons?doma ... com\&sz=32)

Hermes Agent+2



Then open MT5:

1. Go to Tools → Options → Expert Advisors.
2. Enable Allow WebRequest for listed URL.
3. Add `http://127.0.0.1:8642`.
4. Click OK.

## 4. Create an MQL5-to-Hermes connection

Here is a starter EA that sends the previous completed M15 gold candle's close and the previous H1 close to Hermes.

Create `Hermes_Gold_Bridge.mq5` in MetaEditor under `MQL5/Experts/`.

```

#property strict
#property version "1.00"

input string HermesURL =
"http://127.0.0.1:8642/v1/chat/completions";

input string HermesKey =
"replace-with-a-long-random-secret";

datetime lastBar = 0;

// Escape text for a JSON string
string JsonEscape(string s)
{
StringReplace(s, "\\", "\\\\");
StringReplace(s, "\"", "\\\"");
StringReplace(s, "\r", "\\r");
StringReplace(s, "\n", "\\n");
return s;
}

int OnInit()
{
EventSetTimer(10);
Print("Hermes Gold Bridge started");
return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
EventKillTimer();
}

void OnTimer()
{
datetime bar = iTime(_Symbol, PERIOD_M15, 1);

if(bar == 0 || bar == lastBar)
return;

double closeM15 = iClose(_Symbol, PERIOD_M15, 1);
double closeH1 = iClose(_Symbol, PERIOD_H1, 1);

if(closeM15 <= 0 || closeH1 <= 0)
return;

string prompt =
"Analyze the following gold market data. "
"Do not place trades. "
"Return BUY, SELL, or WAIT with a brief reason. "
"Symbol: " + _Symbol +
", M15 closed price: " + DoubleToString(closeM15, 3) +
", H1 closed price: " + DoubleToString(closeH1, 3);

string payload =
"{\"model\":\"hermes-agent\","
"\"stream\":false,"
"\"messages\":[{\"role\":\"user\","
"\"content\":\"" + JsonEscape(prompt) + "\"}]}";

char request[];
char response[];
string responseHeaders;

StringToCharArray(payload, request, 0,
WHOLE_ARRAY, CP_UTF8);
ArrayResize(request, ArraySize(request) - 1);

string headers =
"Content-Type: application/json\r\n"
"Authorization: Bearer " + HermesKey + "\r\n";

ResetLastError();

int status = WebRequest(
"POST",
HermesURL,
headers,
15000,
request,
response,
responseHeaders
);

if(status == 200)
{
lastBar = bar;
Print("Hermes response: ",
CharArrayToString(response, 0, -1, CP_UTF8));
}
else
{
Print("Hermes HTTP status: ", status,
" MQL5 error: ", GetLastError());
}
}

```

This is a connection test rather than a full trading engine. It logs the raw API response in the MT5 Experts tab, doesn't yet read your custom indicators, and doesn't execute trades.

To integrate your existing Half Trend, ADX, or Stochastic indicators, use `iCustom()` and `CopyBuffer()` in the EA, then include their actual signal values in the request. Also add a proper JSON parser and validate the returned decision before consuming it.

For live trading: keep the EA fail-closed if Hermes is unavailable, use closed candles, enforce risk limits in MQL5, and test on a demo account. The Strategy Tester cannot directly execute `WebRequest()`, so historical testing needs recorded or simulated Hermes responses.&#x20;

[image](https://www.google.com/s2/favicons?doma ... com\&sz=32)

MQL5 Reference+1



## 5. My recommended next step

I would start with AI-assisted signal validation, not automatic trade execution. Hermes can act as a second layer that checks your existing MQL5 indicator signals and explains why it agrees or disagrees.

You could eventually create a multi-agent trading system:

- Agent 1: H1 gold trend analysis
- Agent 2: M15 entry confirmation
- Agent 3: ADX and momentum filtering
- Agent 4: Fibonacci support and resistance
- Agent 5: Risk management

These would be separate analysis roles coordinated through Hermes, rather than five independent trading systems.

One question that determines how we proceed:

Have you already installed Hermes Agent on your Windows 11 PC?

Yes, Hermes is installed and running

No, I need to install Hermes first

I have Ollama and want Hermes to use my local models

Continue setup&#x20;
These users thanked the author Banzai for the post (total 2):
Jedidiah, Jimmy


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

26
Banzai wrote: Thu Jun 09, 2022 2:56 am Uh no, I don't know Japanese.
I'm American. But I do like anime and manga.
haha I like it too.
This new anime,
Ya Boy Kongming!, is very popular in Japan recently.
These users thanked the author Jedidiah for the post (total 3):
Knight, Banzai, vvFish
Do not show pity: life for life, eye for eye, tooth for tooth, hand for hand, and foot for foot.
Deuteronomy 19:21

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

29

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')
    
These users thanked the author Banzai for the post (total 2):
vvFish, Jedidiah