File size: 3,532 Bytes
35676b4
 
 
 
 
 
 
 
 
 
 
 
e6496c0
 
 
 
 
 
 
35676b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import logging

import httpx

from ingestion.alphavantage import AV_BASE, _next_key, _key_count, _is_rate_limited

logger = logging.getLogger(__name__)


def _parse_transcript(data: dict) -> str:
    raw = data.get("transcript", "")
    if isinstance(raw, list):
        lines = []
        for item in raw:
            speaker = item.get("speaker", "")
            title = item.get("title", "")
            label = f"{speaker} ({title})" if title else speaker
            lines.append(f"{label}: {item.get('content', '')}")
        return "\n".join(lines)
    if raw is None:
        return ""
    return str(raw)


def fetch_transcript(ticker: str, quarter: str) -> str:
    """Fetch earnings call transcript from Alpha Vantage.

    Args:
        ticker: Stock symbol e.g. "AAPL"
        quarter: Alpha Vantage format e.g. "2024Q4"

    Returns:
        Transcript text, or empty string if unavailable.
    """
    rate_limited_keys = 0
    n_keys = max(_key_count(), 1)

    for attempt in range(n_keys):
        key = _next_key()
        key_label = f"key[...{key[-4:]}]"
        try:
            resp = httpx.get(
                AV_BASE,
                params={
                    "function": "EARNINGS_CALL_TRANSCRIPT",
                    "symbol": ticker.upper(),
                    "quarter": quarter,
                    "apikey": key,
                },
                timeout=30,
            )
            resp.raise_for_status()
            data = resp.json()

            if "Information" in data:
                rate_limited_keys += 1
                logger.warning(
                    "[transcript] %s %s %s β€” rate limit hit (attempt %d/%d): %s",
                    ticker, quarter, key_label, attempt + 1, n_keys, data["Information"],
                )
                continue

            if "Note" in data:
                rate_limited_keys += 1
                logger.warning(
                    "[transcript] %s %s %s β€” API note/rate limit (attempt %d/%d): %s",
                    ticker, quarter, key_label, attempt + 1, n_keys, data["Note"],
                )
                continue

            if "Error Message" in data:
                logger.warning(
                    "[transcript] %s %s %s β€” API error: %s",
                    ticker, quarter, key_label, data["Error Message"],
                )
                return ""

            text = _parse_transcript(data)
            if text:
                logger.info(
                    "[transcript] %s %s β€” fetched %d chars via %s",
                    ticker, quarter, len(text), key_label,
                )
            else:
                logger.info(
                    "[transcript] %s %s β€” API returned empty transcript (no content available) via %s",
                    ticker, quarter, key_label,
                )
            return text

        except ValueError:
            raise
        except Exception as e:
            logger.warning(
                "[transcript] %s %s %s β€” request error (attempt %d/%d): %s",
                ticker, quarter, key_label, attempt + 1, n_keys, e,
            )
            continue

    if rate_limited_keys == n_keys:
        logger.error(
            "[transcript] %s %s β€” ALL %d keys exhausted by rate limits. No transcript fetched.",
            ticker, quarter, n_keys,
        )
    else:
        logger.error(
            "[transcript] %s %s β€” failed after %d attempt(s). No transcript fetched.",
            ticker, quarter, n_keys,
        )
    return ""