narrata turns price series into short text that an LLM can reason about quickly.
It is designed for situations where a chart is easy for a human to read, but you need an agent to consume the same information as text.
安装
来自PyPI:
pip install narrata带有增强后端的可选附加功能:
pip install "narrata[all]"快速入门
narrate(...) 获取一个带有日期时间索引的pandas OHLCV DataFrame。
import yfinance as yf
from narrata import narrate
df = yf.download("AAPL", period="1y", multi_level_index=False)
print(narrate(df, ticker="AAPL"))任何数据源都可以工作——金融、OpenBB、CSV、数据库——只要你有一个至少有 Close 列和a DatetimeIndex. 列名不区分大小写(close 工作也很好 Close), Adj Close 自动优先于原始 Close 当两者都存在时,以及 Volume 是可选的。 对于较短的历史记录或缺失的列,narratia会继续运行,并默默地省略它无法计算的部分——占位符文本上没有浪费标记。
仅关闭模式: 如果你的数据只有收盘价(或收盘价+成交量),没有开盘价/高/低,那么叙事工作——总结、制度、指标、符号编码和支撑/阻力都正常运行。图案和烛台部分会自动省略。
输出示例:
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)令牌压缩
叙述的重点是在不浪费代币的情况下,将价格背景融入LLM提示中。 在251天的AAPL OHLCV数据帧上:
| 表示 | 代币(gpt-4o) |
|---|---|
df.to_string() | ~9,000 |
df.to_csv() | ~10,700 |
narrate(df) | ~260 |
那是 ~35-41x压缩 同时保持制度、指标、模式和支撑/阻力。
复制此比较:
import tiktoken
import pandas as pd
from narrata import narrate
enc = tiktoken.encoding_for_model("gpt-4o")
# df = your OHLCV DataFrame
print(f"Raw: {len(enc.encode(df.to_string())):,} tokens")
print(f"CSV: {len(enc.encode(df.to_csv())):,} tokens")
print(f"narrate: {len(enc.encode(narrate(df))):,} tokens")后备vs额外(相同输入)
使用相同的静态真实市场MSFT数据集(251个每日点,金融夹具):
在比较回退与附加时,使用单独的干净虚拟环境。
仅回退(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)附带额外服务(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)本次运行的主要区别:
Regime改变:Regime: Downtrend since 2026-01-29 (high volatility)->Regime: Ranging since 2025-02-18 (low volatility)SAX(16)改变:SAX(16): aaabdfggggggffdb->SAX(16): aaabdefggggggfed
加密数据适配器
用于常见加密数据源的内置适配器:
from narrata import from_ccxt, from_coingecko, narrate
# ccxt (Binance, Coinbase, Kraken, etc.)
import ccxt
exchange = ccxt.binance()
ohlcv = exchange.fetch_ohlcv("BTC/USDT", "15m", limit=200)
df = from_ccxt(ohlcv, ticker="BTC/USDT")
print(narrate(df, currency_symbol="$", precision=0))
# CoinGecko (close + volume only, no OHLC)
data = cg.get_coin_market_chart_by_id(id="bitcoin", vs_currency="usd", days=90)
df = from_coingecko(data, ticker="BTC")
print(narrate(df, currency_symbol="$", precision=0))
# yfinance works directly — no adapter needed
import yfinance as yf
df = yf.download("BTC-USD", period="1y", multi_level_index=False)
print(narrate(df, ticker="BTC", precision=0))CoinGecko数据没有开/高/低,因此模式和烛台部分被默默地省略了。所有其他部分正常工作。
编写自己的输出
当您想要完全控制时,请使用较低级别的功能:
from narrata import analyze_summary, describe_summary, make_sparkline
summary = analyze_summary(df)
text_block = describe_summary(summary)
spark = make_sparkline(df["Close"].tolist(), width=12)
print(text_block)
print(f"Close sparkline: {spark}")比较两个时期
compare(...) 产生了一个紧凑的差异叙述,展示了一个系列在两个时间窗口之间是如何变化的:
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"))输出示例:
AAPL: 2025-01-02..2025-03-31 → 2025-04-01..2025-06-30
Price: 243.54 → 215.30 (-11.6%)
Range: [220.10, 260.40] → [195.40, 230.10]
Regime: Uptrend (low vol) → Downtrend (high vol)
RSI(14): 58.2 (neutral) → 32.4 (neutral-bearish)
MACD: bullish → bearish crossover
Volume: 1.02x avg (average) → 0.85x avg (below average)
Volatility: 42nd pctl (moderate) → 85th pctl (high)
SAX(16): ddccbbaa → aabbccdd
Support: 225.40, 220.10 → 195.40, 200.10
Resistance: 255.80, 260.40 → 225.80, 230.10输出格式
有四种输出格式可供选择: plain, markdown_kv, toon,以及 json.
from narrata import narrate
plain_text = narrate(df, output_format="plain")
markdown_text = narrate(df, output_format="markdown_kv")
json_text = narrate(df, output_format="json")LLM稳健性的数字分割
digit_tokenize(...) 当您的下游模型难以处理长或密集的数字字符串时,它很有用。
为什么这可以帮助:
- 一些标记器将长数字分割成不一致的块。
- 当许多小数/符号靠得很近时,较小的模型可能不太稳定。
- 拆分数字可以减少提示和工具输出中的数字解析歧义。
何时使用:
- 将其用于数字密集的提示(价格、百分比、ID、许多小数)。
- 当人类可读性比模型鲁棒性更重要时,请关闭它。
例子:
from narrata import digit_tokenize
print(digit_tokenize("Price 171.24, move +3.2%"))
#
# Price 1 7 1 . 2 4 , move + 3 . 2 %依赖项
可选附加功能:
indicators:pandas-ta-openbb(导入路径保持不变pandas_ta)patterns:pandas-ta-openbb(烛台图案后端)regimes:ruptures(用于变化点状态检测)symbolic:tslearn,ruptures(ruptures目前支持Python\
Claude Desktop configuration
添加到您的 claude_desktop_config.json:
{
"mcpServers": {
"narrata": {
"command": "uvx",
"args": ["narrata-mcp"]
}
}
}日间意识
narratia自动检测亚日频率(1min, 5min, 15min, 30min, hourly)并缩放指示器默认值,以便回溯窗口覆盖与每日模式相同的日历时间范围。对于自动检测可能失败的不完整或间隔不均匀的数据,请通过以下方式明确传递频率 frequency="15min" 在Python API中, --frequency 15min 在CLI上,或 frequency MCP工具中的字段。使用 frequency="irregular" 对于没有固定间隔的完全非结构化数据,这保持了每日刻度指示器的默认值,但将单位标记为“条形”而不是“天”。
| 参数 | 每日 | 15min | 5min |
|---|---|---|---|
| SMA交叉 | 50/200 | 10/40 | 30/120 |
| 成交量回顾 | 20天 | 26巴(~1天) | 78巴(~1天) |
| 波动率回顾 | 252巴(约1年) | 520巴(约20天) | 1560巴(约30天) |
RSI(14)、MACD(12/26/9)和布林带(20)保持其标准默认值——从业者在不同时间范围内使用这些值。
输出标签自动适应:
AAPL (130 pts, 15min): ▂▅▄▄▃▃▂▁▆▃▃▃▇▆▇█▇▇█▆
Date range: 2025-11-06 to 2025-11-12
Range: [$268.73, $275.55] Mean: $271.67 Std: $2.34
Start: $268.73 End: $273.47 Change: +1.76%
Regime: Ranging since 2025-11-06 (low volatility)
RSI(14): 42.5 (neutral-bearish) MACD: bearish crossover 6 days ago
BB: below lower band (squeeze)
SMA 10/40: golden cross 60 bars ago
Volume: 2.89x 26-bar avg (unusually high)
Volatility: 2nd percentile (extremely low)
SAX(16): dddcbacbbcfghghh
Patterns: Ascending triangle forming since 2025-11-10
Candlestick: Doji on 2025-11-12
Support: $272.02 (89 touches), $268.40 (52 touches) Resistance: $274.96 (56 touches)特性
- OHLCV数据帧的输入验证
- 带日期范围上下文的摘要分析
- 制度分类(
Uptrend/Downtrend/Ranging) - 日内感知指标 --亚日柱的自动缩放SMA、交易量和波动率默认值
- RSI和MACD解释(使用
pandas_ta指示线(如果可用) - 体积分析(与移动平均线的比率)
- 布林带位置和挤压检测
- 移动平均交叉检测(黄金/死亡交叉)
- 波动性百分位数排名
- SAX符号编码
- ASTRIDE自适应符号编码(需要
ruptures) - 模式检测加烛台检测(
pandas_ta首先,内部后备) - 支撑/阻力提取
- 紧凑的Unicode火花线
- 输出格式助手(
plain,markdown_kv,toon) - 中高层
narrate(...)构成 - 通过以下方式进行周期比较
compare(...)
常见问题解答
如果我已经在使用OpenBB、yfinance或其他数据SDK,那么叙述是多余的吗?
号码 narrata 是互补的。
它位于现有数据层之上,将OHLCV数据转换为紧凑的、LLM就绪的叙述性文本。
典型流程:
Data source (OpenBB / yfinance / CSV / DB)
-> pandas DataFrame (OHLCV)
-> narrata
-> concise narrative context for an LLMnarrata 与数据源无关:如果你能生成一个标准的OHLCV DataFrame,你就可以使用它。
叙述是调用LLM还是提供LLM端点?
不,这是故意的。
narrata 是一个具有确定性、程序化分析和叙述的纯Python库。它不进行LLM API调用,也不提供模型端点。
将其用作管道组件:
your data -> narrata text output -> your chosen LLM/runtime这使库保持轻量级、可测试性和提供者无关性。
引用
如果你使用 narrata 在研究、出版物或公共项目中:
@software{miklitz_narrata,
author = {Miklitz, Marcin},
title = {narrata},
url = {https://github.com/marcinmiklitz/narrata},
license = {MIT}
}您还可以使用GitHub上的“引用此存储库”按钮或中的元数据 CITATION.cff.
