Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-monorepo-parallel/llms.txt

Use this file to discover all available pages before exploring further.

The scraper/parser pipeline is typed through three model files. ScraperMessage represents a raw Telegram message as fetched by ScraperService. ParserMessageRaw<M> and ParserMessageSuccess<M> wrap a ScraperMessage with a typed data field containing fields extracted by ParserService. SignalEntryModel defines the full shape of a CryptoYoda trade signal, including fields that are populated outside the parser (such as publishedAt and note).

ScraperMessage

ScraperMessage is the raw output of ScraperService.scrapeDay(). Every field maps directly to properties on a gram.js Message object.
// packages/core/src/model/ScraperMessage.model.ts
export interface ScraperMessage {
  id: number;
  channel: string;
  content: string;
  date: Date;
}
id
number
The Telegram message ID, taken from message.id. Unique within a channel.
channel
string
The channel username or peer identifier passed to scrapeDay() (e.g. "crypto_yoda_channel").
content
string
The full plaintext body of the message, taken from message.message.
date
Date
Message send time, converted from the Unix seconds timestamp on the Telegram message (message.date * 1000).

ParserMessageRaw<M>

ParserMessageRaw<M> is the output of ParserService.parseDay(). It extends ScraperMessage with a data field that holds either the fully-extracted typed object or null when extraction failed. ParserMessageSuccess<M> is a narrowed variant where data is guaranteed non-null.
// packages/core/src/model/ParserMessage.model.ts
import { ExtractedData, FieldMapping } from "./ParseFormat.model";
import { ScraperMessage } from "./ScraperMessage.model";

export interface ParserMessageRaw<M extends FieldMapping> extends ScraperMessage {
  data: ExtractedData<M> | null;
}

export interface ParserMessageSuccess<M extends FieldMapping> extends ScraperMessage {
  data: ExtractedData<M>;
}
data
ExtractedData<M> | null
The extracted typed fields when all required patterns matched, or null when any required field’s pattern did not match (or its validate() function returned false). Filter for non-null data to get only successful parses.
ParserMessageSuccess<M>
interface
A narrowed variant of ParserMessageRaw<M> where data is guaranteed non-null. Use this type after filtering out messages where data === null.
All other fields (id, channel, content, date) are inherited from ScraperMessage unchanged.

SignalEntryModel

SignalEntryModel defines the complete shape of a structured CryptoYoda trade signal. It includes fields extracted by the parser (symbol, direction, entry, targets, stoploss) as well as fields populated outside the parser (publishedAt, note). Note that CryptoYodaScreenService parses against the local SignalFields type — SignalEntryModel is the broader contract for a fully-assembled signal record.
// packages/core/src/model/SignalEntry.model.ts
interface SignalEntryModel {
  publishedAt: string;
  symbol: string;
  direction: "long" | "short";
  entry: { from: number; to: number };
  targets: number[];
  stoploss: number;
  note: string;
}

export { SignalEntryModel }
publishedAt
string
ISO string representation of the signal publication time. Populated outside the parser pipeline (not extracted from message text by SIGNAL_FORMAT).
symbol
string
The trading pair symbol, e.g. "BTCUSDT". Corresponds to the symbol field extracted by SIGNAL_FORMAT from the #SYMBOL/USDT hashtag pattern.
direction
"long" | "short"
Trade direction — "long" for ЛОНГ signals, "short" for ШОРТ signals. Corresponds to the direction field extracted by SIGNAL_FORMAT.
entry
{ from: number; to: number }
The entry price zone as a { from, to } range. Corresponds to the entry field extracted by SIGNAL_FORMAT. Both values are positive finite numbers with from < to.
targets
number[]
Array of take-profit price targets. Corresponds to the targets field extracted by SIGNAL_FORMAT using multi: true. At least one target is required for the message to parse successfully.
stoploss
number
Stop-loss price level. Corresponds to the stoploss field extracted by SIGNAL_FORMAT from the СТОП-ЛОСС: line.
note
string
Free-text note attached to the signal. Not extracted by the parser — populated downstream after the pipeline returns.

Pipeline Flow

The scraper and parser interfaces connect in a linear pipeline inside CryptoYodaScreenService. The parser operates over the local SignalFields type (a subset of SignalEntryModel), not over SignalEntryModel itself.
// packages/core/src/lib/services/screen/CryptoYodaScreenService.ts (simplified)

type SignalFields = {
  symbol: string;
  direction: "short" | "long";
  entry: { from: number; to: number };
  targets: number[];
  stoploss: number;
};

// Step 1 — fetch raw Telegram messages for a given day
const scraperList: ScraperMessage[] = await scraperService.scrapeDay(
  "crypto_yoda_channel",
  date
);

// Step 2 — apply SIGNAL_FORMAT regex extraction to every message
const parserList: ParserMessageRaw<SignalFields>[] = await parserService.parseDay(
  scraperList,
  SIGNAL_FORMAT
);

// Step 3 — filter to successful parses only
const signals = parserList.filter((msg) => msg.data !== null);
  1. ScraperService.scrapeDay(channel, date) — iterates Telegram messages for the channel on date and returns ScraperMessage[].
  2. ParserService.parseDay(messages, format) — runs each message body through the SIGNAL_FORMAT field extractors and returns ParserMessageRaw<SignalFields>[]. Messages where any required field fails to match get data: null.
  3. CryptoYodaScreenService — wraps both steps. Call screenDay(date) for the full pipeline or parseDay(scraperList) if you already have scraped messages.
CryptoYodaScreenService.parseDay() accepts a pre-fetched ScraperMessage[] list, letting you reuse scraped data across multiple parse formats without re-fetching from Telegram.

Build docs developers (and LLMs) love