finance.position (171 rows, 2020-06-07 to 2025-12-19) is the only source of InvestEngine + Schwab trade history pre-dating the broker-sync project. This provider reads it once and pushes every row into the correct WF account (.L tickers → IE ISA, others → Schwab). Dedup: external_id = 'finance-mysql:position:<PK>' — idempotent on re-run. Auth: aiomysql as MySQL root (user-authorized) against the standalone mysql:8.4 in-cluster service. New CLI: broker-sync finance-mysql-import New tests: 5 unit tests covering route, symbol normalise, BUY/SELL detection. poetry run pytest -q → 114 passed, 1 skipped poetry run mypy → clean (aiomysql shielded with type: ignore) poetry run ruff check → clean
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from decimal import Decimal
|
|
|
|
from broker_sync.models import AccountType, ActivityType
|
|
from broker_sync.providers.finance_mysql import _normalise_symbol, _route, _row_to_activity
|
|
|
|
|
|
def test_lse_ticker_routes_to_investengine() -> None:
|
|
acct, t, ccy = _route("VUAG.L")
|
|
assert acct == "invest-engine-primary"
|
|
assert t is AccountType.ISA
|
|
assert ccy == "GBP"
|
|
|
|
|
|
def test_us_ticker_routes_to_schwab() -> None:
|
|
assert _route("META") == ("schwab-workplace", AccountType.GIA, "USD")
|
|
assert _route("FLME_US_EQ") == ("schwab-workplace", AccountType.GIA, "USD")
|
|
|
|
|
|
def test_normalise_symbol() -> None:
|
|
assert _normalise_symbol("VUAG.L") == "VUAG"
|
|
assert _normalise_symbol("VUSA.L") == "VUSA"
|
|
assert _normalise_symbol("META") == "META"
|
|
assert _normalise_symbol("FLME_US_EQ") == "FLME"
|
|
assert _normalise_symbol("FOO_EQ") == "FOO"
|
|
|
|
|
|
def test_row_to_buy_activity() -> None:
|
|
row = {
|
|
"id": "123456",
|
|
"ticker": "VUAG.L",
|
|
"buy_price": 85.5,
|
|
"num_shares": 10.0,
|
|
"currency": "GBP",
|
|
"buy_date": datetime(2022, 3, 15, 10, 30),
|
|
"account_id": 1,
|
|
}
|
|
a = _row_to_activity(row)
|
|
assert a.external_id == "finance-mysql:position:123456"
|
|
assert a.account_id == "invest-engine-primary"
|
|
assert a.account_type is AccountType.ISA
|
|
assert a.activity_type is ActivityType.BUY
|
|
assert a.symbol == "VUAG" # .L stripped
|
|
assert a.quantity == Decimal("10.0")
|
|
assert a.unit_price == Decimal("85.5")
|
|
assert a.currency == "GBP"
|
|
assert a.date == datetime(2022, 3, 15, 10, 30, tzinfo=UTC)
|
|
|
|
|
|
def test_row_to_sell_when_qty_negative() -> None:
|
|
row = {
|
|
"id": "x",
|
|
"ticker": "META",
|
|
"buy_price": 450.0,
|
|
"num_shares": -2.5, # sell
|
|
"currency": "USD",
|
|
"buy_date": datetime(2024, 8, 5),
|
|
"account_id": 1,
|
|
}
|
|
a = _row_to_activity(row)
|
|
assert a.activity_type is ActivityType.SELL
|
|
assert a.quantity == Decimal("2.5") # absolute
|
|
assert a.account_id == "schwab-workplace"
|
|
assert a.symbol == "META"
|