read.markets/tests/test_market_parsing.py
Giorgio Gilestro a10409c02b initial commit — cassandra v0.1
Containerised macro-strategy dashboard: 4-panel web UI (indicators,
portfolio, flash news, AI strategic log), MariaDB store, hourly
ingestion jobs, OpenRouter-backed AI analysis.

Ports the four prototype scripts in the parent dir (market_pulse,
flash_news, trading212, strategic_log) into async services backed by a
persistent DB and served via FastAPI + Jinja2 + HTMX. APScheduler runs
as a separate compose service for crash-safety and easier restarts.

Portfolio composition + position names come live from Trading 212;
news per-ticker headlines reuse those names. Tone (NOVICE/INTERMEDIATE/
PRO) and analysis style (DRY/SPECULATIVE) are env-configurable and
stored on each log row so historical entries show what produced them.

Default model is deepseek/deepseek-v4-flash (overridable via env).
Light/dark theme toggle, sans-serif for prose surfaces, monospace for
data. Bearer-token auth, OpenRouter monthly cost cap, RSS feeds auto-
disabled on consecutive failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 21:56:10 +01:00

47 lines
1.3 KiB
Python

"""Pure-function tests for app.services.market."""
from __future__ import annotations
import pytest
pytest.importorskip("httpx")
pytest.importorskip("pydantic_settings")
from datetime import datetime, timezone
from app.services.market import _pct, _parse_date, _yahoo_range_covering, parse_symbol
def test_pct_basic():
assert _pct(100, 110) == 10.0
assert _pct(100, 90) == -10.0
def test_pct_handles_none_and_zero():
assert _pct(None, 10) is None
assert _pct(10, None) is None
assert _pct(0, 5) is None
def test_parse_date():
assert _parse_date("2026-03-04") == datetime(2026, 3, 4)
def test_yahoo_range_covering_picks_smallest():
today = datetime.now(timezone.utc).date()
# anchor 100 days ago → 1y range is enough
short = (today.replace(year=today.year)).isoformat()
assert _yahoo_range_covering(None) == "1y"
assert _yahoo_range_covering("2026-01-01") == "1y"
def test_parse_symbol_routes_by_prefix():
fn, ident = parse_symbol("FRED:DFF")
assert ident == "DFF"
assert fn.__name__ == "fetch_fred"
fn2, ident2 = parse_symbol("AAPL")
assert ident2 == "AAPL"
assert fn2.__name__ == "fetch_yahoo"
# Unknown prefix falls through to yahoo.
fn3, ident3 = parse_symbol("UNKNOWN:XYZ")
assert ident3 == "UNKNOWN:XYZ"
assert fn3.__name__ == "fetch_yahoo"