LLM-oriented narration and compression for time series data
Project description
narrata
narrata turns OHLCV price series into compact, deterministic text summaries optimized for LLM context. It has two goals:
- Make agents understand time series. A raw data dump doesn't give an LLM the context it needs — regime, trend, support/resistance, indicator state. narrata extracts that structure and renders it as natural-language text an agent can act on.
- Minimal token usage. Every token in a prompt costs latency and money. narrata keeps output as compact as possible — sections that cannot be computed (short history, missing columns) are silently omitted rather than padded with placeholder text.
Installation
pip install narrata
Install optional backends:
pip install "narrata[all]"
Requires Python 3.11+ and pandas 2.0+.
Quickstart
narrate(...) takes a pandas OHLCV DataFrame with a datetime index.
import yfinance as yf
from narrata import narrate
df = yf.download("AAPL", period="1y", multi_level_index=False)
print(narrate(df, ticker="AAPL"))
Column names are case-insensitive (close works as well as Close), Adj Close is preferred over raw Close when both exist, and Volume is optional.
Close-only data works too — patterns and candlestick sections are omitted automatically, everything else runs normally.
For short histories or missing columns, narrata keeps running and silently omits sections it cannot compute — no wasted tokens on placeholder text.
Example output:
AAPL (251 pts, daily): ▅▄▃▁▂▁▁▂▂▂▄▄▆▇▇██▆▆▆
Date range: 2025-02-14 to 2026-02-13
Range: [171.67, 285.92] Mean: 235.06 Std: 28.36
Start: 243.54 End: 255.78 Change: +5.03%
Regime: Uptrend since 2025-05-07 (low volatility)
RSI(14): 39.6 (neutral-bearish) MACD: bearish crossover 0 days ago
BB: lower half
SMA 50/200: golden cross
Volume: 0.94x 20-day avg (average)
Volatility: 84th percentile (high)
SAX(16): ecabbabbdegghhhg
Candlestick: Inside Bar on 2026-02-10
Support: 201.77 (26 touches), 208.38 (23 touches) Resistance: 270.88 (24 touches), 257.57 (22 touches)
Fallback vs extras (same input)
Using the same static real-market MSFT dataset (251 daily points, yfinance fixture):
Fallback-only (pip install narrata):
MSFT (251 pts, daily): ▂▁▁▁▃▄▅▇▇█▇███▇▆▅▆▆▂
Date range: 2025-02-14 to 2026-02-13
Range: [354.56, 542.07] Mean: 466.98 Std: 49.62
Start: 408.43 End: 401.32 Change: -1.74%
Regime: Downtrend since 2026-01-29 (high volatility)
RSI(14): 32.4 (neutral-bearish) MACD: bearish crossover 11 days ago
BB: lower half
SMA 50/200: death cross 17 days ago
Volume: 0.74x 20-day avg (below average)
Volatility: 94th percentile (extremely high)
SAX(16): aaabdfggggggffdb
Candlestick: Inside Bar on 2026-02-13
Support: 393.67 (15 touches), 378.77 (8 touches) Resistance: 510.83 (34 touches), 481.63 (21 touches)
With extras (pip install "narrata[all]"):
MSFT (251 pts, daily): ▂▁▁▁▃▄▅▇▇█▇███▇▆▅▆▆▂
Date range: 2025-02-14 to 2026-02-13
Range: [354.56, 542.07] Mean: 466.98 Std: 49.62
Start: 408.43 End: 401.32 Change: -1.74%
Regime: Ranging since 2025-02-18 (low volatility)
RSI(14): 32.4 (neutral-bearish) MACD: bearish crossover 11 days ago
BB: lower half
SMA 50/200: death cross 17 days ago
Volume: 0.74x 20-day avg (below average)
Volatility: 94th percentile (extremely high)
SAX(16): aaabdefggggggfed
Candlestick: Inside Bar on 2026-02-13
Support: 393.67 (15 touches), 378.77 (8 touches) Resistance: 510.83 (34 touches), 481.63 (21 touches)
Crypto data
from narrata import from_ccxt, from_coingecko, narrate
# ccxt (Binance, Coinbase, Kraken, etc.)
df = from_ccxt(exchange.fetch_ohlcv("BTC/USDT", "15m"), ticker="BTC/USDT")
# CoinGecko (close-only)
df = from_coingecko(cg.get_coin_market_chart_by_id(...), ticker="BTC")
# yfinance — works directly, no adapter needed
Compare two periods
from narrata import compare
df_q1 = df["2025-01":"2025-03"]
df_q2 = df["2025-04":"2025-06"]
print(compare(df_q1, df_q2, ticker="AAPL"))
Produces a compact diff narrative with → arrows showing changes in price, regime, indicators, symbolic encoding, and support/resistance between the two periods.
CLI
# CSV, TSV, or Parquet (auto-detected from extension)
narrata prices.csv --ticker AAPL
narrata prices.tsv --ticker AAPL
narrata prices.parquet --ticker AAPL
# Pipe from stdin (use --input-format for non-CSV)
cat prices.tsv | narrata --input-format tsv --ticker AAPL
# Output formats: plain, markdown_kv, toon, json
narrata prices.csv --format json
# Precision and ASTRIDE encoding
narrata prices.csv --precision 0 --symbolic-method astride
# Intraday data (frequency auto-detected, or explicit for patchy data)
narrata intraday_15m.csv --ticker AAPL --currency '$'
narrata intraday_15m.csv --ticker AAPL --frequency 15min
# Compare two periods
narrata compare q1.csv q2.csv --ticker AAPL
Run narrata --help for all options.
Digit Splitting for LLM Robustness
digit_tokenize(...) can help when your downstream model is sensitive to dense numeric strings.
Use it when you have many prices, percentages, or long decimals in prompt context.
from narrata import digit_tokenize
print(digit_tokenize("Price 171.24, move +3.2%"))
# <digits-split>
# Price 1 7 1 . 2 4 , move + 3 . 2 %
Intraday awareness
narrata auto-detects sub-daily frequencies (1min, 5min, 15min, 30min, hourly) and scales indicator parameters so lookback windows cover the same calendar-time horizons as daily mode. For example, on 15-minute bars SMA crossover uses 10/40 instead of 50/200, and volume lookback uses 26 bars (~1 trading day) instead of 20 days. Output labels adapt automatically ("SMA 10/40", "26-bar avg").
For patchy or unevenly-spaced data, pass the frequency explicitly: narrate(df, frequency="15min") or --frequency 15min on the CLI. Use "irregular" for data with no fixed interval — units display as "bars" instead of "days".
Features
- Input validation for OHLCV DataFrames
- Summary analysis with date range context
- Regime classification (
Uptrend/Downtrend/Ranging) - Intraday-aware indicators — auto-scaled defaults for sub-daily bars
- RSI and MACD interpretation
- Bollinger Band and moving average crossover descriptions
- Volatility and volume context
- SAX symbolic encoding
- ASTRIDE adaptive symbolic encoding (with
ruptures) - Pattern and candlestick detection
- Support/resistance extraction
- Sparkline generation
- Output formatting (
plain,markdown_kv,toon,json)
FAQ
Is narrata redundant if I already use OpenBB, yfinance, or another data SDK?
No. narrata is complementary. It sits on top of your data access layer and converts OHLCV data into concise, LLM-ready narrative text.
Does narrata call an LLM or provide LLM endpoints?
No. narrata is a pure Python library with deterministic, programmatic analysis and narration. It does not call LLM APIs.
Citation
If you use narrata in research or public projects, cite this package using CITATION.cff.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file narrata-0.1.3.tar.gz.
File metadata
- Download URL: narrata-0.1.3.tar.gz
- Upload date:
- Size: 73.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6ae2b6b0303e159c5815ecac059120fa53d069456335860d96c0333d15c0878
|
|
| MD5 |
c91c47a1e4b13cdf031be70777646bc4
|
|
| BLAKE2b-256 |
b2d102b1c988252723d28433f185bb16783d55e69783f4d14f0bd1ca45204b3b
|
Provenance
The following attestation bundles were made for narrata-0.1.3.tar.gz:
Publisher:
release.yml on marcinmiklitz/narrata
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
narrata-0.1.3.tar.gz -
Subject digest:
c6ae2b6b0303e159c5815ecac059120fa53d069456335860d96c0333d15c0878 - Sigstore transparency entry: 1096737332
- Sigstore integration time:
-
Permalink:
marcinmiklitz/narrata@ca4c966655c1a6d1d62efc7903fcad1cf5b978ee -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/marcinmiklitz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ca4c966655c1a6d1d62efc7903fcad1cf5b978ee -
Trigger Event:
push
-
Statement type:
File details
Details for the file narrata-0.1.3-py3-none-any.whl.
File metadata
- Download URL: narrata-0.1.3-py3-none-any.whl
- Upload date:
- Size: 41.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
803177409b8db909dc76386c152423bb25d0c3e91e6c345c8590c6c3432560f4
|
|
| MD5 |
bb2b0274d7266d750d2639f4fbebf43b
|
|
| BLAKE2b-256 |
06e1c1714c5e3bf2178e78ced8ed96578143497cc97536ead3009fae92e4d87d
|
Provenance
The following attestation bundles were made for narrata-0.1.3-py3-none-any.whl:
Publisher:
release.yml on marcinmiklitz/narrata
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
narrata-0.1.3-py3-none-any.whl -
Subject digest:
803177409b8db909dc76386c152423bb25d0c3e91e6c345c8590c6c3432560f4 - Sigstore transparency entry: 1096737495
- Sigstore integration time:
-
Permalink:
marcinmiklitz/narrata@ca4c966655c1a6d1d62efc7903fcad1cf5b978ee -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/marcinmiklitz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ca4c966655c1a6d1d62efc7903fcad1cf5b978ee -
Trigger Event:
push
-
Statement type: