Skip to main content

kronos-finance

Pythonic wrapper around the Kronos foundation model for OHLCV forecasting across any market.

Built on Kronos

Kronos is the first open-source foundation model for financial candlesticks (K-lines), by the NeoQuasar team, accepted at AAAI 2026, MIT-licensed.

This package (kronos-finance) is a wrapper that turns the upstream research codebase into a pip-installable library with a CLI, a dashboard, multi-source data loaders, and comprehensive tests. All model code comes from the original project — see the Citation section.

A virtual environment keeps your system Python clean and avoids the error: externally-managed-environment (PEP 668) error on Ubuntu 23.04+, macOS Homebrew Python, and Fedora 39+.

python -m venv .venv
source .venv/bin/activate          # macOS / Linux
# .venv\Scripts\activate           # Windows PowerShell

Every install command below assumes you've activated a venv first.

Installation

pip install kronos-finance                       # core (CUDA-enabled PyTorch, ~800MB)
pip install kronos-finance[cn]                   # + AKShare for Chinese A-shares
pip install kronos-finance[global]               # + yfinance for global equities
pip install kronos-finance[crypto]               # + CCXT for crypto exchanges
pip install kronos-finance[qlib]                 # + Qlib for CN finetune data
pip install kronos-finance[ui]                   # + Flask + Plotly for the dashboard
pip install kronos-finance[analysis]             # + pandas-ta + quantstats
pip install kronos-finance[all]                  # everything above

The default install includes CUDA-enabled PyTorch and works on CPU or GPU. For CPU-only or a specific CUDA version, see Hardware below before installing.

Verify the install:

kronos --version

Quickstart (60 seconds)

from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv
import pandas as pd

df = load_ohlcv("AAPL", period="2y", interval="1d")
wrapper = load_kronos(model_id="small")
y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
pred = wrapper.predict(
    df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
    x_timestamp=df["timestamps"].tail(400), y_timestamp=y_ts, pred_len=30,
)
print(pred.head())

Features

  • One-line predict on any market — US equities, CN A-shares, crypto, HK, JP, EU.
  • Multi-source loaders — AKShare, yfinance, CCXT, Qlib, local CSV.
  • Ticker auto-detection — type "600519" and AKShare is picked; type "BTC/USDT" and CCXT is picked.
  • Bundled ticker catalog + user-extendable ~/.kronos/tickers.json.
  • CLI: kronos predict, kronos backtest, kronos ui, kronos tickers.
  • Flask dashboard with candlestick chart, autocomplete, model selector, indicator overlay.
  • Optional indicators (RSI, MACD, BBands) and HTML tearsheets (quantstats).
  • 12 runnable examples + comprehensive docs + a 35-term glossary.

How to predict any ticker in the world

Market Ticker format Source Install extra
US equity AAPL, MSFT, NVDA yfinance [global]
US ETF SPY, QQQ, IWM yfinance [global]
US index ^GSPC, ^DJI, ^IXIC yfinance [global]
CN A-share 600519, 000001, 002594 AKShare [cn]
CN index 000300, 000905 AKShare [cn]
Crypto pair BTC/USDT, ETH/USDT CCXT (Binance default) [crypto]
HK stock 0700.HK, 9988.HK yfinance [global]
JP stock 7203.T, 6758.T yfinance [global]
EU stock ASML.AS, SAP.DE yfinance [global]
Local CSV path to .csv csv_path= (core)

The source="auto" default routes the ticker to the right loader based on its shape.

CLI reference

kronos predict TICKER [--source auto] [--model small] [--interval 1d]
                     [--pred-len 30] [--lookback 400] [--export forecast.csv]
                     [--device cpu]
kronos batch TICKERS_FILE [--output ./out] [--model small] [--pred-len 30]
kronos backtest TICKER [--period 1y] [--interval 1d] [--source auto]
                      [--export tearsheet.html]
kronos ui [--host 127.0.0.1] [--port 5000] [--debug]
kronos tickers list
kronos tickers search QUERY [--market cn|us|crypto|...]
kronos tickers add SYMBOL NAME SOURCE [--exchange binance]
kronos --version
kronos --help

Examples:

kronos predict 600519 --source akshare
kronos predict BTC/USDT --source ccxt --exchange binance

Exit codes: 0 ok, 1 generic error, 2 usage error, 130 SIGINT (clean Ctrl+C).

Python API

from kronos_finance import load_kronos          # model
from kronos_finance.data import load_ohlcv       # data fetchers
from kronos_finance.tickers import catalog, search, add_user_ticker
from kronos_finance.analysis import (
    enrich_with_indicators, forecast_to_returns, make_tearsheet,
)
from kronos_finance import (                      # errors
    KronosFinanceError, TickerNotFoundError,
    DataSourceError, ModelLoadError, PredictionError, CatalogError,
)

Full reference: docs/API.md.

Dashboard

Launch with kronos ui (default http://127.0.0.1:5000):

  • Ticker input with autocomplete from the bundled + user catalog.
  • Candlestick chart with historical in green/red and forecast in blue/purple.
  • Model selector (mini / small / base with parameter counts).
  • Source + interval pickers.
  • Indicator overlay (RSI / MACD / BBands).
  • Recent predictions history with CSV export.
  • Dark / light theme toggle.

Dashboard quickstart

  1. kronos ui from your shell.
  2. Open http://localhost:5000 in any modern browser.
  3. Type a ticker (e.g. AAPL, 600519, BTC/USDT).
  4. Click Predict.
  5. The forecast paints on the chart; the metrics card shows the predicted close and expected return; the row appears in the Recent predictions table.
  6. Click CSV on any row to download that prediction.

The dashboard is also a JSON API. You can drive it from any HTTP client — see examples/22_dashboard_api_reference.py for curl, Python requests, and JavaScript fetch examples for every endpoint.

Full tour (anatomy of the page, every UI element, every error message, production deployment, extending the dashboard): docs/DASHBOARD.md.

Examples

Twenty-two runnable examples covering beginner through advanced workflows. Each one is a complete, runnable file with a docstring explaining what it demonstrates, when to use it, what to expect, and common pitfalls.

# Scenario Extra needed
01 Quickstart: predict AAPL 30 days [global]
02 Multi-ticker US watchlist [global]
03 CN A-share with column-rename walkthrough [cn]
04 Crypto: BTC/USDT 5m from Binance [crypto]
05 Batch sweep across many tickers [global]
06 Indicators + quantstats HTML tearsheet [global,analysis]
07 CLI: kronos predict from a shell script [global]
08 CLI: kronos backtest from a shell script [global]
09 Launch the Flask dashboard [ui]
10 Save/load predictions: CSV, JSON, Parquet [global,analysis]
11 Qlib-format CSV for the upstream finetune [qlib]
12 Load a local Kronos checkpoint (none)
13 Multi-timeframe: 1d + 1h + 5m on one ticker [global]
14 Quantile bands via sample paths [global]
15 Recursive (autoregressive) 1-year forecast [global]
16 Adapt any CSV (English / Chinese / custom columns) (none)
17 Pre-flight environment health check (CI-friendly) (none)
18 Failure mode runbook (6 cases + recovery) (none)
19 Daily cron-job style forecast with lockfile + logs [global]
20 Forecast entirely from a local CSV (no network) (none)
21 Portfolio construction: 3 weighting schemes [global]
22 Dashboard JSON API reference (curl/Python/JS) (none)

Quick-pick by goal:

  • "Show me how to forecast one ticker" → examples/01_quickstart_predict.py
  • "Run a daily forecast across my watchlist" → examples/19_cronjob_daily_forecast.py
  • "Evaluate Kronos vs buy-and-hold" → examples/06_indicators_and_tearsheet.py
  • "I have my own CSV / proprietary data" → examples/16_csv_with_arbitrary_columns.py or examples/20_local_csv_user_data.py
  • "Troubleshoot my installation" → examples/17_healthcheck_environment.py then examples/18_failure_modes_and_recovery.py
  • "Build a portfolio with Kronos signals" → examples/21_portfolio_kronos_weighting.py

Each file starts with a docstring explaining what the example teaches. Browse the index in examples/README.md.

Want to wire Kronos into a trading strategy? See Trading Strategy use cases for backtesting frameworks, broker APIs (IBKR / Alpaca / ccxt), creative uses (volatility sizing, options premium, pairs), live automation patterns, and the disclaimers you need to read first.

Trading Strategy use cases

⚠️ Disclaimer — read this before you trade real money.

This software is for research, education, and software development. Kronos is a statistical model — its outputs are predictions, not advice. The authors, contributors, and Kronos upstream maintainers are not licensed financial advisors, brokers, or dealers. Nothing in this package or repository constitutes a recommendation to buy, sell, or hold any security, derivative, cryptocurrency, or other instrument.

No warranty of profit. Any trading strategy you build using these forecasts can — and will, eventually — lose money. Past model accuracy is not a guarantee of future returns. Backtesting is not the same as live trading because (a) you saw the historical data the model was trained on, (b) you didn't pay slippage, spreads, commissions, fees, funding, borrow, taxes, or market impact, (c) you assumed infinite liquidity and immediate fills. Real markets punish all three.

Regulatory. Depending on where you live, running automated trading software against a brokerage may require a license, registration, or disclosure. You are solely responsible for understanding the law in your jurisdiction before you connect any of the code below to a real account. Examples below reference third-party libraries for technical capability — they are not endorsements.

Risk controls first. Before any live automation: paper-trade it for ≥30 trading days, log every decision, set position-size limits, hard-stop daily-loss circuit breakers, and never risk more than you can afford to lose outright. If a strategy can't survive paper trading, it won't survive real trading.

So: read on for what's possible, build with care, and own the risks.

From forecast to actionable strategy

A Kronos prediction is one input — not a complete strategy. A complete strategy is the loop:

[Forecast]  ->  [Signal]  ->  [Sizing]  ->  [Execution]  ->  [Review]
     ^                                                      |
     +------------------------------------------------------+

This section walks through each ring of that loop with the libraries, APIs, and patterns people actually use.

1. Signal generation — turning forecasts into trades

A Kronos forecast is a 30-bar (or whatever horizon) predicted close trajectory. To turn that into a trade signal, you have many choices. Each has different risk profiles.

Signal logic What it does When to use it
Direction Long if pred_close > last_close, short otherwise First-pass prototype. Loses to spread + slippage.
Threshold Long only if expected_return > +X% (e.g. +3%) Skips weak forecasts; better hit rate.
Magnitude-weighted Position size scales with expected_return (with cap) "Conviction" sizing. Common in quant funds.
Volatility-adjusted Compare predicted return to predicted path vol (Sharpe-like) Avoids being long into a forecasted-messy period.
Regime-conditional Long only when RSI<70 AND expected_return>+2% Reduces drawdown vs raw direction.
Crossover Long when forecast close crosses predicted SMA from below Trend-following flavor.
Path consistency Long if the forecast is monotonic up (no flip-flops) Filters "noise" predictions.

A small pattern, copyable:

import pandas as pd
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

wrapper = load_kronos(model_id="small", device="cpu")
df = load_ohlcv("AAPL", period="1y", interval="1d")
last_close = df["close"].iloc[-1]
y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
pred = wrapper.predict(
    df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
    x_timestamp=df["timestamps"].tail(400),
    y_timestamp=pd.Series(y_ts, name="timestamps"),
    pred_len=30,
)
close_30d = pred["close"].iloc[-1]
expected_return = (close_30d - last_close) / last_close

# Magnitude-weighted long-only with a confidence threshold.
THRESHOLD = 0.03       # require >3% predicted return to engage
SIZE_CAP = 0.10        # never more than 10% of equity in this name
if expected_return > THRESHOLD:
    target_weight = min(expected_return, SIZE_CAP) * 1.0  # tune the multiplier
    side, weight = "BUY", float(target_weight)
elif expected_return < -THRESHOLD:
    side, weight = "SELL", float(min(-expected_return, SIZE_CAP))
else:
    side, weight = "HOLD", 0.0
print(f"{side} {weight:+.2%}")

Don't trust the above numbers. They're a starting point. Backtest, paper-trade, and tune — don't trust a single backtest either; it overfits by construction.

2. Backtesting frameworks

You can wire the wrapper.predict() output into any standard Python backtesting framework. The list below is ordered roughly from simplest to most production-grade.

Quick-and-dirty (you write the loop): see examples/05_batch_predict.py and examples/06_indicators_and_tearsheet.py. ~50 lines of Python, total control, zero dependencies.

backtrader — event-driven, indicator-rich, mature.

import backtrader as bt
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

class KronosSignal(bt.Strategy):
    params = dict(horizon=30, retrain_every=20, threshold=0.03)
    def next(self):
        if len(self.data) % self.p.retrain_every != 0:
            return
        df = self.data.p.dataname  # pre-fetched DataFrame
        # ... call wrapper.predict on df.iloc[:len(self.data)] ...
        # ... emit self.buy() / self.sell() based on expected_return ...

cerebro = bt.Cerebro()
cerebro.addstrategy(KronosSignal)
cerebro.adddata(load_ohlcv("AAPL", period="5y", interval="1d"))
cerebro.broker.setcash(100_000)
cerebro.run()

vectorbt — vectorized, fast, lots of plots. Best for parameter sweeps because it's 100x faster than event-driven frameworks.

zipline-reloaded — the original Quantopian engine. Best if you're porting an old Quantopian algo.

lean / QuantConnect Lean (C# / Python) — institutional-grade, multi-asset, comes with a cloud research environment. Free for paper trading.

freqtrade — dedicated to crypto. Has its own strategy DSL and a backtesting CLI. Plug Kronos as a custom "predictor" source.

nautilus_trader — Rust core, Python API. Professional-grade event-driven backtest + live. Designed for HFT-grade realism.

backtesting.py — minimal, well-documented, ~5-line strategies. Perfect for "I just want to know if the idea is nonsense" sanity checks.

The most common backtest bug is look-ahead bias. Kronos is fit on history up to time T. To predict T+1, it must see only data up to and including T. Re-feeding the actual T+1 close (even by accident via an off-by-one) gives you an oracle that doesn't exist live. The recursive-prediction pattern in examples/15_recursive_predict.py shows the safe way to chain multi-step predictions without leaking.

3. Live trading APIs (brokerage integrations)

Once a backtest is convincing, you can wire the same signal-generation code to a live broker. You are responsible for testing in paper mode first, complying with broker terms of service, and any regulatory requirements in your jurisdiction.

Broker / API Asset classes API style Notes
Interactive Brokers (ib_insync) Stocks, options, futures, FX, bonds worldwide Python wrapper over TWS API Mature. Supports paper trading. Read IBKR docs.
Alpaca (alpaca-py) US stocks + crypto REST + WebSocket Commission-free, paper-trading key is free in minutes. Great starting point.
TD Ameritrade / Schwab US stocks + options REST Being deprecated — Schwab is the successor. OAuth flow.
Tradier US stocks + options REST Developer-friendly. Free sandbox.
Binance / Coinbase / Kraken (via ccxt) Crypto REST + WebSocket ccxt gives a uniform interface to 100+ exchanges.
OANDA (v20 REST) FX, CFDs, metals REST Practice account available.
Polygon.io US stocks, options, forex, crypto REST + WebSocket Real-time + historical ticks. Free tier limited.
Tradegate / DEGIRO / IBKR EU EU stocks varies For European markets.

A minimal Alpaca example to show the wiring (paper trading only — do NOT use live keys until you've tested):

# pip install alpaca-py
from alpaca_trade_api.rest import REST, TimeFrame
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

API_KEY = "PAPER_KEY_HERE"          # <-- from alpaca.markets paper account
API_SECRET = "PAPER_SECRET_HERE"    # <-- never commit real keys
BASE_URL = "https://paper-api.alpaca.markets"

api = REST(API_KEY, API_SECRET, BASE_URL)

# 1. Get Kronos forecast for a ticker
wrapper = load_kronos(model_id="small", device="cpu")
df = load_ohlcv("AAPL", period="1y", interval="1d")
pred = wrapper.predict(...)
last_close = float(df["close"].iloc[-1])
expected = (float(pred["close"].iloc[-1]) - last_close) / last_close

# 2. Decide side + size from the forecast
if expected > 0.03:
    side, qty = "buy", 10
elif expected < -0.03:
    side, qty = "sell", 10
else:
    side, qty = None, 0

# 3. Submit a paper order
if side:
    api.submit_order(
        symbol="AAPL", qty=qty, side=side,
        type="market", time_in_force="day",
    )

Do not run this code with live keys without weeks of paper trading. The model can (and will) make confident wrong predictions. Position sizing, stop-losses, daily-loss circuit breakers, and broker kill switches must be in place first.

4. Creative non-obvious uses

Beyond "buy if up, sell if down," Kronos predictions have uses that don't fit the simple long/short template. Each scenario below is walked through end-to-end: what it does, why it works, what to install, and a copyable command sequence or Python snippet that runs it.

4.1 Volatility-aware position sizing

What it does: Inverts the usual "size up when the forecast is big" intuition. Wide predicted distributions = uncertain forecasts = shrink the position. Tight distributions = confident forecasts = let the position breathe.

Why it works: Kelly-criterion-style sizing says position size should be proportional to edge / variance. Most retail traders do the opposite (large bets on exciting forecasts). Kronos's predicted distribution width is a free, well-calibrated variance estimate.

Install:

pip install kronos-finance[global]

Run:

python examples/14_quantile_bands.py        # generates P10/P50/P90 bands
python -c "
import pandas as pd
bands = pd.read_csv('AAPL_quantile_bands.csv')  # produced by example 14
p10, p90 = bands['P10'].iloc[-1], bands['P90'].iloc[-1]
last_close = bands['P50'].iloc[0]                # the P50 at horizon start
band_width = (p90 - p10) / last_close * 100       # as a % of last close
print(f'30d P90-P10 band width: {band_width:.1f}% of last close')
# <10% = tight, confident -> allow up to 10% of equity
# 10-25% = medium       -> allow 5% of equity
# >25% = wide, fuzzy    -> 0% of equity (sit out)
"

The full pipeline is in examples/14_quantile_bands.py. To wire it into sizing, swap the simple expected_return lookup in examples/21_portfolio_kronos_weighting.py for the (expected_return / band_width) Sharpe-like ratio.

4.2 Options premium selling

What it does: Compares Kronos's predicted 30-day range to the options market's implied-volatility-implied range. If Kronos is narrower than the market, the option premium is overpriced — sell it. If Kronos is wider, the market is underpricing risk — buy it (or skip).

Why it works: Options are priced on volatility, not direction. Kronos's predicted distribution width is a realized-vol-style estimate; comparing it to implied vol from options chains reveals where the market is wrong.

Install:

pip install kronos-finance[global]
pip install yfinance                  # already in [global] but explicit
pip install mibian                    # Black-Scholes implied-vol calculator
pip install wallstreet                # alternative IBKR-lite options chain

Run:

import yfinance as yf
import pandas as pd
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

# 1. Get Kronos's predicted 30-day range
wrapper = load_kronos(model_id="small", device="cpu")
df = load_ohlcv("AAPL", period="1y", interval="1d")
y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
pred = wrapper.predict(
    df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
    x_timestamp=df["timestamps"].tail(400),
    y_timestamp=pd.Series(y_ts, name="timestamps"),
    pred_len=30,
)
kronos_low  = float(pred["low"].min())
kronos_high = float(pred["high"].max())
kronos_range_pct = (kronos_high - kronos_low) / float(df["close"].iloc[-1]) * 100
print(f"Kronos predicted 30d range: {kronos_low:.2f} - {kronos_high:.2f} "
      f"({kronos_range_pct:.1f}% wide)")

# 2. Get the options market's implied vol (ATM 30-day call)
tkr = yf.Ticker("AAPL")
chain = tkr.option_chain()                      # nearest expiry chain
# Find the strike nearest to last close
last_close = float(df["close"].iloc[-1])
calls = chain.calls
nearest_idx = (calls["strike"] - last_close).abs().idxmin()
iv_pct = float(calls.loc[nearest_idx, "impliedVolatility"]) * 100
print(f"Options market implied vol:  {iv_pct:.1f}% "
      f"({calls.loc[nearest_idx, 'strike']} strike)")

# 3. Compare
if kronos_range_pct < iv_pct * 0.7:
    print(">> KRONOS NARROWER THAN MARKET — premium is overpriced.")
    print(">> STRATEGY: sell a covered call (or cash-secured put).")
elif kronos_range_pct > iv_pct * 1.3:
    print(">> KRONOS WIDER THAN MARKET — market is underpricing risk.")
    print(">> STRATEGY: buy protective put (or stay flat / avoid selling premium).")
else:
    print(">> Roughly aligned — no edge, sit out.")

Risk note: Selling options has unlimited downside on uncovered naked calls. Only sell covered calls (against shares you own) or cash-secured puts (against cash equal to strike × 100). Never naked.

4.3 Pairs / stat-arb trading

What it does: Forecast two correlated instruments, watch the spread between them, and trade when the spread's predicted move diverges from its realized move.

Why it works: Two correlated names usually mean-revert. When the spread drifts, it's a temporary dislocation. Kronos forecasts the direction of the reversion; you trade the magnitude.

Install:

pip install kronos-finance[global]
pip install statsmodels                  # for ADF test (mean-reversion proof)
pip install scikit-learn                 # for rolling correlation

Run:

import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import adfuller
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

PAIR_A, PAIR_B = "KO", "PEP"             # or "BTC/USDT", "ETH/USDT"
HORIZON = 30

# 1. Confirm the pair actually mean-reverts (skip if not)
a = load_ohlcv(PAIR_A, period="2y", interval="1d")
b = load_ohlcv(PAIR_B, period="2y", interval="1d")
spread = a["close"].values - b["close"].values
adf_p = adfuller(spread, maxlag=5)[1]
print(f"ADF p-value on spread: {adf_p:.3f} "
      f"(<0.05 = mean-reverting, good candidate)")

if adf_p >= 0.05:
    print("Spread is NOT mean-reverting. Choose another pair.")
else:
    # 2. Forecast each leg separately
    wrapper = load_kronos(model_id="small", device="cpu")
    def forecast_one(ticker):
        d = load_ohlcv(ticker, period="1y", interval="1d")
        last = d["timestamps"].iloc[-1]
        y_ts = pd.date_range(last, periods=HORIZON + 1, freq="1D")[1:]
        return wrapper.predict(
            df=d[["open", "high", "low", "close", "volume", "amount"]].tail(400),
            x_timestamp=d["timestamps"].tail(400),
            y_timestamp=pd.Series(y_ts, name="timestamps"),
            pred_len=HORIZON,
        )
    pred_a, pred_b = forecast_one(PAIR_A), forecast_one(PAIR_B)

    # 3. Predicted spread move
    pred_spread_t0 = float(pred_a["close"].iloc[0] - pred_b["close"].iloc[0])
    pred_spread_tH = float(pred_a["close"].iloc[-1] - pred_b["close"].iloc[-1])
    pred_delta = pred_spread_tH - pred_spread_t0
    print(f"\nPredicted spread change over {HORIZON}d: {pred_delta:+.2f}")

    # 4. Decide side
    last_spread = float(a["close"].iloc[-1] - b["close"].iloc[-1])
    z_score = (last_spread - spread.mean()) / spread.std()
    print(f"Current z-score: {z_score:+.2f}")

    # Simple rule: if z>1 and Kronos thinks spread falls, long B/short A
    #              if z<-1 and Kronos thinks spread rises, long A/short B
    if z_score > 1.0 and pred_delta < 0:
        action = f"LONG {PAIR_B} / SHORT {PAIR_A}"
    elif z_score < -1.0 and pred_delta > 0:
        action = f"LONG {PAIR_A} / SHORT {PAIR_B}"
    else:
        action = "HOLD (no mean-reversion signal)"
    print(f">> ACTION: {action}")

Risk note: Pairs fail when the correlation breaks (one company merges, scandal, delisting). Always have an exit rule: if the rolling 60-day correlation drops below 0.5, unwind the pair.

4.4 Event-driven reaction

What it does: Establishes a "no-event" forecast, lets the event happen (earnings, Fed, CPI), then runs a second forecast using the post-event price action. The gap between the two is the "surprise" you can trade.

Why it works: Markets move on the gap between expectation and reality. Kronos's pre-event forecast IS the expectation model; the post-event model captures the new reality. The difference is the trade.

Install:

pip install kronos-finance[global]
pip install pandas_market_calendars       # NYSE / earnings calendar

Run (manual event workflow):

import json
from datetime import datetime
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

# 1. BEFORE the event: capture the baseline forecast
EVENT_DATE = "2026-10-30"               # edit to your event
TICKER = "AAPL"

df = load_ohlcv(TICKER, period="1y", interval="1d")
last_ts_before = df["timestamps"].iloc[-1]
y_ts = pd.date_range(last_ts_before, periods=31, freq="1D")[1:]

wrapper = load_kronos(model_id="small", device="cpu")
baseline = wrapper.predict(
    df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
    x_timestamp=df["timestamps"].tail(400),
    y_timestamp=pd.Series(y_ts, name="timestamps"),
    pred_len=30,
)
baseline_record = {
    "ticker": TICKER,
    "event": "earnings",                # edit
    "captured_at": datetime.utcnow().isoformat(),
    "predicted_30d_close": float(baseline["close"].iloc[-1]),
}
with open(f"baseline_{TICKER}_{EVENT_DATE}.json", "w") as f:
    json.dump(baseline_record, f, indent=2)
print(f"Saved baseline to baseline_{TICKER}_{EVENT_DATE}.json")
print(f"  Pre-event predicted 30d close: ${baseline_record['predicted_30d_close']:.2f}")

# 2. WAIT FOR THE EVENT. Then run the same script with the same args.
#    Kronos will see new price action and produce a NEW forecast.
# 3. Compare the two:
#    new_record = {...}
#    surprise_pct = (new_record["predicted_30d_close"]
#                     - baseline_record["predicted_30d_close"]) / last * 100
#    if surprise_pct > +5%: market is more bullish than expected -> LONG
#    if surprise_pct < -5%: market is more bearish -> SHORT or reduce

For an automated approach, use pandas_market_calendars to know when US equities are closed for events, then run this script daily.

4.5 Crypto funding-rate arbitrage

What it does: Perpetual futures charge "funding" every 8 hours — longs pay shorts when funding is positive (the contract is priced above spot). Kronos's predicted direction can confirm or contradict the funding-implied direction. When they disagree, you trade.

Why it works: Funding rates embed market sentiment (longs are willing to pay shorts for leverage). Kronos gives an independent direction signal. Combining the two lets you fade extreme funding when Kronos disagrees, with the funding payment compensating you while you wait.

Install:

pip install kronos-finance[crypto]      # installs ccxt

Run:

import ccxt
import pandas as pd
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

SYMBOL = "BTC/USDT"
exchange = ccxt.binance({"enableRateLimit": True})

# 1. Get current funding rate (positive = longs pay shorts)
funding = exchange.fetch_funding_rate(SYMBOL)
rate_pct = float(funding["fundingRate"]) * 100
print(f"Current funding rate: {rate_pct:+.4f}% per 8h "
      f"({rate_pct * 3 * 365:+.1f}% annualized)")

# 2. Get Kronos's predicted direction
wrapper = load_kronos(model_id="small", device="cpu")
df = load_ohlcv(SYMBOL, source="ccxt", exchange="binance",
                interval="5m", limit=500)
y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=121, freq="5min")[1:]
pred = wrapper.predict(
    df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
    x_timestamp=df["timestamps"].tail(400),
    y_timestamp=pd.Series(y_ts, name="timestamps"),
    pred_len=120, T=1.0, top_p=0.9,
)
last_close = float(df["close"].iloc[-1])
expected = (float(pred["close"].iloc[-1]) - last_close) / last_close * 100
print(f"Kronos expected 10h move: {expected:+.2f}%")

# 3. Decision matrix
if rate_pct > 0.01 and expected < -0.5:
    print(">> Funding is POSITIVE (longs over-leveraged), Kronos is BEARISH.")
    print(">> ACTION: SHORT the perp, LONG spot to delta-hedge.")
    print("            Collect funding every 8h until Kronos turns bullish.")
elif rate_pct < -0.01 and expected > 0.5:
    print(">> Funding is NEGATIVE (shorts over-leveraged), Kronos is BULLISH.")
    print(">> ACTION: LONG the perp, SHORT spot to delta-hedge.")
    print("            Collect funding until Kronos turns bearish.")
else:
    print(">> No edge: funding and Kronos agree. Skip.")

Risk note: Funding-rate arb involves both legs moving. If the perp dumps and the spot doesn't, your hedge ratio drifts. Re-hedge every hour or use a market-neutral rebalancer. Also: exchange fees on the spot leg eat into the funding collection.

4.6 Slack / Discord / Telegram signal bot

What it does: Posts daily forecast summaries to a private chat channel. Read-only — no trading. Useful as a "second opinion" or as a notification system before you trade manually.

Why it works: Decisions made in isolation are worse than decisions made with a daily nudge. Even a 30-line bot changes behavior because it forces the model output into a human-readable summary at a consistent time each day.

Install:

pip install kronos-finance[global]
pip install slack-sdk                    # for Slack
pip install discord.py                   # for Discord
pip install python-telegram-bot          # for Telegram

Slack example (read the Slack docs on webhooks first):

# 1. Create an Incoming Webhook in your Slack workspace.
#    Settings -> Apps -> Incoming Webhooks -> Add to Slack.
#    Copy the webhook URL.
import os
from slack_sdk.webhook import WebhookClient
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

slack_url = os.environ["SLACK_WEBHOOK_URL"]   # never commit this
client = WebhookClient(slack_url)

WATCHLIST = ["AAPL", "MSFT", "NVDA"]
wrapper = load_kronos(model_id="small", device="cpu")

def fmt(ticker):
    df = load_ohlcv(ticker, period="1y", interval="1d")
    y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
    pred = wrapper.predict(
        df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
        x_timestamp=df["timestamps"].tail(400),
        y_timestamp=pd.Series(y_ts, name="timestamps"),
        pred_len=30,
    )
    last = float(df["close"].iloc[-1])
    p30 = float(pred["close"].iloc[-1])
    ret = (p30 - last) / last * 100
    arrow = "🟢" if ret > 0 else "🔴"
    return f"{arrow} *{ticker}*: ${last:.2f} → ${p30:.2f} ({ret:+.2f}% in 30d)"

message = "*Kronos daily forecast*\n" + "\n".join(fmt(t) for t in WATCHLIST)
client.send_text(text=message)

The cron entry (Linux/Mac):

0 8 * * 1-5  cd /home/user/kronos-finance && \
            /usr/bin/python3 daily_signal_post.py

For Windows Task Scheduler: same pattern, point at python.exe and daily_signal_post.py. For GitHub Actions schedule: see examples/19_cronjob_daily_forecast.py for the lockfile + JSONL-log pattern that translates directly.

Bot safety: never put trade execution logic in the same bot as the Slack poster. Run them in separate processes with separate credentials. If the Slack token leaks, the worst case is "spammer posts bad forecasts to your private channel."

4.7 Risk dashboard for held positions

What it does: Runs Kronos against every position you currently hold and alerts if the predicted 30-day drawdown exceeds your pre-set loss tolerance. No trading — pure early warning.

Why it works: Most losses come from holding a position through a regime change. Kronos sees the regime shift in price + volume before the news does. The signal isn't "sell" — it's "re-evaluate this position."

Install:

pip install kronos-finance[global]

Run:

# positions.csv format: ticker,shares,cost_basis
# AAPL,100,150.00
# NVDA,50,400.00
import pandas as pd
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

positions = pd.read_csv("positions.csv")
LOSS_TOLERANCE_PCT = 15                    # alert if predicted drawdown > this
wrapper = load_kronos(model_id="small", device="cpu")

alerts = []
for _, row in positions.iterrows():
    ticker, shares, cost = row["ticker"], int(row["shares"]), float(row["cost_basis"])
    df = load_ohlcv(ticker, period="1y", interval="1d")
    y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
    pred = wrapper.predict(
        df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
        x_timestamp=df["timestamps"].tail(400),
        y_timestamp=pd.Series(y_ts, name="timestamps"),
        pred_len=30,
    )
    worst_predicted = float(pred["low"].min())
    worst_drawdown_pct = (worst_predicted - cost) / cost * 100
    current_pnl = (float(df["close"].iloc[-1]) - cost) / cost * 100

    if worst_drawdown_pct < -LOSS_TOLERANCE_PCT:
        alerts.append({
            "ticker": ticker,
            "shares": shares,
            "current_pnl_pct": round(current_pnl, 2),
            "predicted_30d_worst_drawdown_pct": round(worst_drawdown_pct, 2),
            "action": "REVIEW POSITION",
        })
    else:
        print(f"  {ticker:6s}: current P&L {current_pnl:+.2f}%, "
              f"predicted worst 30d drawdown {worst_drawdown_pct:+.2f}% — OK")

if alerts:
    print("\n!!! POSITIONS NEED REVIEW !!!")
    for a in alerts:
        print(f"  {a['ticker']}: {a['predicted_30d_worst_drawdown_pct']:+.2f}% "
              f"predicted drawdown vs {a['current_pnl_pct']:+.2f}% current P&L")
else:
    print("\nAll positions within predicted risk tolerance.")

Pipe the alerts list to your Slack webhook from 4.6 above for a real-time risk dashboard.

4.8 Macro overlay / market-timing filter

What it does: Runs Kronos once a day on broad indexes (SPY, QQQ, BTC, gold ETF). When the aggregate expected return across all of them is negative, raise cash. When positive, be fully invested.

Why it works: Markets cluster — when broad indexes sell off, most stocks sell off with them. A single-market signal can shield a long-only portfolio from the worst drawdowns. It's not fancy, it's not high-Sharpe, but it's historically robust and easy to implement.

Install:

pip install kronos-finance[global]

Run:

from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

UNIVERSE = ["SPY", "QQQ", "IWM", "GLD"]    # broad-market ETFs
THRESHOLD_PCT = 1.0                       # raise cash if aggregate < +1%
wrapper = load_kronos(model_id="small", device="cpu")

expected_returns = []
for ticker in UNIVERSE:
    df = load_ohlcv(ticker, period="1y", interval="1d")
    y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
    pred = wrapper.predict(
        df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
        x_timestamp=df["timestamps"].tail(400),
        y_timestamp=pd.Series(y_ts, name="timestamps"),
        pred_len=30,
    )
    last = float(df["close"].iloc[-1])
    p30 = float(pred["close"].iloc[-1])
    expected_returns.append((p30 - last) / last * 100)

aggregate = sum(expected_returns) / len(expected_returns)
print(f"Aggregate 30d expected return across {UNIVERSE}: {aggregate:+.2f}%")

if aggregate > THRESHOLD_PCT:
    print(">> MACRO SIGNAL: bullish. Be long.")
elif aggregate < -THRESHOLD_PCT:
    print(">> MACRO SIGNAL: bearish. Raise cash or hedge.")
else:
    print(">> MACRO SIGNAL: neutral. No action.")

Run this daily and act on the signal. Add a "no-trade zone" (aggregate in (-1%, +1%)) to avoid over-trading.

4.9 News corroboration with FinBERT

What it does: Pulls today's news for a ticker, runs sentiment analysis with FinBERT, and combines it with Kronos's expected return. When both agree, conviction is high. When they disagree, the model output is suspect.

Why it works: Models fail differently. Kronos fails on regime changes it hasn't seen; FinBERT fails on sarcasm or unusual phrasing. Two independent signals are more reliable than one, especially when the signal disagrees.

Install:

pip install kronos-finance[global]
pip install transformers torch
pip install newsapi-python                # news API client
# export NEWSAPI_KEY=...

Run:

import os
import pandas as pd
from transformers import pipeline
from newsapi import NewsApiClient
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

newsapi = NewsApiClient(api_key=os.environ["NEWSAPI_KEY"])
finbert = pipeline("sentiment-analysis",
                   model="ProsusAI/finbert",
                   tokenizer="ProsusAI/finbert")

TICKER = "AAPL"
QUERY = TICKER if TICKER != "BTC/USDT" else "bitcoin"

# 1. News sentiment
articles = newsapi.get_everything(q=QUERY, language="en",
                                  page_size=20, sort_by="relevancy")
scores = []
for a in articles["articles"]:
    text = (a["title"] or "") + ". " + (a["description"] or "")
    if text.strip():
        r = finbert(text[:512])[0]                  # FinBERT caps at 512 tokens
        # Map to signed score: +1 positive, -1 negative, 0 neutral
        sign = 1.0 if r["label"] == "positive" else (-1.0 if r["label"] == "negative" else 0.0)
        scores.append(sign * r["score"])
news_signal = sum(scores) / len(scores) if scores else 0.0
print(f"FinBERT news signal ({len(scores)} articles): {news_signal:+.3f}")

# 2. Kronos signal
wrapper = load_kronos(model_id="small", device="cpu")
df = load_ohlcv(TICKER, period="1y", interval="1d")
y_ts = pd.date_range(df["timestamps"].iloc[-1], periods=31, freq="1D")[1:]
pred = wrapper.predict(
    df=df[["open", "high", "low", "close", "volume", "amount"]].tail(400),
    x_timestamp=df["timestamps"].tail(400),
    y_timestamp=pd.Series(y_ts, name="timestamps"),
    pred_len=30,
)
last = float(df["close"].iloc[-1])
p30 = float(pred["close"].iloc[-1])
kronos_signal = (p30 - last) / last * 100
print(f"Kronos 30d signal: {kronos_signal:+.2f}%")

# 3. Combine
def bucket(x, bull=2.0, bear=-2.0):
    if x > bull:  return "BULLISH"
    if x < bear:  return "BEARISH"
    return "NEUTRAL"

k = bucket(kronos_signal)
n = bucket(news_signal * 100)              # scale FinBERT to comparable magnitude
print(f"\nKronos: {k}  |  News: {n}")

if k == n and k != "NEUTRAL":
    print(">>> BOTH AGREE — HIGH CONVICTION trade.")
elif k != "NEUTRAL" and n != "NEUTRAL" and k != n:
    print(">>> CONFLICT — investigate before trading (regime change likely).")
else:
    print(">>> No actionable signal.")

Risk note: News APIs have rate limits. newsapi free tier caps at 100 requests/day; for production use, cache the day's articles and only re-query on ticker change.

4.10 Multi-signal ensemble

What it does: Combines Kronos with every other signal in this section into a single weighted score per ticker per day. The weighting is the art — start with equal weights and let walk-forward validation tune them.

Why it works: Ensemble methods beat single models in nearly every domain, finance included. Kronos is one of many inputs; the ensemble averages out the per-model failure modes.

Install:

pip install kronos-finance[global,analysis]

Run:

# Wire all the signals from sections 4.1 - 4.9 into a single daily score.
# The pattern is identical each time:
#   1. Run the signal logic on the same ticker / same day.
#   2. Map the output to a signed number in [-1, +1].
#   3. Multiply by a weight; sum across signals.
#   4. Trade when the weighted sum crosses a threshold.

import pandas as pd
from kronos_finance import load_kronos
from kronos_finance.data import load_ohlcv

WEIGHTS = {
    "kronos_direction":    0.30,    # 4.1 / standard signal
    "kronos_vol_sizing":   0.15,    # 4.1 / Sharpe-like ratio
    "options_iv_edge":     0.15,    # 4.2
    "pairs_signal":        0.10,    # 4.3
    "macro_overlay":       0.15,    # 4.8
    "news_sentiment":      0.15,    # 4.9
}
TICKER = "AAPL"

# Compute each component (pseudo-code — wire the sections above):
components = {
    "kronos_direction":    0.40,    # +0.40 = +4% expected return normalized
    "kronos_vol_sizing":   0.20,
    "options_iv_edge":     -0.30,   # market is pricing more vol than Kronos
    "pairs_signal":        0.50,
    "macro_overlay":       0.60,
    "news_sentiment":      0.20,
}
ensemble_score = sum(WEIGHTS[k] * v for k, v in components.items())
print(f"Ensemble score for {TICKER}: {ensemble_score:+.3f}")

if ensemble_score > 0.30:
    print(">>> Strong ensemble signal: consider long.")
elif ensemble_score < -0.30:
    print(">>> Strong ensemble signal: consider short / reduce.")
else:
    print(">>> Weak or mixed: hold current position.")

Use examples/06_indicators_and_tearsheet.py's quantstats HTML output to compare the ensemble's performance against any single signal over the same window. The ensemble almost always wins on risk-adjusted return, but check anyway.

5. Live automation patterns

A daily forecast that fires automatically (cron / Task Scheduler) is the most common production pattern. See examples/19_cronjob_daily_forecast.py for the basics. To go further:

  • Multi-ticker fan-out with retry. Wrap each forecast in try/except + exponential backoff. One bad ticker (rate-limited, delisted) should not stop the rest.
  • Slack/email on anomaly. If a forecast's expected return moves

    3% from yesterday's prediction, send an alert. The model's change is often more informative than its level.

  • Health checks before orders. Run the health-check example (examples/17_healthcheck_environment.py) as a precondition. If CUDA is broken or HF cache is corrupt, halt the auto-trader.
  • Audit log every order. Persist every order decision (input DataFrame, prediction, signal logic, sizing, timestamp, account state) to a JSONL file. When (not if) you need to debug a bad day, the audit log is the only way.
  • Daily-loss circuit breaker. Track cumulative P&L for the day; if it's worse than -X% of starting equity, halt the bot for the rest of the day. This is more important than the strategy itself.
  • Idempotency keys. Network retries can submit duplicate orders. Use idempotency keys (Alpaca, IBKR both support them) keyed on (ticker, date, side, qty).
  • Read-only mode first. For the first 30 days, run the bot with execution disabled — log what it would have done. Compare against your paper broker's actual fill prices. Only enable execution when the read-only logs match the broker.

6. Tools that pair well with Kronos

You don't need to write everything from scratch. These composable pieces each fill a gap.

  • vectorbt.pro — paid, but the only Python backtester that runs a 10-year tick-level crypto strategy in seconds. Worth it for serious sweeps.
  • quantstats — already a dep of [analysis]. Pull-lev, factor analysis, HTML tearsheets. Use it to compare strategy variants.
  • riskfolio-lib — portfolio optimization (HRP, mean-variance, Black-Litterman). Combine Kronos expected returns with riskfolio-lib's covariance model for a complete portfolio construction stack.
  • empyrical (deprecated) / pyfolio-reloaded — risk and performance metrics.
  • pandas-ta — already a dep. ~130 indicators. Don't reinvent RSI / MACD / Bollinger / ATR / OBV.
  • yfinance — already a dep. Free OHLCV for US/CN/HK tickers with no API key.
  • akshare — already a dep. CN A-share data.
  • ccxt — already a dep. 100+ crypto exchanges.
  • alpaca-trade-api / ib_insync / binance — broker APIs as listed above.
  • great-expectations — schema validation on input data. Catches "today's data has NaN, the model silently predicted NaN, and the bot bought 10,000 shares of garbage." Cheap insurance.
  • pydantic — already a dep. Validate signal outputs before they hit the broker.
  • apscheduler / croniter — robust scheduling if you don't want raw cron.

7. What "good" looks like

After 6 months of running a Kronos-based strategy on a paper account, expect:

  • Win rate: 50-60% on daily-bar direction. Higher than random but lower than naive backtests suggest.
  • Sharpe ratio: 0.5-1.5 after costs, if you've tuned the signal logic well. Realistic, not glamorous.
  • Max drawdown: 10-25% on the underlying, regardless of strategy. The model doesn't prevent drawdowns; it filters which direction you take them.
  • Turnover: 1-5 trades/day across a 10-ticker watchlist if you retrain daily. Costs matter: even $0.005/share adds up at 1k shares/day.
  • Drift: the model's accuracy will slowly degrade over months as market regime shifts. Plan to re-evaluate the signal logic quarterly.

If your backtest claims >2.0 Sharpe, >70% win rate, and <5% drawdown on daily bars, it has a bug. Go find it.

8. Where to learn more

  • examples/05_batch_predict.py — multi-ticker sweep
  • examples/06_indicators_and_tearsheet.py — indicators + quantstats
  • examples/14_quantile_bands.py — distribution-aware sizing
  • examples/19_cronjob_daily_forecast.py — daily automation
  • examples/21_portfolio_kronos_weighting.py — three weighting schemes
  • Backtesting frameworks: see the table above; start with backtesting.py for sanity checks, then vectorbt for sweeps, then backtrader for realism.
  • Live execution: start with Alpaca paper trading (free API key, 5-minute setup) before touching any real-money broker.

And again: this is research software. Don't risk what you can't afford to lose. The first version of your strategy should run in paper mode for at least 30 trading days before you ever let it touch a live account.

Troubleshooting

The most common pitfalls — full list in docs/TROUBLESHOOTING.md:

  • error: externally-managed-environment — you're trying to pip install into system Python. Use a venv (see the section at the top of this README).
  • Model download stalls / 401 from HuggingFace — set HF_TOKEN or run huggingface-cli login.
  • CUDA version mismatch — install PyTorch from the matching CUDA index URL.
  • yfinance rate limit — switch to auto source or add period= to limit.
  • AKShare returns empty — the ticker may have delisted. Try yfinance with 600519.SS.
  • Playwright browser missing — playwright install --with-deps chromium.
  • max_context exceeded — reduce lookback or use Kronos-mini (2048 context).
  • Tz-aware timestamp warning — convert to UTC and drop the tz before passing in.
  • PermissionError on Windows — run your terminal as Administrator or use a venv.

Hardware (CPU vs CUDA)

Kronos runs on both CPU and CUDA GPUs. The default pip install kronos-finance installs the standard PyPI torch wheel, which is CUDA-enabled by default and works on either. There's no separate "GPU version" of kronos-finance.

You pick the device at runtime via --device (CLI) or device= (Python):

kronos predict AAPL --device cpu       # default; works everywhere
kronos predict AAPL --device cuda     # first GPU
kronos predict AAPL --device cuda:0   # specific GPU
wrapper = load_kronos(model_id="small", device="cuda")

Which device should I use?

Model Params CPU latency (30-step forecast) CUDA latency Recommendation
Kronos-mini 4.1M ~2s ~0.2s Either works
Kronos-small 24.7M ~5s ~0.3s Either works
Kronos-base 102.3M ~15s ~1s CUDA recommended

Memory

  • CPU: ~1 GB RAM for small, +500 MB for base.
  • CUDA: ~1 GB VRAM for small, ~2 GB VRAM for base. An RTX 3060 (12 GB) is plenty.

Pinning your CUDA version

If you have an NVIDIA GPU, install PyTorch from the matching CUDA index URL before installing kronos-finance to control which CUDA toolkit version is bundled:

# CUDA 12.1 — match your NVIDIA driver
pip install torch --index-url https://download.pytorch.org/whl/cu121
pip install kronos-finance[global]

# CUDA 11.8
pip install torch --index-url https://download.pytorch.org/whl/cu118
pip install kronos-finance[global]

# CPU-only (smaller download, ~200 MB instead of ~800 MB)
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install kronos-finance[global]

Apple Silicon (M1/M2/M3)

pip install kronos-finance on Apple Silicon uses PyTorch's MPS backend automatically. Pass --device mps in the CLI or device="mps" in the API.

Common CUDA errors

  • CUDA error: no kernel image is available — your PyTorch CUDA version doesn't match your NVIDIA driver. Reinstall PyTorch from the matching index URL above.
  • CUDA out of memory — your GPU is too small. Use Kronos-mini or reduce lookback. On CPU there's no such limit (just slower).
  • CUDA unavailable but requested — your install doesn't have CUDA support, or no GPU is visible. nvidia-smi to check.

How it works

Ticker (e.g. "AAPL")
    │
    ▼  load_ohlcv() auto-detects source -> yfinance
pd.DataFrame [timestamps, open, high, low, close, volume, amount]
    │
    ▼  load_kronos() downloads Kronos-small from HuggingFace
KronosWrapper (model + tokenizer + predictor)
    │
    ▼  wrapper.predict() runs autoregressive Transformer
pd.DataFrame [predicted OHLCV]
    │
    ▼  analysis: indicators + backtest -> HTML tearsheet

Performance & limits

Setting Default Cap Note
pred_len 30 1000 (CLI), no cap in API Larger = slower inference
lookback 400 512 (small/base/large), 2048 (mini) Auto-truncated
sample_count 1 20 Quantile bands need >1
Memory (CPU, small) ~1GB — +500MB for base
Memory (CUDA, base) ~2GB VRAM — RTX 3060+ recommended
Latency (CPU, 30-step, 1 ticker) ~5s — ~1s on CUDA

Development

git clone https://github.com/lordxmen2k/kronos-finance.git
cd kronos-finance
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,test,ui]"
pytest                         # unit + CLI tests
ruff check src/                # lint

See docs/CONTRIBUTING.md.

Citation

If you use this in research, please cite the original Kronos paper:

@inproceedings{kronos2026,
  title  = {Kronos: A Foundation Model for the Language of Financial Markets},
  author = {Shi, Yu and others},
  booktitle = {AAAI},
  year   = {2026},
}

And this wrapper:

@software{kronos_finance,
  author = {lordxmen2k},
  title  = {kronos-finance: A Python wrapper for Kronos},
  year   = {2026},
  url    = {https://github.com/lordxmen2k/kronos-finance}
}

License

MIT — see LICENSE for the full text. The original Kronos project is also MIT; see src/kronos_finance/_vendor/LICENSE_KRONOS for the vendored upstream license.

Acknowledgements

Glossary

See docs/INSTALL.md#glossary for the full 35-term glossary. A quick index of the most important ones:

  • OHLCV — Open, High, Low, Close, Volume. The five columns Kronos expects.
  • K-line — Chinese term for candlestick; same thing.
  • Context length / max_context — maximum past bars the model can see (512 for small/base/large; 2048 for mini).
  • AR / autoregressive — generates outputs one step at a time.
  • Tokenizer — converts continuous OHLCV to discrete tokens before the Transformer.
  • Sample count — number of forecast paths to draw; more = smoother quantile band.
  • Tearsheet — one-page performance report; quantstats generates HTML.
  • Nucleus sampling (top-p) — sampling from smallest token set whose cumulative prob ≥ p.
  • Temperature (T) — sampling temperature; T<1 conservative, T>1 exploratory.
  • Quantile band — uncertainty interval drawn when sample_count > 1.
  • PEP 668 — Python spec marking system Python as externally managed; use venv.
  • Twine — twine upload dist/* publishes to PyPI.
  • Wheel (.whl) — built distribution format.

Release files for kronos-finance 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kronos-finance 0.1.4
File Size Uploaded
kronos_finance-0.1.4.tar.gz 169.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kronos-finance 0.1.4
File Interpreter ABI Platform
kronos_finance-0.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 318.7 kB

Release files / kronos_finance-0.1.4.tar.gz

Download URL kronos_finance-0.1.4.tar.gz
Size 169.5 kB
Tags Source
SHA-256 checksum
How to use checksums
3da467fb18c315bb346e5c0ee91172178252984bdf19595877db14bc50de8ad6
BLAKE2b-256 checksum
How to use checksums
4856ccd980cb7e0833aaa46bf7c458261349737081fa057ae6d796d4e56b4af1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release files / kronos_finance-0.1.4-py3-none-any.whl

Download URL kronos_finance-0.1.4-py3-none-any.whl
Size 149.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
865fd89488527a12ec40797af2bc4b81dbb6612b741b96e91d00b7bfbbe92ebc
BLAKE2b-256 checksum
How to use checksums
1cade358c0cb8f77182aef1dfbe6f695f0656f20660cf70bc73fa2b2c3528911
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.4

Release history Release notifications | RSS feed

0.1.5

2 release files

This release

0.1.4 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page