< Back to Blog

Using a Candlestick Foundation Model for Crypto Trading: Kronos on Alpaca

TL;DR / Key Takeaways

  • Kronos adds an AI candlestick forecast layer to the Alpaca crypto trading bot.
  • The live system uses triple confirmation: sentiment, technical indicators, and Kronos forecast direction.
  • CPU inference on a headless VM is viable because the model is used as a filter, not a high-frequency signal.
  • The article reports early live behavior honestly instead of treating AI model output as guaranteed alpha.

Most retail algo-trading signal stacks look the same. RSI, EMA, MACD, maybe a sentiment feed. Some newer setups bolt on an LLM-based news summarizer and call it AI. That is fine. It works, sometimes, in certain regimes.

Kronos is something different. It is a foundation model for financial candlestick forecasting — a tokenized-bar transformer trained on roughly 12 billion OHLCV records from 45 global exchanges. You feed it a sequence of price candles, and it predicts the next four. Not a classification label, not a buy/sell signal, not a sentiment score. Actual predicted candle values: open, high, low, close.

I wired it into my Alpaca crypto bot in April 2026. Here is what that actually looks like.

What Kronos is and why it is unusual

The model is open-source, published on Hugging Face by NeoQuasar under the handle NeoQuasar/Kronos-small. There is a small variant (99 MB), a base, and a large. I am running the small on a headless Ubuntu VM because it fits comfortably in CPU memory and inference time is acceptable for a 15-minute entry loop.

The tokenizer is NeoQuasar/Kronos-Tokenizer-base at 15.8 MB. The tokenizer is what makes this architecture different from a standard sequence model. Instead of text tokens, it processes OHLCV bars — each candlestick gets tokenized into a structured representation that the transformer can attend over. This is not a repurposed language model fine-tuned on price data. It is a model designed from the ground up for financial time series.

Training corpus is approximately 12 billion candlestick records across 45 exchanges, with heavy crypto representation. That matters because the crypto bot targets BTC, ETH, SOL, and DOGE — assets that are well-represented in the training data.

The model accepts up to 512 bars as context and returns 4 predicted candles. In my setup I use a 60-bar rolling context window on 1-hour crypto bars. The prediction covers the next 4 hours of price action.

Why almost nobody is using this yet

The model was released mid-2025. As of April 2026 there are almost no tutorials, no blog posts beyond the model card, and no open-source trading bots wired to it publicly. The reasons are:

The pipeline is non-obvious. You cannot just call model.predict(price_array). You have to pull OHLCV bar data in the correct format, run it through the custom tokenizer, feed the token sequence to the predictor, and then interpret the output candles to derive a directional signal. Each of those steps requires reading source code rather than documentation, because the documentation is sparse.

Most retail traders reach for indicators because indicators are well-documented, simple to implement, and their failure modes are understood. Kronos is harder to integrate and its failure modes are less intuitive. When RSI gives you a bad signal you know why. When Kronos gives you a bad forecast the reason lives somewhere in 12 billion training examples.

How kronos_signal.py works

| Signal layer | Data used | Decision role | Failure mode it helps offset | | --- | --- | --- | --- | | Adanos sentiment | Market/news sentiment feed | Directional context | Technical-only false positives | | Technical indicators | Price and volume history | Trend and momentum confirmation | Sentiment noise | | Kronos forecast | Candlestick sequence prediction | AI confirmation filter | Lagging indicator entries |

The signal module loads the model and tokenizer once at import time. Not per cycle, not per symbol — once. The objects stay in memory for the lifetime of the process. Reloading 99 MB on every 15-minute cycle is unnecessary and slows the entry loop materially.

# Source: predictandprofit.io
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

MODEL_ID = "NeoQuasar/Kronos-small"
TOKENIZER_ID = "NeoQuasar/Kronos-Tokenizer-base"
MIN_CONFIDENCE = 0.55
CONTEXT_BARS = 60

tokenizer = AutoTokenizer.from_pretrained(TOKENIZER_ID)
model = AutoModelForCausalLM.from_pretrained(MODEL_ID)
model.eval()

The get_signal() function takes a symbol string and asset type, pulls historical bars from Alpaca, tokenizes them, runs inference, and returns a structured dict.

# Source: predictandprofit.io
from alpaca.data.historical import CryptoHistoricalDataClient
from alpaca.data.requests import CryptoBarsRequest
from alpaca.data.timeframe import TimeFrame
from datetime import datetime, timedelta, timezone

def get_kronos_signal(symbol: str) -> dict:
    client = CryptoHistoricalDataClient()
    end = datetime.now(timezone.utc)
    start = end - timedelta(hours=CONTEXT_BARS + 4)

    request = CryptoBarsRequest(
        symbol_or_symbols=symbol,
        timeframe=TimeFrame.Hour,
        start=start,
        end=end,
    )
    bars = client.get_crypto_bars(request).df

    if bars.empty or len(bars) < CONTEXT_BARS:
        return {"signal": "FLAT", "confidence": 0.0, "actionable": False}

    # Pull the last CONTEXT_BARS rows, extract OHLCV
    recent = bars.tail(CONTEXT_BARS)[["open", "high", "low", "close", "volume"]]
    ohlcv = recent.values.tolist()

    # Tokenize and run inference
    inputs = tokenizer(ohlcv, return_tensors="pt")
    with torch.no_grad():
        outputs = model(**inputs)

    # Interpret predicted candles
    predicted_candles = outputs.sequences  # shape: (1, 4, 5)
    last_close = ohlcv[-1][3]
    predicted_close = predicted_candles[0][-1][3].item()

    pct_change = (predicted_close - last_close) / last_close

    if pct_change > 0.003:
        signal = "UP"
        confidence = min(abs(pct_change) * 50, 1.0)
    elif pct_change < -0.003:
        signal = "DOWN"
        confidence = min(abs(pct_change) * 50, 1.0)
    else:
        signal = "FLAT"
        confidence = 1.0 - abs(pct_change) * 100

    return {
        "signal": signal,
        "confidence": round(confidence, 3),
        "actionable": confidence >= MIN_CONFIDENCE,
    }

The confidence derivation is intentionally simple. If Kronos predicts a 0.3% move up, that is weak — inside the noise of normal hourly variance. If it predicts 0.8%, I care. The scaling factor maps that into a 0–1 confidence range where anything below 0.55 is treated as uncertain.

Honest accuracy numbers

On 1-hour BTC/USD bars: price prediction error consistently under 0.4%. Directional accuracy: 25–75% depending on market regime.

That range is wide and I want to be clear about what it means. In low-volatility trending regimes, directional accuracy runs toward the high end. In choppy sideways price action or during news-driven shock moves, Kronos has no edge. A 12-billion-bar training corpus does not help you predict a macro shock.

The 25% floor is important to understand. Random chance is 50% for binary direction. A model consistently hitting 25% directional accuracy is actively wrong — it has learned something, just inverted. That does not happen often, but it is why Kronos is a confirmation signal in my stack, not the primary signal.

If the model is confidently wrong 30% of the time in certain regimes, using it as a primary signal will blow up your P&L in those regimes. Using it as a third gate after technicals and sentiment means the only trades it kills are the ones that were already marginal.

How it fits into the dual-bot architecture

Crypto bot (crypto_bot.py): Kronos is the backup signal. The primary signal is Adanos sentiment — social sentiment from Reddit and prediction market activity. When Adanos comes back neutral (buzz exists but no clear directional lean), Kronos gets queried. If Kronos says UP with confidence above 0.55 and nothing bearish is in the news feed, the bot enters a smaller-than-normal position. Kronos-only entries are sized at 60% of the sentiment-confirmed entry size.

Stock bot (bot.py): Kronos is the third gate in a triple-confirmation system. Technicals must fire (RSI in range, EMA trending, MACD positive and rising). Finnhub news must not be bearish. Kronos must confirm UP. All three gates open or no trade. This eliminated the problem the bot had in its first week: the momentum lane was firing on technical signals that looked good but were entering into crowded, extended setups that reversed immediately. Adding Kronos as a final check cut those bad entries significantly.

CPU inference on a headless VM

The bot runs on an Ubuntu VM with no GPU. Inference on Kronos-small takes 2–4 seconds per symbol on CPU. The crypto entry loop runs every 15 minutes, scanning 4 symbols. At 4 seconds per Kronos call plus API latency, the loop completes in under 30 seconds on the worst cycle. That is acceptable.

The next architecture decision I am evaluating: run the larger Kronos variant (Kronos-base or Kronos-large) on a Windows host machine with 64 GB RAM and a dedicated GPU, and have the VM call it over the local network. The larger model should improve directional accuracy in trending regimes, and offloading inference to a GPU reduces the per-call time to under a second. That upgrade would also make the system extensible — one GPU inference server serving both bots and potentially a future options signal layer.

First live result

On April 6, 2026, the first BTC/USD paper trade closed at +$1.39 profit on a $50 position — +2.85%. Small number. But it proved the full pipeline worked end to end: bar data in, tokenizer, inference, signal out, entry, position tracking, trailing stop, exit. The plumbing held.

The 30-day paper trading evaluation runs through early May 2026. I will publish results regardless of outcome.


The Kronos integration is part of the Alpaca trading system, which is separate from the Kalshi weather bot. If you are interested in the Kalshi system:

Get the Predict & Profit Source Code — $67

Read the Ebook — $9.99 on Amazon

Frequently Asked Questions

Q: What role does Kronos play in the crypto bot?

A: Kronos provides an AI forecast layer for candlestick direction. It is used as one confirmation signal, not as an independent permission to trade.

Q: Why combine Kronos with sentiment and technical indicators?

A: Different signal families fail in different ways. Requiring agreement reduces false positives and keeps the bot from entering trades on a single noisy model output.

Q: Can Kronos run on a CPU-only VM?

A: Yes, for this use case. The bot uses periodic inference as a filter, so it does not need GPU-speed prediction for high-frequency execution.

Related Reading