Skip to content

Paper Trading

Simulated trading in the right sidebar. Market and limit orders, positions, trade history. Default balance: $10,000. Limit orders execute automatically when price conditions are met, including for symbols not currently on the chart.

Configuration

typescript
new ChartSpire({
  paperTradingEnabled: true,
  paperTradingMaxTrades: 1000,
  paperTradingPriceTrackingInterval: 30000,
  paperTradingPriceTrackingLogging: true,
  paperTradingUseExternalStorage: false,
  defaultOpenComponents: ['papertrading'],  // optional: open panel on load
  // ...
})
OptionTypeDefaultDescription
paperTradingEnabledbooleanfalseEnable paper trading panel
paperTradingMaxTradesnumber1000Max trades in history (min: 1)
paperTradingPriceTrackingIntervalnumber30000Price check interval for limit orders (ms, min: 1000)
paperTradingPriceTrackingLoggingbooleantrueDebug logging for price tracking
paperTradingUseExternalStoragebooleanfalseUse callbacks instead of localStorage

Panel Tabs

  • Trade: Place market or limit orders (buy/sell)
  • Positions: Open positions with P&L, close individually
  • Orders: Pending limit orders, cancel as needed
  • Trades: Trade history
  • Cash: Add/remove cash, reset account

Storage

Local (default): Data in localStorage under key chartspire-paper-trading.

External: Persists data to your backend instead of localStorage. Set paperTradingUseExternalStorage: true and register callbacks before creating the chart:

typescript
import ChartSpire, { setPaperTradingChangeCallback, setGetPaperTradingDataCallback } from '@chartspire/chartspire'

setPaperTradingChangeCallback(async (data) => {
  const res = await fetch('/api/paper-trading/save', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  })
  return res.ok
})

setGetPaperTradingDataCallback(async () => {
  const res = await fetch('/api/paper-trading/load')
  return res.ok ? await res.json() : null
})

const chart = new ChartSpire({
  paperTradingEnabled: true,
  paperTradingUseExternalStorage: true,
  // ...
})

Initialization loads data when the panel opens. Return false from the save callback on failure.

Data Structure

typescript
interface PaperTradingData {
  balance: number
  orders: Order[]
  positions: Position[]
  trades: Trade[]
}

interface Order {
  id: string
  symbol: string
  symbolType?: string
  side: 'buy' | 'sell'
  type: 'MARKET' | 'LIMIT'
  quantity: number
  price: number
  status: 'PENDING' | 'FILLED' | 'CANCELLED'
  timestamp: number
}

interface Position {
  id: string
  symbol: string
  symbolType?: string
  side: 'buy' | 'sell'
  quantity: number
  averagePrice: number
  unrealizedPnL: number
  timestamp: number
}

interface Trade {
  id: string
  orderId: string
  symbol: string
  side: 'buy' | 'sell'
  quantity: number
  price: number
  timestamp: number
}