File size: 2,997 Bytes
f6dac2a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""Canonical constants for sg-eqdp-scanner.

Single source of truth referenced by analytical code, tests, and docs.
Bump deliberately, not accidentally — any change here invalidates downstream
results. Cross-referenced by `memory.md` and `docs/STRATEGY.md`.
"""

from __future__ import annotations

from typing import Final

import pandas as pd

# yfinance ticker convention
SGX_SUFFIX: Final[str] = ".SI"
STI_TICKER: Final[str] = "^STI"

# Programme events. Keys are stable IDs used in abnormal_returns.event_id.
EVENTS: Final[dict[str, str]] = {
    "announcement": "2025-02-21",
    "tranche_1": "2025-07-21",
    "tranche_2": "2025-11-19",
    "expansion": "2026-02-12",
}


def event_date(event_id: str) -> pd.Timestamp:
    """Return the canonical event date as a tz-naive Timestamp."""
    return pd.Timestamp(EVENTS[event_id]).tz_localize(None)


# The nine EQDP-appointed managers across both tranches.
EQDP_MANAGERS: Final[tuple[str, ...]] = (
    # Tranche 1 (Jul 2025)
    "Avanda Investment Management",
    "Fullerton Fund Management",
    "JPMorgan Asset Management",
    # Tranche 2 (Nov 2025)
    "Amova Asset Management",  # formerly Nikko AM
    "AR Capital",
    "BlackRock",
    "Eastspring Investments",
    "Lion Global Investors",
    "Manulife Investment Management",
)

EQDP_MANAGER_TRANCHE: Final[dict[str, int]] = {
    "Avanda Investment Management": 1,
    "Fullerton Fund Management": 1,
    "JPMorgan Asset Management": 1,
    "Amova Asset Management": 2,
    "AR Capital": 2,
    "BlackRock": 2,
    "Eastspring Investments": 2,
    "Lion Global Investors": 2,
    "Manulife Investment Management": 2,
}

# Candidate-score weights — see docs/METHODOLOGY.md §3. Must sum to 1.0.
SCORE_WEIGHTS: Final[dict[str, float]] = {
    "liquidity_rise": 0.30,
    "institutional_proxy": 0.20,
    "index_inclusion": 0.15,
    "broker_named": 0.15,
    "filing_present": 0.20,
}

# CAPM β estimation window — 252 trading days (≈1 year) ending 30 days before event.
CAPM_BETA_WINDOW_DAYS: Final[int] = 252
CAPM_GAP_DAYS: Final[int] = 30

# Default event windows (trading-day offsets relative to event date).
EVENT_WINDOWS: Final[dict[str, tuple[int, int]]] = {
    "announcement": (-5, 20),
    "tranche_1": (-1, 10),
    "tranche_2": (-1, 10),
    "expansion": (-1, 20),
}

# Benchmark options for abnormal-return computation.
BENCHMARK_CAPM: Final[str] = "capm"
BENCHMARK_FF3: Final[str] = "ff3"
BENCHMARK_MARKET: Final[str] = "market_adjusted"
BENCHMARKS: Final[tuple[str, ...]] = (BENCHMARK_CAPM, BENCHMARK_FF3, BENCHMARK_MARKET)

# Tier identifiers used everywhere in the codebase and DB.
TIER_T1: Final[str] = "T1"
TIER_T2: Final[str] = "T2"
TIER_T3: Final[str] = "T3"
TIER_CONTROL: Final[str] = "control"
TIER_NONE: Final[str] = "none"
TIERS: Final[tuple[str, ...]] = (TIER_T1, TIER_T2, TIER_T3, TIER_CONTROL, TIER_NONE)

# Bootstrap configuration — docs/METHODOLOGY.md §10.
BOOTSTRAP_REPLICATIONS: Final[int] = 5_000
BOOTSTRAP_BLOCK_LENGTH: Final[int] = 5