Add Tencent quote implementation plan
This commit is contained in:
203
docs/superpowers/plans/2026-07-08-tencent-quotes.md
Normal file
203
docs/superpowers/plans/2026-07-08-tencent-quotes.md
Normal file
@@ -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=<symbol>` 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.
|
||||
Reference in New Issue
Block a user