mostlyright.finance.transcripts.role_parser
Transcript-anchored role-attribution parser.
Role attribution is settlement-sensitive: a turn mis-attributed from analyst to executive flips an earnings-mention settlement. Acoustic diarization is not used for the analyst/executive split; roles come from transcript text.
The transcript hands us roles for free:
- the operator announces every analyst by name and firm
(“Our first question comes from John DiFucci from Guggenheim”) —
role_source="transcript_structural"; - analysts self-identify on taking the mic —
role_source="transcript_self_id"; - executives are the named answerers from the prepared-remarks roster —
role_source="transcript_structural"/transcript_self_id; - a mangled surname (
DeFucciforDiFucci,ElnickforZelnick) is resolved by fuzzy-matching against the published participant roster, gated on the cleanly-transcribed firm token, since firm names survive STT exactly —role_source="roster_match".
A turn that cannot be anchored to any of the three transcript-anchored sources
gets role_source="diarization_advisory" (or "unknown"), never a
Kalshi-countable source. That drives the fail-closed exclusion downstream in
mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_occurrence().
Fuzzy matching uses the stdlib difflib.get_close_matches() rather than a
third-party fuzzy library: the surname-repair job is small and difflib’s ratio is
sufficient, so no new dependency is needed. The match is gated on the firm token
so a close surname at the wrong firm cannot be adopted.
Functions
Section titled “Functions”| Function | Description |
|---|---|
fuzzy_match_surname(spoken, roster, …[, …]) | Repair a mangled analyst surname against the published roster. |
parse_operator_announcements(text) | Extract operator analyst hand-off announcements from text. |
parse_self_identifications(text) | Extract analyst self-identifications (“This is |
Classes
Section titled “Classes”| Class | Description |
|---|---|
RoleParser([roster]) | Attribute transcript turns to speaker roles from text cues only. |
RosterEntry(name, firm[, role]) | A published-participant roster row: canonical name, firm, and role. |
Turn(speaker_name, speaker_role, role_source) | A single attributed transcript turn. |
class mostlyright.finance.transcripts.role_parser.RoleParser(roster=None)
Section titled “class mostlyright.finance.transcripts.role_parser.RoleParser(roster=None)”Bases: object
Attribute transcript turns to speaker roles from text cues only.
Construct with the published participant roster ((name, firm) pairs
or RosterEntry rows). attribute_turns walks a segmented
transcript and assigns each turn a speaker_role + role_source,
resolving mangled analyst surnames via fuzzy_match_surname() gated on
the firm token. A turn with no structural cue and no roster hit is left
role_source="diarization_advisory", never a Kalshi-countable source.
attribute_turns(transcript, roster=None)
Section titled “attribute_turns(transcript, roster=None)”Attribute every turn in transcript to a role from text cues.
transcript is either the raw transcript text (parsed for operator
announcements, self-identifications, and label lines) or a list of
pre-segmented turn
dicts ({"speaker_name": ..., "label": ..., "text": ...}). roster
overrides the instance roster for this call.
Each returned Turn carries speaker_role + role_source.
Un-anchorable turns get role_source="diarization_advisory".
class mostlyright.finance.transcripts.role_parser.RosterEntry(name, firm, role=‘unknown’)
Section titled “class mostlyright.finance.transcripts.role_parser.RosterEntry(name, firm, role=‘unknown’)”Bases: object
A published-participant roster row: canonical name, firm, and role.
role is one of the SPEAKER_ROLE_VALUES (for example
sell_side_analyst for an analyst, company_executive for a named
executive). The firm token gates the fuzzy match: a surname is only repaired
against entries sharing the cleanly-transcribed firm.
class mostlyright.finance.transcripts.role_parser.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)
Section titled “class mostlyright.finance.transcripts.role_parser.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)”Bases: object
A single attributed transcript turn.
speaker_role is a SPEAKER_ROLE_VALUES member; role_source is a
ROLE_SOURCE_VALUES member. An un-anchorable turn carries
role_source="diarization_advisory" (or "unknown"), never a
Kalshi-countable source, so the downstream fail-closed filter excludes it
from the Kalshi count.
- Parameters:
confidence: float
Section titled “confidence: float”role_source: str
Section titled “role_source: str”speaker_name: str | None
Section titled “speaker_name: str | None”speaker_role: str
Section titled “speaker_role: str”mostlyright.finance.transcripts.role_parser.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)
Section titled “mostlyright.finance.transcripts.role_parser.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)”Repair a mangled analyst surname against the published roster.
spoken is the (possibly mis-transcribed) surname as heard —
"DeFucci" for "DiFucci", "Elnick" for "Zelnick". roster
is the published participant list as (name, firm) pairs (or
RosterEntry rows). firm_token is the cleanly-transcribed firm and
gates the fuzzy match: only roster entries whose firm matches firm_token
(exact, normalized) are candidates, so a close surname at the wrong firm can
never be adopted and cause a cross-firm mis-attribution.
Returns the roster’s canonical surname on a confident match, else None.
Uses the stdlib difflib.get_close_matches(): the surname-repair job is
small, and difflib’s SequenceMatcher ratio comfortably resolves the one-char
and phonetic drifts observed (DeFucci→DiFucci ratio ~0.86,
Elnick→Zelnick ~0.77), so no third-party fuzzy dependency is needed.
mostlyright.finance.transcripts.role_parser.parse_operator_announcements(text)
Section titled “mostlyright.finance.transcripts.role_parser.parse_operator_announcements(text)”Extract operator analyst hand-off announcements from text.
Returns one dict per announcement:
{"name": "John DiFucci", "firm": "Guggenheim", "role_source": "transcript_structural"}The operator names every analyst by name and firm; this is the most reliable
transcript-anchored role cue. The extracted analyst is a
sell_side_analyst, and the caller assigns the role. Firm is captured
verbatim so it can gate a downstream roster fuzzy-match on a mangled surname.
mostlyright.finance.transcripts.role_parser.parse_self_identifications(text)
Section titled “mostlyright.finance.transcripts.role_parser.parse_self_identifications(text)”Extract analyst self-identifications (“This is