CoolFace
Apppublic

lindonstout21/Kronos

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
App README

Kronos Price Forecasting Service

A FastAPI microservice that forecasts OHLCV price bars using the Kronos foundation model (Kronos-small + Kronos-Tokenizer-base). It fetches market data via yfinance, runs the model, and returns predicted candles as JSON — ready to plug into the Stock Command Center dashboard.

┌────────────┐   POST /predict        ┌─────────────────────┐
│  Frontend  │ ─────────────────────► │ prediction_service  │
│ (dashboard)│ ◄───────────────────── │   (FastAPI/uvicorn) │
└────────────┘   JSON forecast        └─────────┬───────────┘
                                                 │
                          ┌──────────────────────┼───────────────────────┐
                          ▼                       ▼                        ▼
                   data_fetcher.py          forecaster.py            kronos/ (model)
                   (yfinance + cache)   (load + predict wrapper)   (cloned upstream)

Layout

FilePurpose
prediction_service.pyFastAPI app: /health, /predict, /predict/{symbol}
forecaster.pyLoads Kronos once; turns a history DataFrame into a forecast
data_fetcher.pyyfinance OHLCV fetch + on-disk TTL cache, formatted for Kronos
test_kronos_integration.pyEnd-to-end tests against real data + real model
kronos/Cloned upstream Kronos repo (provides the model package)
requirements.txtPython dependencies

Setup

Requires Python 3.9+.

bash
cd ~/stock-forecasting
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

The model weights (~25M params for Kronos-small) and tokenizer are pulled from the Hugging Face Hub on first run and cached under ~/.cache/huggingface. Inference runs on CUDA or Apple mps automatically if available, otherwise CPU.

Running the service

bash
# from the project root, with the venv active
uvicorn prediction_service:app --host 0.0.0.0 --port 8000
# or simply:
python prediction_service.py

On startup the service loads the model. Watch for startup: model ready. Until then /predict returns 503.

Configuration (environment variables)

VariableDefaultMeaning
KRONOS_MODELNeoQuasar/Kronos-smallModel checkpoint
KRONOS_TOKENIZERNeoQuasar/Kronos-Tokenizer-baseTokenizer checkpoint
KRONOS_DEVICEautoForce cpu, cuda, or mps
HOST / PORT0.0.0.0 / 8000Bind address
LOG_LEVELINFOPython log level

Endpoints

GET /health

json
{ "status": "ok", "model_loaded": true, "model": "NeoQuasar/Kronos-small",
  "device": "cpu", "max_lookback": 512 }

POST /predict

Forecast from history you supply. The most recent 512 bars are used (Kronos-small's context limit); older bars are ignored.

Request body:

json
{
  "symbol": "AAPL",
  "forecast_periods": 120,
  "temperature": 1.0,
  "top_p": 0.9,
  "sample_count": 1,
  "historical_data": [
    {"open": 195.1, "high": 196.4, "low": 194.0, "close": 195.8,
     "volume": 54213000, "timestamp": "2026-05-01T00:00:00"},
    "... oldest first ..."
  ]
}
  • open/high/low/close are required per bar; volume defaults to 0, timestamp is optional (a daily index is synthesized if omitted, but supplying real timestamps gives the model better temporal features).
  • forecast_periods defaults to 120, range 1..2000.
  • Sampling defaults: temperature=1.0, top_p=0.9, sample_count=1.

Response:

json
{
  "symbol": "AAPL",
  "forecast_periods": 120,
  "lookback_used": 512,
  "device": "cpu",
  "predictions": [
    {"timestamp": "2026-05-02T00:00:00", "open": 196.0, "high": 197.2,
     "low": 195.1, "close": 196.8, "volume": 51200000.0, "amount": 1.0e10}
  ]
}

POST /predict/{symbol}

One-shot convenience: the service fetches history itself via yfinance, then forecasts. No need to ship candles from the browser.

bash
curl -X POST http://localhost:8000/predict/BTC-USD \
  -H 'Content-Type: application/json' \
  -d '{"forecast_periods": 24, "period": "1mo", "interval": "1h"}'

period/interval use yfinance conventions (1mo, 2y, max; 1d, 1h, 5m). Returns the same PredictResponse shape as /predict.

Error codes

CodeMeaning
400Malformed historical_data
404Symbol not found / no data from yfinance
422Valid request but forecast couldn't be produced (e.g. NaNs, bad horizon)
503Model still loading — retry shortly
500Unexpected inference error

Frontend integration (Stock Command Center)

The dashboard is a single-file static app, so call the service over fetch. Run the service locally (or deploy it somewhere reachable) and point the frontend at it.

js
async function getForecast(symbol, periods = 24) {
  const res = await fetch(`http://localhost:8000/predict/${symbol}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ forecast_periods: periods, period: "1mo", interval: "1h" }),
  });
  if (!res.ok) throw new Error(`forecast failed: ${res.status} ${await res.text()}`);
  const data = await res.json();
  // data.predictions: [{ timestamp, open, high, low, close, volume, amount }, ...]
  return data.predictions;
}

If you already hold candles in the browser (e.g. from Massive), use /predict instead and pass them as historical_data (oldest first).

CORS: the dashboard runs on a different origin (Netlify) than this service. Add CORSMiddleware before exposing it to the deployed frontend:

python
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://reliable-snickerdoodle-e9cca1.netlify.app", "http://localhost:*"],
    allow_methods=["POST", "GET"],
    allow_headers=["*"],
)

Forecasts are advisory, not financial advice. Kronos is a probabilistic sampler — raise sample_count to average multiple Monte-Carlo paths for a more stable central estimate, at proportional latency cost.

Testing

bash
.venv/bin/python -m pytest test_kronos_integration.py -v -s

The tests fetch real data (BTC-USD hourly, AAPL daily fallback), generate a 24-period forecast, and validate the output shape via both the forecaster module and the FastAPI TestClient. They skip (rather than fail) when offline or when the model can't be downloaded.

Notes & limits

  • Kronos-small context window is 512 bars — longer histories are truncated to the most recent 512.
  • First request after startup is slowest (model warm-up). Keep the process warm.
  • yfinance results are cached on disk for 15 minutes (data_fetcher.py, DEFAULT_TTL_SECONDS) under .cache/market_data/.