Analytics (0.8)
@tradecanvas/analytics ships a headless, bar-by-bar strategy
backtester with portfolio tracking and a summary risk-metrics calculator.
Pair it with replay mode to visualize trades on the chart.
Live backtest — SMA(10/30) cross
365 days of synthetic price data, $10k initial cash, 0.05% commission, 0.03% slippage.
Running backtest…
↑ Live SMA(10/30) cross-over backtest on 365 days of deterministic synthetic data. Scrub the slider or press Play to step through the equity curve bar-by-bar.
Backtester
Execution model:
- Strategy fn runs at close of each bar.
- Orders placed on bar N fill on bar N+1 — market at open; limit/stop when the bar trades through the trigger price.
- Equity is marked to close of every bar.
import { Backtester, FixedCommission, PercentSlippage } from '@tradecanvas/analytics'
const bt = new Backtester({
initialCash: 10_000,
commission: new FixedCommission(2),
slippage: new PercentSlippage(0.0005),
allowShort: true,
})
const result = bt.run(historicalBars, (ctx) => {
if (!ctx.position) {
ctx.placeOrder({ side: 'long', type: 'market', quantity: 1 })
} else if (ctx.bar.close > ctx.position.averagePrice * 1.02) {
ctx.close()
}
})
console.log(result.metrics.sharpe, result.metrics.maxDrawdownPct) StrategyContext
| Field / method | Description |
|---|---|
bar | Current bar. |
index | Index of bar in the input series. |
history | Bars up to and including bar. |
position | Current position or null. |
cash | Available cash. |
equity | Cash + mark-to-market position value. |
placeOrder(order) | Queue order for next bar. |
close(tag?) | Market-close current position. |
cancel(orderId) | Cancel a pending order. |
Commission & slippage models
FixedCommission(perTrade)PercentCommission(rate)— fraction of notional, e.g.0.001= 10 bps.PerShareCommission(perShare, minimum?)PercentSlippage(rate)— adverse fraction of price.RangeBasedSlippage(factor)— proportional to the bar's range.
Portfolio
Tracks cash, one net position, realized P&L, and the equity curve.
const portfolio = new Portfolio({ initialCash: 10_000 })
portfolio.applyFill({ ... })
portfolio.mark(time, price) // record equity point
portfolio.getPosition() // → { side, quantity, averagePrice, ... } | null
portfolio.getTrades() // → closed trades
portfolio.getEquityCurve() // → equity points
portfolio.equity(price) // mark-to-market Risk metrics
import { computeRiskMetrics } from '@tradecanvas/analytics'
const m = computeRiskMetrics(initialCash, equityCurve, trades, {
periodsPerYear: 252, // optional; auto-detected from timestamps
riskFreeRate: 0.03,
})
m.totalReturnPct
m.cagr
m.sharpe
m.sortino
m.calmar
m.maxDrawdownPct
m.winRate
m.profitFactor
m.expectancy Pair the result's equityCurve with EquityCurveRenderer to visualize backtests.