@startsimpli/api 0.5.17 → 0.5.18
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 +7 -0
- package/src/index.ts +11 -0
- package/src/lib/markets-api.ts +254 -5
package/package.json
CHANGED
|
@@ -78,6 +78,13 @@ export const ENDPOINTS = {
|
|
|
78
78
|
EARNINGS_CALENDAR: 'api/v1/markets/calendar',
|
|
79
79
|
TRADING_SNAPSHOTS: 'api/v1/markets/trading/snapshots',
|
|
80
80
|
|
|
81
|
+
// Options (TENTATIVE — schemas to be confirmed by claude-mac via agent_bridge req 42c763b2)
|
|
82
|
+
OPTIONS_CHAIN: 'api/v1/markets/options/chain',
|
|
83
|
+
OPTIONS_IV: 'api/v1/markets/options/iv',
|
|
84
|
+
OPTIONS_SKEW: 'api/v1/markets/options/skew',
|
|
85
|
+
OPTIONS_GREEKS: 'api/v1/markets/options/greeks',
|
|
86
|
+
VIX_TERM: 'api/v1/markets/vix_term',
|
|
87
|
+
|
|
81
88
|
// Sources ops
|
|
82
89
|
SOURCES_HEALTH: 'api/v1/sources/ops/health',
|
|
83
90
|
} as const;
|
package/src/index.ts
CHANGED
|
@@ -231,4 +231,15 @@ export type {
|
|
|
231
231
|
TradingSnapshot,
|
|
232
232
|
TradingSnapshotsResponse,
|
|
233
233
|
TradingSnapshotsParams,
|
|
234
|
+
OptionSide,
|
|
235
|
+
OptionContract,
|
|
236
|
+
OptionsChainParams,
|
|
237
|
+
OptionsChainResponse,
|
|
238
|
+
OptionsIvPoint,
|
|
239
|
+
OptionsIvResponse,
|
|
240
|
+
OptionsSkewPoint,
|
|
241
|
+
OptionsSkewResponse,
|
|
242
|
+
OptionsGreeks,
|
|
243
|
+
VixTermPoint,
|
|
244
|
+
VixTermResponse,
|
|
234
245
|
} from './lib/markets-api';
|
package/src/lib/markets-api.ts
CHANGED
|
@@ -215,15 +215,115 @@ 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
|
+
export interface OptionsIvPoint {
|
|
275
|
+
date: string;
|
|
276
|
+
iv30: number;
|
|
277
|
+
iv60: number | null;
|
|
278
|
+
iv90: number | null;
|
|
279
|
+
iv30Rank: number | null;
|
|
280
|
+
iv30Percentile: number | null;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface OptionsIvResponse {
|
|
284
|
+
symbol: string;
|
|
285
|
+
count: number;
|
|
286
|
+
results: OptionsIvPoint[];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface OptionsSkewPoint {
|
|
290
|
+
strike: number;
|
|
291
|
+
iv: number;
|
|
292
|
+
side: OptionSide;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface OptionsSkewResponse {
|
|
296
|
+
symbol: string;
|
|
297
|
+
expiry: string;
|
|
298
|
+
spot: number;
|
|
299
|
+
results: OptionsSkewPoint[];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface OptionsGreeks {
|
|
303
|
+
symbol: string;
|
|
304
|
+
expiry: string;
|
|
305
|
+
strike: number;
|
|
306
|
+
side: OptionSide;
|
|
307
|
+
delta: number;
|
|
308
|
+
gamma: number;
|
|
309
|
+
theta: number;
|
|
310
|
+
vega: number;
|
|
311
|
+
iv: number;
|
|
312
|
+
computedAt: string | null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export interface VixTermPoint {
|
|
316
|
+
symbol: 'VIX' | 'VIX3M' | 'VIX6M' | 'VIX1Y' | string;
|
|
317
|
+
value: number;
|
|
318
|
+
percentile252d: number | null;
|
|
319
|
+
dailyDelta: number | null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export interface VixTermResponse {
|
|
323
|
+
generatedAt: string;
|
|
324
|
+
points: VixTermPoint[];
|
|
325
|
+
}
|
|
326
|
+
|
|
227
327
|
export interface AlpacaSnapshot {
|
|
228
328
|
equity: number;
|
|
229
329
|
cash: number;
|
|
@@ -282,6 +382,85 @@ export interface MarketsHealth {
|
|
|
282
382
|
sourceInstances: SourceInstanceHealth[];
|
|
283
383
|
}
|
|
284
384
|
|
|
385
|
+
// Django serializes Decimal/numeric fields as strings (e.g. "219.440002").
|
|
386
|
+
// Coerce known numeric fields to numbers so consumers can call .toFixed safely.
|
|
387
|
+
const n = (v: unknown): number => typeof v === 'number' ? v : Number(v);
|
|
388
|
+
const maybeN = (v: unknown): number | null => v == null ? null : n(v);
|
|
389
|
+
|
|
390
|
+
function coerceBar(b: PriceBar): PriceBar {
|
|
391
|
+
return { ...b, open: n(b.open), high: n(b.high), low: n(b.low), close: n(b.close), volume: maybeN(b.volume) };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function coerceReturns(r: ReturnsAnalytics): ReturnsAnalytics {
|
|
395
|
+
return { ...r, startPrice: n(r.startPrice), endPrice: n(r.endPrice), simpleReturn: n(r.simpleReturn), logReturn: n(r.logReturn) };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function coercePairSpread(p: PairSpreadPoint): PairSpreadPoint {
|
|
399
|
+
return { ...p, longClose: n(p.longClose), shortClose: n(p.shortClose), spread: n(p.spread), logSpread: n(p.logSpread) };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function coerceBreadth(b: SectorBreadthPoint): SectorBreadthPoint {
|
|
403
|
+
return { ...b, nUp: n(b.nUp), nTotal: n(b.nTotal), pctUp: n(b.pctUp), meanReturn: n(b.meanReturn), returnDispersion: n(b.returnDispersion) };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function coercePosition(p: AlpacaPosition): AlpacaPosition {
|
|
407
|
+
return {
|
|
408
|
+
...p,
|
|
409
|
+
qty: n(p.qty),
|
|
410
|
+
marketValue: n(p.marketValue),
|
|
411
|
+
costBasis: n(p.costBasis),
|
|
412
|
+
unrealizedPl: n(p.unrealizedPl),
|
|
413
|
+
strike: maybeN(p.strike),
|
|
414
|
+
contracts: maybeN(p.contracts),
|
|
415
|
+
delta: maybeN(p.delta),
|
|
416
|
+
gamma: maybeN(p.gamma),
|
|
417
|
+
theta: maybeN(p.theta),
|
|
418
|
+
vega: maybeN(p.vega),
|
|
419
|
+
iv: maybeN(p.iv),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function coerceContract(c: OptionContract): OptionContract {
|
|
424
|
+
return {
|
|
425
|
+
...c,
|
|
426
|
+
strike: n(c.strike),
|
|
427
|
+
bid: maybeN(c.bid),
|
|
428
|
+
ask: maybeN(c.ask),
|
|
429
|
+
last: maybeN(c.last),
|
|
430
|
+
iv: maybeN(c.iv),
|
|
431
|
+
delta: maybeN(c.delta),
|
|
432
|
+
gamma: maybeN(c.gamma),
|
|
433
|
+
theta: maybeN(c.theta),
|
|
434
|
+
vega: maybeN(c.vega),
|
|
435
|
+
oi: n(c.oi),
|
|
436
|
+
volume: n(c.volume),
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function coerceIvPoint(p: OptionsIvPoint): OptionsIvPoint {
|
|
441
|
+
return {
|
|
442
|
+
...p,
|
|
443
|
+
iv30: n(p.iv30),
|
|
444
|
+
iv60: maybeN(p.iv60),
|
|
445
|
+
iv90: maybeN(p.iv90),
|
|
446
|
+
iv30Rank: maybeN(p.iv30Rank),
|
|
447
|
+
iv30Percentile: maybeN(p.iv30Percentile),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function coerceSkewPoint(p: OptionsSkewPoint): OptionsSkewPoint {
|
|
452
|
+
return { ...p, strike: n(p.strike), iv: n(p.iv) };
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function coerceVixPoint(p: VixTermPoint): VixTermPoint {
|
|
456
|
+
return {
|
|
457
|
+
...p,
|
|
458
|
+
value: n(p.value),
|
|
459
|
+
percentile252d: maybeN(p.percentile252d),
|
|
460
|
+
dailyDelta: maybeN(p.dailyDelta),
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
285
464
|
export class MarketsApi {
|
|
286
465
|
constructor(private client: ApiClient) {}
|
|
287
466
|
|
|
@@ -296,22 +475,31 @@ export class MarketsApi {
|
|
|
296
475
|
}
|
|
297
476
|
|
|
298
477
|
async getBars(symbol: string, params?: BarsParams): Promise<PriceBar[] | PaginatedResponse<PriceBar>> {
|
|
299
|
-
|
|
478
|
+
const res = await this.client.fetch.get<PriceBar[] | PaginatedResponse<PriceBar>>(
|
|
300
479
|
ENDPOINTS.INSTRUMENT_BARS(symbol),
|
|
301
480
|
{ params: params as Record<string, unknown> | undefined },
|
|
302
481
|
);
|
|
482
|
+
if (Array.isArray(res)) return res.map(coerceBar);
|
|
483
|
+
return { ...res, results: res.results.map(coerceBar) };
|
|
303
484
|
}
|
|
304
485
|
|
|
305
486
|
async getLatestBar(symbol: string, interval: BarInterval = '1d'): Promise<PriceBar> {
|
|
306
|
-
|
|
487
|
+
const b = await this.client.fetch.get<PriceBar>(ENDPOINTS.INSTRUMENT_LATEST(symbol), {
|
|
307
488
|
params: { interval },
|
|
308
489
|
});
|
|
490
|
+
return coerceBar(b);
|
|
309
491
|
}
|
|
310
492
|
|
|
311
493
|
async getAnalytics(symbol: string, params: AnalyticsParams): Promise<AnalyticsResponse> {
|
|
312
|
-
|
|
494
|
+
const res = await this.client.fetch.get<AnalyticsResponse>(ENDPOINTS.INSTRUMENT_ANALYTICS(symbol), {
|
|
313
495
|
params: params as unknown as Record<string, unknown>,
|
|
314
496
|
});
|
|
497
|
+
if (params.metric === 'returns') return coerceReturns(res as ReturnsAnalytics);
|
|
498
|
+
if (params.metric === 'pair_spread' && Array.isArray(res)) return (res as PairSpreadPoint[]).map(coercePairSpread);
|
|
499
|
+
if (params.metric === 'volatility' && Array.isArray(res)) {
|
|
500
|
+
return (res as VolatilityPoint[]).map((v) => ({ ...v, volatility: n(v.volatility) }));
|
|
501
|
+
}
|
|
502
|
+
return res;
|
|
315
503
|
}
|
|
316
504
|
|
|
317
505
|
async getNews(symbol: string, params?: NewsParams): Promise<InstrumentNewsResponse> {
|
|
@@ -325,9 +513,10 @@ export class MarketsApi {
|
|
|
325
513
|
}
|
|
326
514
|
|
|
327
515
|
async getSectorBreadth(params: SectorBreadthParams): Promise<SectorBreadthResponse> {
|
|
328
|
-
|
|
516
|
+
const res = await this.client.fetch.get<SectorBreadthResponse>(ENDPOINTS.SECTOR_BREADTH, {
|
|
329
517
|
params: params as unknown as Record<string, unknown>,
|
|
330
518
|
});
|
|
519
|
+
return { ...res, results: res.results.map(coerceBreadth) };
|
|
331
520
|
}
|
|
332
521
|
|
|
333
522
|
async getEarningsCalendar(params: EarningsCalendarParams): Promise<EarningsCalendarResponse> {
|
|
@@ -337,9 +526,69 @@ export class MarketsApi {
|
|
|
337
526
|
});
|
|
338
527
|
}
|
|
339
528
|
|
|
529
|
+
// ===== Options + VIX (tentative — see agent_bridge req 42c763b2) =======
|
|
530
|
+
|
|
531
|
+
async getOptionsChain(params: OptionsChainParams): Promise<OptionsChainResponse> {
|
|
532
|
+
const res = await this.client.fetch.get<OptionsChainResponse>(ENDPOINTS.OPTIONS_CHAIN, {
|
|
533
|
+
params: params as unknown as Record<string, unknown>,
|
|
534
|
+
});
|
|
535
|
+
return {
|
|
536
|
+
...res,
|
|
537
|
+
spot: n(res.spot),
|
|
538
|
+
contracts: (res.contracts ?? []).map(coerceContract),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async getOptionsIv(symbol: string): Promise<OptionsIvResponse> {
|
|
543
|
+
const res = await this.client.fetch.get<OptionsIvResponse>(ENDPOINTS.OPTIONS_IV, {
|
|
544
|
+
params: { symbol },
|
|
545
|
+
});
|
|
546
|
+
return { ...res, results: (res.results ?? []).map(coerceIvPoint) };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async getOptionsSkew(symbol: string, expiry: string): Promise<OptionsSkewResponse> {
|
|
550
|
+
const res = await this.client.fetch.get<OptionsSkewResponse>(ENDPOINTS.OPTIONS_SKEW, {
|
|
551
|
+
params: { symbol, expiry },
|
|
552
|
+
});
|
|
553
|
+
return { ...res, spot: n(res.spot), results: (res.results ?? []).map(coerceSkewPoint) };
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
async getOptionsGreeks(symbol: string, expiry: string, strike: number, side: OptionSide): Promise<OptionsGreeks> {
|
|
557
|
+
const res = await this.client.fetch.get<OptionsGreeks>(ENDPOINTS.OPTIONS_GREEKS, {
|
|
558
|
+
params: { symbol, expiry, strike, side },
|
|
559
|
+
});
|
|
560
|
+
return {
|
|
561
|
+
...res,
|
|
562
|
+
strike: n(res.strike),
|
|
563
|
+
delta: n(res.delta),
|
|
564
|
+
gamma: n(res.gamma),
|
|
565
|
+
theta: n(res.theta),
|
|
566
|
+
vega: n(res.vega),
|
|
567
|
+
iv: n(res.iv),
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
async getVixTerm(): Promise<VixTermResponse> {
|
|
572
|
+
const res = await this.client.fetch.get<VixTermResponse>(ENDPOINTS.VIX_TERM);
|
|
573
|
+
return { ...res, points: (res.points ?? []).map(coerceVixPoint) };
|
|
574
|
+
}
|
|
575
|
+
|
|
340
576
|
async getTradingSnapshots(params?: TradingSnapshotsParams): Promise<TradingSnapshotsResponse> {
|
|
341
|
-
|
|
577
|
+
const res = await this.client.fetch.get<TradingSnapshotsResponse>(ENDPOINTS.TRADING_SNAPSHOTS, {
|
|
342
578
|
params: params as Record<string, unknown> | undefined,
|
|
343
579
|
});
|
|
580
|
+
return {
|
|
581
|
+
...res,
|
|
582
|
+
results: res.results.map((s) => ({
|
|
583
|
+
...s,
|
|
584
|
+
alpaca: {
|
|
585
|
+
...s.alpaca,
|
|
586
|
+
equity: n(s.alpaca.equity),
|
|
587
|
+
cash: n(s.alpaca.cash),
|
|
588
|
+
buyingPower: n(s.alpaca.buyingPower),
|
|
589
|
+
positions: (s.alpaca.positions ?? []).map(coercePosition),
|
|
590
|
+
},
|
|
591
|
+
})),
|
|
592
|
+
};
|
|
344
593
|
}
|
|
345
594
|
}
|