feat: add Nailong Fund advisor capabilities

This commit is contained in:
Windows
2026-09-11 09:47:40 +08:00
parent 5907fcd6d2
commit a4499defee
143 changed files with 12255 additions and 89 deletions
@@ -4,7 +4,7 @@ from decimal import Decimal
import httpx
import pytest
from app.infrastructure.fund_market_adapter import EastmoneyFundAdapter
from app.infrastructure.fund_market_adapter import EastmoneyFundAdapter, HqFundMarketAdapter
@pytest.mark.asyncio
@@ -43,3 +43,34 @@ async def test_adapter_retries_then_returns_degraded_history() -> None:
assert result == {"degraded": True}
assert calls == 2
await client.aclose()
@pytest.mark.asyncio
async def test_hq_adapter_maps_snapshot_fields_without_network() -> None:
calls = 0
def loader(*, target_date: str, fund_codes: list[str]) -> list[dict[str, object]]:
nonlocal calls
calls += 1
assert fund_codes == ["159511"]
return [{
"基金代码": "159511",
"基金名称": "南方测试基金",
"基金净值": "1.23",
"日期": target_date,
"日涨幅": "0.82%",
"最近半年": "8.10%",
"最近一年": "12.30%",
"今年以来": "4.50%",
"成立以来": "30.00%",
"是否盘中": True,
}]
adapter = HqFundMarketAdapter(loader)
target_date = date.today()
assert await adapter.fetch_names(["159511"]) == {"159511": "南方测试基金"}
assert (await adapter.fetch_quotes(["159511"]))["159511"]["nav"] == Decimal("1.23")
history = await adapter.fetch_history("159511", target_date)
assert history["half_year_return"] == Decimal("8.10")
assert history["one_year_return"] == Decimal("12.30")
assert calls == 1
@@ -0,0 +1,97 @@
from typing import Any
import pytest
import hq
def test_hq_market_accepts_requested_fund_codes(monkeypatch) -> None:
monkeypatch.setattr(hq, "_get_names", lambda codes: {code: f"基金-{code}" for code in codes})
monkeypatch.setattr(
hq,
"_get_history",
lambda codes, _target_date: {
code: {
"基金净值": "1.20",
"日期": "2026-09-10",
"日涨幅": "0.10%",
"最近半年": "8.00%",
"最近一年": "12.00%",
"今年以来": "4.00%",
"成立以来": "30.00%",
}
for code in codes
},
)
monkeypatch.setattr(
hq,
"_get_quotes",
lambda codes: {code: {"基金净值": "1.23", "日涨幅": "0.20%"} for code in codes},
)
monkeypatch.setattr(hq, "is_market_trading_time", lambda: True)
result: list[dict[str, Any]] = hq.get_southern_fund_market(
target_date="2026-09-10",
fund_codes=["159511"],
)
assert [row["基金代码"] for row in result] == ["159511"]
assert result[0]["基金净值"] == "1.23"
def test_hq_nav_history_filters_invalid_observations(monkeypatch) -> None:
monkeypatch.setattr(
hq,
"_fetch_nav_records",
lambda *_args, **_kwargs: [
{"FSRQ": "2026-09-09", "DWJZ": "1.20"},
{"FSRQ": "2026-09-08", "DWJZ": "0"},
{"FSRQ": "2026-08-01", "DWJZ": "1.10"},
],
)
result = hq.get_southern_fund_nav_history("159511", "2026-09-01", "2026-09-10")
assert result == [{"fund_code": "159511", "trade_date": "2026-09-09", "nav": "1.20"}]
def test_hq_history_paginates_a_bounded_date_range_without_total_count(
monkeypatch: pytest.MonkeyPatch,
) -> None:
pages = {
1: [{"FSRQ": "2026-09-03", "DWJZ": "1.03"}, {"FSRQ": "2026-09-02", "DWJZ": "1.02"}],
2: [{"FSRQ": "2026-09-01", "DWJZ": "1.01"}, {"FSRQ": "2026-08-29", "DWJZ": "1.00"}],
3: [],
}
monkeypatch.setattr(
hq,
"_fetch_nav_page",
lambda _code, page, _start, _end: (pages[page], None),
)
result = hq._fetch_nav_records("159511", "2026-08-01", "2026-09-10", all_pages=True)
assert [row["FSRQ"] for row in result] == [
"2026-09-03", "2026-09-02", "2026-09-01", "2026-08-29"
]
def test_tencent_exchange_quote_parser_keeps_only_compatible_fields() -> None:
fields = [""] * 34
fields[3] = "1.234"
fields[4] = "1.200"
fields[6] = "1000"
fields[33] = "2.83"
payload = f'v_sz159511="{"~".join(fields)}";'.encode("gbk")
quotes = hq._parse_tencent_exchange_quotes(payload, ("159511",))
assert quotes == {
"159511": {
"last_price": "1.234",
"previous_close": "1.200",
"change_pct": "2.83",
"volume": "1000",
"turnover_amount": None,
}
}