Metadata-Version: 2.4
Name: finlet
Version: 2.0.0
Summary: A historical market simulation environment for testing AI trading agent reasoning against real market data.
Project-URL: Homepage, https://finlet.dev
Project-URL: Repository, https://github.com/justnau1020/finlet
Project-URL: Documentation, https://finlet.dev/agent-guide.html
Project-URL: Issues, https://github.com/justnau1020/finlet/issues
Author: Finlet Contributors
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Keywords: agent,ai,backtesting,llm,mcp,simulation,trading
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27
Requires-Dist: mcp>=1.27.0
Requires-Dist: rich>=13.0
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: bandit>=1.7; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: hypothesis>=6.100; extra == 'dev'
Requires-Dist: mypy-boto3-s3>=1.34; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pip-audit>=2.7; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.25; extra == 'dev'
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
Requires-Dist: pytest-xdist>=3.5; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Requires-Dist: types-markdown>=3.6; extra == 'dev'
Provides-Extra: ingest
Requires-Dist: beautifulsoup4>=4.12; extra == 'ingest'
Requires-Dist: boto3>=1.34; extra == 'ingest'
Requires-Dist: curl-cffi>=0.15.0; extra == 'ingest'
Requires-Dist: databento>=0.50; extra == 'ingest'
Requires-Dist: edgartools>=3.0; extra == 'ingest'
Requires-Dist: fredapi>=0.5; extra == 'ingest'
Requires-Dist: httpx>=0.27; extra == 'ingest'
Requires-Dist: pandas>=2.0; extra == 'ingest'
Requires-Dist: pyarrow>=15.0; extra == 'ingest'
Requires-Dist: structlog>=24.1; extra == 'ingest'
Requires-Dist: tenacity>=8.2; extra == 'ingest'
Requires-Dist: yfinance>=0.2.36; extra == 'ingest'
Provides-Extra: server
Requires-Dist: aiosqlite>=0.20; extra == 'server'
Requires-Dist: alembic>=1.14; extra == 'server'
Requires-Dist: argon2-cffi>=23.1.0; extra == 'server'
Requires-Dist: boto3>=1.34; extra == 'server'
Requires-Dist: cryptography>=46.0.6; extra == 'server'
Requires-Dist: edgartools>=3.0; extra == 'server'
Requires-Dist: fastapi>=0.115; extra == 'server'
Requires-Dist: finnhub-python>=2.4; extra == 'server'
Requires-Dist: fredapi>=0.5; extra == 'server'
Requires-Dist: greenlet>=3.0; extra == 'server'
Requires-Dist: joserfc>=1.0; extra == 'server'
Requires-Dist: markdown>=3.6; extra == 'server'
Requires-Dist: mcp[auth]>=1.27.0; extra == 'server'
Requires-Dist: pyarrow>=15.0; extra == 'server'
Requires-Dist: pydantic[email]>=2.9; extra == 'server'
Requires-Dist: pyotp>=2.9; extra == 'server'
Requires-Dist: qrcode[pil]>=7.4; extra == 'server'
Requires-Dist: sentry-sdk[fastapi]>=2.0.0; extra == 'server'
Requires-Dist: sqlmodel>=0.0.22; extra == 'server'
Requires-Dist: stripe>=8.0; extra == 'server'
Requires-Dist: structlog>=24.1; extra == 'server'
Requires-Dist: uvicorn[standard]>=0.30; extra == 'server'
Provides-Extra: sms
Requires-Dist: boto3>=1.34; extra == 'sms'
Description-Content-Type: text/markdown

---
tags: [finlet, readme, overview, trading, AI]
keywords: Finlet, evaluation harness, AI trading agents, backtesting alternative, FastAPI, MCP
priority: normal
---

# Finlet

**An evaluation harness for AI trading agents.** Daily-timeframe. US equities. Long-only.

Test your AI agent's *reasoning* — not just its algorithms.

Finlet is an agent evaluation harness that tests whether AI trading agents make sound decisions with the information available to them. It provides a simulation clock that controls what data your agent can see, a Date Ceiling Enforcer that strips future data from every response, and a tamper-evident reasoning trace that logs every query and decision with SHA-256 checksums.

This is not a backtester. Backtesters test strategies against historical data. Finlet tests whether your agent *reasons well* given the same information a human analyst would have had on that date.

## Why Finlet?

Most backtesting frameworks test *algorithms*. Finlet tests *reasoning*.

As a backtester, Finlet would lose to QuantConnect on every metric — data coverage, asset classes, speed. As an evaluation harness, it has no direct product competitor.

- **Date Ceiling Enforcer** — Defense-in-depth runtime enforcement strips any data past the simulation clock. Property-based tests prove no future item passes. No competitor has an equivalent.
- **SHA-256 Reasoning Trace** — Every query, decision, and order is logged with tamper-evident checksums. No backtester tracks agent reasoning.
- **Cross-Scenario Leaderboard** — Standardized evaluation with composite scoring across returns, risk, drawdown, reasoning quality, and information efficiency
- **Real data sources** — S3 Parquet data lake (prices, news, fundamentals, sentiment) is the primary hot path; SEC EDGAR filings, FRED economic indicators, and Finnhub (fallback for news + fundamentals when S3 is unavailable) round out the surface — not synthetic datasets
- **MCP native** — Connect Claude Code (or any MCP client) directly as a trading agent via 16 purpose-built tools

## v1 Scope

Finlet v1 is intentionally scoped to daily-timeframe, US-equities, long-only evaluation. These are design choices, not gaps:

| Scope Decision | Rationale |
|---|---|
| **EOD data only** | Agent evaluation tests reasoning quality, not execution speed |
| **US equities only** | Deepest data coverage (price, filings, economic, news) |
| **Long-only** | Isolates reasoning from margin/borrow mechanics |
| **No partial fills** | Honest about what EOD data supports (no order book depth) |
| **No live/paper trading** | Evaluation harness, not trading platform |

See [docs/V1_SCOPE.md](docs/V1_SCOPE.md) for the complete scope document with rationale.

---

## Quick Start

> **Finlet's launch user is an LLM agent invoking via MCP, not a human typing CLI commands.** The `finlet` binary bootstraps credentials and, by default, runs a stdio bridge to hosted `/mcp`; self-host operators use `finlet mcp serve --self-host` for the local FastMCP server. The agent host (Claude Desktop, Codex CLI, Cursor, Windsurf, custom MCP client) invokes the tools. The web dashboard at finlet.dev is a read-only monitoring surface. Setup guide for agents: [finlet.dev/setup](https://finlet.dev/setup). Machine-readable index: [finlet.dev/llms.txt](https://finlet.dev/llms.txt).

### 1. Install

```bash
pip install finlet
```

### 2. Authenticate

```bash
finlet auth login
```

Opens a browser to run the OAuth 2.1 + PKCE flow and persists a Bearer JWT at `~/.finlet/credentials` (mode 0600). The default MCP stdio bridge refreshes that credential when needed before forwarding calls to hosted `/mcp`. Headless / CI runs may use `finlet register --email you@example.com` to mint an api_key and pass it via `FINLET_API_KEY` instead.

### 3. Wire `finlet mcp serve` into your agent host

**Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) under `mcpServers`:

```json
{
  "mcpServers": {
    "finlet": {
      "command": "finlet",
      "args": ["mcp", "serve"]
    }
  }
}
```

**Codex CLI** — run from your terminal:

```sh
codex mcp add finlet -- finlet mcp serve
```

**Generic MCP Client (Cursor, Windsurf, custom)** — point your client at the stdio bridge with the same JSON schema:

```json
{
  "command": "finlet",
  "args": ["mcp", "serve"]
}
```

### 4. Dialog with your agent

Open the agent host. Ask:

```text
Create a session named 'my-first-sim' starting 2024-01-02 with $100,000
capital and tickers AAPL, MSFT, GOOGL. Then place a market BUY order
for 10 shares of AAPL and show me the portfolio.
```

The agent invokes the MCP tools (`create_session`, `submit_order`, `get_portfolio`) and reports back. The simulation clock starts FROZEN at the supplied start date; no future data leaks through the Date Ceiling Enforcer. See the MCP Tools section below or `dashboard/agent-guide.html` (`/agent-guide`) for the full tool inventory.

### 5. Watch your agent work in the monitoring dashboard

Visit `https://finlet.dev` (or `http://localhost:8000` when running locally) to see the read-only monitoring surface for your agent's sessions: equity curve, positions, trade log, reasoning trace, plugin health, leaderboard. The dashboard observes — it does not control. All write paths are MCP-only post-2026-05-22; legacy write-path UI is preserved at `legacy/dashboard-ui/`. See `docs/decisions/ui-removal-launch-cut.md` and `docs/decisions/v2-pure-mcp-migration.md` for the rationale.

### Direct REST API (alternative to CLI/MCP)

Any HTTP client works:

```python
import httpx

async with httpx.AsyncClient(base_url="http://localhost:8000") as client:
    clock = await client.get(f"/sessions/{session_id}/clock")
    prices = await client.get(
        f"/sessions/{session_id}/market/price",
        params={"ticker": "AAPL", "period": "3mo"},
    )
    await client.post(f"/sessions/{session_id}/trade/order", json={
        "side": "BUY",
        "ticker": "AAPL",
        "quantity": 50,
        "order_type": "MARKET",
        "reasoning": "Strong Q4 earnings beat, raising guidance, reasonable P/E",
    })
```

---

## Features

### Simulation Clock

Three modes to control how time flows:

| Mode | Behavior |
|------|----------|
| **FROZEN** | Clock is stopped. Agent can make unlimited queries at the current time. |
| **STEPPING** | Clock advances by a fixed interval when explicitly told to step. |
| **CONTINUOUS** | Clock advances in real-time at a configurable speed multiplier. |

The clock only moves forward when explicitly advanced. No implicit time progression.

### Date Ceiling Enforcer

Defense-in-depth protection against future data leakage:

- All timestamps in response data must be <= current sim clock time
- Items with timestamps after the ceiling are **stripped**
- Items without timestamps are **excluded by default**
- Strip counts are logged internally but **never exposed to the agent** (that would leak information about future data existence)

### Portfolio Engine

Full portfolio tracking with computed metrics:

- Cash tracking, position management, P&L (realized + unrealized)
- Sharpe ratio, max drawdown, win rate
- Equity curve time series for charting

### Order System

Market, limit, and stop orders with full lifecycle tracking:

```
PENDING -> FILLED | CANCELLED | REJECTED
```

Each order supports an optional `reasoning` field for trace logging.

### Plugin System

Extensible data source architecture. Built-in plugins connect to real financial APIs:

| Plugin | Data Type | API Key | Notes |
|--------|-----------|---------|-------|
| **PricePlugin** | Price (OHLCV) | None | S3 Parquet-backed historical price data. Hot path — no Finnhub fallback. |
| **FundamentalsPlugin** | Fundamentals | None | S3 Parquet quarterly financials. Default for fundamentals queries. |
| **NewsPlugin** | News | None | S3 Parquet historical news headlines. Default for news queries. |
| **SentimentPlugin** | Analyst Ratings | None | S3 Parquet analyst ratings, price targets, consensus. |
| **EDGAR** | SEC Filings | None* | 10-K, 10-Q, 8-K, 13-F, Form 4. *Requires User-Agent email. |
| **FRED** | Economic | Free key | GDP, unemployment, CPI, rates. ALFRED vintage dates. |
| **Finnhub** | News + Fundamentals (fallback) | User key | Fallback only — used when S3 news or fundamentals are unavailable. 60 calls/min free tier. |

```bash
# Configure plugin API keys
finlet plugins add finnhub --api-key=YOUR_KEY
finlet plugins add fred --api-key=YOUR_KEY
```

Plugin configuration is stored at `~/.finlet/plugins.json` (never committed to git).

### Reasoning Trace

Every agent interaction is logged:

- **Action type**: What kind of query or action (price, news, filing, order, etc.)
- **Sim time + real time**: When it happened in simulation and wall clock
- **Request params**: What was requested
- **Response summary**: What was returned
- **Reasoning**: Agent's own explanation for the action
- **Latency**: How long the operation took

### Leaderboard

Standardized evaluation across 5 scenarios with composite scoring. Agents are scored on:

- Portfolio returns vs. benchmark
- Risk-adjusted returns (Sharpe ratio)
- Maximum drawdown
- Reasoning quality
- Information efficiency (returns per API call)

Opt in to data sharing to appear on the public leaderboard and compare your agent against others.

---

## Architecture

```
+-------------------------------------------------------------+
|                     AI Trading Agent                         |
|               (Claude Code, custom bot, etc.)                |
+----------+------------------------------+--------------------+
           | MCP (stdio)                  | REST API
           v                              v
+-------------------------------------------------------------+
|                        Finlet Server                         |
|  +-----------+  +-----------+  +----------------------+     |
|  |  MCP      |  |  FastAPI   |  |   Static Dashboard   |     |
|  |  Server   |  |  Routes    |  |   (HTML/JS/CSS)      |     |
|  +-----+-----+  +-----+-----+  +----------------------+     |
|        +-------+-------+                                     |
|                v                                             |
|  +------------------------------------------------------+   |
|  |                   Session Engine                       |   |
|  |  +---------+  +-----------+  +------------------+     |   |
|  |  |  Clock   |  | Portfolio  |  |  Order Executor  |     |   |
|  |  | (frozen) |  |  Engine    |  |                  |     |   |
|  |  +---------+  +-----------+  +------------------+     |   |
|  +------------------------+-------------------------------+   |
|                           v                                   |
|  +------------------------------------------------------+   |
|  |              Date Ceiling Enforcer                     |   |
|  |         (strips data after sim clock time)             |   |
|  +------------------------+-------------------------------+   |
|                           v                                   |
|  +------------------------------------------------------+   |
|  |                 Plugin Registry                        |   |
|  |  +----------+ +---------+ +-------+ +------+ +---------+ |   |
|  |  |  Price   | | S3 News | | EDGAR | | FRED | | Finnhub | |   |
|  |  |(S3 Parqt)| | + Fund. | |(filing| |(econ)| |(fallback| |   |
|  |  |          | | + Sent. | |       | |      | | news+fd)| |   |
|  |  +----------+ +---------+ +-------+ +------+ +---------+ |   |
|  +------------------------------------------------------+   |
|                           |                                   |
|  +------------------------v-------------------------------+   |
|  |            SQLite (per-session DB)                      |   |
|  |     ~/.finlet/sessions/{id}/session.db                  |   |
|  +------------------------------------------------------+   |
+-------------------------------------------------------------+
```

---

## API Reference

Base URL: `http://localhost:8000` (local) or `https://finlet.dev` (cloud)

### Sessions

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/sessions` | Create a new session |
| `GET` | `/sessions` | List all sessions |
| `GET` | `/sessions/{id}` | Get session state |
| `DELETE` | `/sessions/{id}` | End a session |
| `POST` | `/sessions/{id}/configure` | Update session config |

### Clock

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/sessions/{id}/clock` | Current clock state |
| `POST` | `/sessions/{id}/clock/freeze` | Freeze the clock |
| `POST` | `/sessions/{id}/clock/step` | Step by interval |
| `POST` | `/sessions/{id}/clock/step-to` | Step to a specific time |
| `POST` | `/sessions/{id}/clock/play` | Start continuous mode |
| `POST` | `/sessions/{id}/clock/stop` | Stop continuous mode |

### Market Data

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/sessions/{id}/market/price` | Price data (OHLCV) |
| `POST` | `/sessions/{id}/market/search-news` | Search news articles |
| `GET` | `/sessions/{id}/market/fundamentals` | Company fundamentals |
| `GET` | `/sessions/{id}/market/filings` | SEC filings |
| `GET` | `/sessions/{id}/market/economic` | Economic indicators |

### Trading

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/sessions/{id}/trade/order` | Submit an order |
| `GET` | `/sessions/{id}/trade/orders` | List orders |
| `GET` | `/sessions/{id}/trade/orders/{oid}` | Order detail |
| `DELETE` | `/sessions/{id}/trade/orders/{oid}` | Cancel an order |

### Portfolio

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/sessions/{id}/portfolio` | Portfolio state + metrics |
| `GET` | `/sessions/{id}/portfolio/history` | Equity curve time series |
| `GET` | `/sessions/{id}/trace` | Reasoning trace log |

Full interactive docs at `/docs` when the server is running.

---

## MCP Tools

When connected via MCP, these tools are available to your AI agent:

| Tool | Description |
|------|-------------|
| `get_price_data` | Fetch OHLCV price data for a ticker |
| `search_news` | Search news articles by keyword/ticker |
| `get_fundamentals` | Get company financial fundamentals |
| `get_filings` | Retrieve SEC filings |
| `get_economic_data` | Get economic indicators (GDP, CPI, etc.) |
| `submit_order` | Place a buy/sell order |
| `get_portfolio` | View current portfolio state |
| `get_sim_time` | Check current simulation time |
| `advance_time` | Step the simulation clock forward |
| `freeze_time` | Freeze the simulation clock |

---

## CLI Reference

The `finlet` binary is the minimal bootstrap shell. All agent operations (session create / list / delete, advance, order, portfolio, status, benchmark *) go through the MCP tool surface invoked by your agent host — not the CLI. See `docs/decisions/v2-pure-mcp-migration.md` for the v2.0.0 rationale.

```bash
finlet serve                    # Start API server + dashboard (operator surface)
finlet mcp serve                # Stdio bridge to hosted /mcp — wire into your agent host
finlet mcp serve --self-host    # Local FastMCP server for self-host operators
finlet auth login               # OAuth 2.1 + PKCE browser bootstrap
finlet auth logout              # Clear ~/.finlet/credentials
finlet auth status              # Report local credential state
finlet register --email EMAIL   # Mint a bootstrap api_key (CI / headless)
finlet plugins list             # Show plugin status
finlet plugins add NAME         # Configure a plugin
  --api-key TEXT                #   API key for the plugin
finlet plugins remove NAME      # Clear a plugin's credentials
finlet manual [TOPIC]           # Local markdown topic viewer
finlet --version                # Diagnostics
```

---

## Tech Stack

- **Python 3.12+** — Async throughout, type hints everywhere
- **FastAPI** — REST API with automatic OpenAPI docs
- **MCP SDK** — Model Context Protocol server for LLM integration
- **SQLite** — Per-session database via aiosqlite + SQLModel
- **TradingView Lightweight Charts** — Dashboard charting (MIT, via CDN)

## Contributing

1. Clone the repo
2. Install in dev mode: `pip install -e ".[dev]"`
3. Run tests: `pytest`
4. Follow the code conventions in `CLAUDE.md`

Key rules:
- All datetimes in UTC internally
- Async functions everywhere — no sync I/O in the hot path
- Error messages must be specific and actionable
- Every plugin response goes through the date ceiling enforcer
- Mock external APIs in tests — no real network calls

## Disclaimers

Finlet is provided for educational and research purposes only. It is not financial advice and should not be used as the basis for any investment decisions.

Past performance of simulated trading strategies does not guarantee future results. All market data is historical and provided by third-party sources subject to their respective terms of service.

Finlet is not affiliated with any stock exchange, broker, or financial institution.

## License

Proprietary
