Every piece of shared infrastructure in this monorepo lives insideDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/backtest-kit/backtest-monorepo-parallel/llms.txt
Use this file to discover all available pages before exploring further.
@pro/core and is injected into globalThis.core at startup. Adding a capability — a new database service, a scraper, a Redis cache — follows a fixed, five-step recipe. Strategy files under ./content/ never change; they simply start seeing the new property on core after the next build.
The five-step recipe
Drop
XService.ts under packages/core/src/lib/services/<category>/. The category is just a directory label — use db for Mongo-backed services, core for business-logic services, screen for data-scraping services, and so on.// packages/core/src/lib/services/core/XService.ts
export class XService {
public async doSomething(param: string): Promise<string> {
return `result for ${param}`;
}
}
export default XService;
Open
packages/core/src/lib/core/types.ts and add one symbol to the relevant group. The existing file shows the pattern:// packages/core/src/lib/core/types.ts
const coreServices = {
scraperService: Symbol('scraperService'),
parserService: Symbol('parserService'),
xService: Symbol('xService'), // ← add this
}
Symbols are the only keys the
di-kit activator uses for wiring — the string name is for debugging only.Open
packages/core/src/lib/core/provide.ts and add a provide(...) call in the appropriate block (or create a new block for a new category). The provide function is imported from the "pro" activator created in di.ts:// packages/core/src/lib/core/provide.ts
import XService from '../services/core/XService';
import { provide } from './di';
import TYPES from './types';
{
provide(TYPES.scraperService, () => new ScraperService());
provide(TYPES.parserService, () => new ParserService());
provide(TYPES.xService, () => new XService()); // ← add this
}
Open
packages/core/src/lib/index.ts and add one inject<XService>(...) entry to the matching group. This is the object that gets assigned to globalThis.core:// packages/core/src/lib/index.ts
import XService from './services/core/XService';
const coreServices = {
scraperService: inject<ScraperService>(TYPES.scraperService),
parserService: inject<ParserService>(TYPES.parserService),
xService: inject<XService>(TYPES.xService), // ← add this
};
export const ioc = {
...baseServices,
...coreServices,
...dbServices,
...screenServices,
};
packages/core/build/index.cjs — the CommonJS runtime bundlepackages/core/types.d.ts — the rolled-up declaration fileNo file under
./content/ ever needs to change when new services are added. The globalThis.core surface is extended automatically because strategy files access it at evaluation time, after the DI container has been initialised by the @pro/core bundle.Adding a new Mongo collection
A Mongo-backed service follows the five steps above, but also requires a schema file and aBaseCRUD wrapper.
1 — Define the schema
Createpackages/core/src/schema/<Name>.schema.ts. Model it on the existing Candle.schema.ts, which defines a Mongoose model and a compound unique index whose shape matches the natural context key for that collection:
findOneAndUpdate upserts from all 9 parallel symbol runners never produce duplicate documents.
2 — Wrap in a BaseCRUD subclass
BaseCRUD is a di-factory class factory. Passing your Mongoose model to it gives the subclass create, update, findById, findByFilter, findAll, iterate, and paginate for free:
3 — Register, expose, build
Follow steps 2–5 of the five-step recipe above (add a symbol, provide the service, expose it onioc, run npm run build).
Adding a new Redis-cached lookup
For O(1) string lookups that should survive between ticks but not require Mongo round-trips, extendBaseMap:
BaseMap uses ioredis under the hood and exposes set, get, delete, has, clear, toArray, size, and async iterators (iterate, keys, values). The connectionKey argument namespaces every key as <connectionKey>:<userKey> so multiple map services never collide in the same Redis instance.
Pass -1 for no TTL
Keys persist until explicitly deleted. Use this for canonical lookups — exchange metadata, symbol precision, market-open flags — where stale data would cause incorrect signals.
Pass positive seconds for time-bounded cache
Entries expire automatically. Use this for price snapshots, order-book depths, or anything where a slightly stale value is acceptable and auto-eviction reduces memory pressure.