Metadata-Version: 2.5
Name: valuein-sdk
Version: 6.2.0
Summary: Point-in-time, survivorship-free SEC EDGAR fundamentals in Python: DuckDB SQL over Parquet, traceable fact_ids, factor backtests.
Project-URL: Homepage, https://valuein.biz
Project-URL: Documentation, https://valuein.biz/developers/python-sdk
Project-URL: Repository, https://github.com/valuein/valuein
Project-URL: Bug Tracker, https://github.com/valuein/valuein/issues
Project-URL: Examples, https://github.com/valuein/valuein/tree/main/examples
Project-URL: Pricing, https://valuein.biz/pricing
Author-email: Valuein <support@valuein.biz>
License: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: EDGAR,SEC,XBRL,ai-agents,backtesting,duckdb,factor-investing,finance,financial-statements,fundamentals,parquet,point-in-time,quant,survivorship-bias
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: duckdb<1.5,>=1.4.4
Requires-Dist: httpx[http2]>=0.27.0
Requires-Dist: jinja2>=3.1.6
Requires-Dist: pandas>=2.2.3
Requires-Dist: pyarrow>=18.0.0
Requires-Dist: pydantic>=2.9.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: all
Requires-Dist: matplotlib>=3.8.0; extra == 'all'
Requires-Dist: numpy>=1.26.0; extra == 'all'
Requires-Dist: opentelemetry-api>=1.20.0; extra == 'all'
Requires-Dist: polars>=0.20.0; extra == 'all'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.20.0; extra == 'otel'
Provides-Extra: polars
Requires-Dist: polars>=0.20.0; extra == 'polars'
Provides-Extra: research
Requires-Dist: ipykernel>=6.29.0; extra == 'research'
Requires-Dist: jupytext>=1.16.0; extra == 'research'
Requires-Dist: matplotlib>=3.8.0; extra == 'research'
Requires-Dist: numpy>=1.26.0; extra == 'research'
Provides-Extra: test
Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
Requires-Dist: pytest>=9.0.0; extra == 'test'
Description-Content-Type: text/markdown

[![Valuein](https://www.valuein.biz/valuein/twitter-rounded.png)](https://valuein.biz)

[![PyPI version](https://img.shields.io/pypi/v/valuein-sdk?cacheSeconds=300)](https://pypi.org/project/valuein-sdk/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://pypi.org/project/valuein-sdk/)
[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-green)](https://www.apache.org/licenses/LICENSE-2.0)

# valuein-sdk

Point-in-time, survivorship-free SEC EDGAR fundamentals for Python.

`valuein-sdk` gives you standardized financial statements from SEC filings back to
1993, queried with SQL in an in-process DuckDB. Every query sees only what was public
at your `as_of` date: rows are filtered on `accepted_at`, the moment the SEC accepted
the filing. Delisted, acquired and bankrupt companies stay in the data, and index
membership is stored as historical spells, so a universe on a past date is the one that
existed then. Every fact carries a `fact_id` and the accession number of the filing it
came from, so any number can be traced back to its source document.

[Documentation](https://valuein.biz/developers/python-sdk) ·
[Examples](https://github.com/valuein/valuein/tree/main/examples) ·
[SQL cookbook](https://valuein.biz/developers/sql-cookbook) ·
[Schema](https://valuein.biz/developers/schema) ·
[Pricing](https://valuein.biz/pricing)

## Install

```bash
pip install valuein-sdk
```

Python 3.10 or newer. Optional extras:

| Extra | Adds |
|---|---|
| `valuein-sdk[polars]` | `client.to_polars()` and `PriceQuery.to_polars()` |
| `valuein-sdk[research]` | NumPy, Matplotlib and a Jupyter kernel for notebooks |
| `valuein-sdk[otel]` | OpenTelemetry spans (emitted only if your app configures a tracer) |
| `valuein-sdk[all]` | `polars`, `otel`, NumPy and Matplotlib |

## Quickstart: 30 seconds, no token

With no API key the client runs on the free **sample** tier: S&P 500 constituents over
the last five years. There is nothing to sign up for.

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:  # no token: free sample tier
    print(client.resolve("AAPL"))  # ticker -> CIK, name, active flag

    revenue = client.run_template(
        "fundamentals_by_ticker",
        ticker="AAPL",
        form_types=["10-K"],
        metrics=["TotalRevenue", "NetIncome"],
    )
    print(revenue.head())
```

`resolve` returns one row per identifier with `cik`, `symbol`, `name`, `is_active`,
`valid_from`, `valid_to` and `matched_by`. The template returns one row per fact:
`filing_date`, `report_date`, `period_end`, `fiscal_period`, `standard_concept`,
`numeric_value`, `unit`. A 10-K also carries prior-year comparatives, so use
`period_end`, not `report_date`, to tell which year a number belongs to.

The same data from the shell:

```bash
valuein query 'SELECT symbol, name, sector FROM "references" WHERE is_active LIMIT 5'
valuein doctor   # plan, snapshot, tables and cache, with no secrets
```

To use your plan, set a token and run the same code:

```bash
export VALUEIN_API_KEY="your_token"   # or put it in a .env file
```

## Core ideas

| Idea | What it means in code |
|---|---|
| **Point in time** | `ValueinClient(as_of=...)` takes a timezone-aware `datetime` (default: now, UTC). Point-in-time tables are filtered to `accepted_at <= as_of`. If that filter cannot be applied, the SDK raises `ValueinIntegrityError` instead of returning unfiltered rows. |
| **Restatements** | A later filing that reports a period again adds new rows with their own `accepted_at`. Under an `as_of` you see what was public then. The `ratio` view keeps only the latest vintage per ratio and period as of the cutoff. |
| **Survivorship-free** | `references` and `entity` include inactive companies. `index_membership` stores each membership as a spell (`effective_date`, `removal_date`), so `pit_universe(date)` returns the index as it was on that date. |
| **CIK over ticker** | Every method that takes a ticker also takes a CIK (`"0000320193"`, `"320193"` or `320193`). A CIK never changes; a ticker can be retired and later reused by another company. |
| **Traceable numbers** | Each `fact` row has a `fact_id` (SHA-256 of `entity_id`, `accession_id`, `concept`, `period_end`, `unit`) and the `accession_id` of its SEC filing. `FactRecord.verify()` recomputes the hash; `filing_links()` gives the EDGAR URLs. |
| **Local compute** | Queries run in an in-process DuckDB over Parquet streamed from the Valuein gateway. Downloads are cached on disk. There is no database to set up. |

## Recipes

Every recipe runs on the free sample tier unless its heading says otherwise.

### Get fundamentals for a ticker as of a date

```python
from datetime import datetime, timezone

from valuein_sdk import ValueinClient

as_of = datetime(2024, 6, 30, tzinfo=timezone.utc)

with ValueinClient(as_of=as_of) as client:
    facts = client.read_table("fact", tickers="AAPL")

assert (facts["accepted_at"] <= as_of).all()  # nothing from after the cutoff

annual_revenue = (
    facts[(facts["standard_concept"] == "TotalRevenue") & (facts["fiscal_period"] == "FY")]
    .sort_values("accepted_at")
    .drop_duplicates("period_end", keep="last")  # latest value known on as_of
    .sort_values("period_end")
)
print(annual_revenue[["period_end", "numeric_value", "unit", "accepted_at", "fact_id"]])
```

`read_table(table, tickers=...)` fetches only that company's partition where the
gateway serves one, and applies the same `as_of` filter. For backtests use
`numeric_value`. With an explicit `as_of` (or a bundle), `value_current`, `restated`
and `restatement_count` are recomputed from the filings accepted by the cutoff;
`value_as_filed` is as exported. Without an explicit `as_of` they are the latest
export's values.

### Screen a universe without survivorship bias

```python
from datetime import datetime, timezone

from valuein_sdk import ValueinClient

as_of = datetime(2023, 6, 30, tzinfo=timezone.utc)

with ValueinClient(as_of=as_of) as client:
    members = client.pit_universe("2023-06-30")  # S&P 500 on that date
    roe = client.run_query(
        """
        SELECT entity_id AS cik, value AS roe, period_end
        FROM ratio
        WHERE ratio_name = 'return_on_equity'
          AND fiscal_period = 'FY'
          AND period_end >= DATE '2022-06-30'  -- skip stale fiscal years
        QUALIFY row_number() OVER (PARTITION BY entity_id ORDER BY period_end DESC) = 1
        """
    )

screen = members.merge(roe, on="cik").nlargest(10, "roe")
print(screen[["cik", "ticker_at_date", "company_name", "roe", "period_end", "removal_date"]])
print("members later removed from the index:", members["removal_date"].notna().sum())
```

`pit_universe` returns one row per member with `cik`, `company_name`, `ticker_at_date`,
`sector`, `industry`, `effective_date` and `removal_date`. Companies that left the index
after the date are still members on it. The `ratio` table holds annual (`fiscal_period =
'FY'`) and trailing-twelve-month (`'TTM'`) rows: always filter on `fiscal_period`, or
each company appears twice.

### Backtest a factor (Pro or Institutional)

The quant layer needs daily prices (`stock_price_daily`), which only the Pro and
Institutional plans carry.

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:  # reads VALUEIN_API_KEY
    result = client.factor_backtest(
        "return_on_equity",  # a ratio_name, a factor_scores column, or "mom_12m"
        start="2018-01-01",
        end="2024-12-31",
        freq="QE",
    )

print(result.summary())  # net and gross results, with the execution lag and cost used
print(result.mean_ic, result.ic_ir)  # rank information coefficient and its stability
print(result.quantile_summary())  # mean return per quantile
```

`factor_backtest` builds the universe at each rebalance date from index membership,
reads each signal value as of that date, prices trades with a one-day execution lag
(`execution_lag=1`) and charges 10 bps per unit traded (`cost_bps=10.0`). Positions in
companies that delist are truncated, not dropped, and the summary lists what the study
could not use. For a custom signal, run the three steps yourself:

```python
from valuein_sdk import ValueinClient, backtest

with ValueinClient() as client:
    universe = client.universe("SP500", start="2018-01-01", end="2024-12-31", freq="QE")
    panel = client.signal_panel(universe, ratios=["return_on_equity", "debt_to_equity"])
    panel = client.forward_returns(panel)  # appends forward-return columns

panel["score"] = panel["return_on_equity"].rank(pct=True) - panel["debt_to_equity"].rank(pct=True)
result = backtest(panel, signal="score", quantiles=5, cost_bps=10.0)
print(result.summary())
```

From the shell: `valuein backtest return_on_equity --start 2018-01-01 --end 2024-12-31
--freq QE`. More recipes, including Monte Carlo and reproducible bundles, are in the
[Python SDK docs](https://valuein.biz/developers/python-sdk).

### Prices and total return

Every tier has `stock_price`: one close per company at each fiscal period end and each
completed month end, with the listing's `total_return_index` (splits and dividends
reinvested).

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:
    print(client.stock_price("AAPL"))  # latest row
    monthly = client.run_query(
        """
        SELECT price_date, close, total_return_index
        FROM stock_price
        WHERE entity_id = '0000320193' AND observation = 'monthly'
        ORDER BY price_date
        """
    )

tri = monthly["total_return_index"]
print(f"12-month total return: {tri.iloc[-1] / tri.iloc[-13] - 1:.1%}")
```

Daily bars (`stock_price_daily`) are on Pro and Institutional:

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:
    bars = (
        client.prices(["AAPL", "MSFT"])
        .between("2024-01-01", None)
        .fields("close", "total_return")
        .to_pandas()
    )
    growth = client.total_return("AAPL", "2020-01-01", "2024-12-31")  # a fraction
    # one column per ticker
    panel = client.price_panel(["AAPL", "MSFT"], "2024-01-01", "2024-12-31")
```

Use a total-return basis (`total_return_index`, or the `total_return` field, which is
the default) for return math. Raw `close` is not split-adjusted, and adding `div_cash` on
top of a total-return series counts dividends twice. `stock_price_daily` has one row per
security per day, so a company with two share classes has two rows per day; the SDK's
price methods pick one bar per company per day for you.

### Run a SQL template

The package ships parameterized SQL templates for common analyses: margins, free cash
flow, Piotroski and Altman inputs, DuPont, restatement history, filing delays and more.

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:
    print(client.list_templates())
    fcf = client.run_template(
        "free_cash_flow",
        ticker="MSFT",
        form_types=["10-K"],
        start_date="2021-01-01",
        end_date="2025-12-31",
    )
print(fcf.head())
```

Pass parameters as keywords: `run_template(name, **params)`. Every value is validated
and quoted before the SQL is rendered: tickers such as `"BRK-B"` (share classes take a
hyphen, as `references` spells them), XBRL
frames such as `"CY2023"`, `"CY2024Q1"` or `"CY2024Q1I"`, `fiscal_period` one of `Q1`
to `Q4`, `FY` or `TTM`, and `min_rank` a percentile from `0.0` to `1.0`. Common
parameters you omit get defaults: `form_types` is `["10-K", "10-Q"]`, `start_date` five
years ago, `end_date` today, and `fiscal_period` is `"FY"` for a template that reads
`ratio` (otherwise `"Q1"`).
Templates read the same point-in-time views as `run_query`. The smart-money templates
(`insider_buys`, `blockholders`, `top_institutional_holders`, `manager_portfolio`) need
Institutional; `price_history`, `price_return` and `price_momentum_screen` need Pro or
Institutional. Copy-paste SQL patterns are in the
[SQL cookbook](https://valuein.biz/developers/sql-cookbook).

### Raw SQL over the tables

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:
    current_members = client.run_query(
        """
        SELECT r.cik, r.symbol, r.name, r.sector
        FROM "references" r
        JOIN index_membership im ON im.cik = r.cik
        WHERE im.index_name = 'SP500'
          AND im.removal_date IS NULL
          AND r.is_active AND r.is_primary_ticker
        ORDER BY r.symbol
        """
    )
print(current_members.head())
```

- Tables mount on first use; a query can reference any table on your plan.
- `"references"` is an SQL keyword: keep the double quotes.
- `run_query` accepts one `SELECT`, `WITH` or `EXPLAIN` statement. Writes, file and
  network functions (`read_parquet`, `glob`, ...) and system catalogs are rejected.
- Other outputs: `client.to_arrow(sql)`, `client.to_polars(sql)`, and
  `client.stream(sql)` for results too large to hold in memory.
- Columns: `client.get_schema("fact")`, or the
  [schema reference](https://valuein.biz/developers/schema).

### Verify a number back to the SEC filing

```python
from valuein_sdk import FactRecord, ValueinClient

with ValueinClient() as client:
    facts = client.read_table("fact", tickers="AAPL")
    row = (
        facts[(facts["standard_concept"] == "TotalRevenue") & (facts["fiscal_period"] == "FY")]
        .sort_values(["period_end", "accepted_at"])
        .iloc[-1]
    )
    record = FactRecord.model_validate(row.to_dict())
    assert record.verify()  # fact_id matches the hash of its identity fields

    links = client.filing_links("AAPL", form_types=["10-K"])
    filing = links[links["accession_id"] == record.accession_id].iloc[0]

print(record.numeric_value, record.unit, record.period_end, record.fact_id)
print(filing["inline_viewer_url"] or filing["document_url"] or filing["sec_url"])
```

`filing_links` returns `accession_id`, `form_type`, `filing_date`, `accepted_at` and
four EDGAR URLs per filing: `inline_viewer_url` (the Inline XBRL viewer; `None` for older
filings), `document_url`, `viewer_url` and `sec_url` (the filing index, always present).
`compute_fact_id(entity_id, accession_id, concept, period_end, unit)` computes the same
hash as a plain function.

### Track restatements

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:
    events = client.restatements(ticker="AAPL")

columns = ["period_end", "standard_concept", "first_value", "current_value", "disclosure_class"]
print(events[columns])
```

`restatements()` reads `restatement_events` on every tier, including with no token.
Each row is one reported value that a later filing changed, with both accession
numbers. `disclosure_class` is `non_reliance` (an 8-K Item 4.02 notice), `amended` (a
10-K/A or 10-Q/A) or `undisclosed` (the change appeared in a routine filing). Filter with
`disclosure_class=`, `severity=` and `min_delta_pct=`.

### Value a company and compare its peers

```python
from valuein_sdk import ValueinClient

with ValueinClient() as client:
    result = client.dcf("AAPL", stage1_growth_rate=0.08)
    table = client.peers("AAPL", n=5)

print(result.value_per_share, result.price, result.upside)
print(result.inputs_frame()[["name", "value", "fact_id", "accession_id"]])
print(table[["ticker", "is_subject", "pe_ratio", "roe"]])
```

`dcf()` is the MCP `compute_dcf` model (two-stage, free cash flow growing at your
`stage1_growth_rate`, discounted at `wacc`, default 9%) computed in-process from the
annual filings known at the client's `as_of`: nothing is sent to the MCP server. Every input
(operating cash flow, capex, debt, cash, shares) carries the `fact_id` and filing it came
from, defaults are flagged in `result.assumptions`, and `projection_frame()` and
`sensitivity_frame()` give the year-by-year cash flows and a 5x5 grid. The growth rate is
yours: the SDK never guesses one. `peers()` returns the subject first, then companies sharing
its SIC code (S&P 500 members first), with the pipeline ratios as of the same date; pass
`peers=["MSFT", "GOOGL"]` to choose them yourself. They replace the deprecated
`generate_dcf_xlsx()` / `generate_comps_xlsx()` for the numbers; the files themselves
come from the Workspace.

## Plans and tiers

The SDK reads your plan from your token; your code does not change between plans. The
same token unlocks the bulk data API, the MCP server and the Valuein Workspace at your
plan.

| Plan | Key in code | How to get it | Coverage |
|---|---|---|---|
| Sample | `sample` | No token | S&P 500 constituents, last five years |
| Benchmark | `sp500` | Free registration | Every S&P 500 constituent, full history |
| Pro | `pro` | Paid | Full US universe, plus daily prices (`stock_price_daily`) |
| Institutional | `full` | Paid | Pro, plus insider, 13F/13D/13G and Form ADV tables |

`client.plan` shows the active plan. `get_plans()` returns each plan's request rate
limits from the gateway. Prices and history windows: [valuein.biz/pricing](https://valuein.biz/pricing).

## Tables

The table list and every column come from the gateway's manifest at runtime: use
`client.tables()` and `client.get_schema(table)`, and `client.manifest()` for the
snapshot and schema version you are reading. "PIT" means the view is filtered to
`accepted_at <= as_of`.

| Table | What it holds | PIT | Plans |
|---|---|---|---|
| `references` | Start here. One row per security: `cik`, `symbol`, `name`, `sector`, `industry`, `is_active`, `is_primary_ticker`, FIGI identifiers | | All |
| `entity` | Company master: addresses, SIC code, state of incorporation, former names | | All |
| `security` | Ticker history with `valid_from` / `valid_to` | | All |
| `filing` | One row per SEC filing: `accession_id`, `form_type`, `filing_date`, `report_date`, `accepted_at`, `primary_document` | Yes | All |
| `filing_registrant` | Every registrant of a filing (`accession_id`, `entity_id`). `filing` lists a co-registrant filing under one CIK; join this table to `filing` on `accession_id` to list a company's filings including the combined ones. The join to `filing` applies the point-in-time filter | | Where the plan's manifest lists it |
| `fact` | Standardized financial facts: `standard_concept`, raw XBRL `concept`, `numeric_value`, `unit`, `period_end`, `fiscal_period`, `accepted_at`, `fact_id`, `accession_id`. `priority = 1` marks the tag elected for its concept in a filing, decided at export by tag precedence | Yes | All |
| `ratio` | Computed ratios by company and period: `ratio_name`, `category`, `value`, `fiscal_period` (`FY` or `TTM`). `confidence_score` is computed over the full export history and is not point-in-time under a past `as_of` | Yes | All |
| `standard_concept` | Dictionary of the standardized concepts in `fact.standard_concept` | | All |
| `taxonomy_guide` | Reference for the raw us-gaap tags in `fact.concept` | | All |
| `index_membership` | Index membership spells keyed on `cik`: `index_name`, `effective_date`, `removal_date` (exclusive) | | All |
| `factor_scores` | Cross-sectional factor scores and percentile ranks by company and period | Yes | All |
| `earnings_signals` | EPS trend estimate and surprise, revenue growth, price momentum | Yes | All |
| `stock_price` | Close and `total_return_index` at period ends and completed month ends | Yes | All |
| `stock_price_daily` | Daily OHLCV bars, `div_cash`, `split_factor`, `total_return_index`, `is_primary_listing` | Yes | Pro, Institutional |
| `restatement_events` | Values a later filing changed, with both accessions and a `disclosure_class` | | All |
| Smart money | `insider_party`, `insider_filing`, `insider_transaction`, `insider_ownership`, `institutional_filing`, `institutional_holding` | Yes, except `insider_party` | Institutional |
| Form ADV | `investment_adviser`, `investment_adviser_private_fund` (keyed on CRD, not CIK) | | Institutional |

Join keys: `fact.entity_id`, `filing.entity_id` and `security.entity_id` hold the CIK;
`fact.accession_id = filing.accession_id`; `index_membership.cik = "references".cik`.
There is no `is_sp500` column: join `index_membership` instead. For one quarter of a
cash-flow item use `COALESCE(derived_quarterly_value, numeric_value)`, because Q2 and Q3
10-Qs report year-to-date figures.

## API at a glance

Everything below imports from `valuein_sdk`. Every public method is typed and
documented: `help(ValueinClient.read_table)` shows the arguments, the returned columns
and the errors, and `client.capabilities()` returns the whole surface as a dictionary.

`ValueinClient(as_of=None, api_key=None, gateway_url=None, tables=None, config=None)`

| Method | Returns | Use it to |
|---|---|---|
| **Read data** | | |
| `read_table(table, *, columns=None, limit=None, tickers=None)` | `DataFrame` | Read a table through its point-in-time view, optionally for some companies |
| `run_query(sql)` | `DataFrame` | Run one read-only DuckDB query |
| `run_template(name, **params)` | `DataFrame` | Run a packaged SQL template |
| `list_templates()` | `list[str]` | List template names |
| `to_arrow(sql)` / `to_polars(sql)` / `stream(sql, batch_size=10_000)` | Arrow / Polars / iterator | Other output formats |
| `get(table)` | `DataFrame` | Download the raw published table, without the `as_of` filter |
| **Companies and universes** | | |
| `resolve(identifiers)` | `DataFrame` | See which company each ticker or CIK matches |
| `pit_universe(as_of_date, index="SP500")` | `DataFrame` | Index members on a past date |
| `factor_scores(ticker=None, sector=None, min_composite_rank=None, limit=100)` | `DataFrame` | Factor scores and ranks |
| `earnings_signals(ticker=None, min_surprise_pct=None, direction=None, limit=100)` | `DataFrame` | EPS surprise and trend |
| `restatements(*, ticker=None, disclosure_class=None, severity=None, min_delta_pct=None, limit=100)` | `DataFrame` | Restatement events |
| `filing_links(ticker, form_types=None, start_date=None, end_date=None, limit=20)` | `DataFrame` | EDGAR URLs per filing |
| **Prices** | | |
| `stock_price(ticker, date=None)` | `DataFrame` | Latest (or dated) close; every tier |
| `prices(tickers)` | `PriceQuery` | Daily bars: `.between()`, `.fields()`, `.last()`, `.to_pandas()`; Pro+ |
| `total_return(ticker, start, end, *, basis="total_return")` | `float` | Total return over a window; Pro+ |
| `price_panel(tickers, start, end, *, field="total_return")` | `DataFrame` | Wide price matrix; Pro+ |
| `pit_panel(tickers, dates, *, concepts=None)` | `DataFrame` | Price and fundamentals known by the end (UTC) of each date; flows as trailing twelve months; Pro+ |
| `pit_valuation_ratios(ticker, as_of)` | `DataFrame` | P/E, P/S, P/B and EV/Revenue on a date, on trailing-twelve-month flows; Pro+ |
| `dcf(ticker, *, stage1_growth_rate, stage1_years=None, terminal_growth_rate=None, wacc=None, fcf_base_override=None, shares_override=None)` | `DcfResult` | Local forward DCF (the MCP `compute_dcf` model): EV, equity, per share, upside, projection and sensitivity frames, every input with its `fact_id` and filing |
| `peers(ticker, *, n=5, peers=None, categories=None)` | `DataFrame` | Subject row first, then SIC-matched peers (or yours) with pipeline ratios (P/E, P/B, EV/EBITDA, margins, ROE, ROIC, FCF yield, ...) as of the client's `as_of` |
| `price_health()` / `corporate_actions(ticker)` | `dict` / `DataFrame` | Price data coverage; splits and dividends; Pro+ |
| **Quant research (Pro+)** | | |
| `factor_backtest(signal, *, start, end, freq="ME", ...)` | `BacktestResult` | Universe, signal, forward returns and backtest in one call |
| `universe(index_name="SP500", *, dates=None, start=None, end=None, freq="ME")` | `DataFrame` | Index members at each rebalance date (`dates=` works on every tier) |
| `signal_panel(universe, *, ratios=, concepts=, factors=, momentum_months=)` | `DataFrame` | Signal values accepted by the end (UTC) of each rebalance date; flow `concepts` (revenue, income, cash flow) as trailing twelve months |
| `forward_returns(panel, *, execution_lag=1)` | `DataFrame` | Return to the next rebalance, delisting-aware |
| `trading_days(start, end)` / `rebalance_dates(start, end, freq=)` | `DatetimeIndex` / `list[str]` | Calendars built from actual trading days |
| `write_bundle(path, *, tables, ...)` / `ValueinClient.from_bundle(path)` | `BundleManifest` / client | Freeze a data slice with SHA-256 hashes; replay it offline |
| **Session** | | |
| `plan` / `as_of` | `str` / `datetime` | Active plan and cutoff |
| `tables()` / `get_schema(table)` | `list[str]` / `dict` | Tables on your plan; column types |
| `me()` / `manifest()` / `health()` | `dict` | Token, snapshot and gateway status |
| `capabilities()` / `diagnostics()` / `usage()` | `dict` / `dict` / report | Machine-readable surface; redacted support bundle; download budget |
| `cache_info()` / `clear_cache(*, table=None)` | `dict` | On-disk cache |
| `report_issue(message, *, category="bug", ...)` | `FeedbackReceipt` | Report a bug or a data problem; works with no token |
| `close()` | | Release DuckDB and HTTP resources (or use `with`) |
| **Deprecated (removed in 7.0)** | | Each emits a `DeprecationWarning`. For the file, use the Valuein Workspace or the MCP tool of the same name; for the numbers, use `dcf()` and `peers()` (local, no MCP call). Pro+, token required |
| `generate_dcf_xlsx(ticker, *, as_of_date=None, depth="standard", stage1_growth_rate=None, wacc=None, terminal_growth_rate=None, stage1_years=None, dcf_result=None, company_name=None)` | `dict` | DCF workbook rendered by the MCP server; runs `compute_dcf` first from `stage1_growth_rate` (or renders your `dcf_result`); `as_of_date` defaults to the client's `as_of` |
| `generate_comps_xlsx(ticker, *, peers=None, as_of_date=None, peer_rows=None, subject_company_name=None, notes=None)` | `dict` | Peer-comparables workbook; peers from `get_peer_comparables` (or your `peer_rows`); `as_of_date` defaults to the client's `as_of` |
| `generate_research_brief_docx(ticker, *, as_of_date=None, depth="full", peers=None, title=None, sections=None, abstract=None, snapshot=None, citations=None, company_name=None, author_name=None)` | `dict` | Typesets a brief you wrote (`title` and `sections` required) as a Word document |

Also exported: `backtest`, `block_bootstrap`, `bootstrap`, `gbm` and `correlated_gbm`
(every simulation requires a `seed`), `AlphaEngine` with built-in factors such as `ROE`
and `GROSS_MARGIN`, `FactRecord`, `compute_fact_id`, `ValueinConfig` and the
`ValueinError` hierarchy. `valuein_sdk.aio.AsyncValueinClient` is the asyncio client.

## Command line

The `valuein` command installs with the package and uses the same token rules.

| Command | Does |
|---|---|
| `valuein doctor` | Print a redacted diagnostics bundle |
| `valuein tables` | List tables on your plan |
| `valuein schema TABLE` | Show a table's columns and types |
| `valuein query "SQL" [--format csv\|json]` | Run a read-only query |
| `valuein prices TICKER [--start] [--end] [--last N]` | Daily bars (Pro+) |
| `valuein health [TICKERS...]` | Price data coverage (Pro+) |
| `valuein backtest SIGNAL --start --end [--freq] [--quantiles] [--cost-bps] ...` | Factor backtest (Pro+) |
| `valuein bundle PATH --tables T1 T2 ...` | Write an offline research bundle |
| `valuein cache stats` / `valuein cache clear [--table T]` | Inspect or clear the cache, offline |

## Configuration

### Environment variables

| Variable | Effect |
|---|---|
| `VALUEIN_API_KEY` | Your token, read when `api_key` is not passed. A `.env` file found from the current directory upward is loaded once per process. |
| `VALUEIN_NO_DOTENV` | `1`, `true`, `yes` or `on` (any case) skips loading `.env`; any other value, `0` included, loads it. |
| `VALUEIN_GATEWAY_URL` | Override the data gateway URL (development). |
| `VALUEIN_MCP_URL` | Override the MCP server URL used by `report_issue()` and the deprecated `generate_*` methods. |
| `VALUEIN_PLANS_ENDPOINT` | Override the URL that `get_plans()` reads. |
| `VALUEIN_DISABLE_TELEMETRY` | `1` or `true` turns off OpenTelemetry spans. |
| `VALUEIN_DEBUG` | Make the CLI print full tracebacks. |
| `XDG_CACHE_HOME` | Cache root (default `~/.cache`); the SDK uses `valuein-sdk/` under it. |

`api_key=None` (the default) reads the environment, `api_key=""` forces the sample
tier, and any other string is used as the token.

### ValueinConfig

```python
from valuein_sdk import ValueinClient, ValueinConfig

config = ValueinConfig(memory_limit="8GB", threads=8, safe_mode=True)
with ValueinClient(config=config) as client:
    print(client.run_query('SELECT count(*) AS n FROM "references"'))
```

| Field | Default | Meaning |
|---|---|---|
| `memory_limit` | `"4GB"` | DuckDB memory ceiling; DuckDB spills to disk above it |
| `threads` | `4` | DuckDB threads |
| `temp_directory` | `None` | Spill and download directory; `None` uses a temporary directory removed on `close()` |
| `max_temp_directory_size` | `"20GB"` | Cap on spill-disk use |
| `connect_timeout` / `read_timeout` | `30.0` / `120.0` | HTTP timeouts in seconds |
| `cache_enabled` / `cache_dir` / `cache_max_bytes` | `True` / `None` / 4 GiB | On-disk Parquet cache |
| `cache_revalidate` | `True` | Check a cached file's ETag once per session; if the check cannot run, the cached copy is used |
| `safe_mode` | `False` | Block DuckDB file and network access; use it when running SQL written by a model |
| `hot_data` | `True` | Include filings published since the last weekly snapshot in `filing`, `fact` and `ratio` |
| `hot_index_ttl_s` / `hot_max_partitions` | `60.0` / `200` | How often to re-check for those filings; most companies folded in per table |

`ValueinConfig` is frozen: build a new one instead of changing a field.

## Errors

Every SDK error subclasses `ValueinError`, and every message says what was wrong and how
to fix it.

| Exception | Raised when |
|---|---|
| `ValueinAuthError` | The token is invalid, expired or revoked |
| `ValueinPlanError` | The request needs a higher plan |
| `ValueinNotFoundError` | A table, ticker or CIK is not found, or is outside your plan's universe |
| `ValueinRateLimitError` | Too many requests; `.retry_after` holds seconds when the gateway sends it |
| `ValueinAPIError` | The gateway returned a 5xx; `.status_code` holds it |
| `ValueinConnectionError` | The gateway cannot be reached (also a `ConnectionError`) |
| `ValueinTimeoutError` | A request timed out after retries (also a `TimeoutError`) |
| `ValueinValidationError` | An argument, query or parameter is invalid (also a `ValueError`) |
| `ValueinDataError` | A query failed in DuckDB, a table could not be loaded for your plan, or a bundle file failed its hash check |
| `ValueinIntegrityError` | The point-in-time filter could not be applied to a table |
| `ValueinConfigError` | `ValueinConfig` or a `tables=` list is invalid |
| `FeedbackError` | The server rejected a `report_issue()` call |

```python
from valuein_sdk import ValueinClient, ValueinError

with ValueinClient() as client:
    try:
        client.run_query("DELETE FROM fact")
    except ValueinError as exc:
        print(type(exc).__name__, exc)  # ValueinValidationError and the reason
```

## Using valuein-sdk with AI coding assistants

Paste this block into ChatGPT, Claude, Copilot or your agent's system prompt before
asking for code that uses this library.

```text
You are writing Python that uses the valuein-sdk library (PyPI: valuein-sdk).

Core API
- from valuein_sdk import ValueinClient
- Use it as a context manager:
  with ValueinClient(as_of=datetime(2024, 6, 30, tzinfo=timezone.utc)) as client: ...
- as_of must be a timezone-aware datetime. It defaults to now (UTC). Point-in-time tables
  (filing, fact, ratio, factor_scores, earnings_signals, stock_price, stock_price_daily
  and the smart-money tables) only show rows with accepted_at <= as_of.
- client.read_table(table, columns=None, limit=None, tickers=None) -> DataFrame
- client.run_query(sql) -> DataFrame. One SELECT, WITH or EXPLAIN statement. Values go
  inline as SQL literals. No read_parquet, no file or network functions.
- client.run_template(name, **params) -> DataFrame. Keyword arguments only.
  client.list_templates() lists the names.
- Also: client.resolve(identifiers), client.pit_universe("YYYY-MM-DD"),
  client.restatements(ticker=...), client.filing_links(ticker),
  client.stock_price(ticker), client.get_schema(table), client.tables(),
  client.capabilities().
- Quant layer, which needs a Pro or Institutional plan because it reads
  stock_price_daily: client.factor_backtest(signal, start=, end=, freq=),
  client.universe(), client.signal_panel(), client.forward_returns(),
  valuein_sdk.backtest().

Rules
1. Identify companies by CIK (10 digits, zero-padded, e.g. "0000320193"). Resolve a
   ticker once with client.resolve() and carry the CIK. Tickers get reused by other
   companies; CIKs do not.
2. Never invent, estimate or round a financial number. Only report values the library
   returned. If a query returns no rows, say so.
3. When you report a number from the fact table, cite its fact_id and accession_id. Use
   client.filing_links() for the SEC URL.
4. For backtests, set as_of and use fact.numeric_value. With an explicit as_of,
   value_current, restated and restatement_count are recomputed at the cutoff;
   without one they are the latest export's values.
5. In SQL, write "references" with double quotes (it is a keyword). Index membership is
   a JOIN on index_membership.cik; there is no is_sp500 column.
6. ratio has FY and TTM rows: always filter fiscal_period. A 10-K carries prior-year
   comparatives: group by period_end, not report_date. For one quarter of a cash-flow
   item use COALESCE(derived_quarterly_value, numeric_value).
7. For return math use total_return_index (or the SDK's total_return basis), never raw
   close, and never add div_cash on top of it.
8. client.get(table) skips the as_of filter. Use read_table or run_query instead.
9. To run SQL you generated, create the client with
   config=ValueinConfig(safe_mode=True).
10. SDK errors are ValueinError subclasses whose message states the fix. Read it before
    retrying.
```

The SDK is data plus local compute: every query, ratio and backtest runs in your
process. Documents (DCF and comps workbooks, research briefs) come from the Valuein
Workspace or the MCP server at `https://mcp.valuein.biz/mcp`, which agents that call
tools instead of writing Python can use with the same token.

## Examples and notebooks

Runnable examples live in the public [valuein/valuein](https://github.com/valuein/valuein)
repository:

- [Python scripts](https://github.com/valuein/valuein/tree/main/examples/python)
- [Jupyter notebooks](https://github.com/valuein/valuein/tree/main/examples/notebooks)

| Notebook | Open |
|---|---|
| 01 · Quickstart: your first query | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/01_quickstart.ipynb) |
| 02 · Fundamentals as of a date | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/02_fundamentals_as_of_a_date.ipynb) |
| 03 · Survivorship-free screening | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/03_survivorship_free_screening.ipynb) |
| 04 · Point-in-time factor backtest | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/04_pit_factor_backtest.ipynb) |
| 05 · Prices and total return | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/05_prices_and_total_return.ipynb) |
| 06 · Restatements and how they were disclosed | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/06_restatements.ipynb) |
| 07 · Smart money: insiders, 13F holders, advisers | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/07_smart_money.ipynb) |
| 08 · Verify a number against its SEC filing | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/08_verify_a_number.ipynb) |
| 09 · Use Valuein from an AI assistant | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/09_ai_assistant.ipynb) |
| 10 · Discounted cash flow valuation | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/10_dcf_valuation.ipynb) |
| 11 · Piotroski F-score screen | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/11_piotroski_screen.ipynb) |
| 12 · Earnings quality: the accrual anomaly | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/12_earnings_quality.ipynb) |
| 13 · Sector comparison: DuPont ROE | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/13_sector_comparison.ipynb) |
| 14 · Restatement alpha | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/14_restatement_alpha.ipynb) |
| 15 · Capital allocation | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/15_capital_allocation.ipynb) |
| 16 · Filing delay | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/valuein/valuein/blob/main/examples/notebooks/16_filing_delay.ipynb) |

## Support

- Documentation: [valuein.biz/developers/python-sdk](https://valuein.biz/developers/python-sdk)
- Questions and bugs: [github.com/valuein/valuein/issues](https://github.com/valuein/valuein/issues)
  or support@valuein.biz
- A wrong number: `client.report_issue("...", category="data_quality", fact_id=..., ticker=...)`
  sends it to the Valuein team from your code, with no token needed.

## License

Apache License 2.0. See the [license text](https://www.apache.org/licenses/LICENSE-2.0).

This library and its data are for research and education. Nothing here is financial
advice.
