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.
BaseCRUD is a di-factory factory that wraps any Mongoose model with a complete CRUD surface — adding a new collection follows the same three-step pattern established by CandleDbService. You define the schema, extend BaseCRUD, and register the service in the DI container. The factory provides create, update, findById, findByFilter, findAll, iterate, and paginate at no additional cost, and you override only the methods that need custom atomicity semantics.
Schema — define the Mongoose model
Create a schema file under Key rules to follow in your own schema:
packages/core/src/schema/<Name>.schema.ts. Follow the Candle.schema.ts pattern exactly: define three exports — a DTO interface for write inputs, a Document interface for the Mongoose layer, and a Row interface for read outputs (with id, timestamps, and any derived fields).Here is the complete Candle.schema.ts as the canonical reference:- Enable
timestampswithcreatedAt/updatedAtmapped to human-readable field names (createDate,updatedDate). - Define a compound unique index whose fields exactly match the filter you will use in the
findOneAndUpdateupsert — this is what makes concurrent writes safe across 9 parallel symbol runners. - Export the
Model, theDtointerface (write input), and theRowinterface (read output withidand dates).
Service — extend BaseCRUD
Create The methods you receive from
Override only the methods that require custom logic — every other method falls through to the
packages/core/src/lib/services/db/<Name>DbService.ts. Call BaseCRUD(YourModel) as the base class — the factory mixes in all standard CRUD methods automatically.Here is the complete CandleDbService as the canonical reference:BaseCRUD without any override:| Method | Signature |
|---|---|
create | (dto: object) => Promise<Row> |
update | (id: string, dto: object) => Promise<Row> |
findById | (id: string) => Promise<Row> |
findByFilter | (filter: object, sort?: object) => Promise<Row | null> |
findAll | (filter?: object, limit?: number) => Promise<Row[]> |
iterate | (filter?: object, sort?: object) => AsyncGenerator<Row> |
paginate | (filter: object, pagination: { limit: number; offset: number }, sort?: object) => Promise<{ rows: Row[]; total: number }> |
BaseCRUD implementation.Register — wire into the DI container
Follow the full five-step recipe from Adding a New Service to register the new
DbService:- The file already exists from the previous step.
- Add
nameDbService: Symbol('nameDbService')to thedbServicesgroup inpackages/core/src/lib/core/types.ts. - Add
provide(TYPES.nameDbService, () => new NameDbService())topackages/core/src/lib/core/provide.ts. - Add
nameDbService: inject<NameDbService>(TYPES.nameDbService)to thedbServicesgroup inpackages/core/src/lib/index.ts. - Run
npm run buildto regeneratetypes.d.ts.
core.nameDbService is globally typed and accessible from strategy files.The upsert pattern explained
CandleDbService overrides create() to use findOneAndUpdate with $setOnInsert rather than a plain Model.create(). This is the correct atomicity contract for the parallel runner:
upsert: true— create the document if it does not exist; update if it does.$setOnInsert— the payload is only applied when the document is being inserted for the first time. If all 9 symbol runners try to write the same candle concurrently, the first one wins and the rest are no-ops — no data is overwritten, noE11000duplicate key errors are thrown.new: true— return the document as it exists after the operation (inserted or found).setDefaultsOnInsert: true— apply schema defaults when inserting.