@startsimpli/api 0.5.17 → 0.5.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/constants/endpoints.ts +6 -0
- package/src/index.ts +13 -0
- package/src/lib/markets-api.ts +259 -5
package/package.json
CHANGED
|
@@ -78,6 +78,12 @@ export const ENDPOINTS = {
|
|
|
78
78
|
EARNINGS_CALENDAR: 'api/v1/markets/calendar',
|
|
79
79
|
TRADING_SNAPSHOTS: 'api/v1/markets/trading/snapshots',
|
|
80
80
|
|
|
81
|
+
// Options — schemas aligned with brain-trading prefs (agent_bridge req b09a6bb6)
|
|
82
|
+
OPTIONS_CHAIN: 'api/v1/markets/options/chain',
|
|
83
|
+
OPTIONS_IV_HISTORY: (symbol: string) => `api/v1/markets/instruments/${symbol}/options/iv/history`,
|
|
84
|
+
OPTIONS_GREEKS: 'api/v1/markets/options/greeks',
|
|
85
|
+
VIX_TERM: 'api/v1/markets/vix_term',
|
|
86
|
+
|
|
81
87
|
// Sources ops
|
|
82
88
|
SOURCES_HEALTH: 'api/v1/sources/ops/health',
|
|
83
89
|
} as const;
|
package/src/index.ts
CHANGED
|
@@ -231,4 +231,17 @@ export type {
|
|
|
231
231
|
TradingSnapshot,
|
|
232
232
|
TradingSnapshotsResponse,
|
|
233
233
|
TradingSnapshotsParams,
|
|
234
|
+
OptionSide,
|
|
235
|
+
OptionContract,
|
|
236
|
+
OptionsChainParams,
|
|
237
|
+
OptionsChainResponse,
|
|
238
|
+
OptionsIvPoint,
|
|
239
|
+
OptionsIvResponse,
|
|
240
|
+
OptionsIvHistoryParams,
|
|
241
|
+
OptionsSkewPoint,
|
|
242
|
+
OptionsGreeks,
|
|
243
|
+
VixTenor,
|
|
244
|
+
VixTermState,
|
|
245
|
+
VixTermPoint,
|
|
246
|
+
VixTermResponse,
|
|
234
247
|
} from './lib/markets-api';
|
package/src/lib/markets-api.ts
CHANGED
|
@@ -215,15 +215,126 @@ export interface PairSnapshot {
|
|
|
215
215
|
[key: string]: unknown;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
export type OptionSide = 'call' | 'put';
|
|
219
|
+
|
|
218
220
|
export interface AlpacaPosition {
|
|
219
221
|
symbol: string;
|
|
220
222
|
qty: number;
|
|
221
223
|
marketValue: number;
|
|
222
224
|
costBasis: number;
|
|
223
225
|
unrealizedPl: number;
|
|
226
|
+
// Optional options-position fields (populated server-side when symbol is OCC-format)
|
|
227
|
+
underlying?: string | null;
|
|
228
|
+
optionSide?: OptionSide | null;
|
|
229
|
+
strike?: number | null;
|
|
230
|
+
expiry?: string | null;
|
|
231
|
+
contracts?: number | null;
|
|
232
|
+
delta?: number | null;
|
|
233
|
+
gamma?: number | null;
|
|
234
|
+
theta?: number | null;
|
|
235
|
+
vega?: number | null;
|
|
236
|
+
iv?: number | null;
|
|
224
237
|
[key: string]: unknown;
|
|
225
238
|
}
|
|
226
239
|
|
|
240
|
+
// ===== Options + VIX =====================================================
|
|
241
|
+
// Schemas are TENTATIVE pending claude-mac confirmation (agent_bridge req
|
|
242
|
+
// 42c763b2). All numeric fields run through coerce helpers since Django
|
|
243
|
+
// serializes Decimals as strings.
|
|
244
|
+
|
|
245
|
+
export interface OptionContract {
|
|
246
|
+
strike: number;
|
|
247
|
+
side: OptionSide;
|
|
248
|
+
bid: number | null;
|
|
249
|
+
ask: number | null;
|
|
250
|
+
last: number | null;
|
|
251
|
+
iv: number | null;
|
|
252
|
+
delta: number | null;
|
|
253
|
+
gamma: number | null;
|
|
254
|
+
theta: number | null;
|
|
255
|
+
vega: number | null;
|
|
256
|
+
oi: number;
|
|
257
|
+
volume: number;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export interface OptionsChainResponse {
|
|
261
|
+
symbol: string;
|
|
262
|
+
expiry: string;
|
|
263
|
+
spot: number;
|
|
264
|
+
contracts: OptionContract[];
|
|
265
|
+
expiries?: string[];
|
|
266
|
+
computedAt?: string | null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface OptionsChainParams {
|
|
270
|
+
symbol: string;
|
|
271
|
+
expiry?: string;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Per brain-trading req b09a6bb6 — atm/skew/put-call ratios as a single snapshot row
|
|
275
|
+
export interface OptionsIvPoint {
|
|
276
|
+
snapshotDate: string;
|
|
277
|
+
atmIv: number;
|
|
278
|
+
atmCallIv: number | null;
|
|
279
|
+
atmPutIv: number | null;
|
|
280
|
+
ivSkew25d: number | null;
|
|
281
|
+
ivSkewOtmPuts: number | null;
|
|
282
|
+
putCallVolumeRatio: number | null;
|
|
283
|
+
putCallOiRatio: number | null;
|
|
284
|
+
// Populated once 20+ days of history accumulate
|
|
285
|
+
ivRank30d: number | null;
|
|
286
|
+
ivRank252d: number | null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface OptionsIvResponse {
|
|
290
|
+
symbol: string;
|
|
291
|
+
results: OptionsIvPoint[];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface OptionsIvHistoryParams {
|
|
295
|
+
symbol: string;
|
|
296
|
+
since?: string;
|
|
297
|
+
until?: string;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Skew is derived client-side from chain (per brain-trading guidance);
|
|
301
|
+
// kept as a local UI type, not an endpoint.
|
|
302
|
+
export interface OptionsSkewPoint {
|
|
303
|
+
strike: number;
|
|
304
|
+
iv: number;
|
|
305
|
+
side: OptionSide;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export interface OptionsGreeks {
|
|
309
|
+
symbol: string;
|
|
310
|
+
expiry: string;
|
|
311
|
+
strike: number;
|
|
312
|
+
side: OptionSide;
|
|
313
|
+
delta: number;
|
|
314
|
+
gamma: number;
|
|
315
|
+
theta: number;
|
|
316
|
+
vega: number;
|
|
317
|
+
iv: number;
|
|
318
|
+
computedAt: string | null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export type VixTenor = '9D' | '30D' | '3M' | string;
|
|
322
|
+
export type VixTermState = 'contango' | 'backwardation';
|
|
323
|
+
|
|
324
|
+
export interface VixTermPoint {
|
|
325
|
+
tenor: VixTenor;
|
|
326
|
+
symbol: string;
|
|
327
|
+
value: number;
|
|
328
|
+
percentile252d: number | null;
|
|
329
|
+
delta1d: number | null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export interface VixTermResponse {
|
|
333
|
+
generatedAt: string;
|
|
334
|
+
state: VixTermState;
|
|
335
|
+
points: VixTermPoint[];
|
|
336
|
+
}
|
|
337
|
+
|
|
227
338
|
export interface AlpacaSnapshot {
|
|
228
339
|
equity: number;
|
|
229
340
|
cash: number;
|
|
@@ -282,6 +393,85 @@ export interface MarketsHealth {
|
|
|
282
393
|
sourceInstances: SourceInstanceHealth[];
|
|
283
394
|
}
|
|
284
395
|
|
|
396
|
+
// Django serializes Decimal/numeric fields as strings (e.g. "219.440002").
|
|
397
|
+
// Coerce known numeric fields to numbers so consumers can call .toFixed safely.
|
|
398
|
+
const n = (v: unknown): number => typeof v === 'number' ? v : Number(v);
|
|
399
|
+
const maybeN = (v: unknown): number | null => v == null ? null : n(v);
|
|
400
|
+
|
|
401
|
+
function coerceBar(b: PriceBar): PriceBar {
|
|
402
|
+
return { ...b, open: n(b.open), high: n(b.high), low: n(b.low), close: n(b.close), volume: maybeN(b.volume) };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function coerceReturns(r: ReturnsAnalytics): ReturnsAnalytics {
|
|
406
|
+
return { ...r, startPrice: n(r.startPrice), endPrice: n(r.endPrice), simpleReturn: n(r.simpleReturn), logReturn: n(r.logReturn) };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function coercePairSpread(p: PairSpreadPoint): PairSpreadPoint {
|
|
410
|
+
return { ...p, longClose: n(p.longClose), shortClose: n(p.shortClose), spread: n(p.spread), logSpread: n(p.logSpread) };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function coerceBreadth(b: SectorBreadthPoint): SectorBreadthPoint {
|
|
414
|
+
return { ...b, nUp: n(b.nUp), nTotal: n(b.nTotal), pctUp: n(b.pctUp), meanReturn: n(b.meanReturn), returnDispersion: n(b.returnDispersion) };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function coercePosition(p: AlpacaPosition): AlpacaPosition {
|
|
418
|
+
return {
|
|
419
|
+
...p,
|
|
420
|
+
qty: n(p.qty),
|
|
421
|
+
marketValue: n(p.marketValue),
|
|
422
|
+
costBasis: n(p.costBasis),
|
|
423
|
+
unrealizedPl: n(p.unrealizedPl),
|
|
424
|
+
strike: maybeN(p.strike),
|
|
425
|
+
contracts: maybeN(p.contracts),
|
|
426
|
+
delta: maybeN(p.delta),
|
|
427
|
+
gamma: maybeN(p.gamma),
|
|
428
|
+
theta: maybeN(p.theta),
|
|
429
|
+
vega: maybeN(p.vega),
|
|
430
|
+
iv: maybeN(p.iv),
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function coerceContract(c: OptionContract): OptionContract {
|
|
435
|
+
return {
|
|
436
|
+
...c,
|
|
437
|
+
strike: n(c.strike),
|
|
438
|
+
bid: maybeN(c.bid),
|
|
439
|
+
ask: maybeN(c.ask),
|
|
440
|
+
last: maybeN(c.last),
|
|
441
|
+
iv: maybeN(c.iv),
|
|
442
|
+
delta: maybeN(c.delta),
|
|
443
|
+
gamma: maybeN(c.gamma),
|
|
444
|
+
theta: maybeN(c.theta),
|
|
445
|
+
vega: maybeN(c.vega),
|
|
446
|
+
oi: n(c.oi),
|
|
447
|
+
volume: n(c.volume),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function coerceIvPoint(p: OptionsIvPoint): OptionsIvPoint {
|
|
452
|
+
return {
|
|
453
|
+
...p,
|
|
454
|
+
atmIv: n(p.atmIv),
|
|
455
|
+
atmCallIv: maybeN(p.atmCallIv),
|
|
456
|
+
atmPutIv: maybeN(p.atmPutIv),
|
|
457
|
+
ivSkew25d: maybeN(p.ivSkew25d),
|
|
458
|
+
ivSkewOtmPuts: maybeN(p.ivSkewOtmPuts),
|
|
459
|
+
putCallVolumeRatio: maybeN(p.putCallVolumeRatio),
|
|
460
|
+
putCallOiRatio: maybeN(p.putCallOiRatio),
|
|
461
|
+
ivRank30d: maybeN(p.ivRank30d),
|
|
462
|
+
ivRank252d: maybeN(p.ivRank252d),
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function coerceVixPoint(p: VixTermPoint): VixTermPoint {
|
|
467
|
+
return {
|
|
468
|
+
...p,
|
|
469
|
+
value: n(p.value),
|
|
470
|
+
percentile252d: maybeN(p.percentile252d),
|
|
471
|
+
delta1d: maybeN(p.delta1d),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
285
475
|
export class MarketsApi {
|
|
286
476
|
constructor(private client: ApiClient) {}
|
|
287
477
|
|
|
@@ -296,22 +486,31 @@ export class MarketsApi {
|
|
|
296
486
|
}
|
|
297
487
|
|
|
298
488
|
async getBars(symbol: string, params?: BarsParams): Promise<PriceBar[] | PaginatedResponse<PriceBar>> {
|
|
299
|
-
|
|
489
|
+
const res = await this.client.fetch.get<PriceBar[] | PaginatedResponse<PriceBar>>(
|
|
300
490
|
ENDPOINTS.INSTRUMENT_BARS(symbol),
|
|
301
491
|
{ params: params as Record<string, unknown> | undefined },
|
|
302
492
|
);
|
|
493
|
+
if (Array.isArray(res)) return res.map(coerceBar);
|
|
494
|
+
return { ...res, results: res.results.map(coerceBar) };
|
|
303
495
|
}
|
|
304
496
|
|
|
305
497
|
async getLatestBar(symbol: string, interval: BarInterval = '1d'): Promise<PriceBar> {
|
|
306
|
-
|
|
498
|
+
const b = await this.client.fetch.get<PriceBar>(ENDPOINTS.INSTRUMENT_LATEST(symbol), {
|
|
307
499
|
params: { interval },
|
|
308
500
|
});
|
|
501
|
+
return coerceBar(b);
|
|
309
502
|
}
|
|
310
503
|
|
|
311
504
|
async getAnalytics(symbol: string, params: AnalyticsParams): Promise<AnalyticsResponse> {
|
|
312
|
-
|
|
505
|
+
const res = await this.client.fetch.get<AnalyticsResponse>(ENDPOINTS.INSTRUMENT_ANALYTICS(symbol), {
|
|
313
506
|
params: params as unknown as Record<string, unknown>,
|
|
314
507
|
});
|
|
508
|
+
if (params.metric === 'returns') return coerceReturns(res as ReturnsAnalytics);
|
|
509
|
+
if (params.metric === 'pair_spread' && Array.isArray(res)) return (res as PairSpreadPoint[]).map(coercePairSpread);
|
|
510
|
+
if (params.metric === 'volatility' && Array.isArray(res)) {
|
|
511
|
+
return (res as VolatilityPoint[]).map((v) => ({ ...v, volatility: n(v.volatility) }));
|
|
512
|
+
}
|
|
513
|
+
return res;
|
|
315
514
|
}
|
|
316
515
|
|
|
317
516
|
async getNews(symbol: string, params?: NewsParams): Promise<InstrumentNewsResponse> {
|
|
@@ -325,9 +524,10 @@ export class MarketsApi {
|
|
|
325
524
|
}
|
|
326
525
|
|
|
327
526
|
async getSectorBreadth(params: SectorBreadthParams): Promise<SectorBreadthResponse> {
|
|
328
|
-
|
|
527
|
+
const res = await this.client.fetch.get<SectorBreadthResponse>(ENDPOINTS.SECTOR_BREADTH, {
|
|
329
528
|
params: params as unknown as Record<string, unknown>,
|
|
330
529
|
});
|
|
530
|
+
return { ...res, results: res.results.map(coerceBreadth) };
|
|
331
531
|
}
|
|
332
532
|
|
|
333
533
|
async getEarningsCalendar(params: EarningsCalendarParams): Promise<EarningsCalendarResponse> {
|
|
@@ -337,9 +537,63 @@ export class MarketsApi {
|
|
|
337
537
|
});
|
|
338
538
|
}
|
|
339
539
|
|
|
540
|
+
// ===== Options + VIX (tentative — see agent_bridge req 42c763b2) =======
|
|
541
|
+
|
|
542
|
+
async getOptionsChain(params: OptionsChainParams): Promise<OptionsChainResponse> {
|
|
543
|
+
const res = await this.client.fetch.get<OptionsChainResponse>(ENDPOINTS.OPTIONS_CHAIN, {
|
|
544
|
+
params: params as unknown as Record<string, unknown>,
|
|
545
|
+
});
|
|
546
|
+
return {
|
|
547
|
+
...res,
|
|
548
|
+
spot: n(res.spot),
|
|
549
|
+
contracts: (res.contracts ?? []).map(coerceContract),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async getOptionsIvHistory(params: OptionsIvHistoryParams): Promise<OptionsIvResponse> {
|
|
554
|
+
const { symbol, ...rest } = params;
|
|
555
|
+
const res = await this.client.fetch.get<OptionsIvResponse>(ENDPOINTS.OPTIONS_IV_HISTORY(symbol), {
|
|
556
|
+
params: rest as Record<string, unknown>,
|
|
557
|
+
});
|
|
558
|
+
return { ...res, results: (res.results ?? []).map(coerceIvPoint) };
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async getOptionsGreeks(symbol: string, expiry: string, strike: number, side: OptionSide): Promise<OptionsGreeks> {
|
|
562
|
+
const res = await this.client.fetch.get<OptionsGreeks>(ENDPOINTS.OPTIONS_GREEKS, {
|
|
563
|
+
params: { symbol, expiry, strike, side },
|
|
564
|
+
});
|
|
565
|
+
return {
|
|
566
|
+
...res,
|
|
567
|
+
strike: n(res.strike),
|
|
568
|
+
delta: n(res.delta),
|
|
569
|
+
gamma: n(res.gamma),
|
|
570
|
+
theta: n(res.theta),
|
|
571
|
+
vega: n(res.vega),
|
|
572
|
+
iv: n(res.iv),
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
async getVixTerm(): Promise<VixTermResponse> {
|
|
577
|
+
const res = await this.client.fetch.get<VixTermResponse>(ENDPOINTS.VIX_TERM);
|
|
578
|
+
return { ...res, points: (res.points ?? []).map(coerceVixPoint) };
|
|
579
|
+
}
|
|
580
|
+
|
|
340
581
|
async getTradingSnapshots(params?: TradingSnapshotsParams): Promise<TradingSnapshotsResponse> {
|
|
341
|
-
|
|
582
|
+
const res = await this.client.fetch.get<TradingSnapshotsResponse>(ENDPOINTS.TRADING_SNAPSHOTS, {
|
|
342
583
|
params: params as Record<string, unknown> | undefined,
|
|
343
584
|
});
|
|
585
|
+
return {
|
|
586
|
+
...res,
|
|
587
|
+
results: res.results.map((s) => ({
|
|
588
|
+
...s,
|
|
589
|
+
alpaca: {
|
|
590
|
+
...s.alpaca,
|
|
591
|
+
equity: n(s.alpaca.equity),
|
|
592
|
+
cash: n(s.alpaca.cash),
|
|
593
|
+
buyingPower: n(s.alpaca.buyingPower),
|
|
594
|
+
positions: (s.alpaca.positions ?? []).map(coercePosition),
|
|
595
|
+
},
|
|
596
|
+
})),
|
|
597
|
+
};
|
|
344
598
|
}
|
|
345
599
|
}
|