From c5d9a9a0eed3abfdf243527c53b45c77640d4344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Wed, 8 Jul 2026 21:57:05 +0800 Subject: [PATCH] Add Tencent quote implementation plan --- .../plans/2026-07-08-tencent-quotes.md | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-08-tencent-quotes.md diff --git a/docs/superpowers/plans/2026-07-08-tencent-quotes.md b/docs/superpowers/plans/2026-07-08-tencent-quotes.md new file mode 100644 index 0000000..f1631aa --- /dev/null +++ b/docs/superpowers/plans/2026-07-08-tencent-quotes.md @@ -0,0 +1,203 @@ +# Tencent Quotes Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Display Tencent realtime A-share prices in the holdings table and account summary after a manual quote refresh. + +**Architecture:** Add a focused market quote client that parses Tencent responses and returns typed quote snapshots. Keep quotes in `TradingService` as an in-memory cache, then pass quote prices into the existing pure position calculations so GUI display updates without writing realtime prices into SQLite. + +**Tech Stack:** Python 3.11, urllib.request, Decimal, PySide6, pytest. + +--- + +## File Structure + +- Create `src/grid_trading/market/__init__.py`: market package marker. +- Create `src/grid_trading/market/tencent.py`: Tencent symbol inference, response parsing, and HTTP quote provider. +- Modify `src/grid_trading/domain/models.py`: add `QuoteSnapshot`. +- Modify `src/grid_trading/domain/calculations.py`: let quote snapshots override manual price when computing positions. +- Modify `src/grid_trading/services/trading_service.py`: add quote provider injection, `refresh_quotes`, and quote cache use in summaries. +- Modify `src/grid_trading/ui/main_window.py`: make the toolbar refresh action fetch Tencent quotes and display quote refresh errors. +- Create `tests/test_tencent_quotes.py`: parser and symbol inference tests. +- Modify `tests/test_calculations.py`: verify quote price overrides manual price. +- Modify `tests/test_services.py`: verify service refresh uses fake quote provider and summaries use realtime price. + +## Tasks + +### Task 1: Tencent Quote Parser + +**Files:** +- Create: `src/grid_trading/market/__init__.py` +- Create: `src/grid_trading/market/tencent.py` +- Test: `tests/test_tencent_quotes.py` + +- [ ] **Step 1: Write failing parser tests** + +```python +from decimal import Decimal + +from grid_trading.market.tencent import infer_tencent_symbol, parse_tencent_response + + +def test_infer_tencent_symbol_from_a_share_code(): + assert infer_tencent_symbol("000001") == "sz000001" + assert infer_tencent_symbol("600000") == "sh600000" + assert infer_tencent_symbol("sz000001") == "sz000001" + + +def test_parse_tencent_response_extracts_realtime_fields(): + raw = ( + 'v_sz000001="51~平安银行~000001~10.60~10.47~10.44~0~0~0~' + '10.60~0~10.59~0~10.58~0~10.57~0~10.56~0~10.61~0~10.62~0~' + '10.63~0~10.64~0~10.65~0~~20260708161430~0.13~1.24~10.63~10.34";' + ).encode("gbk") + + [quote] = parse_tencent_response(raw) + + assert quote.symbol == "sz000001" + assert quote.code == "000001" + assert quote.name == "平安银行" + assert quote.price == Decimal("10.60") + assert quote.previous_close == Decimal("10.47") + assert quote.open_price == Decimal("10.44") + assert quote.change == Decimal("0.13") + assert quote.change_percent == Decimal("1.24") + assert quote.high == Decimal("10.63") + assert quote.low == Decimal("10.34") +``` + +- [ ] **Step 2: Run parser tests and verify they fail** + +Run: `pytest tests/test_tencent_quotes.py -v` +Expected: FAIL because `grid_trading.market` does not exist. + +- [ ] **Step 3: Implement parser and provider** + +Define `QuoteFetchError`, `infer_tencent_symbol`, `parse_tencent_response`, and `TencentQuoteProvider.fetch_quotes(instruments)`. + +- [ ] **Step 4: Run parser tests and verify they pass** + +Run: `pytest tests/test_tencent_quotes.py -v` +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add src/grid_trading/market tests/test_tencent_quotes.py +git commit -m "feat: add Tencent quote parser" +``` + +### Task 2: Quote Prices In Calculations And Service + +**Files:** +- Modify: `src/grid_trading/domain/models.py` +- Modify: `src/grid_trading/domain/calculations.py` +- Modify: `src/grid_trading/services/trading_service.py` +- Test: `tests/test_calculations.py` +- Test: `tests/test_services.py` + +- [ ] **Step 1: Write failing calculation and service tests** + +Add a calculation test where an `Instrument` has `manual_price=Decimal("9")`, a `QuoteSnapshot(price=Decimal("10"))` is passed, and `PositionSummary.current_price` becomes `10` with `price_source == "tencent"`. + +Add a service test with a fake provider: + +```python +class FakeQuoteProvider: + def fetch_quotes(self, instruments): + return { + "510300": QuoteSnapshot( + symbol="sh510300", + code="510300", + name="沪深300ETF", + price=Decimal("4.12"), + source="tencent", + ) + } +``` + +The test should call `service.refresh_quotes()` and assert `get_position_summaries()[0].current_price == Decimal("4.12")`. + +- [ ] **Step 2: Run tests and verify they fail** + +Run: `pytest tests/test_calculations.py tests/test_services.py -v` +Expected: FAIL because `QuoteSnapshot` and service quote cache are not implemented. + +- [ ] **Step 3: Implement quote snapshot flow** + +Add `QuoteSnapshot` to models. Add `quote_snapshots` argument to `calculate_positions`. Add `quote_provider` injection, `_quote_cache`, and `refresh_quotes` to `TradingService`. + +- [ ] **Step 4: Run tests and verify they pass** + +Run: `pytest tests/test_calculations.py tests/test_services.py -v` +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add src/grid_trading/domain src/grid_trading/services tests/test_calculations.py tests/test_services.py +git commit -m "feat: use realtime quotes in summaries" +``` + +### Task 3: GUI Refresh Wiring + +**Files:** +- Modify: `src/grid_trading/ui/main_window.py` +- Test: `tests/test_ui.py` + +- [ ] **Step 1: Update GUI smoke expectation** + +Extend the existing main window smoke test to assert a refresh button exists with text containing `行情`. + +- [ ] **Step 2: Run UI test and verify it fails** + +Run: `pytest tests/test_ui.py -v` +Expected: FAIL because the toolbar still uses the old generic refresh text. + +- [ ] **Step 3: Implement GUI quote refresh** + +Change the toolbar button to `刷新行情`. Wire it to call `service.refresh_quotes()` before `refresh_all()`. Keep post-save refreshes as local recalculation without forcing network calls. + +- [ ] **Step 4: Run UI test and verify it passes** + +Run: `pytest tests/test_ui.py -v` +Expected: PASS. + +- [ ] **Step 5: Commit** + +```bash +git add src/grid_trading/ui/main_window.py tests/test_ui.py +git commit -m "feat: refresh Tencent quotes from GUI" +``` + +### Task 4: Verification + +**Files:** +- Modify: `README.md` + +- [ ] **Step 1: Run all tests** + +Run: `pytest -v` +Expected: all tests pass. + +- [ ] **Step 2: Run CLI smoke** + +Run: `python -m grid_trading.app --help` +Expected: help text prints normally. + +- [ ] **Step 3: Update README** + +Document that the `刷新行情` button fetches Tencent quotes from `http://qt.gtimg.cn/q=` and that realtime quotes are not persisted. + +- [ ] **Step 4: Commit** + +```bash +git add README.md +git commit -m "docs: document Tencent quote refresh" +``` + +## Self-Review + +- Spec coverage: parser, symbol inference, realtime price priority, service cache, manual GUI refresh, and tests are covered. +- Scope check: no auto-refresh, no grid order suggestions, and no quote persistence are included. +- Placeholder scan: no unresolved placeholders or ambiguous steps remain.