A lightweight, modular core library designed for backtesting trading strategies in financial markets.
Install the full or light version depending on your use case:
# Full version (with built-in indicators and helpers)
npm install trading-cycle
# Light version (core only)
npm install trading-cycle
import {
Candles,
PositiveValues,
NegativeValues,
Renko,
TimeRenko,
TestLogic,
PositiveTimeLength,
NegativeTimeLength,
FakeTrader,
LogCandle,
RenkoCounter
} from 'trading-cycle/full';
import { TradingCycle } from 'trading-cycle/light';
- Full: Includes core, built-in indicators, helpers, and utility functions.
-
Light: Contains only the core functionality —
TradingCycle
class and abstract handler interfaces.
Trading Cycle ships with a set of predefined handlers you can use out of the box:
Candles,
PositiveValues,
NegativeValues,
Renko,
TimeRenko,
TestLogic,
PositiveTimeLength,
NegativeTimeLength,
FakeTrader,
LogCandle,
RenkoCounter
A default preset is provided under trading-cycle/config
, pre-wiring common handlers with sensible defaults (e.g. Renko bar size of 0.05):
import defaultPreset from 'trading-cycle/config';
This preset includes handlers such as:
candles
log-candles
renko-0.05
time-renko
renko-counter
test-logic
fake-trader
Customize or extend it by supplying your own HandlerConfig[]
when creating TradingCycle
.
Below is a concise description of a custom handler, illustrating how you can extend the core functionality:
-
Class Declaration
Create a class that extendsAbstractHandler
. -
Internal State
Define private fields (e.g.,prev
) to store data between ticks. -
Constructor
Callsuper(state, config)
to initialize base properties and set up your own fields. -
Override
doExecute()
- Access input values via
this.v
and stored state viathis._s
. - Implement your logic (e.g., detect when the current close is lower than the previous close).
- Update internal state (
this.prev = this.v.input;
). - Return a result only when the condition is met.
- Access input values via
NegativeValues handler example (pseudocode):
Extends
AbstractHandler
, tracks the previous candle inprev
, and returns the current candle when its close is less than the previous close.
A quick outline to run a backtest with CSV data:
import TradingCycle from 'trading-cycle';
import handlers from 'trading-cycle/full';
import defaultPreset from 'trading-cycle/config';
// Initialize the cycle
const cycle = new TradingCycle(handlers, defaultPreset);
// Load your CSV into `ticks: Array<{ o, h, l, c, v }>`
// e.g. via `csv-parse` or any CSV loader
ticks.forEach(tick => cycle.execute(tick));
// Inspect results
console.log(cycle.state);
-
new TradingCycle(handlerClasses: Record<string, HandlerConstructor>, preset: HandlerConfig[])
— Create a cycle with provided handlers and configuration presets. -
execute(tick: any): void
— Run all handlers on a single market tick; results accumulate incycle.state
. -
cycle.state: Record<string, any[]>
— Current output arrays for each handler.
- Modularity: Handlers are self-contained units; you can add, remove or customize them without affecting others.
- Flexibility: Presets define the pipeline; you control data flow by composing inputs and defaults.
-
Simplicity: Core abstractions (
TradingCycle
,AbstractHandler
) remain minimal and clear.
This project uses GitHub Actions for CI and Codecov for coverage reporting.
- Add Codecov action: No extra npm packages needed, simply configure the workflow.
-
Create a secret: Add
CODECOV_TOKEN
in your repository's Settings > Secrets.
name: CI
on:
push:
branches: [ main ]
pull_request:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Run tests
run: yarn test --coverage
- name: Upload to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
MIT