mostlyright.finance.transcripts
mostlyright.finance.transcripts — the earnings-call transcript pipeline.
This sub-namespace provides the complete earnings-call research pipeline:
Engine (capture → STT → fact/ledger → segment bus)
: The on-device capture + STT engine for the transcripts.live source and the
engine used by the default transcripts.hosted path. Heavy runtime
dependencies (faster-whisper for STT, av for the transient
audio extract) live behind the [transcripts] optional extra and are
lazy-imported inside the capture/STT methods, so importing this module and
running the unit tests needs only the base finance deps. Captured audio is a
transient artifact, deleted after transcription; only transcript text and
derived facts survive.
Adapter + stream (adapter, _stream)
: The thin EarningsAdapter consumer/parser and the SSE consume_sse reader.
Market resolution (resolver, registries, pit, polymarket_derive)
: The prediction-market earnings-mention universe: resolve a Kalshi
KXEARNINGSMENTION{TICKER} / Polymarket “will knowledge_time = transcript availability — the leakage cutoff).
Canonical schemas are the contract. The engine imports
EarningsTranscriptSchema / EarningsFactSchema directly from
mostlyright.core.schemas and re-exports them here. Those schemas are the
contract for both the local transcripts.live path and the hosted service;
there is no separate byte-equivalent mirror to keep in sync.
class mostlyright.finance.transcripts.EarningsFactSchema
Section titled “class mostlyright.finance.transcripts.EarningsFactSchema”Bases: Schema
schema.finance.fact.v1 — one row per counted mention occurrence.
Carries the six taxonomy dimensions (term match rule, counting mode,
speaker scope, window scope, time, tie-break/resolution) plus the
role_source provenance and the derived kalshi_counted flag, so the
same fact rows resolve correctly under each venue’s wording.
mention_count is the primary integer tally; the boolean (“said at least
once”) is derived as mention_count >= 1. Storing only a bool cannot
settle Polymarket “say X 5+ times” threshold brackets.
schema_id: ClassVar[str]
Section titled “schema_id: ClassVar[str]”class mostlyright.finance.transcripts.EarningsResolution(ticker, target_word, event_date, resolution_rule, resolution_status=‘qualifying_event’)
Section titled “class mostlyright.finance.transcripts.EarningsResolution(ticker, target_word, event_date, resolution_rule, resolution_status=‘qualifying_event’)”Bases: object
The frozen tuple a Kalshi earnings-mention contract resolves to.
Shaped like NHighResolution:
an immutable (ticker, target_word, event_date, resolution_rule) carrier,
so a resolution cannot be mutated downstream. The TypeScript twin freezes the
equivalent object with Object.freeze.
- Parameters:
event_date: date
Section titled “event_date: date”resolution_rule: str
Section titled “resolution_rule: str”resolution_status: str
Section titled “resolution_status: str”Whether a qualifying call exists for this date. "qualifying_event" for
a normal resolution; "no_qualifying_event" when the Polymarket derive
sees a postponed or cancelled call. It carries a default so the Kalshi
resolve path never has to set it.
target_word: str
Section titled “target_word: str”ticker: str
Section titled “ticker: str”class mostlyright.finance.transcripts.EarningsTranscriptSchema
Section titled “class mostlyright.finance.transcripts.EarningsTranscriptSchema”Bases: Schema
schema.finance.transcript.v1 — one row per transcript segment.
schema_id: ClassVar[str]
Section titled “schema_id: ClassVar[str]”class mostlyright.finance.transcripts.EndOfCall(call_id)
Section titled “class mostlyright.finance.transcripts.EndOfCall(call_id)”Bases: object
End-of-call control sentinel: the streaming engine finished call_id.
Published by SegmentBus.close() and delivered last to every live
subscriber, so the SSE route can emit a terminating end_of_call frame and
close the connection cleanly, leaving no dangling generator or leaked
subscriber. It is a text/control marker and carries no audio.
- Parameters: call_id (str)
call_id: str
Section titled “call_id: str”class mostlyright.finance.transcripts.FactDelta(term_canonical, matched_surface_form, mention_count, speaker_role, role_source, speaker_name, kalshi_counted, is_final, spoken_at, stream_seq, compound_type=‘standalone’, resolution_status=‘provisional’, source=‘earnings_call’)
Section titled “class mostlyright.finance.transcripts.FactDelta(term_canonical, matched_surface_form, mention_count, speaker_role, role_source, speaker_name, kalshi_counted, is_final, spoken_at, stream_seq, compound_type=‘standalone’, resolution_status=‘provisional’, source=‘earnings_call’)”Bases: object
A provisional live fact delta counted off a final segment, never a partial.
Shaped for schema.finance.fact.v1: resolution_status="provisional"
marks it an early live signal rather than a settlement source, is_final=True
is STT finality and is orthogonal to authority, and kalshi_counted is
derived through the fail-closed
validate_kalshi_counted_occurrence(). Text and facts only, no audio.
compound_type is per delta: one delta per (term, compound_type), never
an aggregate mixing types, so the venue filters in fact_builder apply
row-wise (closed candidates go to Polymarket human review; Kalshi resolves No
on closed). It defaults to "standalone", so an existing SSE consumer or a
persisted delta written before the field existed counts for both venues
exactly as it did before.
- Parameters:
compound_type: str
Section titled “compound_type: str”is_final: bool
Section titled “is_final: bool”kalshi_counted: bool
Section titled “kalshi_counted: bool”matched_surface_form: str
Section titled “matched_surface_form: str”mention_count: int
Section titled “mention_count: int”resolution_status: str
Section titled “resolution_status: str”role_source: str
Section titled “role_source: str”source: str
Section titled “source: str”speaker_name: str | None
Section titled “speaker_name: str | None”speaker_role: str
Section titled “speaker_role: str”spoken_at: float
Section titled “spoken_at: float”stream_seq: int
Section titled “stream_seq: int”term_canonical: str
Section titled “term_canonical: str”to_stt_count(, turn_index=None)
Section titled “to_stt_count(, turn_index=None)”Map this delta to a build_fact_rows stt_counts occurrence record.
compound_type survives from the live classifier through the occurrence
record into the fact row, so the venue filters and the fail-loud
closed-candidate resolution operate on the same value end to end.
turn_index, when known, links the occurrence to the role-parser turns
list so build_fact_rows re-derives the speaker scope from the
authoritative turn.
Temporal mapping: this delta’s spoken_at is an engine-relative float
(seconds into the stream, for example 12.5), not a wallclock. It maps
into offset_seconds, the schema’s engine-relative integer audit field
that build_fact_rows already accepts. It is never emitted as the
occurrence’s spoken_at: that schema column is timestamp_utc and
pyarrow silently coerces a float to microseconds-after-epoch, persisting
1970-01-01 00:00:00.000012+00:00 as the temporal audit marker.
spoken_at is left absent, since the column is nullable; a caller
holding a genuine tz-aware wallclock sets it on the occurrence record
explicitly, and build_fact_rows fails loud on any value that is not a
tz-aware datetime.
class mostlyright.finance.transcripts.FactLedger(root=None)
Section titled “class mostlyright.finance.transcripts.FactLedger(root=None)”Bases: _ParquetLedger
Append-only schema.finance.fact.v1 counted-mention ledger (no audio).
- Parameters: root (Path | str | None)
class mostlyright.finance.transcripts.ResumeIncomplete(from_seq, earliest_retained_seq)
Section titled “class mostlyright.finance.transcripts.ResumeIncomplete(from_seq, earliest_retained_seq)”Bases: object
Resume marker: from_seq predates the ring buffer’s earliest retained seq.
Yielded first to a resubscribing consumer whose requested from_seq is
older than anything the bounded ring buffer still holds. The gap between
from_seq and earliest_retained_seq cannot be replayed from the bus, so
the consumer must reconcile from the authoritative post-call ledger. This is an
explicit signal, never a silent gap.
earliest_retained_seq: int
Section titled “earliest_retained_seq: int”from_seq: int
Section titled “from_seq: int”class mostlyright.finance.transcripts.RoleParser(roster=None)
Section titled “class mostlyright.finance.transcripts.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.RosterEntry(name, firm, role=‘unknown’)
Section titled “class mostlyright.finance.transcripts.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.Segment(text, is_final, spoken_at, stream_seq, knowledge_time, fact_deltas=)
Section titled “class mostlyright.finance.transcripts.Segment(text, is_final, spoken_at, stream_seq, knowledge_time, fact_deltas=)”Bases: object
One streaming transcript segment: text only, never audio.
is_final is STT-segment finality only (partial versus final text) and
never gates settlement authority. spoken_at is the aired event-time
wallclock of the window’s start; knowledge_time is the STT-finalization /
publish wallclock (>= spoken_at). fact_deltas is populated on final
segments only.
- Parameters:
fact_deltas: list[FactDelta]
Section titled “fact_deltas: list[FactDelta]”is_final: bool
Section titled “is_final: bool”knowledge_time: float
Section titled “knowledge_time: float”spoken_at: float
Section titled “spoken_at: float”stream_seq: int
Section titled “stream_seq: int”class mostlyright.finance.transcripts.SegmentBus(, subscriber_queue_maxsize=256, ring_buffer_size=128, subscriber_hard_maxsize=None)
Section titled “class mostlyright.finance.transcripts.SegmentBus(, subscriber_queue_maxsize=256, ring_buffer_size=128, subscriber_hard_maxsize=None)”Bases: object
In-process asyncio per-call pub/sub bus; single process only.
- Parameters:
async close(call_id)
Section titled “async close(call_id)”Signal end-of-call: deliver an EndOfCall marker to subscribers.
Each live subscriber receives the marker last, after any queued items; the
SSE route yields it as a terminating end_of_call frame and then closes
cleanly. Idempotent: closing a call with no subscribers is a no-op.
is_closed(call_id)
Section titled “is_closed(call_id)”Whether call_id has emitted end-of-call (a finished call).
async publish(call_id, item)
Section titled “async publish(call_id, item)”Publish item to call_id — fan-out + ring-buffer the finals.
Rejects audio bytes and any object that is not a Segment or FactDelta. Appends final items (a final Segment or any FactDelta) to the per-call ring buffer, then offers to every subscriber buffer with drop-oldest-partial backpressure. The publisher never blocks.
subscribe(call_id, from_seq=None)
Section titled “subscribe(call_id, from_seq=None)”Subscribe to call_id; return an async generator of items.
When from_seq is given, this first replays ring-buffer finals with
stream_seq > from_seq, plus any fact delta at from_seq, which
shares its parent segment’s seq and trails it on the wire (see
_replay_after()). If from_seq predates the ring buffer’s earliest
retained seq, a ResumeIncomplete marker is yielded first, telling
the consumer to reconcile from the ledger rather than gapping silently.
When from_seq is None, it backfills the bounded ring buffer of recent
finals. It then drains the live buffer.
- Return type:
AsyncIterator[Segment|FactDelta|ResumeIncomplete|EndOfCall] - Parameters:
subscriber_count(call_id)
Section titled “subscriber_count(call_id)”Number of live subscribers on call_id (0 after clean teardown).
class mostlyright.finance.transcripts.SegmentBusProtocol(*args, **kwargs)
Section titled “class mostlyright.finance.transcripts.SegmentBusProtocol(*args, **kwargs)”Bases: Protocol
The publish/subscribe contract the in-process bus satisfies.
A future cross-process (Redis/Memorystore) backplane would implement this same interface, so a multi-node deployment can fan segments across processes without changing the streaming engine or the SSE endpoint.
async publish(call_id, item)
Section titled “async publish(call_id, item)”subscribe(call_id, from_seq=None)
Section titled “subscribe(call_id, from_seq=None)”- Return type:
AsyncIterator[Segment|FactDelta|ResumeIncomplete|EndOfCall] - Parameters:
class mostlyright.finance.transcripts.StreamingTranscriber(, transcriber=None, initial_prompt_terms=None, market_terms=None, turn_provider=None, model_size=‘small’)
Section titled “class mostlyright.finance.transcripts.StreamingTranscriber(, transcriber=None, initial_prompt_terms=None, market_terms=None, turn_provider=None, model_size=‘small’)”Bases: object
VAD-chunked streaming STT → partial/final segments + final-only fact deltas.
- Parameters:
- transcriber (
Callable[...,str] |None) – A callable(window_pcm, *, initial_prompt) -> text. Pass a streaming audio transcriber explicitly. Omitting it creates a placeholder that raisesNotImplementedErrorwhen transcription begins. - initial_prompt_terms (
Sequence[str] |None) – The market strike terms the per-callinitial_promptis seeded from (reusesseed_initial_prompt()). - market_terms (
Sequence[Mapping[str,object]] |None) – Per-term market specs (term_canonicalat minimum) the final-only counter runs over. When empty, no fact deltas are produced. - turn_provider (
Callable[[Segment],Turn|None] |None) – Maps a segment to the role-parserTurn(speaker_role / role_source) driving the fail-closed Kalshi rule. Defaults to anunknown/diarization_advisoryturn (Kalshi-excluded — fail-closed). - model_size (str)
- transcriber (
fact_deltas: list[FactDelta]
Section titled “fact_deltas: list[FactDelta]”All fact deltas emitted this run (final-only) — inspectable by callers.
run(frame_iter)
Section titled “run(frame_iter)”Consume (pcm_frame, spoken_at) frames → yield partial/final Segments.
For each VAD speech run: yields one revisable partial per accumulated
window (is_final=False), then one final (is_final=True) on the
speech-end boundary — or, for a run still open when the frames run out,
at end of stream (a trailing silence frame is a caller convenience, never
a precondition for a run being finalised and counted). The final segment
carries the stabilised text of the entire run — every window’s
transcription, with the overlap de-duplicated — not just the post-flush
tail, so a term spoken early in a continuous run is counted exactly once.
Fact deltas are computed on the final segment only and
appended to fact_deltas (and to the final segment’s
fact_deltas list).
class mostlyright.finance.transcripts.TranscriptLedger(root=None)
Section titled “class mostlyright.finance.transcripts.TranscriptLedger(root=None)”Bases: _ParquetLedger
Append-only schema.finance.transcript.v1 segment ledger (no audio).
- Parameters: root (Path | str | None)
class mostlyright.finance.transcripts.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)
Section titled “class mostlyright.finance.transcripts.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.apply_kalshi_filter(rows)
Section titled “mostlyright.finance.transcripts.apply_kalshi_filter(rows)”Set kalshi_counted on every row via the fail-closed rule.
kalshi_counted = validate_kalshi_counted_occurrence(role_source, speaker_role) — True only when both the role_source and the speaker_role
are Kalshi-anchorable. Un-anchorable occurrences (analyst Q&A,
diarization_advisory role_source, unknown speaker) get False but
are retained in the returned rows for the Polymarket any-speaker count.
Returns new row dicts and does not mutate the inputs.
mostlyright.finance.transcripts.build_fact_rows(stt_counts, turns, market_terms, , ticker, call_id, event_time=None)
Section titled “mostlyright.finance.transcripts.build_fact_rows(stt_counts, turns, market_terms, , ticker, call_id, event_time=None)”Build schema.finance.fact.v1 rows — one per (ticker, call_id, term, occurrence).
stt_counts is a sequence of per-term occurrence records from the STT
counter, each carrying at minimum term (the canonical market term),
matched_surface_form (the actually-spoken string), and a turn_index
linking the occurrence to the turns list (so its speaker role is known).
Optional per-occurrence keys: offset_seconds, segment, confidence.
turns are role-parser Turn records (index-aligned with the
transcript). market_terms is the per-term market spec carrying
counting_mode, threshold_n, window_scope, term_match_rule,
term_accepted_forms (JSON string), term_canonical — used to populate
the venue-rule fields on each row.
Returns the rows with kalshi_counted already derived via
apply_kalshi_filter() (fail-closed). Every occurrence is retained for
the Polymarket any-speaker count; only kalshi_counted distinguishes the
Kalshi-countable subset.
mostlyright.finance.transcripts.derive(event)
Section titled “mostlyright.finance.transcripts.derive(event)”Derive a frozen EarningsResolution from a Polymarket event payload.
The payload is untrusted. The derive refuses (returns None) when:
- the slug/title does not look like an earnings-mention market;
- the
tickeris missing or not inEARNINGS_SERIES_ROSTER; - a
webcast_urlis present but its host is not onWEBCAST_HOST_ALLOWLIST(refused before anything is derived); target_wordorevent_dateis missing or unparseable.
A postponed or cancelled call yields a resolution with
resolution_status="no_qualifying_event".
- Return type:
EarningsResolution|None - Parameters: event (dict [str , Any ])
mostlyright.finance.transcripts.detect_provider(webcast_url)
Section titled “mostlyright.finance.transcripts.detect_provider(webcast_url)”Name the webcast provider for webcast_url, or None.
The host is first checked against WEBCAST_HOST_ALLOWLIST: a
non-allowlisted host returns None and is never fingerprinted from a raw
substring, so a hostile evil.example/q4inc cannot spoof a provider. An
allowlisted host is then matched against
WEBCAST_PROVIDER_FINGERPRINTS
by host substring.
- Return type:
str|None - Returns:
The canonical provider id (
"q4"etc.), orNonewhen the host is non-allowlisted or matches no fingerprint. - Parameters: webcast_url (str)
mostlyright.finance.transcripts.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)
Section titled “mostlyright.finance.transcripts.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.parse_operator_announcements(text)
Section titled “mostlyright.finance.transcripts.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.resolve(contract_id, event_date, , target_word)
Section titled “mostlyright.finance.transcripts.resolve(contract_id, event_date, , target_word)”Resolve a Kalshi earnings-mention contract to its frozen tuple.
The contract_id is the Kalshi market identifier — one of the three root
variants KXEARNINGSMENTION<TICKER> (e.g. KXEARNINGSMENTIONNKE),
KXMENTIONEARN<TICKER>, or the Berkshire fixed root KXBRKEM. The
suffix is the ticker; it is validated against
EARNINGS_SERIES_ROSTER.
- Parameters:
- contract_id (
str) – Kalshi market identifier. Case-insensitive. - event_date (
date) – The calendar date the call airs / the market settles for. Must be adatetime.datewith no time component. - target_word (
str) – The word/phrase the market resolves on. Required, non-empty.
- contract_id (
- Return type:
EarningsResolution - Returns:
A frozen
EarningsResolution. - Raises:
- TypeError –
contract_idis not a string,event_dateis adatetime(or any non-date), ortarget_wordis not a string. - ValueError –
contract_idmatches no known root, the ticker suffix is empty, the ticker is not inEARNINGS_SERIES_ROSTER, ortarget_wordis empty.
- TypeError –
Modules
Section titled “Modules”| Module | Description |
|---|---|
adapter | Earnings catalog adapter. |
capture | Earnings webcast capture fleet. |
catalog | Earnings discovery-time membership registries. |
fact_builder | Earnings fact-row builder + fail-closed Kalshi filter. |
ledger | Append-only transcript + fact parquet ledger. |
pit | Point-in-time alignment + leakage wiring for earnings facts. |
polymarket_derive | Polymarket earnings-mention derive + webcast provider fingerprint. |
registries | Earnings codegen registries — source-of-truth dicts. |
resolver | Kalshi earnings-mention resolver. |
role_parser | Transcript-anchored role-attribution parser. |
segment_bus | In-process asyncio segment/fact pub-sub bus. |
streaming_transcriber | Streaming STT engine. |
stt | faster-whisper STT transcriber + alias-aware mention counter. |