Foundation data structures and utilities for spot market trading bookkeeping, backtesting, and algorithmic trading.
This library provides comprehensive building blocks for trading systems in two main areas:
- Position tracking - Long and short positions with lot-level accounting (FIFO/LIFO)
- Portfolio management - Multi-currency positions with corporate actions support
- Order management - Full order lifecycle tracking
- Portfolio valuation - Real-time value and P&L calculations
- Market data - Price snapshots, quotes, and bars
- Corporate actions - Stock splits, dividends, spinoffs, mergers, hard forks, airdrops
- Data structures - CircularBuffer, Deque, PriorityQueue, RBTree
- Online statistics - O(1) cumulative mean, variance, covariance, correlation, beta, skewness, kurtosis
- Rolling statistics - Sliding window SMA, EMA, EWMA, variance, z-scores (O(1)), min/max (O(1)), median/quantile (O(n))
- Numeric utilities - Array-based stats (mean, variance, correlation), series transforms (returns, lag/lead, winsorize), ranking (argsort, spearman)
- Probabilistic structures - CountMinSketch, BloomFilter
- Performance metrics - Drawdown/drawup calculations with Kahan summation for numerical stability
This library provides primitives, not complete systems. It does NOT include:
- Strategy engines or signal generation
- Matching engines or broker simulators
- Backtesting frameworks or event loops
- Data fetching or storage
- Charting or visualization
npm install @junduck/trading-coreThis library supports two approaches for managing positions, depending on your needs:
For simpler workflows where you already have execution prices and quantities. Ideal for backtesting, importing trades, and simple portfolio tracking.
createPosition() → Position(initial cash)
|
|-- openLong/closeLong/openShort/closeShort() → Position updated
|
|-- market conditions update
|
|-- appraisePosition/appraisePortfolio() → Portfolio value
import { createPosition, openLong, closeLong, appraisePosition } from "@junduck/trading-core";
const position = createPosition(100_000);
openLong(position, "BTC", 50_000, 10, 10);
closeLong(position, "BTC", 55_000, 5, 10, "FIFO");
// Market update
const snapshot = { price: new Map([["BTC", 52_000]]), timestamp: new Date() };
const value = appraisePosition(position, snapshot);For realistic trading with order lifecycle management. Ideal for order book simulation, partial fills, and order state tracking.
Order (intent)
|
|-- validateOrder() --> invalid → rejectOrder() → OrderState(REJECT)
|
|-- validateOrder() --> valid → acceptOrder() → OrderState(OPEN)
|
|-- fillOrder() → Fill + OrderState(PARTIAL/FILLED)
| |
| v
| processFill() → Position updated
|
|-- cancelOrder() → OrderState(CANCELLED)
import { buyOrder, acceptOrder, fillOrder, processFill } from "@junduck/trading-core";
const order = buyOrder({ symbol: "BTC", quant: 10, price: 50_000 });
const orderState = acceptOrder(order);
const fill = fillOrder({ state: orderState, quant: 5, price: 50_000, commission: 10 });
const effect = processFill(position, fill); // Updates position
// Partial fill: orderState.status === "PARTIAL"
cancelOrder(orderState); // Cancel remainingWhen to use each:
- Direct manipulation: Backtesting with complete data, importing historical trades, simple scenarios
- Order abstraction: Order book simulation, partial fills, realistic order lifecycle, complex systems
Both styles update the same Position structure and can be mixed as needed.
For streaming data scenarios, use online statistics that update incrementally with O(1) complexity:
Create instance → new data arrives → update(x) → returns new value + internal state updated
import { CMA, CuVar, RollingMax, EWMA } from "@junduck/trading-core";
// Create statistics trackers
const priceAvg = new CMA();
const priceVar = new CuVar();
const rolling5High = new RollingMax(5);
const ewma = new EWMA(0.1);
// WebSocket example: update on each tick
websocket.on('message', (data) => {
const price = data.price;
const mean = priceAvg.update(price); // Cumulative mean
const variance = priceVar.update(price); // Cumulative variance
const high5 = rolling5High.update(price); // 5-period high
const smoothed = ewma.update(price); // Exponentially weighted MA
console.log({ mean, variance, high5, smoothed });
});Use cases:
- Real-time monitoring: Track live market statistics without storing historical data
- Memory efficiency: O(1) space complexity regardless of data volume
- Stream processing: Calculate metrics on continuous data feeds
- High-frequency: Fast updates suitable for tick-by-tick processing
Fixed-size buffer that automatically overwrites old data - perfect for sliding windows without manual cleanup:
import { CircularBuffer } from "@junduck/trading-core";
const lastPrices = new CircularBuffer<number>(3);
lastPrices.push(100); // [100]
lastPrices.push(102); // [100, 102]
lastPrices.push(101); // [100, 102, 101]
lastPrices.push(103); // [102, 101, 103] - overwrites oldest (100)
console.log(lastPrices.toArray()); // [102, 101, 103]
console.log(lastPrices.size()); // 3Overwriting behavior is intentional and useful:
- No need to manually remove old elements
- Constant memory usage for sliding windows
- Perfect for last-N-ticks scenarios
- Ideal for maintaining recent history in streaming contexts
Min-heap implementation for efficient order matching in a limit order book:
import { PriorityQueue } from "@junduck/trading-core";
type Order = { price: number; size: number; id: string };
// Bid queue: buyers (highest price has priority)
const bids = new PriorityQueue<Order>((a, b) => b.price - a.price);
// Ask queue: sellers (lowest price has priority)
const asks = new PriorityQueue<Order>((a, b) => a.price - b.price);
// Market makers place orders
bids.push({ price: 50000, size: 2, id: "B1" });
bids.push({ price: 50100, size: 1, id: "B2" }); // Better bid
bids.push({ price: 49900, size: 5, id: "B3" });
asks.push({ price: 50200, size: 1, id: "A1" });
asks.push({ price: 50150, size: 2, id: "A2" }); // Better ask
asks.push({ price: 50300, size: 3, id: "A3" });
// Check best bid/ask (top of book)
console.log(bids.peek()); // { price: 50100, size: 1, id: "B2" } - highest bid
console.log(asks.peek()); // { price: 50150, size: 2, id: "A2" } - lowest ask
// Market order arrives: match best prices
const bestBid = bids.pop();
const bestAsk = asks.pop();
console.log(`Spread: ${bestAsk.price - bestBid.price}`); // 50Use cases:
- Limit order book implementation
- Best bid/ask tracking
- Order matching engines
- Event scheduling by timestamp
Position - Represents a currency account with:
- Cash balance
- Long positions (Map of symbol → LongPosition)
- Short positions (Map of symbol → ShortPosition)
- Realized P&L and commission tracking
Portfolio - Multi-currency portfolio containing:
- Map of currency → Position
- Portfolio metadata (id, name, timestamps)
Order & Fill:
- Order: Trading intent (BUY/SELL with OPEN/CLOSE effect)
- Fill: Actual execution record (price, quantity, commission)
Market Data:
- MarketSnapshot: Point-in-time market prices
- MarketQuote: Bid/ask quotes
- MarketBar: OHLCV bars
- Universe: Collection of tradable assets
Containers:
CircularBuffer<T>- Fixed-size circular buffer with O(1) push/popDeque<T>- Double-ended queuePriorityQueue<T>- Min-heap based priority queueRBTree<T>- Red-Black Tree for sorted operations
Online Statistics (Cumulative):
CMA- Cumulative moving averageCuVar,CuStddev- Variance and standard deviationCuCov,CuCorr,CuBeta- Covariance, correlation, betaCuSkew,CuKurt- Skewness and kurtosisCuHistogram- Dynamic histogram
Rolling Window Statistics:
SMA,EMA,EWMA- Moving averagesRollingVar,RollingStddev- Variance and standard deviationRollingVarEW,RollingStddevEW- Exponentially weighted variantsRollingZScore,RollingZScoreEW- Standardized scoresRollingCov,RollingCorr,RollingBeta- Covariance, correlation, betaRollingMin,RollingMax,RollingMinMax- Extrema trackingRollingArgMin,RollingArgMax- Extrema with indicesRollingMedian,RollingQuantile- Order statistics (O(n) using QuickSelect)RollingSkew,RollingKurt- Higher momentsRollingHistogram- Rolling histogram
Probabilistic Structures:
CountMinSketch- Space-efficient frequency estimationBloomFilter- Probabilistic set membership
Utilities:
Kahan- Numerically stable summationSmoothedAccum- Exponential smoothingmaxDrawDown(),maxRelDrawDown()- Drawdown metricsmaxDrawUp(),maxRelDrawUp()- Drawup metricsexp_factor(),wilders_factor()- Smoothing factors
Numeric Utilities (Array-based):
sum,min,max,argmin,argmax- Array aggregationsmean,variance,stddev,skew,kurt- Descriptive statisticscov,corr,spearman- Correlation measuresmedian,quantile- Order statisticscumsum,diff,pctChange,returns,logReturns- Series transformsnorm,lag,lead,coalesce,locf,winsorize- Data preparationargsort,rank- Ranking utilitiesgcd,lcm,lerp,clamp- Math utilities
All portfolio utilities are under the pu namespace to avoid naming conflicts with position-level utilities (both have openLong, closeLong, openShort, closeShort functions).
Portfolio Management:
pu.create(id, name)- Create a new portfoliopu.createPosition(portfolio, currency, initialCash?, time?)- Create a position in portfoliopu.getPosition(portfolio, currency)- Get position for currencypu.getCash(portfolio, currency)- Get cash balance for currencypu.getCurrencies(portfolio)- Get all currency codes in portfoliopu.getAllSymbols(portfolio)- Get all symbols organized by currencypu.hasAsset(portfolio, asset)- Check if asset exists in portfolio
Trading (Portfolio-level):
pu.openLong(portfolio, asset, price, quantity, commission?, time?)- Open or add to long positionpu.closeLong(portfolio, asset, price, quantity, commission?, strategy?, time?)- Close long positionpu.openShort(portfolio, asset, price, quantity, commission?, time?)- Open or add to short positionpu.closeShort(portfolio, asset, price, quantity, commission?, strategy?, time?)- Close short position
Corporate Actions (Portfolio-level):
pu.handleSplit(portfolio, asset, ratio, time?)- Handle stock splitpu.handleCashDividend(portfolio, asset, amountPerShare, taxRate?, time?)- Handle cash dividendpu.handleSpinoff(portfolio, asset, newSymbol, ratio, time?)- Handle spinoffpu.handleMerger(portfolio, asset, newSymbol, ratio, cashComponent?, time?)- Handle merger
Crypto Actions (Portfolio-level):
pu.handleHardFork(portfolio, asset, newSymbol, ratio?, time?)- Handle hard forkpu.handleAirdrop(portfolio, currency, holderSymbol, airdropSymbol, amountPerToken?, fixedAmount?, time?)- Handle airdroppu.handleTokenSwap(portfolio, asset, newSymbol, ratio?, time?)- Handle token swappu.handleStakingReward(portfolio, asset, rewardPerToken, time?)- Handle staking rewards
Position-level functions (exported directly):
createPosition(initialCash?, time?)- Create a Position objectopenLong(pos, symbol, price, quantity, commission?, time?)- Open or add to long positioncloseLong(pos, symbol, price, quantity, commission?, strategy?, time?)- Close long positionopenShort(pos, symbol, price, quantity, commission?, time?)- Open or add to short positioncloseShort(pos, symbol, price, quantity, commission?, strategy?, time?)- Close short positionvalidatePosition(pos)- Validate position integrity
createUniverse(assets, timestamp?)- Create a Universe with filtering capabilitiesappraisePosition(position, snapshot)- Calculate total position valueappraisePortfolio(portfolio, snapshot)- Calculate portfolio value across currenciescalculateUnrealizedPnL(position, snapshot)- Calculate unrealized profit/lossisAssetValidAt(asset, timestamp)- Check if asset is valid at timestamp
Order Creation:
buyOrder(opts)- Create BUY order to open long positionsellOrder(opts)- Create SELL order to close long positionshortOrder(opts)- Create SELL order to open short positioncoverOrder(opts)- Create BUY order to close short position (cover)
Order Lifecycle:
acceptOrder(order, time?)- Accept order and create OrderState with status "OPEN"rejectOrder(order, time?)- Reject order and create OrderState with status "REJECT"cancelOrder(state, time?)- Cancel active order by updating state to "CANCELLED"
Order Validation:
validateOrder(order, position, snapshot)- Validate order against position and market state
fillOrder(opts)- Fill an order and create Fill receipt, updates OrderStateprocessFill(position, fill, closeStrategy?)- Process a fill to update positionapplyFill(position, fill, closeStrategy?)- (deprecated) Apply a single fill to positionapplyFills(position, fills, closeStrategy?)- (deprecated) Apply multiple fills sequentially
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:ui # UI mode
npm run test:coverage # Coverage reportnpm run build # Build to dist/
npm run dev # Watch mode
npm run typecheck # Type checking onlyMIT
Documentation and core implementation assistance by Claude (Anthropic).