Run from the kronos-india folder:
python main.py [arguments]Which Kronos model to use for prediction.
| Value | Params | Context Window | Speed (RTX 5070) | Best For |
|---|---|---|---|---|
small |
24.7M | 512 candles (~3 months of 1h data) | ~2-5 sec/stock | Default, balanced |
mini |
4.1M | 2048 candles (~1 year of 1h data) | ~1-2 sec/stock | Longer history |
base |
102.3M | 512 candles | ~8-12 sec/stock | Best accuracy |
python main.py --variant small
python main.py --variant base
python main.py --variant miniWhich market-cap tier to scan for gainers and losers.
| Value | Universe | Stocks Scanned |
|---|---|---|
all |
Large + Mid + Small cap | ~500 stocks |
large |
Nifty 100 | ~100 stocks |
mid |
Nifty Midcap 150 | ~150 stocks |
small |
Nifty Smallcap 250 | ~250 stocks |
python main.py --cap all
python main.py --cap large
python main.py --cap mid
python main.py --cap smallChooses the stock universe. This is the most important lever for signal quality.
scanner(default) — ranks each cap tier's biggest gainers & losers by today's % move, i.e. the day's most volatile movers.nifty50— runs a fixed list of Nifty 50 constituents, no ranking.nifty100— runs a fixed list of Nifty 100 constituents.
Why this matters: the scanner selects stocks because they moved the most
today — by construction the volatile, news-driven names. Backtests and live
tracking both show Kronos has a (small) directional edge on calm, liquid
large-caps and essentially none on volatile movers. The nifty50 / nifty100
universes point the model at the stocks it predicts best, instead of the day's
chaos. The constituent lists are fetched live from NSE (with a hardcoded
fallback). --cap and --top are ignored when a fixed universe is selected.
python main.py --universe nifty50 # fixed liquid large-caps
python main.py --universe nifty100 --interval 1h --trackHow many top gainers and top losers to pick per cap tier.
Default is 10 (10 gainers + 10 losers per tier).
python main.py --top 5 # top 5 gainers + 5 losers per tier
python main.py --top 15 # top 15 gainers + 15 losers per tierCandle size used for fetching data and predicting. Smaller intervals give more candles per day — better resolution for intraday trades.
| Value | Candles/day | Context (512 candles) | History available | Best For |
|---|---|---|---|---|
1h |
7 | ~73 trading days | 730 days | Swing / multi-day |
15m |
25 | ~20 trading days | 60 days | Intraday (recommended) |
5m |
75 | ~7 trading days | 60 days | Detailed intraday |
1m |
375 | ~1.5 trading days | 7 days only | Scalping (limited) |
python main.py --interval 1h # default
python main.py --interval 15m --days 1 # 25 candles for tomorrow
python main.py --interval 5m --days 1 # 75 candles for tomorrowTip: For
--days 1(intraday), use--interval 15mor--interval 5m. For--days 3or more, stick with--interval 1hto have enough history.
How many trading days ahead to predict. Each day = 7 hourly candles (NSE session).
Default is 3 (21 candles = next 3 trading days).
| Value | Candles | Use When |
|---|---|---|
1 |
7 | Pure intraday — tomorrow only |
3 |
21 | Short swing trade (default) |
5 |
35 | Weekly outlook |
Kronos-small and Kronos-base have a 512-candle context window. Predicting more days extends the forecast horizon without reducing context.
python main.py --days 1 # tomorrow only
python main.py --days 3 # next 3 days (default)
python main.py --days 5 # full weekNumber of times Kronos runs prediction per stock. Results are aggregated using median. More samples = more stable signal + better confidence estimate, but slower.
| Value | Speed | Use When |
|---|---|---|
5 |
Very fast | Quick test / exploration |
20 |
Fast (default) | Daily use |
50 |
Moderate | Higher conviction needed |
100 |
Slow | Maximum confidence |
python main.py --samples 5
python main.py --samples 50Skip the NSE scanner entirely and predict specific stocks directly. Accepts one or more NSE ticker symbols or company names — the pipeline resolves names to symbols automatically using a fuzzy index of 2,456 NSE equities.
# By exact symbol
python main.py --symbols RELIANCE
python main.py --symbols RELIANCE TCS INFY HDFCBANK
# By company name (partial names work too)
python main.py --symbols "reliance industries" "hdfc bank" "bajaj finance"
python main.py --symbols "infosys" "zomato" --samples 30
# Mix of symbols and names
python main.py --symbols RELIANCE "hdfc bank" INFYIf a name is ambiguous or the company uses an abbreviated name in the exchange listing (e.g. TCS is listed as "TCS", SBI as "SBI"), a warning is printed and you can fall back to the exact symbol instead.
Save actionable signals (LONG/SHORT only, NO TRADE excluded) to a timestamped CSV
inside the outputs/ folder.
python main.py --saveOutput path: outputs/signals_YYYYMMDD_HHMM_<variant>.csv
Skip the news sentiment analysis step (step 4/6). Useful for faster runs or when offline.
By default, sentiment is on — FinBERT classifies recent Google News headlines per stock into BULLISH / BEARISH / NEUTRAL and displays the result alongside each signal. No API key required.
python main.py --no-sentiment # skip sentiment, faster run
python main.py --symbols RELIANCE --no-sentiment # quick single-stock testNumber of concurrent network workers used when fetching data (OHLCV download, trend analysis, news sentiment, and current-price lookups). These steps are I/O-bound, so running them in parallel substantially speeds up large scans.
Default is 8. Use --workers 1 for fully serial fetching (e.g. to debug, or
if you hit news-feed rate limits).
python main.py --cap all --top 10 # parallel fetch (default 8 workers)
python main.py --workers 1 # serial (slowest, most conservative)Log all actionable signals to the prediction tracker database (outputs/tracker.db).
The candle interval used for the run is stored alongside each signal and replayed during
evaluation — a 15m signal is evaluated on 15m candles, a 1h signal on 1h candles.
The eval_by date is calculated using NSE holiday-aware business days.
After --days trading days have passed, run tracker.py evaluate to check outcomes.
Use this daily to build a performance record and measure model accuracy over time.
python main.py --track
python main.py --cap large --days 3 --track
python main.py --interval 15m --days 1 --track # stores interval=15m for evaluation# Quick test on specific stocks
python main.py --symbols RELIANCE TCS INFY --samples 5
# Full daily scan — all cap tiers, log signals for tracking
python main.py --track
# Large cap only, save CSV + track
python main.py --cap large --save --track
# Mid cap, high-accuracy model, 50 samples
python main.py --cap mid --variant base --samples 50
# Small cap top 5 per tier, tomorrow only
python main.py --cap small --top 5 --days 1
# Best accuracy run
python main.py --variant base --samples 100 --cap large --save --trackThe tracker logs every LONG/SHORT signal and evaluates actual outcomes after the prediction window closes. Use it to measure win rate, P&L, and which setups work best.
# Check outcomes for all matured signals (run after --days trading days)
python tracker.py evaluate
# View full performance report
python tracker.py report
# List all logged signals (last 50)
python tracker.py show
# Force-evaluate signals whose eval_by has passed but were skipped due to data gaps
# (signals with eval_by still in the future are skipped with a clear message)
python tracker.py evaluate --force
# Import a saved signals CSV into the tracker (for runs done without --track)
python tracker.py import --csv outputs/signals_20260604_0410_base.csv
python tracker.py import --csv outputs/signals_...csv --days 1 --interval 15mIf you ran the pipeline with --save but forgot --track, the signals only
exist in the CSV. The import command loads them into tracker.db so they can
be evaluated and reported like any tracked signal:
logged_atis parsed from the filename timestamp (signals_YYYYMMDD_HHMM_*.csv)eval_byis computed from--days(default 3) using NSE holiday-aware business days--intervalsets which candle size to replay during evaluation (default1h)- Duplicate rows (same symbol + direction + logged_at) are skipped, so re-importing is safe
In the Streamlit dashboard, the same feature appears as an "Import CSV into Tracker" expander in the Tracker section whenever saved CSVs exist.
Total signals evaluated : 24
Wins : 15 (62.5%)
Losses : 7
Expired (no hit) : 2
Avg win P&L : +5.8%
Avg loss P&L : -2.4%
Expectancy : +2.7% per trade
--- By Confluence Level ---
STRONG : 8 trades | Win rate 75% | Avg P&L +3.8%
MODERATE : 12 trades | Win rate 58% | Avg P&L +2.1%
--- By Confidence Level ---
HIGH : 10 trades | Win rate 80% | Avg P&L +4.1%
MEDIUM : 11 trades | Win rate 55% | Avg P&L +1.8%
LOW : 3 trades | Win rate 33% | Avg P&L -0.6%
--- By Sentiment ---
BULLISH : 9 trades | Win rate 78% | Avg P&L +3.9%
NEUTRAL : 11 trades | Win rate 55% | Avg P&L +1.4%
BEARISH : 4 trades | Win rate 25% | Avg P&L -1.2%
--- By Cap Tier ---
LARGE cap : 10 trades | Win rate 70% | Avg P&L +3.2%
SMALL cap : 8 trades | Win rate 50% | Avg P&L +0.8%
--- By Direction ---
LONG : 18 trades | Win rate 67% | Avg P&L +2.9%
SHORT : 6 trades | Win rate 50% | Avg P&L +1.1%
--- By Interval ---
1h : 14 trades | Win rate 64% | Avg P&L +2.4%
15m : 10 trades | Win rate 60% | Avg P&L +3.1%
--- Streaks ---
Current streak : 3 × WIN
Best win streak : 5 in a row
Worst loss run : 3 in a row
- P&L is calculated from the actual next-day open price, not the signal's logged close price
- Targets and stop-losses are recalculated from that actual open using the original percentages
- EXPIRED trades are closed at the last candle of the
eval_bydate eval_bydates skip weekends and official NSE holidays
backtest.py replays Kronos over historical intraday windows so you can measure
the model and tune the signal logic without waiting weeks for live outcomes. It
reuses the exact tracker outcome logic, so backtest results and live --track
results are directly comparable.
It reports three things:
- Forecast accuracy — the raw model's directional hit rate (vs a 50% coin flip) and final-close MAPE, independent of any signal thresholds.
- Signal performance — WIN / LOSS / EXPIRED, win rate and average P&L at the current settings.
- Config sweep (
--sweep) — re-scores the cached predictions across a grid oftarget_quantile×min_dir_agreementso you can pick the best defaults. The model runs once per window; the sweep itself is almost free.
# Forecast accuracy + signal performance on a basket
python backtest.py --symbols RELIANCE TCS INFY HDFCBANK --anchors 25
# Tune the target-quantile / agreement defaults
python backtest.py --symbols TATASTEEL VEDL SAIL ADANIENT --days 3 --sweep
# Isolate pure forecast/target quality (skip trend confluence)
python backtest.py --symbols RELIANCE INFY --no-trend --sweepKey options: --anchors N (historical windows per symbol), --samples N
(ensemble size), --variant, --interval, --days, --no-trend, --sweep.
Two findings worth knowing: the forecast directional edge is strongest on
liquid large-caps (calm names, small moves) and weakest on volatile movers; and
a reachable target (target_quantile ≈ 0.5, the default) produces a higher win
rate and far fewer expiries than targeting the single most optimistic predicted
price. See signal_generator.py (TARGET_QUANTILE, MIN_DIR_AGREEMENT).
[BUY ^] INFY [HIGH confidence] Confluence: STRONG ***
Entry: 1202.50
Target: 1237.40 (+2.9%)
Stop Loss: 1190.50 (-1.0%)
R:R Ratio: 2.90:1
Trend: BULLISH (score +5/8)
Sentiment: BULLISH (0.81, 5 headlines)
* Kronos predicts upside 2.9% (target 2.9% / stop 1.0%)
* Monthly: BULLISH (+3.6%) | Weekly: BULLISH (+2.9%)
* RSI 63.7 | ADX 19.0 | Score +5/8
* RVOL 2.1x [VOLUME SPIKE - confirms move] | OBV RISING
| Field | Meaning |
|---|---|
BUY ^ / SELL v |
Long (buy) or Short (sell) signal |
Confidence |
How closely Kronos's samples agreed (HIGH / MEDIUM / LOW) |
Confluence |
Alignment between Kronos signal and weekly + monthly trend |
Entry |
Current price — your trade entry |
Target |
TARGET_QUANTILE (median) of Kronos's per-sample predicted range |
Stop Loss |
Kronos's opposite extreme, clamped to 1.0–2.5% risk band |
R:R Ratio |
Reward-to-risk ratio — minimum 1.5:1 to take the trade |
Sentiment |
FinBERT news sentiment — BULLISH / BEARISH / NEUTRAL |
Trend score |
-8 (strongly bearish) to +8 (strongly bullish) |
RVOL |
Today's volume vs 20-day average — spike confirms the move |
OBV |
On Balance Volume trend — RISING means money flowing in |
| Level | Meaning |
|---|---|
STRONG *** |
Trend score >= +3 (LONG) or <= -3 (SHORT) — strongly agrees with Kronos |
MODERATE ** |
Trend score +1 or +2 (LONG) or -1 or -2 (SHORT) — leans same way |
WEAK * |
Trend score 0 — neutral, take smaller position |
AGAINST TREND |
Trend opposes signal direction — trade blocked, shown as NO TRADE |
Kronos forecasts a full OHLCV range for the prediction window — both a predicted high and a predicted low. The signal generator uses both, instead of imposing fixed profit/loss percentages.
| Target | Stop-loss | |
|---|---|---|
| LONG | TARGET_QUANTILE of per-sample peak highs |
opposite extreme, clamped to the 1.0–2.5% risk band |
| SHORT | TARGET_QUANTILE of per-sample trough lows |
opposite extreme, clamped to the 1.0–2.5% risk band |
Direction is chosen by the ensemble's net-direction vote (the side most of
Kronos's samples close toward), not the larger excursion — a volatile up-spike
can occur even when most samples close lower. A signal only fires if at least
MIN_DIR_AGREEMENT (55%) of samples agree on the direction, the predicted move
toward the target is at least 1.5%, and the reward:risk is at least 1.5:1
(lowered from 2:1 after backtesting — see the Backtesting section).
Why this replaced the old fixed 5–7% target / 2.5% stop:
- The old 5% minimum missed real moves. Large- and mid-cap stocks (TECHM,
INFY, HCLTECH) routinely move 2–4% intraday but rarely 5%+. Those valid,
correctly-predicted moves were discarded as
NO TRADE. In one tracked run, 13 of 14 signals expired with the stock moving the right direction (e.g. THERMAX +2.7%) but never reaching the 7% target — a 0% win rate despite correct direction calls. - The old 7% cap clipped the big winners. When Kronos predicts a large move (e.g. an 8–10% fall, as has happened on large caps), capping the target at 7% left profit on the table and understated the trade's true reward:risk.
- A stop derived from the prediction is meaningful. The stop now sits at the level where Kronos's own forecast is invalidated (its predicted opposite extreme), not at an arbitrary fixed percentage. Because it varies per stock, the R:R filter now does real work — it rejects setups where the predicted risk is large relative to the predicted reward, instead of every trade passing at a constant 2.0.
The two clamps are safety rails, not the main logic:
SL_CAP_PCT = 2.5— caps maximum loss per trade. If Kronos predicts a downside larger than 2.5% on a long, the stop is held at 2.5% (a transient predicted dip could stop you out early — the accepted cost of a hard risk ceiling).SL_FLOOR_PCT = 1.0— prevents a too-tight stop on a narrow predicted range, which would whipsaw out on normal intraday noise.
These four constants live at the top of pipeline/signal_generator.py
(MIN_MOVE_PCT, SL_CAP_PCT, SL_FLOOR_PCT, MIN_RR_RATIO) and can be tuned
once enough tracked signals reveal the best thresholds.
- Predictions cover the next
--daystrading days (default 3 days = 21 hourly candles) - NSE trading hours: 9:15 AM to 3:30 PM IST
- Always place a hard stop-loss order with your broker — do not rely on manual exits
- Kronos predictions are probabilistic, not guaranteed
- Run the pipeline after market close (after 3:30 PM IST) for next-day signals
- Use
--trackdaily to build a performance record — aim for 20-30 signals before drawing conclusions