# YouTube-calls section for gen_page.py (exec'd); builds YT_HTML import pandas as pd,json,html RSY=f'{L}/results' YS=pd.read_csv(f'{RSY}/yt_summary.csv').iloc[0]; YC=pd.read_csv(f'{RSY}/yt_calls_scored.csv'); YG=pd.read_csv(f'{RSY}/yt_by_group.csv'); YL=pd.read_csv(f'{RSY}/yt_vs_selflog.csv') YI=pd.read_csv(f'{RSY}/yt_stream_inventory.csv'); YD=pd.read_csv(f'{RSY}/yt_trading_day_coverage.csv'); YM=json.load(open(f'{RSY}/yt_disc_model.json')); YF=pd.read_csv(f'{RSY}/yt_disc_feature_contrast.csv'); YT=pd.read_csv(f'{RSY}/yt_disc_taken_vs_skipped.csv') if __import__('os').path.exists(f'{RSY}/yt_disc_taken_vs_skipped.csv') else None nos=YD[YD.flag.fillna('').str.startswith('NO_STREAM')].date.tolist(); part=int(YD.flag.fillna('').str.startswith('PARTIAL').sum()) sc=YC[YC.status=='scored'].copy(); sc['stream']=[f"{d} {t}" for u,d,t in zip(sc.url,sc.date,sc.entry_ist.str[11:16])] rows=''.join(f"{r.stream}{r.idx} {int(r.strike)} {r.side}{r.sl:g}{'*' if r.sl_fallback else ''} / {r.tg:g}{'*' if r.tg_fallback else ''}{r.entry_bar} @ {r.entry_px:.1f}{r.exit_reason} {r.exit_hm}{r.net_pts:+.1f}{r.claim}{html.escape(str(r.setup))}" for r in sc.itertuples()) CT=""+rows+"
Stream time (IST, links to the original stream at that second)ContractSL / target pts (*=fallback)Entry bar @ priceExitNet ptsHis stated outcomeSetup
" cl=sc[sc.claim.isin(['T','SL'])]; nT=int((cl.claim=='T').sum()); nTT=int(((cl.claim=='T')&(sc.exit_reason=='T')).sum()) G=YG.rename(columns={'Unnamed: 0':'group'}) ov=YL.copy(); ovs=ov.groupby('d').agg(log_rows=('row_id','size'),log_wins=('status_log',lambda s:(s=='WIN').sum()),log_pts=('result_pts','sum'),matched_yt_10min=('min_dt',lambda s:int((s<=10).sum()))).reset_index().rename(columns={'d':'date'}) ovs['stream_that_day']=ovs.date.map(lambda d: 'no stream' if d in nos else 'yes'); ovs['yt_fresh_calls']=ovs.date.map(YC.groupby('date').size()).fillna(0).astype(int) S25=ov[ov.d=='2026-09-25']; NM=int((ov.min_dt<=10).sum()); FC=YF.head(6)[['feature','taken_mean','skipped_mean','smd','mw_p','q_bh']] YT_HTML=f"""

YouTube live calls: a verifiable, pre-outcome log tested on real bars

Added after Satya's 23:15 IST (26-Oct) request. Source: the trader's public live streams at youtube.com/@KarolSEBIRegisteredIITian/streams. We read stream metadata and Hindi auto-captions with yt-dlp, with no login, cookies or credentials. 360p two-second clips were used only to read the strike label on screen, then deleted. No video is embedded or redistributed; the links below go to the original streams. Design pre-registered at 23:40 IST in PREREG.md (YOUTUBE-CALLS), before any call was scored. Research only, not advice.

Verdict. We pulled the calls straight from the stream, so the log is written at the moment of each call, before the outcome. On that log, 29 of 58 fresh calls in 31 sessions (25-Aug to 23-Oct-2026) could be scored on real 1m option bars. They made {YS['mean']:+.1f} net pts per trade (median {YS['median']:+.1f}, win {YS.win:.0f}%, p={YS.p:.2f}). That is percentile {YS.rand_pctile:.0f} against random entries at the same minute, and drops to {YS.drop_top3:+.1f} without the best 3 trades. By the pre-registered rule this does not count as an edge. Of the scored calls he said "target done" on {nT}; real bars hit the target on {nTT}. His self-reported log does not line up with the streams. Of its {len(ov)} rows in this window, {NM} matches a stream call within 10 minutes, and {len(S25)} rows ({S25.result_pts.sum():+.0f} pts) are on 25-Sep, a day with no stream. The taken-vs-skipped analysis found no filter that passes the pre-registered extraction rule, so nothing could be tested on 2024–26 history.

YT1. Inventory

{len(YI)} past live streams, {YI.start_ist.min()[:16]} to {YI.start_ist.max()[:16]} IST ({YI.duration_s.sum()/3600:.0f} hours). All have Hindi auto-captions. Over the {len(YD)} trading days from 06-May to 23-Oct-2026: {int((YD.flag.isna()).sum())} days have full market-hours coverage, {part} partial (<80% of 09:15–15:30), and {len(nos)} have no stream ({', '.join(d[5:] for d in nos)}). Some may have been deleted or never streamed; we cannot tell which. Files: yt_stream_inventory.csv · yt_trading_day_coverage.csv.

YT2. Pilot (22-Oct, 21-Oct and 19-Oct streams) and feasibility

YT3. All 33 streams (25-Aug..23-Oct): what was extracted

289 caption-rule candidates → 110 dropped as promo/gold/crypto talk → 179 reviewed → 58 fresh calls. The other 121 were retrospective, commentary, vague, or conditional plans with no activation. Of the 58: 29 scored, 13 not scorable (Sensex after 05-Oct, no real bars), 9 strike unreadable, 7 side not stated. That is about 1.9 extractable calls per session, against the ~10 per day in his self-reported log.

{CT}

Entry = open of the next 1m bar after the call minute, +0.5 slippage. SL/target are from his spoken points (fallback SL 12 Nifty / 20 Sensex, target 2×SL, marked *). Same-bar SL+target counts as SL. Time exit 15:15. −0.5 slippage at exit plus Rs20/order, STT 0.15%, exchange, SEBI, stamp and GST (Nifty lot 65, Sensex 20). Nifty and Sensex points are different sizes (Sensex premiums are roughly 3× Nifty), so the pooled mean is only indicative. Before charges: {sc.gross_pts.mean():+.1f} per trade. With target forced to 2×SL: {sc.net_1to2.mean():+.1f}.

YT4. Stated vs real, by setup and index

{tb(G,1)}

His stated outcomes are the first “target done/touched” or “SL hit” phrase within 40 minutes of the call. They agree with real bars on {(cl.claim==cl.real).mean()*100:.0f}% of the {len(cl)} scored calls where he stated one. A stated “target done” was a real target hit {nTT} of {nT} times. By stated outcome: “target” calls {G.set_index('group').loc['T','pts']:+.1f} per trade (n={int(G.set_index('group').loc['T','n'])}), “SL” calls {G.set_index('group').loc['SL','pts']:+.1f} (n={int(G.set_index('group').loc['SL','n'])}), no claim {G.set_index('group').loc['no_claim','pts']:+.1f} (n={int(G.set_index('group').loc['no_claim','n'])}).

YT5. Against the self-reported log (overlapping dates)

{tb(ovs,0)}

The log covers only 6 dates in this window. On 25-Sep there was no stream, yet the log shows {len(S25)} trades for {S25.result_pts.sum():+.0f} pts. On 05-Oct the log's strikes (Nifty 24800CE, Sensex 79500) are 2,200 Nifty and 7,300 Sensex points away from that day's market (spot ≈22,580 / ≈72,200). On 01-Sep the log has {int(ovs.set_index('date').loc['2026-09-01','log_rows'])} trades ({int(ovs.set_index('date').loc['2026-09-01','log_wins'])} wins), but the stream that day gives no fresh call with an activation. Overall, {NM} of {len(ov)} log rows match a stream call (same side, ±10 min). The self-reported log is not a record of the live calls.

YT6. Taken vs skipped (redo of the discretion analysis with stream calls)

Same method as the Trader discretion section (s9/s10 code unchanged; YouTube calls with known side as “taken”; fit 25-Aug..30-Sep, holdout 1–23-Oct). 51 calls with a known side → 50 with entry-time features; 315 candidates; fit base take rate {YM.get('base_take_rate_fit',0)*100:.0f}%. 48% of his calls have one of our V1–V4/P1–P8 candidates within ±10 min, same direction. The strongest contrasts: he takes trades after a strong move in his direction (side-aligned spot move over 15 min, standardised difference ≈1.0; 15-min premium change 19.5 vs 9.5 for skipped; BH q<0.01). In plain words, he chases momentum.

{tb(FC,3)}

Tree CV AUC {YM['tree']['lodo_auc']:.2f} / holdout {YM['tree']['holdout_auc']:.2f}; logistic {YM['logit']['lodo_auc']:.2f} / {YM['logit']['holdout_auc']:.2f}. No tree leaf had ≥10 takes at ≥1.2× the base rate, so the pre-registered extraction gives no filter. There is nothing to test on held-out data or 2024–26 history, and we did not go looking for one after the fact. Candidates he took: X_OLD −0.8 pts per trade (n=46). Skipped: −4.2 (n=269). Both are ≤0.

YT7. Limits

Files: yt_calls_scored.csv · yt_review_adjudicated.csv · yt_random_bench.csv · yt_by_group.csv · yt_vs_selflog.csv · yt_disc_candidates.csv · yt_disc_model.json · scripts scripts/yt_*.py, s12_yt_discretion.py, s13_yt_disc_model.py.

"""