From 7a44d9e1011f06e1d7b3c6b054cb295e7350e490 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 1 Oct 2026 16:23:43 -0400 Subject: [PATCH] v0.3.1 - Expand on the web view, add a section for S&P trade analysis --- README.md | 48 +++-- pyproject.toml | 7 +- requirements.txt | 3 + src/kalshi_csv/__init__.py | 2 +- src/kalshi_csv/cli.py | 13 +- src/kalshi_csv/parser.py | 257 +++++++++++++++++++++++++-- src/kalshi_csv/static/dashboard.js | 13 ++ src/kalshi_csv/static/modern.css | 45 +++++ src/kalshi_csv/templates/modern.html | 56 ++++++ src/kalshi_csv/web.py | 1 + tests/test_cli.py | 43 +++++ tests/test_parser.py | 218 +++++++++++++++++++++++ tests/test_web.py | 81 +++++++++ 13 files changed, 760 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 44581b1..93a4da8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ Parse Kalshi transaction CSV files and generate IRS Form 8949 tax summaries for event contract trading. +## What's New in 0.3.1 + +- **`--local` Flag**: Add `--local` to either `--legacy-web` or `--modern-web` to bind the server to `127.0.0.1` instead of all network interfaces. +- **S&P 500 Hourly Setups**: The modern dashboard reveals up to five historically profitable setups, grouped by entry-price tier and the contract target hour encoded in the market ticker. Each setup shows its trade record, win rate, net P&L, average S&P 500 index prices at trade open and close, and an expandable list of its top winning trades. +- **Yahoo Finance Integration**: S&P 500 index prices come from Yahoo Finance (`^GSPC`). Only ticker symbols and trade timestamps are requested; transaction CSV data is never sent to a third party, and unavailable market data is reported rather than guessed. +- **Dashboard Data-Flow Documentation**: Developer notes now describe how parsed data is serialized into the HTML template and consumed by Alpine.js. + ## What's New in 0.3.0 - **S&P 500 Contract Performance by Price Tier**: The HTML view groups S&P 500 contracts by entry-price tier and reports trade count, win rate, and net P&L. @@ -13,14 +20,6 @@ Parse Kalshi transaction CSV files and generate IRS Form 8949 tax summaries for pip install kalshi-csv ``` -## What's New in 0.2.0 - -- **Summary Cards**: View key metrics at a glance - Net Realized P&L, Win/Loss Record, Total Volume, and Best/Worst Single Trade -- **Market Breakdown**: See performance by market category with trade counts, win rates, and net P&L -- **Legacy Web Mode**: Browse your portfolio in a retro HTML 4.01 web interface compatible with older browsers (Netscape Navigator, IE 4+) -- **Market Categorization**: Automatic categorization of tickers into 7 market types (Global Soccer, MLB, NPB, NBA Summer League, WNBA, S&P 500, Multivariate Events, Other Markets) -- **Handling of Sold + Acquired Dates**: Instead of handling the aggregated Kalshi trades through approximated dates, we use `VARIOUS` to signal to the IRS that every single underlying transaction in that row independently satisfies the short-term holding period rule (one year or less), even though they were purchased at different times. - ## Getting Your Transactions CSV Download your transaction history from Kalshi: @@ -87,12 +86,18 @@ Start the modern interactive dashboard: kalshi-csv Kalshi-Transactions-2026.csv --modern-web ``` -The modern dashboard is served at `http://127.0.0.1:8080/` by default. It binds to the local machine only because it includes your full trade history. Select a theme in the header; your selection is remembered in that browser. To choose another port: +By default, the modern dashboard binds to `0.0.0.0:8080`, so it may be reachable from other devices on your network. Visit `http://127.0.0.1:8080/` on the same machine, or add `--local` to restrict access to that machine. Select a theme in the header; your selection is remembered in that browser. To choose another port: ```bash kalshi-csv Kalshi-Transactions-2026.csv --modern-web --modern-web-port 3000 ``` +To bind the modern dashboard to this machine only: + +```bash +kalshi-csv Kalshi-Transactions-2026.csv --modern-web --local +``` + ### Sample Output Default mode (Unicode box-drawing): @@ -197,7 +202,7 @@ View your portfolio in a web browser with a retro HTML 4.01 interface compatible kalshi-csv Kalshi-Transactions-2026.csv --legacy-web ``` -This starts an HTTP server on `0.0.0.0:8080` by default. Access it from any machine on your network by navigating to `http://:8080`. +This starts an HTTP server on `0.0.0.0:8080` by default, which may be reachable from other devices on your network. Access it locally at `http://127.0.0.1:8080`, or from another network device at `http://:8080`. To use a different port: @@ -205,6 +210,12 @@ To use a different port: kalshi-csv Kalshi-Transactions-2026.csv --legacy-web --legacy-web-port 3000 ``` +Use `--local` with either web mode to bind only to this machine (`127.0.0.1`): + +```bash +kalshi-csv Kalshi-Transactions-2026.csv --legacy-web --local +``` + ### What's Displayed The web interface shows: @@ -224,11 +235,14 @@ For S&P 500 contracts, the HTML view groups trades into four entry-price tiers: The modern dashboard is a separate HTML5 interface; `--legacy-web` continues to serve the original HTML 4.01 page. It includes: - Portfolio summary cards, an interactive market P&L chart, trade record distribution, and the S&P 500 entry-price analysis +- An interactive S&P 500 hourly suggestion card, which reveals up to five historically profitable tier/hour setups - The full closed-trade history with ticker search, market and side filters, sortable columns, and pagination - An expandable IRS Form 8949 summary - All supplied color palettes, with the selected theme saved in the browser -Alpine.js is bundled with the package and served locally; the dashboard does not load scripts, fonts, or transaction data from third-party services. +Alpine.js is bundled with the package and served locally; the dashboard does not load scripts, fonts, or transaction data from third-party services. The only external request is a read-only S&P 500 price lookup from Yahoo Finance, which sends ticker symbols and trade timestamps but never your transaction CSV. That lookup needs network access and may be unavailable for some dates, in which case the dashboard shows `N/A` instead of a value. + +The S&P 500 suggestion groups trades by entry-price tier and the contract target hour encoded by the ticker's `Hhhmm` field; each trade's actual open time is shown separately in its trade details. Suggested setups must have at least five trades and positive net P&L; candidates are ranked by win rate, then net P&L, and up to five are shown. Each setup shows the average S&P 500 index price (via Yahoo Finance `^GSPC` data) sampled near the trades' open and close timestamps, plus an expandable list of up to three winning trades ranked by net P&L. Win rates count positive-P&L trades over all trades, including pushes in the denominator, and net P&L includes fees. This is a summary of historical results, not a prediction or trading advice. ## Market Categorization @@ -322,6 +336,17 @@ for trade in recent: For developers who want to contribute or run the test suite: +### Modern Dashboard Data Flow + +The modern dashboard does not use a server-side templating engine. Its data moves through these steps: + +1. `KalshiCSV.parse()` populates the Python trade list and summary. In `src/kalshi_csv/web.py`, `render_modern_dashboard_html()` assembles these structures and the market, S&P 500, IRS, and theme data into the `dashboard_data` dictionary. +2. The renderer serializes that dictionary with `json.dumps()` and escapes `&`, `<`, and `>` as Unicode escapes. This keeps CSV-controlled strings inside the JSON data block rather than allowing them to terminate the script element or become markup. +3. The renderer reads `src/kalshi_csv/templates/modern.html` and uses plain string replacement to inject the serialized JSON into `{{DASHBOARD_DATA}}` and the package version into `{{APP_VERSION}}`. These are simple placeholders, not template-engine directives. +4. The page stores the JSON in `", 1)[0] + payload = json.loads(data_block) + + assert payload["sp500_hourly"] == [{ + "price_tier": ">$0.40-$0.70 (At-the-Money / Coincident)", + "hour": 13, + "trades": 1, + "wins": 1, + "losses": 0, + "pushes": 0, + "net_pnl": 0.47, + "win_rate": 100.0, + "average_pnl": 0.47, + "target_hour": "13:00 ET", + "recommended": False, + "market_context": None, + "avg_sp500_open": None, + "avg_sp500_close": None, + }] + + +def test_modern_dashboard_serializes_top_winning_suggestion_trades(sample_csv, monkeypatch): + from kalshi_csv import parser as parser_module + + monkeypatch.setattr( + parser_module.KalshiCSV, + "_sp500_market_context", + lambda self, trades: None, + ) + kalshi = KalshiCSV(sample_csv) + kalshi.parse() + base_trade = dict(kalshi.trades[0]) + base_trade["market_category"] = "S&P 500 (INXU Intraday)" + base_trade["ticker"] = "KXINXU-26JUL07H1600-T7615" + base_trade["side"] = "YES" + base_trade["entry"] = 0.50 + base_trade["exit"] = 1.0 + kalshi.trades = [ + dict(base_trade, pnl_with_fees=pnl) + for pnl in (0.2, 0.5, 0.3, 0.4, 0.1) + ] + + html_content = render_modern_dashboard_html(kalshi, "test.csv") + data_block = html_content.split( + '", 1)[0] + payload = json.loads(data_block) + suggestion = next(item for item in payload["sp500_hourly"] if item["recommended"]) + + assert [trade["pnl"] for trade in suggestion["top_trades"]] == [0.5, 0.4, 0.3] + assert suggestion["top_trades"][0]["open_time_et"] == "Jul 07, 2026 09:48 AM ET" + assert all(trade["pnl"] > 0 for trade in suggestion["top_trades"]) + assert "_trades" not in suggestion def test_modern_dashboard_escapes_csv_values_inside_json_data(sample_csv):