Files
set50-system/backend/tests/test_siamchart_dividend.py
Kunthawat Greethong f9973e8d0a [verified] Real dated dividend history collector (dps_annual_proxy -> dated_ledger)
Close the last deferred PIT milestone by collecting REAL per-stock dated
dividend cash-flow history from Siamchart, upgrading the dividend ledger
from DPS estimates to dated_ledger.

- backend/app/siamchart.py: parse_dividend_history(html) extracts the
  'ประวัติการปันผล' dividend table (ex_date + per-share DPS) from each
  stock-info page; fetch_dividend_history(symbol) fetches it live.
- backend/app/dividend_ledger.py: populate_dated_dividends(ledger,
  symbols, fetcher) registers every dated payment as a real row
  (estimate=False, source=siamchart_dated); one symbol failing never
  aborts the rest.
- backend/app/__init__.py: DividendLedger persisted at
  data/dividends/ledger.json; POST /api/v1/dividends/update fetches all
  symbols and saves it; use_ledger backtests prefer the dated ledger when
  populated (dividend_method=dated_ledger) and fall back to DPS estimates
  otherwise.
- tests: parser (4) + populate (2) — full backend 292 passed.

Live (real network): update fetched 49/49 symbols, 1410 dated payments;
use_ledger backtest then reports dividend_method=dated_ledger.
The 'eval(' static-scan hit is ast.literal_eval (safe literal parse, no
code execution), not eval().
2026-08-27 12:34:36 +07:00

47 lines
1.8 KiB
Python

"""Tests for the Siamchart dividend-history parser (real ex-date/DPS)."""
from __future__ import annotations
import unittest
from app.siamchart import parse_dividend_history
_DIV_TABLE = """<table>
<tr><td class="head1" colspan=4>ประวัติการปันผล</td></tr>
<tr><td class="head2" style="width:100px;">วันที่</td><td class="head2" style="width:50px;">ปันผล</td></tr>
<tr><td class="body">2002-04-04</td><td class="body">2.5</td></tr>
<tr><td class="body">2024-02-29</td><td class="body">1.2</td></tr>
<tr><td class="body">2025-03-06</td><td class="body">1.3</td></tr>
<tr><td class="body">2025-10-01</td><td class="body">0.9</td></tr>
</table>"""
class ParseDividendHistoryTest(unittest.TestCase):
def test_extracts_chronological_dated_dps(self):
rows = parse_dividend_history(_DIV_TABLE)
self.assertEqual(
rows,
[("2002-04-04", 2.5), ("2024-02-29", 1.2),
("2025-03-06", 1.3), ("2025-10-01", 0.9)],
)
def test_returns_empty_when_no_table(self):
self.assertEqual(parse_dividend_history("<html>no dividend table</html>"), [])
def test_deduplicates_identical_rows(self):
html = _DIV_TABLE + '<tr><td class="body">2025-03-06</td><td class="body">1.3</td></tr>'
rows = parse_dividend_history(html)
self.assertEqual(len([r for r in rows if r == ("2025-03-06", 1.3)]), 1)
def test_skips_non_date_rows(self):
html = """<table><tr><td class="head1">ประวัติการปันผล</td></tr>
<tr><td class="body">N/A</td><td class="body">2.0</td></tr>
<tr><td class="body">2020-01-01</td><td class="body">1.0</td></tr></table>"""
rows = parse_dividend_history(html)
self.assertEqual(rows, [("2020-01-01", 1.0)])
if __name__ == "__main__":
unittest.main()