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().
47 lines
1.8 KiB
Python
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()
|