Jaspers Hub

biseroff / alpaca

pluginReviewed★ 0↓ 3

Alpaca: Alpaca market data, account, positions, and order management, with watchlist, account, and order-blotter views.

2.4 KB
1// How Alpaca's fields become flat rows: numbers sent as strings, OCC option symbols, orders.
2
3/** A number from Alpaca's string or number, or null when missing. */
4export function num(value: unknown): number | null {
5 if (value === null || value === undefined || value === '') return null
6 const n = Number(value)
7 return Number.isFinite(n) ? n : null
8}
9
10/** A string or null. */
11export function str(value: unknown): string | null {
12 return value === null || value === undefined || value === '' ? null : String(value)
13}
14
15/** Underlying, expiration (YYYY-MM-DD), type, and strike from an OCC symbol like AAPL250117C00190000. */
16export function parseOcc(symbol: string): {
17 underlying: string | null
18 expiration: string | null
19 type: 'call' | 'put' | null
20 strike: number | null
21} {
22 const m = /^([A-Z0-9.]+?)(\d{6})([CP])(\d{8})$/.exec(symbol.replace(/\s+/g, '').toUpperCase())
23 if (!m) return { underlying: null, expiration: null, type: null, strike: null }
24 const [, root, date, cp, strike] = m as unknown as [string, string, string, string, string]
25 return {
26 underlying: root,
27 expiration: `20${date.slice(0, 2)}-${date.slice(2, 4)}-${date.slice(4, 6)}`,
28 type: cp === 'C' ? 'call' : 'put',
29 strike: Number(strike) / 1000,
30 }
31}
32
33/** The midpoint of a bid and ask, or null when either is missing or zero. */
34export function mid(bid: number | null, ask: number | null): number | null {
35 return bid && ask ? Math.round(((bid + ask) / 2) * 10000) / 10000 : null
36}
37
38/** One order as a flat row. */
39export function orderRow(order: Record<string, unknown>): Record<string, unknown> {
40 return {
41 id: str(order.id),
42 client_order_id: str(order.client_order_id),
43 symbol: str(order.symbol),
44 asset_class: str(order.asset_class),
45 side: str(order.side),
46 type: str(order.type ?? order.order_type),
47 time_in_force: str(order.time_in_force),
48 order_class: str(order.order_class),
49 qty: num(order.qty),
50 notional: num(order.notional),
51 filled_qty: num(order.filled_qty),
52 limit_price: num(order.limit_price),
53 stop_price: num(order.stop_price),
54 trail_price: num(order.trail_price),
55 trail_percent: num(order.trail_percent),
56 filled_avg_price: num(order.filled_avg_price),
57 status: str(order.status),
58 extended_hours: Boolean(order.extended_hours),
59 created_at: str(order.created_at),
60 submitted_at: str(order.submitted_at),
61 filled_at: str(order.filled_at),
62 canceled_at: str(order.canceled_at),
63 expired_at: str(order.expired_at),
64 }
65}