-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathORB_Multi_Model_Indicator.pine
More file actions
2184 lines (1949 loc) · 121 KB
/
ORB_Multi_Model_Indicator.pine
File metadata and controls
2184 lines (1949 loc) · 121 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// ============================================================
// ORB Multi-Model Indicator — Pine Script v6
// 6 Opening Range Breakout models + Combined confidence mode
// New York session | Auto DST via IANA timezone
// ============================================================
//@version=6
indicator("ORB Multi-Model Indicator", shorttitle="ORB-MMI", overlay=true,
max_boxes_count=500, max_lines_count=500, max_labels_count=500)
// ============================================================
// SECTION 1 — INPUTS
// ============================================================
// ── Mode ────────────────────────────────────────────────────
var string TT_MODE = "Multi-Model: each enabled model fires independently.\nCombined: single signal scored by confidence factors."
i_comb_on = input.bool(true, "Enable Combined Signal", group="Mode", tooltip="When on, the Combined confidence-scored signal runs alongside all individual models. No need to switch modes.")
// ── Session ─────────────────────────────────────────────────
i_sessionOpen = input.session("0930-1600", "NY Session Window", group="Session",
tooltip="Full NY trading session for signal gating.")
i_timeCutoff = input.int(12, "Signal Cutoff Hour (EST)", minval=10, maxval=15, group="Session",
tooltip="No new signals after this hour (default noon).")
i_orWidthFilter = input.bool(false, "Enable OR Width Filter", group="Session",
tooltip="When enabled: skips all signals if the OR range exceeds the multiplier below.\nUseful for filtering out wide, noisy ORs on news/high-volatility days.\nLeave OFF until per-pair tuning — the right threshold varies by instrument.")
i_orWidthMaxATR = input.float(1.5, " OR Max Width (× ATR14)", minval=0.5, maxval=5.0, step=0.1, group="Session",
tooltip="Maximum OR range as a multiple of ATR14. Only active when OR Width Filter is enabled.\nStart with 2.0–3.0 and tighten per instrument.")
i_refTF = input.timeframe("5", "Primary Reference TF (M3/M4/M6/M7/M9)", group="Session",
tooltip="Reference TF for M3, M4, M6, M7, M9. CSV data: 5m optimal for these models.\nRun the indicator on any chart TF — entries, SL/TP and win rates stay identical.")
i_refTF_fast = input.timeframe("1", "Fast Reference TF (M1, Combined)", group="Session",
tooltip="Reference TF for M1 Crabel and Combined mode. CSV data: 1m optimal (60–90% WR).\nSet to '5' to match primary TF if preferred.")
// ── OR Window lengths ────────────────────────────────────────
i_crabelMins = input.int(15, "Model 1 — Crabel OR Length (min)", minval=5, maxval=60, group="OR Windows")
// ── Model Toggles ────────────────────────────────────────────
i_m1_on = input.bool(true, "Model 1 — Classic Crabel", group="Model Toggles")
i_m3_on = input.bool(true, "Model 3 — 5-Min Scalper", group="Model Toggles")
i_m4_on = input.bool(true, "Model 4 — Standard 15-Min", group="Model Toggles")
i_m6_on = input.bool(false, "Model 6 — FVG ORB", group="Model Toggles",
tooltip="Disabled by default — CSV backtest shows 14–75% WR with only 3–12 trades/pair (unreliable sample size).")
i_m7_on = input.bool(true, "Model 7 — Gold ORB", group="Model Toggles")
i_m9_on = input.bool(false, "Model 9 — Failed ORB Reversal", group="Model Toggles",
tooltip="Disabled by default — CSV backtest shows 25–67% WR (inconsistent across pairs/TFs).")
i_m10_on = input.bool(false, "Model 10 — BDM ORB", group="Model Toggles",
tooltip="Big Daddy Max's 3-phase ORB: 5-min breakout → 1-min retest → 1-min bounce entry.")
i_minAgree = input.int(1, "Min Models to Show Signal", minval=1, maxval=4,
tooltip="Plotshape and alert only shown if at least this many models agree on direction. Set to 2 for consensus mode. Dashboard score always updates regardless.",
group="Model Toggles")
// ── Model 1 (Crabel) ─────────────────────────────────────────
i_m1_nr7 = input.bool(true, "M1: Require NR7/NR4 compression", group="Model 1 — Crabel")
i_m1_rratio = input.float(2.0, "M1: Risk/Reward ratio", group="Model 1 — Crabel", minval=1.0, step=0.5)
i_m1_volMult = input.float(1.2, "M1: Min RVOL (0 = off)", group="Model 1 — Crabel", minval=0.0, step=0.1,
tooltip="Minimum relative volume for M1 signal. Set to 0 to disable the volume filter.")
// ── Model 3 (5-Min Scalper) ──────────────────────────────────
i_m3_volMult = input.float(1.5, "M3: Min volume multiplier (vs avg)", group="Model 3 — 5-Min Scalper",
minval=1.0, step=0.1)
i_m3_rratio = input.float(1.5, "M3: Risk/Reward ratio", group="Model 3 — 5-Min Scalper", minval=1.0, step=0.5)
// ── Model 4 (Standard 15-Min) ────────────────────────────────
i_m4_volMult = input.float(1.2, "M4: Min volume multiplier", group="Model 4 — 15-Min", minval=1.0, step=0.1)
i_m4_rsiMin = input.int(40, "M4: RSI min (longs)", group="Model 4 — 15-Min", minval=20, maxval=60)
i_m4_rsiMax = input.int(60, "M4: RSI max (shorts)", group="Model 4 — 15-Min", minval=40, maxval=80)
i_m4_rratio = input.float(2.0, "M4: Risk/Reward ratio", group="Model 4 — 15-Min", minval=1.0, step=0.5)
// ── Model 6 (FVG) ────────────────────────────────────────────
i_m6_fvgMinSize = input.float(0.1, "M6: Min FVG size (% of OR range)", group="Model 6 — FVG",
minval=0.05, step=0.05)
i_m6_rratio = input.float(2.0, "M6: Risk/Reward ratio", group="Model 6 — FVG",
minval=1.0, step=0.5)
// ── Model 7 (Gold ORB) ───────────────────────────────────────
i_m7_cutoffHour = input.int(12, "M7: Cutoff hour EST (no entries after)", group="Model 7 — Gold ORB",
minval=10, maxval=15)
i_m7_rratio = input.float(2.0, "M7: Risk/Reward ratio", group="Model 7 — Gold ORB", minval=1.0, step=0.5)
i_m7_volMult = input.float(1.2, "M7: Min RVOL (0 = off)", group="Model 7 — Gold ORB", minval=0.0, step=0.1,
tooltip="Minimum relative volume for M7 Gold ORB signal. Set to 0 to disable.")
// ── Model 9 (Failed ORB) ─────────────────────────────────────
i_m9_rratio = input.float(2.0, "M9: Risk/Reward ratio", group="Model 9 — Failed ORB",
minval=1.0, step=0.5)
i_m9_failBars = input.int(5, "M9: Bars inside to confirm failure", group="Model 9 — Failed ORB",
minval=2, maxval=10)
// ── Model 10 (BDM ORB) ───────────────────────────────────────
i_bdm_retestTol = input.float(0.15, "BDM: Retest tolerance (× ATR)", group="Model 10 — BDM ORB",
minval=0.01, maxval=2.0, step=0.01,
tooltip="How close 1-min close must pull back to the breakout level to qualify as a retest, expressed as a multiple of ATR14. 0.15 = within 15% of one ATR. Increase for wider tolerance on volatile instruments like Gold.")
i_bdm_slMult = input.float(0.5, "BDM: SL ATR multiplier", group="Model 10 — BDM ORB",
minval=0.25, maxval=3.0, step=0.25,
tooltip="SL = OR break level ± (ATR × multiplier). Anchored at OR level, not entry close.")
i_bdm_rratio = input.float(2.0, "BDM: Risk/Reward ratio", group="Model 10 — BDM ORB",
minval=1.0, maxval=5.0, step=0.5)
i_bdm_maxWait = input.int(20, "BDM: Max 1-min bars to wait", group="Model 10 — BDM ORB",
minval=5, maxval=50,
tooltip="Max 1-min bars to wait for retest (Phase 2) or bounce (Phase 3). Resets at Phase 2 confirmation.")
// ── Combined Mode weights ─────────────────────────────────────
i_wVol = input.int(20, "Weight: Volume Confirmation (%)", group="Combined Weights", minval=0, maxval=100)
i_wHTF = input.int(20, "Weight: HTF Trend Alignment (%)", group="Combined Weights", minval=0, maxval=100)
i_wDay = input.int(15, "Weight: Day Type / Gap (%)", group="Combined Weights", minval=0, maxval=100)
i_wNR = input.int(10, "Weight: Narrow Range NR7 (%)", group="Combined Weights", minval=0, maxval=100)
i_wATR = input.int(10, "Weight: ATR Volatility (%)", group="Combined Weights", minval=0, maxval=100)
i_wClose = input.int(10, "Weight: Candle Close Quality (%)", group="Combined Weights", minval=0, maxval=100)
i_wVWAP = input.int(10, "Weight: VWAP Alignment (%)", group="Combined Weights", minval=0, maxval=100)
i_wFlow = input.int(5, "Weight: Order Flow Proxy (%)", group="Combined Weights", minval=0, maxval=100)
i_minScore = input.int(60, "Min Confidence Score to Signal (%)", group="Combined Weights",
minval=40, maxval=95)
// ── HTF Selection ─────────────────────────────────────────────
i_htfTF = input.timeframe("60", "HTF Timeframe for Trend", group="HTF Settings",
tooltip="Used for trend alignment in Combined mode and Model 4.")
// ── DXY Settings ──────────────────────────────────────────────
i_dxy_on = input.bool(false, "Enable DXY Correlation Filter",
group="DXY Settings",
tooltip="Enable for XAUUSD/Gold charts only. Adds inverse DXY factor to Combined mode scoring.")
i_dxy_sym = input.string("DXY", "DXY Symbol",
group="DXY Settings",
tooltip="TradingView symbol for the US Dollar Index. Try 'TVC:DXY' or 'CAPITALCOM:DXY' if default unavailable.")
i_dxy_tf = input.timeframe("60", "DXY Timeframe",
group="DXY Settings",
tooltip="Timeframe for DXY EMA trend read. Default 60 (hourly) gives stable macro bias.")
i_wDXY = input.int(15, "Weight: DXY Inverse Correlation (%)",
group="DXY Settings", minval=0, maxval=100,
tooltip="Weight of DXY factor in Combined mode confidence score. Only active when DXY filter is on.")
i_dxy_hard_gate = input.bool(false, "DXY Hard Gate (suppress Combined signals when DXY opposes)",
group="DXY Settings",
tooltip="When enabled: Combined signals are suppressed if DXY direction opposes Gold direction.")
i_dxy_gate_models = input.bool(false, "DXY Hard Gate for All Models (M1/M3/M4/M6/M7/M9/M10)",
group="DXY Settings",
tooltip="When enabled: all individual model signals are also suppressed if DXY direction opposes Gold direction.")
// ── News / Events ─────────────────────────────────────────────
i_news_on = input.bool(false, "Show CPI/NFP on Day Bias Label (Gold charts only)",
group="News / Events",
tooltip="Displays latest US CPI and NFP readings in the Day Bias label with Gold market implications.\nData from TradingView ECONOMICS series — updates within minutes of official release, not real-time.\nAuto-disabled on non-Gold charts.")
// ── Visuals ───────────────────────────────────────────────────
i_showBox = input.bool(true, "Show OR Range Box", group="Visuals")
i_showMidLine = input.bool(true, "Show OR Midline", group="Visuals")
i_showSLTP = input.bool(true, "Show SL/TP Lines", group="Visuals")
i_showLabels = input.bool(true, "Show Signal Labels", group="Visuals")
i_showDashboard = input.bool(true, "Show Dashboard Table", group="Visuals")
i_showPrimeWindow = input.bool(true, "Highlight Prime Window (9:30–10:00 NY)", group="Visuals",
tooltip="Subtle gold background on the first 30 min of NY session — highest-probability ORB window.\nSignal labels show ★ when fired during this window.")
i_dashPos = input.string("Top Right", "Dashboard Position",
options=["Top Right","Top Left","Bottom Right","Bottom Left"], group="Visuals")
i_showTradeTable = input.bool(true, "Show Entries Table", group="Visuals")
i_showBestModel = input.bool(true, "Show Best Model Table", group="Visuals")
i_tradeTablePos = input.string("Bottom Right","Entries Table Position",
options=["Top Right","Top Left","Bottom Right","Bottom Left"], group="Visuals")
i_bestModelPos = input.string("Bottom Left", "Best Model Table Position",
options=["Top Right","Top Left","Bottom Right","Bottom Left"], group="Visuals")
i_maxTrades = input.int(200, "Max Trades in Memory", group="Visuals", minval=20, maxval=1000)
i_colBull = input.color(color.new(#00E676, 0), "Bull Signal Color", group="Visuals")
i_colBear = input.color(color.new(#FF1744, 0), "Bear Signal Color", group="Visuals")
i_colOR = input.color(color.new(#2196F3, 75), "OR Box Fill", group="Visuals")
i_colORLine = input.color(color.new(#2196F3, 0), "OR Line Color", group="Visuals")
i_colSL = input.color(color.new(#FF5252, 0), "SL Line Color", group="Visuals")
i_colTP = input.color(color.new(#69F0AE, 0), "TP Line Color", group="Visuals")
// ── Dashboard text styling ────────────────────────────────────
i_dashTextSize = input.string("Small", "Dashboard Text Size",
options=["Tiny","Small","Normal"], group="Visuals")
i_colTxtMain = input.color(color.white, "Table: Main Text", group="Visuals")
i_colTxtDim = input.color(color.new(#90CAF9, 0), "Table: Dim Text (OR levels)", group="Visuals")
i_colWinHigh = input.color(color.new(#00E676, 0), "Table: Win% High (≥60%)", group="Visuals")
i_colWinMid = input.color(color.new(#FFD54F, 0), "Table: Win% Mid (50–60%)", group="Visuals")
i_colWinLow = input.color(color.new(#FF6E6E, 0), "Table: Win% Low (<50%)", group="Visuals")
// ── Risk ──────────────────────────────────────────────────────
i_atrLen = input.int(14, "ATR Length", group="Risk", minval=5, maxval=50)
i_atrMult = input.float(1.0,"ATR SL Multiplier", group="Risk", minval=0.5, maxval=5.0, step=0.25,
tooltip="SL = ATR * multiplier from OR edge.")
i_volAvgLen = input.int(20, "Volume Average Length", group="Risk", minval=5, maxval=50)
// ============================================================
// SECTION 2 — CONSTANTS
// ============================================================
string NY_TZ = "America/New_York"
string OR_5MIN_STR = "0930-0935"
string OR_15MIN_STR = "0930-0945"
// ── Dynamic OR end-time string helper ────────────────────────
// Converts 9:30 + durationMins to "HHMM" string (handles hour roll-over)
f_orEndTime(durationMins) =>
totalMins = 9 * 60 + 30 + durationMins
endH = int(totalMins / 60) // int() truncates float division → integer hours
endM = totalMins - endH * 60 // remainder = minutes
hStr = endH < 10 ? "0" + str.tostring(endH) : str.tostring(endH)
mStr = endM < 10 ? "0" + str.tostring(endM) : str.tostring(endM)
"0930-" + hStr + mStr
crabelSessionStr = f_orEndTime(i_crabelMins)
// ── Pip/point size — auto-detected per instrument ─────────────
// Priority: Gold → Silver → JPY → standard forex → everything else (indices, CFDs, futures, crypto)
// Using syminfo.type for forex detection avoids the mintick ambiguity on CFD data feeds.
_pipSize =
str.upper(syminfo.basecurrency) == "XAU" or
str.contains(str.upper(syminfo.ticker), "XAU") or
str.contains(str.upper(syminfo.ticker), "GOLD") ? 0.1 : // Gold (XAUUSD, XAUEUR, GOLD…)
str.upper(syminfo.basecurrency) == "XAG" or
str.contains(str.upper(syminfo.ticker), "XAG") ? 0.001 : // Silver
str.upper(syminfo.basecurrency) == "JPY" or str.upper(syminfo.currency) == "JPY" ? 0.01 : // JPY pairs
syminfo.type == "forex" ? 0.0001 : // All other forex (EUR/USD, GBP/USD…)
1.0 // Indices, futures, CFDs, crypto — 1 point
// ============================================================
// SECTION 3 — GLOBAL STATE (var)
// ============================================================
// Day tracking
var int _lastDay = 0
var bool _newDay = false
// 5-min OR
var float _or5H = na
var float _or5L = na
var bool _or5Complete = false
// 15-min OR
var float _or15H = na
var float _or15L = na
var bool _or15Complete = false
// Crabel OR (user-length)
var float _orCrabelH = na
var float _orCrabelL = na
var bool _orCrabelComplete = false
// FVG state
var float _fvgTop = na
var float _fvgBot = na
var bool _fvgBull = false
var bool _fvgBear = false
var bool _fvgTriggered = false
var int _fvgFormedBar = -1 // bar_index when FVG was detected; gate signal to next bar only
// Model 9 — failed ORB state
var bool _m9BullBreak = false
var bool _m9BearBreak = false
var int _m9BreakBar = 0
var int _m9InsideCount = 0
var bool _m9FiredBull = false // once-per-day guard
var bool _m9FiredBear = false // once-per-day guard
// Previous day OHLC
var float _pdHigh = na
var float _pdLow = na
var float _pdClose = na
// Daily ranges for NR7 (rolling 7)
var float[] _dailyRanges = array.new<float>()
// Visual object arrays (for cleanup)
var box[] _boxes = array.new<box>()
var line[] _lines = array.new<line>()
var label[] _labels = array.new<label>()
// Entry-zone box tracking (parallel to trade arrays)
var box[] _trBoxTP = array.new<box>() // TP zone box per trade
var box[] _trBoxSL = array.new<box>() // SL zone box per trade
// OR box drawn flag (per day)
var bool _or15BoxDrawn = false
// Active OR visual objects — extended each bar, automatically freeze at day end
var line _orHLineActive = na
var line _orLLineActive = na
var line _orMidLineActive = na
var box _orBoxActive = na
// Per-day fired flags — belt-and-suspenders: prevent same model firing twice per day per direction
// Reset in _newDay block; read (not written) inside model functions; set true at caller side
var bool _m1FiredBull = false
var bool _m1FiredBear = false
var bool _m3FiredBull = false
var bool _m3FiredBear = false
var bool _m4FiredBull = false
var bool _m4FiredBear = false
var bool _m7FiredBull = false
var bool _m7FiredBear = false
// M10 BDM ORB — Phase state (bull)
var bool _bdmBullBreak = false // Phase 1 confirmed
var bool _bdmBullRetest = false // Phase 2 retest touched
var bool _bdmFiredBull = false // one-signal-per-day guard
var float _bdmBullBreakLevel = na // OR level broken (Phase 1)
var int _bdmBullWaitBars = 0 // fastEdge count since phase start
var line _bdmBullRetestLine = na // visual retest zone line
// M10 BDM ORB — Phase state (bear)
var bool _bdmBearBreak = false // Phase 1 confirmed
var bool _bdmBearRetest = false // Phase 2 retest touched
var bool _bdmFiredBear = false // one-signal-per-day guard
var float _bdmBearBreakLevel = na // OR level broken (Phase 1)
var int _bdmBearWaitBars = 0 // fastEdge count since phase start
var line _bdmBearRetestLine = na // visual retest zone line
// Signal flags — reset each bar, read by plotshape + alertcondition
var bool sig_m1_bull = false
var bool sig_m1_bear = false
var bool sig_m3_bull = false
var bool sig_m3_bear = false
var bool sig_m4_bull = false
var bool sig_m4_bear = false
var bool sig_m6_bull = false
var bool sig_m6_bear = false
var bool sig_m7_bull = false
var bool sig_m7_bear = false
var bool sig_m9_bull = false
var bool sig_m9_bear = false
var bool sig_m10_bull = false
var bool sig_m10_bear = false
var bool sig_comb_bull = false
var bool sig_comb_bear = false
// Combined mode output
var float _confScore = 0.0
var string _confGrade = "F"
var bool _confBull = false
// Dashboard tables
var table _dashTable = na
var table _checkTable = na
// ── Trade Tracking Arrays ─────────────────────────────────────
var float[] _trEntry = array.new<float>() // entry price
var float[] _trSL = array.new<float>() // stop-loss price
var float[] _trTP = array.new<float>() // take-profit price
var int[] _trModel = array.new<int>() // 1-9, 10=combined
var int[] _trDir = array.new<int>() // 1=bull, -1=bear
var int[] _trStatus = array.new<int>() // 0=active, 1=TP hit, -1=SL hit
var int[] _trBar = array.new<int>() // bar_index at entry
var int[] _trDay = array.new<int>() // YYYYMMDD day ID
// Per-model last signal (size 11: index 0-8 = M1-M9, index 9 = Combined, index 10 = M10)
var float[] _lastEntry = array.new<float>(11, na)
var float[] _lastSL = array.new<float>(11, na)
var float[] _lastTP = array.new<float>(11, na)
var int[] _lastDir = array.new<int>(11, 0)
// Dashboard persistent signal state — reset on new day (0=none, 1=bull, -1=bear)
var int[] _dashSignal = array.new<int>(11, 0)
// Per-model win/loss counters (index 0-8 = M1-M9, 9 = Combined, 10 = M10)
// These accumulate permanently — never trimmed, never cross-contaminated by other models
var int[] _mWins = array.new<int>(11, 0)
var int[] _mLosses = array.new<int>(11, 0)
// Per-model current streak: positive = win streak, negative = loss streak
var int[] _mStreak = array.new<int>(11, 0)
// Per-bar agreement counters — reset every bar, incremented at model call sites
var int _agreeCountBull = 0
var int _agreeCountBear = 0
// Per-model confluence score (0–100) — same 11-slot indexing as _lastEntry
var int[] _lastScore = array.new<int>(11, 0)
// Day bias
var string _dayBias = "NEUTRAL"
var float _biasScore = 0.0
// Day Bias chart label (persistent, moves to latest bar each tick)
var label _biasChartLabel = na
// Combined mode pending setup labels (live limit-order preview)
var bool _combLongFired = false
var bool _combShortFired = false
// Entries + Best Model tables
var table _tradeTable = na
var table _bestTable = na
// Economic event "released today" flags + persistent MoM direction
var bool _cpiToday = false // reset daily — true if CPI released this session
var bool _nfpToday = false // reset daily — true if NFP released this session
var float _lastCpiMoM = 0.0 // NOT reset daily — direction of last CPI reading
var float _lastNfpMoM = 0.0 // NOT reset daily — direction of last NFP reading
// ============================================================
// SECTION 4 — TECHNICAL INDICATORS (global scope)
// ============================================================
atr14 = ta.atr(i_atrLen)
volAvg = ta.sma(volume, i_volAvgLen)
rvol = volume / volAvg
ema20 = ta.ema(close, 20)
ema50 = ta.ema(close, 50)
rsi14 = ta.rsi(close, 14)
vwapVal = ta.vwap(hlc3)
// HTF data — must be at global scope
htfClose = request.security(syminfo.tickerid, i_htfTF, close, lookahead=barmerge.lookahead_off)
htfEma20 = request.security(syminfo.tickerid, i_htfTF, ta.ema(close,20), lookahead=barmerge.lookahead_off)
htfEma50 = request.security(syminfo.tickerid, i_htfTF, ta.ema(close,50), lookahead=barmerge.lookahead_off)
// Daily data
dailyATR = request.security(syminfo.tickerid, "D", ta.atr(10), lookahead=barmerge.lookahead_off)
dHigh = request.security(syminfo.tickerid, "D", high, lookahead=barmerge.lookahead_off)
dLow = request.security(syminfo.tickerid, "D", low, lookahead=barmerge.lookahead_off)
dOpen = request.security(syminfo.tickerid, "D", open, lookahead=barmerge.lookahead_off)
pdHigh = request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_off)
pdLow = request.security(syminfo.tickerid, "D", low[1], lookahead=barmerge.lookahead_off)
// ── Reference TF data — signals, ATR, and confirmations all use these ────────
// On any chart TF, a signal fires exactly once per i_refTF bar close.
// close[1] inside request.security refers to the previous REFERENCE TF bar.
ref_close = request.security(syminfo.tickerid, i_refTF, close, lookahead=barmerge.lookahead_off)
ref_close1 = request.security(syminfo.tickerid, i_refTF, close[1], lookahead=barmerge.lookahead_off)
ref_high = request.security(syminfo.tickerid, i_refTF, high, lookahead=barmerge.lookahead_off)
ref_low = request.security(syminfo.tickerid, i_refTF, low, lookahead=barmerge.lookahead_off)
ref_atr14 = request.security(syminfo.tickerid, i_refTF, ta.atr(i_atrLen), lookahead=barmerge.lookahead_off)
ref_rsi14 = request.security(syminfo.tickerid, i_refTF, ta.rsi(close, 14), lookahead=barmerge.lookahead_off)
ref_vwap = request.security(syminfo.tickerid, i_refTF, ta.vwap(hlc3), lookahead=barmerge.lookahead_off)
ref_rvol = request.security(syminfo.tickerid, i_refTF, volume / ta.sma(volume, i_volAvgLen), lookahead=barmerge.lookahead_off)
ref_bearBar = request.security(syminfo.tickerid, i_refTF, close < open, lookahead=barmerge.lookahead_off)
ref_bullBar = request.security(syminfo.tickerid, i_refTF, close > open, lookahead=barmerge.lookahead_off)
ref_time = request.security(syminfo.tickerid, i_refTF, time, lookahead=barmerge.lookahead_off)
pdClose = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_off)
// New ref-TF bar detected: fires on the FIRST chart bar of each new ref-TF period.
// At this moment ref_close = confirmed close of the bar that just completed (lookahead_off).
// Works correctly on ALL historical bars (unlike barstate.isconfirmed which is always true historically).
_refEdge = ref_time != ref_time[1]
// ── Fast Reference TF data (M1 Crabel + Combined mode) ───────────────────────
// Separate request.security block on i_refTF_fast (default "1").
// RSI included for f_modelScore (M1 uses fast TF for scoring).
fast_close = request.security(syminfo.tickerid, i_refTF_fast, close, lookahead=barmerge.lookahead_off)
fast_close1 = request.security(syminfo.tickerid, i_refTF_fast, close[1], lookahead=barmerge.lookahead_off)
fast_high = request.security(syminfo.tickerid, i_refTF_fast, high, lookahead=barmerge.lookahead_off)
fast_low = request.security(syminfo.tickerid, i_refTF_fast, low, lookahead=barmerge.lookahead_off)
fast_atr14 = request.security(syminfo.tickerid, i_refTF_fast, ta.atr(i_atrLen), lookahead=barmerge.lookahead_off)
fast_vwap = request.security(syminfo.tickerid, i_refTF_fast, ta.vwap(hlc3), lookahead=barmerge.lookahead_off)
fast_rvol = request.security(syminfo.tickerid, i_refTF_fast, volume / ta.sma(volume, i_volAvgLen), lookahead=barmerge.lookahead_off)
fast_rsi14 = request.security(syminfo.tickerid, i_refTF_fast, ta.rsi(close, 14), lookahead=barmerge.lookahead_off)
fast_bullBar = request.security(syminfo.tickerid, i_refTF_fast, close > open, lookahead=barmerge.lookahead_off)
fast_bearBar = request.security(syminfo.tickerid, i_refTF_fast, close < open, lookahead=barmerge.lookahead_off)
fast_time = request.security(syminfo.tickerid, i_refTF_fast, time, lookahead=barmerge.lookahead_off)
_fastEdge = fast_time != fast_time[1]
// ── DXY data — inverse correlation scoring ───────────────────
dxy_close = request.security(i_dxy_sym, i_dxy_tf, close, lookahead=barmerge.lookahead_off)
dxy_ema20 = request.security(i_dxy_sym, i_dxy_tf, ta.ema(close, 20), lookahead=barmerge.lookahead_off)
dxy_ema50 = request.security(i_dxy_sym, i_dxy_tf, ta.ema(close, 50), lookahead=barmerge.lookahead_off)
// ── Economic indicators — CPI & NFP for Gold market context ──
// Always run (Pine v6 requirement); i_news_on + _isGoldChart gate display only.
econ_cpi = request.security("ECONOMICS:USCPI", "D", close, lookahead=barmerge.lookahead_off)
econ_nfp = request.security("ECONOMICS:USNFP", "D", close, lookahead=barmerge.lookahead_off)
// Auto-disable DXY on non-Gold charts (XAU* pairs and GOLD ticker variants)
_isGoldChart = syminfo.basecurrency == "XAU"
or str.contains(str.upper(syminfo.ticker), "XAU")
or str.contains(str.upper(syminfo.ticker), "GOLD")
_dxyActive = i_dxy_on and _isGoldChart
// Economic release detection — ta.change() is non-zero only on the bar the value updates
_cpiMoM = ta.change(econ_cpi)
_nfpMoM = ta.change(econ_nfp)
if not na(_cpiMoM) and _cpiMoM != 0
_cpiToday := true
_lastCpiMoM := _cpiMoM
if not na(_nfpMoM) and _nfpMoM != 0
_nfpToday := true
_lastNfpMoM := _nfpMoM
// OR width gates — true = OR is narrow enough to trade, false = skip all signals
// Pass-through when filter is disabled, or when ATR/OR levels are not yet available
_or5WidthOK = not i_orWidthFilter or na(ref_atr14) or na(_or5H) or na(_or5L) or (_or5H - _or5L) <= i_orWidthMaxATR * ref_atr14
_or15WidthOK = not i_orWidthFilter or na(ref_atr14) or na(_or15H) or na(_or15L) or (_or15H - _or15L) <= i_orWidthMaxATR * ref_atr14
_orCrabelWidthOK = not i_orWidthFilter or na(ref_atr14) or na(_orCrabelH) or na(_orCrabelL) or (_orCrabelH - _orCrabelL) <= i_orWidthMaxATR * ref_atr14
// Gate booleans for all-model DXY filter — true = allow signal, false = suppress
// na-safe: pass-through when DXY data unavailable
_dxyBullGate = not _dxyActive or not i_dxy_gate_models or na(dxy_close) or na(dxy_ema20) or dxy_close < dxy_ema20
_dxyBearGate = not _dxyActive or not i_dxy_gate_models or na(dxy_close) or na(dxy_ema20) or dxy_close > dxy_ema20
// ============================================================
// SECTION 5 — SESSION HELPERS
// ============================================================
f_inSession(sess) =>
not na(time(timeframe.period, sess, NY_TZ))
inNYSession = f_inSession(i_sessionOpen)
nyHour = hour(time, NY_TZ)
pastCutoff = nyHour >= i_timeCutoff
in5MinOR = f_inSession(OR_5MIN_STR)
in15MinOR = f_inSession(OR_15MIN_STR)
inCrabelOR = f_inSession(crabelSessionStr)
// Prime window: first 30 min of NY session (9:30–10:00 EST) — highest-probability ORB window
_primeWindow = inNYSession and nyHour == 9
// ============================================================
// SECTION 6 — DAY DETECTION & DAILY RESET
// ============================================================
// Unique day ID: YYYYMMDD using NY timezone
currentDay = year(time, NY_TZ) * 10000 + month(time, NY_TZ) * 100 + dayofmonth(time, NY_TZ)
_newDay := currentDay != _lastDay
if _newDay
_lastDay := currentDay
// Snapshot previous day OHLC
_pdHigh := pdHigh
_pdLow := pdLow
_pdClose := pdClose
// Rolling 7-day ranges for NR7
todayRange = dHigh - dLow
if not na(todayRange) and todayRange > 0
array.push(_dailyRanges, todayRange)
if array.size(_dailyRanges) > 7
array.shift(_dailyRanges)
// Reset all OR tracking
_or5H := na
_or5L := na
_or5Complete := false
_or15H := na
_or15L := na
_or15Complete := false
_orCrabelH := na
_orCrabelL := na
_orCrabelComplete := false
// Reset FVG state
_fvgTop := na
_fvgBot := na
_fvgBull := false
_fvgBear := false
_fvgTriggered := false
_fvgFormedBar := -1
// Reset model state
_m9BullBreak := false
_m9BearBreak := false
_m9BreakBar := 0
_m9InsideCount := 0
_m9FiredBull := false
_m9FiredBear := false
// Reset OR box drawn flag
_or15BoxDrawn := false
// Reset persistent dashboard signal state for new day
for _ri = 0 to 10
array.set(_dashSignal, _ri, 0)
array.set(_lastScore, _ri, 0)
// Reset per-day fired flags
_m1FiredBull := false
_m1FiredBear := false
_m3FiredBull := false
_m3FiredBear := false
_m4FiredBull := false
_m4FiredBear := false
_m7FiredBull := false
_m7FiredBear := false
// M10 BDM ORB reset
_bdmBullBreak := false
_bdmBullRetest := false
_bdmFiredBull := false
_bdmBullBreakLevel := na
_bdmBullWaitBars := 0
line.delete(_bdmBullRetestLine)
_bdmBullRetestLine := na
_bdmBearBreak := false
_bdmBearRetest := false
_bdmFiredBear := false
_bdmBearBreakLevel := na
_bdmBearWaitBars := 0
line.delete(_bdmBearRetestLine)
_bdmBearRetestLine := na
// Reset Combined pending state
_combLongFired := false
_combShortFired := false
// Reset economic event "released today" flags
_cpiToday := false
_nfpToday := false
// Reset per-bar agreement counters
_agreeCountBull := 0
_agreeCountBear := 0
// Reset all signal flags each bar
sig_m1_bull := false
sig_m1_bear := false
sig_m3_bull := false
sig_m3_bear := false
sig_m4_bull := false
sig_m4_bear := false
sig_m6_bull := false
sig_m6_bear := false
sig_m7_bull := false
sig_m7_bear := false
sig_m9_bull := false
sig_m9_bear := false
sig_m10_bull := false
sig_m10_bear := false
sig_comb_bull := false
sig_comb_bear := false
// ============================================================
// SECTION 7 — OR RANGE TRACKING
// ============================================================
// 5-min OR
if in5MinOR
_or5H := na(_or5H) ? high : math.max(_or5H, high)
_or5L := na(_or5L) ? low : math.min(_or5L, low)
else if not _or5Complete and not na(_or5H)
_or5Complete := true
// 15-min OR
if in15MinOR
_or15H := na(_or15H) ? high : math.max(_or15H, high)
_or15L := na(_or15L) ? low : math.min(_or15L, low)
else if not _or15Complete and not na(_or15H)
_or15Complete := true
// Crabel OR (user-configurable)
if inCrabelOR
_orCrabelH := na(_orCrabelH) ? high : math.max(_orCrabelH, high)
_orCrabelL := na(_orCrabelL) ? low : math.min(_orCrabelL, low)
else if not _orCrabelComplete and not na(_orCrabelH)
_orCrabelComplete := true
// ============================================================
// SECTION 8 — FVG DETECTION (inline, after 15-min OR completes)
// ============================================================
// Bullish FVG: bar[2].high < bar[0].low — a gap up; zone = [high[2], low]
// Bearish FVG: bar[2].low > bar[0].high — a gap down; zone = [high, low[2]]
if _or15Complete and not _fvgTriggered and inNYSession
if high[2] < low and not _fvgBull
_fvgTop := low
_fvgBot := high[2]
_fvgBull := true
_fvgBear := false
_fvgFormedBar := bar_index // record formation bar so signal is blocked this bar
if low[2] > high and not _fvgBear
_fvgTop := low[2]
_fvgBot := high
_fvgBull := false
_fvgBear := true
_fvgFormedBar := bar_index
// ============================================================
// SECTION 9 — NR7 / NR4 DETECTION (functions)
// ============================================================
// NR7: today's range narrowest of last 7 days
f_isNR7() =>
sz = array.size(_dailyRanges)
if sz < 2
false
else
todayR = dHigh - dLow
result = true
for i = 0 to math.min(sz, 7) - 1
if todayR > array.get(_dailyRanges, sz - 1 - i)
result := false
result
f_isNR4() =>
sz = array.size(_dailyRanges)
if sz < 2
false
else
todayR = dHigh - dLow
result = true
for i = 0 to math.min(sz, 4) - 1
if todayR > array.get(_dailyRanges, sz - 1 - i)
result := false
result
isNR7 = f_isNR7()
isNR4 = f_isNR4()
// ============================================================
// SECTION 10 — DRAWING HELPERS (functions)
// These only push to global arrays — allowed from UDFs
// ============================================================
f_drawSLTP(entryP, slP, tpP, clrEntry, clrSL, clrTP) =>
if i_showSLTP
// TP zone: entry → TP (profit zone, semi-transparent)
_tpBox = box.new(bar_index, tpP, bar_index + 20, entryP,
bgcolor=color.new(clrTP, 80), border_color=clrTP, border_width=1)
// SL zone: entry → SL (risk zone, semi-transparent)
_slBox = box.new(bar_index, entryP, bar_index + 20, slP,
bgcolor=color.new(clrSL, 80), border_color=clrSL, border_width=1)
array.push(_boxes, _tpBox)
array.push(_boxes, _slBox)
array.push(_trBoxTP, _tpBox)
array.push(_trBoxSL, _slBox)
// Solid entry line
array.push(_lines, line.new(bar_index, entryP, bar_index + 20, entryP,
color=clrEntry, style=line.style_solid, width=2))
else
array.push(_trBoxTP, box(na))
array.push(_trBoxSL, box(na))
// yMult controls vertical offset so labels from different models don't overlap on the same bar
f_drawLabel(modelName, entryP, slP, tpP, isBull, yMult = 0.6, _score = 0.0) =>
if i_showLabels
yOff = isBull ? low - atr14 * yMult : high + atr14 * yMult
_grade = _score >= 80 ? "A" : _score >= 60 ? "B" : _score >= 40 ? "C" : "F"
scoreLine = _score > 0 ? "\nScore: " + str.tostring(math.round(_score)) + "% [" + _grade + "]" : ""
_prefix = _primeWindow ? "★ " : ""
array.push(_labels, label.new(bar_index, yOff,
text = _prefix + modelName +
"\nEntry: " + str.tostring(entryP, format.mintick) +
"\nSL: " + str.tostring(slP, format.mintick) +
"\nTP: " + str.tostring(tpP, format.mintick) +
scoreLine,
color = isBull ? i_colBull : i_colBear,
style = isBull ? label.style_label_up : label.style_label_down,
textcolor = color.white,
size = size.small))
// Register a new trade: push to tracking arrays + update per-model last-signal state
// modelIdx: 1-9 for M1-M9, 10 for Combined, 11 for M10 BDM (array index = modelIdx - 1)
f_registerTrade(modelIdx, entryP, slP, tpP, isBull) =>
arrIdx = modelIdx - 1
array.push(_trEntry, entryP)
array.push(_trSL, slP)
array.push(_trTP, tpP)
array.push(_trModel, modelIdx)
array.push(_trDir, isBull ? 1 : -1)
array.push(_trStatus, 0)
array.push(_trBar, bar_index)
array.push(_trDay, year(time, NY_TZ) * 10000 + month(time, NY_TZ) * 100 + dayofmonth(time, NY_TZ))
array.set(_lastEntry, arrIdx, entryP)
array.set(_lastSL, arrIdx, slP)
array.set(_lastTP, arrIdx, tpP)
array.set(_lastDir, arrIdx, isBull ? 1 : -1)
array.set(_dashSignal, arrIdx, isBull ? 1 : -1)
// ============================================================
// SECTION 11 — OR BOX DRAWING
// ============================================================
// Draw once per day on first bar after 15-min OR completes
// Lines are created with x2 = bar_index; the extension block below advances x2 each bar
// and stops when _or15Complete resets on a new day — so lines never bleed into the next day
if _or15Complete and not _or15BoxDrawn and not na(_or15H) and not na(_or15L)
_or15BoxDrawn := true
orMid = (_or15H + _or15L) / 2.0
if i_showBox
_orBoxActive := box.new(bar_index, _or15H, bar_index, _or15L,
bgcolor=i_colOR, border_color=i_colORLine, border_width=1)
array.push(_boxes, _orBoxActive)
else
_orBoxActive := na
_orHLineActive := line.new(bar_index, _or15H, bar_index, _or15H,
color=i_colORLine, style=line.style_dashed, width=1)
_orLLineActive := line.new(bar_index, _or15L, bar_index, _or15L,
color=i_colORLine, style=line.style_dashed, width=1)
array.push(_lines, _orHLineActive)
array.push(_lines, _orLLineActive)
if i_showMidLine
_orMidLineActive := line.new(bar_index, orMid, bar_index, orMid,
color=color.new(i_colORLine, 50), style=line.style_dotted, width=1)
array.push(_lines, _orMidLineActive)
else
_orMidLineActive := na
// OR level labels
array.push(_labels, label.new(bar_index, _or15H,
text="OR H: "+str.tostring(_or15H, format.mintick),
color=i_colORLine, style=label.style_label_left, textcolor=color.white, size=size.tiny))
array.push(_labels, label.new(bar_index, _or15L,
text="OR L: "+str.tostring(_or15L, format.mintick),
color=i_colORLine, style=label.style_label_left, textcolor=color.white, size=size.tiny))
if i_showMidLine
array.push(_labels, label.new(bar_index, orMid,
text="OR Mid: "+str.tostring(orMid, format.mintick),
color=color.new(i_colORLine, 50), style=label.style_label_left, textcolor=color.white, size=size.tiny))
// Extend active OR lines to the current bar every bar they're alive
// When _or15Complete resets on a new day, this block doesn't run → lines freeze at day's last bar
if _or15Complete and not na(_orHLineActive)
line.set_x2(_orHLineActive, bar_index)
line.set_x2(_orLLineActive, bar_index)
if not na(_orMidLineActive)
line.set_x2(_orMidLineActive, bar_index)
if not na(_orBoxActive)
box.set_right(_orBoxActive, bar_index)
// ── Per-Model Confluence Score ────────────────────────────────
// Pure function — reads params + global htfEma20/htfEma50. Returns 0–100.
// _rc=close, _rvol=rel-volume, _rvwap=vwap, _rsi=RSI14 — all from model's own TF.
f_modelScore(isBull, _rc, _rvol, _rvwap, _rsi) =>
vwapPts = (isBull ? _rc > _rvwap : _rc < _rvwap) ? 25 : 0
volPts = _rvol >= 2.0 ? 25 : _rvol >= 1.5 ? 18 : _rvol >= 1.2 ? 10 : 0
htfPts = isBull ? (htfEma20 > htfEma50 ? 20 : 0) : (htfEma20 < htfEma50 ? 20 : 0)
rsiPts = isBull ? (_rsi >= 45 and _rsi <= 70 ? 15 : 0)
: (_rsi >= 30 and _rsi <= 55 ? 15 : 0)
agreePts = isBull ? math.max(math.min(_agreeCountBull - 1, 3), 0) * 10
: math.max(math.min(_agreeCountBear - 1, 3), 0) * 10
math.min(vwapPts + volPts + htfPts + rsiPts + agreePts, 100)
// ============================================================
// SECTION 12 — MODEL FUNCTIONS (stateless: M1, M3, M4, M7)
// These only read global state and push to arrays — no scalar writes
// ============================================================
// ── Model 1: Classic Crabel ───────────────────────────────────
// Uses fast TF data (_rc/_rc1/_ratr/_edge) — defaults 1m per CSV backtest (60–67% WR)
f_model1(_firedBull, _firedBear, _rc, _rc1, _ratr, _edge, _rvol) =>
bull = false
bear = false
if _orCrabelComplete and not na(_orCrabelH) and not na(_orCrabelL) and _orCrabelWidthOK
if _edge and not pastCutoff and inNYSession
nrPass = i_m1_nr7 ? (isNR7 or isNR4) : true
volOk = i_m1_volMult <= 0 or _rvol >= i_m1_volMult
if nrPass and volOk
slDist = _ratr * i_atrMult
// Bull breakout: entryP = OR breakout level (buy-stop price, TF-consistent).
// close > _orCrabelH confirms breakout; SL/TP are anchored to the OR level.
if _rc > _orCrabelH and _rc1 <= _orCrabelH and not _firedBull
bull := true
entryP = _orCrabelH
slP = entryP - slDist
tpP = entryP + slDist * i_m1_rratio
f_drawSLTP(entryP, slP, tpP, i_colBull, i_colSL, i_colTP)
f_drawLabel("M1 Crabel", entryP, slP, tpP, true, 0.6, f_modelScore(true, _rc, _rvol, fast_vwap, fast_rsi14))
f_registerTrade(1, entryP, slP, tpP, true)
// Bear breakdown
if _rc < _orCrabelL and _rc1 >= _orCrabelL and not _firedBear
bear := true
entryP = _orCrabelL
slP = entryP + slDist
tpP = entryP - slDist * i_m1_rratio
f_drawSLTP(entryP, slP, tpP, i_colBear, i_colSL, i_colTP)
f_drawLabel("M1 Crabel", entryP, slP, tpP, false, 0.6, f_modelScore(false, _rc, _rvol, fast_vwap, fast_rsi14))
f_registerTrade(1, entryP, slP, tpP, false)
[bull, bear]
// ── Model 3: 5-Min Scalper ────────────────────────────────────
// Uses primary TF data — defaults 5m per CSV backtest (71–85% WR)
f_model3(_firedBull, _firedBear, _rc, _rc1, _ratr, _edge, _rvol, _rvwap) =>
bull = false
bear = false
if _or5Complete and not na(_or5H) and not na(_or5L) and _or5WidthOK
if _edge and not pastCutoff and inNYSession
volOk = _rvol >= i_m3_volMult
slDist = _ratr * i_atrMult
if _rc > _or5H and _rc1 <= _or5H and volOk and _rc > _rvwap and not _firedBull
bull := true
entryP = _or5H
slP = entryP - slDist
tpP = entryP + slDist * i_m3_rratio
f_drawSLTP(entryP, slP, tpP, i_colBull, i_colSL, i_colTP)
f_drawLabel("M3 5min", entryP, slP, tpP, true, 2.2, f_modelScore(true, _rc, _rvol, _rvwap, ref_rsi14))
f_registerTrade(3, entryP, slP, tpP, true)
if _rc < _or5L and _rc1 >= _or5L and volOk and _rc < _rvwap and not _firedBear
bear := true
entryP = _or5L
slP = entryP + slDist
tpP = entryP - slDist * i_m3_rratio
f_drawSLTP(entryP, slP, tpP, i_colBear, i_colSL, i_colTP)
f_drawLabel("M3 5min", entryP, slP, tpP, false, 2.2, f_modelScore(false, _rc, _rvol, _rvwap, ref_rsi14))
f_registerTrade(3, entryP, slP, tpP, false)
[bull, bear]
// ── Model 4: Standard 15-Min ──────────────────────────────────
// Uses primary TF data — RSI also from primary TF
f_model4(_firedBull, _firedBear, _rc, _rc1, _ratr, _edge, _rvol, _rvwap, _rrsi) =>
bull = false
bear = false
if _or15Complete and not na(_or15H) and not na(_or15L) and _or15WidthOK
if _edge and not pastCutoff and inNYSession
volOk = _rvol >= i_m4_volMult
htfBull = htfClose > htfEma20 and htfEma20 > htfEma50
htfBear = htfClose < htfEma20 and htfEma20 < htfEma50
slDist = _ratr * i_atrMult
if _rc > _or15H and _rc1 <= _or15H and not _firedBull
if volOk and _rc > _rvwap and _rrsi >= i_m4_rsiMin and _rrsi <= 70 and htfBull
bull := true
entryP = _or15H
slP = entryP - slDist
tpP = entryP + slDist * i_m4_rratio
f_drawSLTP(entryP, slP, tpP, i_colBull, i_colSL, i_colTP)
f_drawLabel("M4 15min", entryP, slP, tpP, true, 3.0, f_modelScore(true, _rc, _rvol, _rvwap, _rrsi))
f_registerTrade(4, entryP, slP, tpP, true)
if _rc < _or15L and _rc1 >= _or15L and not _firedBear
if volOk and _rc < _rvwap and _rrsi <= i_m4_rsiMax and _rrsi >= 30 and htfBear
bear := true
entryP = _or15L
slP = entryP + slDist
tpP = entryP - slDist * i_m4_rratio
f_drawSLTP(entryP, slP, tpP, i_colBear, i_colSL, i_colTP)
f_drawLabel("M4 15min", entryP, slP, tpP, false, 3.0, f_modelScore(false, _rc, _rvol, _rvwap, _rrsi))
f_registerTrade(4, entryP, slP, tpP, false)
[bull, bear]
// ── Model 7: Gold ORB ─────────────────────────────────────────
// Uses primary TF data — 5m per CSV backtest (46–48% WR; marginal but included)
f_model7(_firedBull, _firedBear, _rc, _rc1, _ratr, _edge, _rvol, _rvwap) =>
bull = false
bear = false
if _or15Complete and not na(_or15H) and not na(_or15L) and _or15WidthOK
if _edge and nyHour < i_m7_cutoffHour and inNYSession
slDist = _ratr * i_atrMult
volOk = i_m7_volMult <= 0 or _rvol >= i_m7_volMult
orMid7 = (_or15H + _or15L) / 2
if _rc > _or15H and _rc1 <= _or15H and not _firedBull and volOk and _rc > _rvwap
bull := true
entryP = _rc // confirmed close above OR high
slP = orMid7 - slDist // SL below OR mid + ATR buffer
riskD = entryP - slP
tpP = entryP + riskD * i_m7_rratio
f_drawSLTP(entryP, slP, tpP, i_colBull, i_colSL, i_colTP)
f_drawLabel("M7 Gold", entryP, slP, tpP, true, 2.2, f_modelScore(true, _rc, _rvol, _rvwap, ref_rsi14))
f_registerTrade(7, entryP, slP, tpP, true)
if _rc < _or15L and _rc1 >= _or15L and not _firedBear and volOk and _rc < _rvwap
bear := true
entryP = _rc // confirmed close below OR low
slP = orMid7 + slDist // SL above OR mid + ATR buffer
riskD = slP - entryP
tpP = entryP - riskD * i_m7_rratio
f_drawSLTP(entryP, slP, tpP, i_colBear, i_colSL, i_colTP)
f_drawLabel("M7 Gold", entryP, slP, tpP, false, 2.2, f_modelScore(false, _rc, _rvol, _rvwap, ref_rsi14))
f_registerTrade(7, entryP, slP, tpP, false)
[bull, bear]
// ============================================================
// SECTION 13 — RUN STATELESS MODELS (Multi-Model mode)
// ============================================================
if true
if i_m1_on
// M1 uses fast TF (default 1m) — best per CSV backtest
[b, s] = f_model1(_m1FiredBull, _m1FiredBear, fast_close, fast_close1, fast_atr14, _fastEdge, fast_rvol)
sig_m1_bull := b and _dxyBullGate
sig_m1_bear := s and _dxyBearGate
if b and _dxyBullGate
_m1FiredBull := true
_agreeCountBull += 1
if s and _dxyBearGate
_m1FiredBear := true
_agreeCountBear += 1
if i_m3_on
// M3 uses primary TF (default 5m) — best per CSV backtest
[b, s] = f_model3(_m3FiredBull, _m3FiredBear, ref_close, ref_close1, ref_atr14, _refEdge, ref_rvol, ref_vwap)
sig_m3_bull := b and _dxyBullGate
sig_m3_bear := s and _dxyBearGate
if b and _dxyBullGate
_m3FiredBull := true
_agreeCountBull += 1
if s and _dxyBearGate
_m3FiredBear := true
_agreeCountBear += 1
if i_m4_on
// M4 uses primary TF (default 5m)
[b, s] = f_model4(_m4FiredBull, _m4FiredBear, ref_close, ref_close1, ref_atr14, _refEdge, ref_rvol, ref_vwap, ref_rsi14)
sig_m4_bull := b and _dxyBullGate
sig_m4_bear := s and _dxyBearGate
if b and _dxyBullGate
_m4FiredBull := true
_agreeCountBull += 1
if s and _dxyBearGate
_m4FiredBear := true
_agreeCountBear += 1
if i_m7_on
// M7 uses primary TF (default 5m)
[b, s] = f_model7(_m7FiredBull, _m7FiredBear, ref_close, ref_close1, ref_atr14, _refEdge, ref_rvol, ref_vwap)
sig_m7_bull := b and _dxyBullGate
sig_m7_bear := s and _dxyBearGate
if b and _dxyBullGate
_m7FiredBull := true
_agreeCountBull += 1
if s and _dxyBearGate
_m7FiredBear := true
_agreeCountBear += 1
// ============================================================
// SECTION 15 — MODEL 6: FVG ORB (INLINE — stateful)
// ============================================================
if i_m6_on and _or15Complete and not na(_or15H) and not na(_or15L) and _or15WidthOK