{"id":50507923,"url":"https://github.com/Yvictor/polars_backtest_extension","last_synced_at":"2026-06-19T11:01:04.305Z","repository":{"id":329587869,"uuid":"1115608942","full_name":"Yvictor/polars_backtest_extension","owner":"Yvictor","description":"Blazingly fast portfolio backtesting for Polars","archived":false,"fork":false,"pushed_at":"2026-02-02T05:34:05.000Z","size":1120,"stargazers_count":6,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-25T20:03:14.063Z","etag":null,"topics":["backtest","polars","polars-extensions","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Yvictor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-13T07:24:31.000Z","updated_at":"2026-03-07T07:47:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Yvictor/polars_backtest_extension","commit_stats":null,"previous_names":["yvictor/polars_backtest_extension"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/Yvictor/polars_backtest_extension","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yvictor%2Fpolars_backtest_extension","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yvictor%2Fpolars_backtest_extension/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yvictor%2Fpolars_backtest_extension/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yvictor%2Fpolars_backtest_extension/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Yvictor","download_url":"https://codeload.github.com/Yvictor/polars_backtest_extension/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Yvictor%2Fpolars_backtest_extension/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34528144,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backtest","polars","polars-extensions","rust"],"created_at":"2026-06-02T18:00:24.356Z","updated_at":"2026-06-19T11:01:04.296Z","avatar_url":"https://github.com/Yvictor.png","language":"Rust","funding_links":[],"categories":["Libraries/Packages/Scripts"],"sub_categories":["Polars plugins"],"readme":"# polars-backtest\n\nBlazingly fast portfolio backtesting for Polars\n\n- Blazingly fast, written in Rust with Arrow\n- Native Polars integration with `df.bt.backtest()` namespace\n- T+1 execution, stop loss, take profit, trailing stop\n- Touched exit with intraday OHLC detection\n\n## Installation\n\n```bash\npip install polars-backtest\n# or\nuv add polars-backtest\n```\n\n## Quick Start\n\n```python\nimport polars as pl\nimport polars_backtest as pl_bt\n\n# Long format data: one row per (date, symbol)\ndf = pl.DataFrame({\n    \"date\": [\"2024-01-01\", \"2024-01-01\", \"2024-01-02\", \"2024-01-02\"],\n    \"symbol\": [\"AAPL\", \"GOOGL\", \"AAPL\", \"GOOGL\"],\n    \"close\": [100.0, 50.0, 102.0, 51.0],\n    \"weight\": [0.6, 0.4, 0.6, 0.4],\n})\n\n# Run backtest\nresult = df.bt.backtest(trade_at_price=\"close\", position=\"weight\")\n```\n\n## Performance\n\n**300-day breakout strategy** (~2000 stocks, 17 years daily data, 12M rows):\n\n```python\n# Finlab\nposition = close \u003e= close.rolling(300).max()\nreport = backtest.sim(position, resample=\"M\")\n\n# polars_backtest\ndf = df.with_columns(\n    (pl.col(\"close\") \u003e= pl.col(\"close\").rolling_max(300).over(\"symbol\"))\n    .alias(\"weight\")\n)\nreport = df.bt.backtest_with_report(position=\"weight\", resample=\"M\")\n```\n\n| | Finlab | polars_backtest |\n|---|--------|-----------------|\n| Time | 3.7s | 244ms |\n| Speedup | 1x | **15x faster** |\n\n```bash\njust bench  # Run benchmarks\n```\n\n## Features\n\n- **Rust Core** - Pure Rust implementation with Arrow\n- **Native Polars** - Works with long format DataFrames, supports Polars expressions\n- **T+1 Execution** - Realistic trading simulation\n- **Risk Management** - Stop loss, take profit, trailing stop, touched exit (OHLC)\n- **Flexible Rebalancing** - Daily, weekly, monthly, or on position change\n- **Claude Code Skill** - AI-powered assistance for backtesting\n\n## Claude Code Integration\n\nGet AI-powered assistance for writing and analyzing backtests.\n\n### Install the Skill\n\n```bash\n# 1. Add the marketplace\n/plugin marketplace add Yvictor/polars_backtest_extension\n\n# 2. Install the plugin\n/plugin install polars-backtest@polars-backtest-marketplace\n```\n\nAfter installation, Claude Code can help you:\n- Write backtest strategies using Polars expressions\n- Understand all parameters and their effects\n- Analyze results with `get_metrics()`, `get_stats()`, etc.\n- Debug and optimize your trading strategies\n\n---\n\n## Usage\n\n### Basic Backtest\n\n```python\nimport polars_backtest as pl_bt\n\n# Function API\nresult = pl_bt.backtest(df, trade_at_price=\"close\", position=\"weight\")\n\n# DataFrame namespace\nresult = df.bt.backtest(trade_at_price=\"close\", position=\"weight\")\n```\n\n### With Expressions\n\n```python\nresult = df.bt.backtest(\n    trade_at_price=\"close\",\n    position=pl.col(\"signal\").cast(pl.Float64),\n    resample=\"M\",\n)\n```\n\n### Full Report with Trades\n\n```python\nreport = pl_bt.backtest_with_report(df, trade_at_price=\"adj_close\", resample=\"M\")\nreport\n```\n\n```\nBacktestReport(\n  creturn_len=4219,\n  trades_count=6381,\n  total_return=8761.03%,\n  cagr=29.85%,\n  max_drawdown=-35.21%,\n  sharpe=1.13,\n  win_ratio=46.33%\n)\n```\n\n```python\nreport.get_stats()  # or report.stats\n```\n\n```\nshape: (1, 15)\n┌────────────┬────────────┬──────┬──────────────┬──────────┬──────────────┬──────────────┐\n│ start      ┆ end        ┆ rf   ┆ total_return ┆ cagr     ┆ max_drawdown ┆ avg_drawdown │\n│ ---        ┆ ---        ┆ ---  ┆ ---          ┆ ---      ┆ ---          ┆ ---          │\n│ date       ┆ date       ┆ f64  ┆ f64          ┆ f64      ┆ f64          ┆ f64          │\n╞════════════╪════════════╪══════╪══════════════╪══════════╪══════════════╪══════════════╡\n│ 2008-10-31 ┆ 2025-12-31 ┆ 0.02 ┆ 87.610293    ┆ 0.298538 ┆ -0.352092    ┆ -0.042957    │\n└────────────┴────────────┴──────┴──────────────┴──────────┴──────────────┴──────────────┘\n┌────────────┬───────────┬──────────────┬───────────────┬──────────┬───────────┬─────────┬───────────┐\n│ daily_mean ┆ daily_vol ┆ daily_sharpe ┆ daily_sortino ┆ best_day ┆ worst_day ┆ calmar  ┆ win_ratio │\n│ ---        ┆ ---       ┆ ---          ┆ ---           ┆ ---      ┆ ---       ┆ ---     ┆ ---       │\n│ f64        ┆ f64       ┆ f64          ┆ f64           ┆ f64      ┆ f64       ┆ f64     ┆ f64       │\n╞════════════╪═══════════╪══════════════╪═══════════════╪══════════╪═══════════╪═════════╪═══════════╡\n│ 0.300815   ┆ 0.249645  ┆ 1.131947     ┆ 1.834553      ┆ 0.195416 ┆ -0.160707 ┆ 0.84784 ┆ 0.463303  │\n└────────────┴───────────┴──────────────┴───────────────┴──────────┴───────────┴─────────┴───────────┘\n```\n\n```python\nreport.creturn   # Cumulative returns DataFrame\nreport.trades    # Trade records with MAE/MFE metrics\nreport.stats     # Statistics (same as get_stats())\n```\n\n### Benchmark Comparison\n\nCompare strategy performance against a benchmark to get alpha, beta, and rolling win rate:\n\n```python\n# Method 1: Use a symbol from your data as benchmark\nreport = df.bt.backtest_with_report(\n    position=\"weight\",\n    benchmark=\"0050\",  # Symbol value (e.g., ETF ticker)\n)\n\n# Method 2: Provide a benchmark DataFrame\nbenchmark_df = pl.DataFrame({\n    \"date\": [...],\n    \"creturn\": [...],  # Cumulative return starting at 1.0\n})\nreport = df.bt.backtest_with_report(position=\"weight\", benchmark=benchmark_df)\n\n# Method 3: Set benchmark after creation\nreport = df.bt.backtest_with_report(position=\"weight\")\nreport.benchmark = benchmark_df\n\n# get_metrics includes benchmark metrics when benchmark is set\nmetrics = report.get_metrics()\n# Includes: alpha, beta, m12WinRate (12-month rolling win rate vs benchmark)\n```\n\n### Liquidity Metrics\n\nGet liquidity metrics by providing optional columns in your DataFrame:\n\n```python\ndf = pl.DataFrame({\n    \"date\": dates,\n    \"symbol\": symbols,\n    \"close\": prices,\n    \"weight\": weights,\n    \"limit_up\": limit_up_prices,      # For buyHigh metric\n    \"limit_down\": limit_down_prices,  # For sellLow metric\n    \"trading_value\": trading_values,  # For capacity metric (e.g., close_raw * volume)\n})\n\nreport = df.bt.backtest_with_report(position=\"weight\")\nmetrics = report.get_metrics(sections=[\"liquidity\"])\n# Includes: buyHigh, sellLow, capacity\n```\n\n| Metric | Required Column | Description |\n|--------|-----------------|-------------|\n| `buyHigh` | `limit_up` | Ratio of entries at limit-up price |\n| `sellLow` | `limit_down` | Ratio of exits at limit-down price |\n| `capacity` | `trading_value` | Strategy capacity (10th percentile of accepted money flow) |\n\nMetrics return `null` if the required column is not present.\n\n### Parameters\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| `trade_at_price` | `\"close\"` | Price column for execution |\n| `position` | `\"weight\"` | Position/weight column |\n| `benchmark` | `None` | Benchmark: symbol str or DataFrame with (date, creturn) |\n| `resample` | `\"D\"` | Rebalance frequency |\n| `resample_offset` | `None` | Delay rebalance by N days (e.g., `\"1d\"`, `\"2d\"`, `\"1W\"`) |\n| `fee_ratio` | `0.001425` | Transaction fee |\n| `tax_ratio` | `0.003` | Transaction tax |\n| `stop_loss` | `1.0` | Stop loss threshold (1.0 = disabled) |\n| `take_profit` | `inf` | Take profit threshold |\n| `trail_stop` | `inf` | Trailing stop: exit when `maxcr - cr \u003e= trail_stop` |\n| `touched_exit` | `False` | Use OHLC for intraday stop detection |\n\n---\n\n### Resample Options\n\n| Value | Description |\n|-------|-------------|\n| `None` | Only rebalance when position changes |\n| `'D'` | Daily |\n| `'W'` | Weekly (last trading day) |\n| `'W-FRI'` | Weekly on Friday |\n| `'M'` | Monthly (last trading day) |\n| `'Q'` | Quarterly |\n| `'Y'` | Yearly |\n\n## Development\n\n### Setup\n\n```bash\njust sync   # Install dependencies\njust build  # Build extension\n```\n\n### Test\n\n```bash\njust test       # Fast tests (81)\njust test-slow  # Slow tests (86)\njust test-all   # All tests\njust bench      # Benchmarks\n```\n\n### Workflow\n\n```bash\njust check      # Rust check\njust test-rust  # Rust tests\njust build      # Build extension\njust test       # Python tests\njust ci         # Full CI\n```\n\n---\n\n## Project Structure\n\n```\npolars_backtest_extension/\n├── btcore/                 # Pure Rust core\n│   └── src/simulation/\n│       ├── wide.rs         # Wide format backtest\n│       └── long.rs         # Long format (Arrow FFI)\n└── polars_backtest/\n    ├── python/             # Python API\n    │   └── polars_backtest/\n    │       ├── namespace.py  # df.bt namespace\n    │       └── wide.py       # Wide format API\n    ├── src/                # PyO3 bindings\n    │   └── lib.rs\n    ├── tests/\n    └── benchmarks/\n```\n\n## License\n\nPolyForm Noncommercial 1.0.0\n\nFor commercial use, please contact the author to obtain a commercial license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYvictor%2Fpolars_backtest_extension","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FYvictor%2Fpolars_backtest_extension","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FYvictor%2Fpolars_backtest_extension/lists"}