Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tripolskypetr/pump-anomaly/llms.txt

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

GetCandles is the single data-layer interface PumpMatrix requires. Implement it once and use it for all three phases: fit(), plan(), and backtest(). The same adapter signature is compatible with backtest-kit’s Exchange.getRawCandles — the argument order matches one-to-one, so the adapter is a thin pass-through if you already use that library.

Type Definition

type CandleInterval = "1m"|"3m"|"5m"|"15m"|"30m"|"1h"|"2h"|"4h"|"6h"|"8h"|"1d";

interface ICandleData {
  timestamp: number; // unix ms, candle OPEN time
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
}

type GetCandles = (
  symbol: string,
  interval: CandleInterval,
  limit?: number,
  sDate?: number,  // inclusive
  eDate?: number,  // exclusive
) => Promise<ICandleData[]>;

Parameters

symbol
string
required
The trading pair to fetch, e.g. "SOLUSDT". Must match the symbol values in your ParserItem array.
interval
CandleInterval
required
The candle timeframe. Training labels are generated on 1m candles; plan() and backtest() also use 1m. Other intervals may be requested for auxiliary analysis.
limit
number
Maximum number of candles to return. When combined with sDate and/or eDate, controls the exact slice returned. See Range Semantics below for all five calling patterns.
sDate
number
Start of the requested range, inclusive, as a Unix timestamp in milliseconds. The library aligns this value down to the nearest candle boundary before using it.
eDate
number
End of the requested range, exclusive, as a Unix timestamp in milliseconds.

Range Semantics

The library calls getCandles in five distinct patterns depending on which combination of arguments are provided. Your adapter must handle all of them correctly. align(t) means floor to the nearest candle boundary for the given interval (Math.floor(t / step) * step).
Call patternRange returned
(limit)[align(now) − limit·step, align(now))
(limit, sDate)[align(sDate), align(sDate) + limit·step)
(limit, _, eDate)[align(eDate) − limit·step, eDate)
(_, sDate, eDate)[align(sDate), eDate), limit derived from range
(limit, sDate, eDate)[align(sDate), …), exactly limit candles
In all cases sDate is inclusive and eDate is exclusive. Returned candles must be sorted ascending by timestamp.

1-Minute Candles Required

Training labels are generated by replaying your prod exit strategy (trailing take, hard stop, peak staleness, life-cap) on 1-minute candles strictly after each signal. The simulation walks every candle in order to detect if a stop-hunt wick would have triggered the hard stop before the trailing take was reached — this path-aware replay is what prevents stop-hunting patterns from being labeled as winning trades. Your getCandles adapter must be able to serve 1m candles for every symbol in your training set. Symbols where the adapter cannot return 1m data are caught and skipped (see Error Handling below) rather than crashing the fit.

Minimal Implementation

A working adapter using ccxt:
import ccxt from "ccxt";
import type { GetCandles } from "pump-anomaly";

const exchange = new ccxt.binance({ enableRateLimit: true });
await exchange.loadMarkets();

const getCandles: GetCandles = async (symbol, interval, limit, sDate, eDate) => {
  const since = sDate ?? undefined;
  const rows = await exchange.fetchOHLCV(symbol, interval, since, limit);
  return rows.map(([timestamp, open, high, low, close, volume]) => ({
    timestamp, open, high, low, close, volume,
  }));
};
For a production-grade adapter that handles all five range patterns, align sDate to the interval boundary before passing it to fetchOHLCV, and enforce the eDate cutoff by filtering the returned rows.

Chunked Fetching

For long training horizons, the library requests up to staleMinutes * 2 + 5 one-minute candles per signal (e.g. 2885 candles for a 24-hour staleMinutes). Many exchange APIs cap responses at 500 candles per request. The library handles this internally via fetchCandlesChunked: it splits the request into chunks of up to 500 candles, advances since by chunkSize * step on each chunk, and merges results with deduplication by timestamp. Your adapter does not need to implement pagination — the library will call it multiple times with smaller ranges automatically. If your adapter already paginates internally, that is fine — there is no conflict. The library-level chunking is a safety net for adapters that do not.

Error Handling

Errors thrown by your adapter during training are caught per-candidate and handled gracefully:
  • The failing candidate burst is skipped — the fit continues with the remaining data.
  • Training does not crash. One broken symbol (e.g. a meme coin delisted mid-history, or a look-ahead guard firing at the end of available data) does not abort the entire fit.
  • The error is recorded in model.labeling.errors as a deduplicated map of exception message text to occurrence count, e.g. { "ccxt: symbol not found": 32 }.
After training, inspect model.labeling to understand why a fit may have produced fewer samples than expected:
model.labeling;
// {
//   candidates: 90,
//   outcomes: {
//     ok: 54,
//     "adapter-error": 18,   // getCandles threw
//     "no-candles": 12,      // getCandles returned empty
//     "no-entry": 6,         // candles exist, entry zone never touched
//   },
//   errors: { "ccxt: symbol not found": 18 },
// }

Sync Alternative (Candle Map)

plan() and backtest() also accept a preloaded { symbol: ICandleData[] } map instead of a GetCandles function. This is useful for backtesting over a fixed dataset loaded from disk, or for testing without a live exchange connection.
const candleMap = { SOLUSDT: loadedCandles };
const trades = model.plan(items, candleMap);
When using the candle map form, plan() and backtest() are synchronous (return TradeSignal[] / BacktestSignal[] directly, not a Promise). If a symbol is missing from the map, that signal falls back to candle-free mode (equivalent to signals()) instead of throwing. The candle map form is not available for fit() — training always requires an async GetCandles because it fetches candles for many symbols and timestamps dynamically.

Build docs developers (and LLMs) love