@startsimpli/api 0.5.16 → 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 +12 -0
- package/src/lib/markets-api.ts +278 -17
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
|
@@ -224,10 +224,22 @@ export type {
|
|
|
224
224
|
EarningsCalendarResponse,
|
|
225
225
|
PairConsensus,
|
|
226
226
|
CommitteeVotes,
|
|
227
|
+
PairMember,
|
|
227
228
|
PairSnapshot,
|
|
228
229
|
AlpacaPosition,
|
|
229
230
|
AlpacaSnapshot,
|
|
230
231
|
TradingSnapshot,
|
|
231
232
|
TradingSnapshotsResponse,
|
|
232
233
|
TradingSnapshotsParams,
|
|
234
|
+
OptionSide,
|
|
235
|
+
OptionContract,
|
|
236
|
+
OptionsChainParams,
|
|
237
|
+
OptionsChainResponse,
|
|
238
|
+
OptionsIvPoint,
|
|
239
|
+
OptionsIvResponse,
|
|
240
|
+
OptionsSkewPoint,
|
|
241
|
+
OptionsSkewResponse,
|
|
242
|
+
OptionsGreeks,
|
|
243
|
+
VixTermPoint,
|
|
244
|
+
VixTermResponse,
|
|
233
245
|
} from './lib/markets-api';
|
package/src/lib/markets-api.ts
CHANGED
|
@@ -188,30 +188,142 @@ export interface EarningsCalendarResponse {
|
|
|
188
188
|
export type PairConsensus = 'BULL' | 'BEAR' | 'FLAT';
|
|
189
189
|
|
|
190
190
|
export interface CommitteeVotes {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
BULL: number;
|
|
192
|
+
BEAR: number;
|
|
193
|
+
FLAT: number;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface PairMember {
|
|
197
|
+
modelName: string;
|
|
198
|
+
features: string[];
|
|
199
|
+
thresholds: [number, number];
|
|
200
|
+
scoreToday: number;
|
|
201
|
+
signalToday: number;
|
|
202
|
+
endValueFull?: number | null;
|
|
194
203
|
}
|
|
195
204
|
|
|
196
205
|
export interface PairSnapshot {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
206
|
+
consensus: PairConsensus;
|
|
207
|
+
topScore: number;
|
|
208
|
+
topSignal: number;
|
|
209
|
+
targetSymbol: string;
|
|
210
|
+
committeeN: number;
|
|
211
|
+
committeeVotes: CommitteeVotes;
|
|
212
|
+
committeeMeanScore: number;
|
|
213
|
+
topConfigName: string;
|
|
214
|
+
members: PairMember[];
|
|
202
215
|
[key: string]: unknown;
|
|
203
216
|
}
|
|
204
217
|
|
|
218
|
+
export type OptionSide = 'call' | 'put';
|
|
219
|
+
|
|
205
220
|
export interface AlpacaPosition {
|
|
206
221
|
symbol: string;
|
|
207
222
|
qty: number;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
223
|
+
marketValue: number;
|
|
224
|
+
costBasis: number;
|
|
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;
|
|
212
237
|
[key: string]: unknown;
|
|
213
238
|
}
|
|
214
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
|
+
|
|
215
327
|
export interface AlpacaSnapshot {
|
|
216
328
|
equity: number;
|
|
217
329
|
cash: number;
|
|
@@ -270,6 +382,85 @@ export interface MarketsHealth {
|
|
|
270
382
|
sourceInstances: SourceInstanceHealth[];
|
|
271
383
|
}
|
|
272
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
|
+
|
|
273
464
|
export class MarketsApi {
|
|
274
465
|
constructor(private client: ApiClient) {}
|
|
275
466
|
|
|
@@ -284,22 +475,31 @@ export class MarketsApi {
|
|
|
284
475
|
}
|
|
285
476
|
|
|
286
477
|
async getBars(symbol: string, params?: BarsParams): Promise<PriceBar[] | PaginatedResponse<PriceBar>> {
|
|
287
|
-
|
|
478
|
+
const res = await this.client.fetch.get<PriceBar[] | PaginatedResponse<PriceBar>>(
|
|
288
479
|
ENDPOINTS.INSTRUMENT_BARS(symbol),
|
|
289
480
|
{ params: params as Record<string, unknown> | undefined },
|
|
290
481
|
);
|
|
482
|
+
if (Array.isArray(res)) return res.map(coerceBar);
|
|
483
|
+
return { ...res, results: res.results.map(coerceBar) };
|
|
291
484
|
}
|
|
292
485
|
|
|
293
486
|
async getLatestBar(symbol: string, interval: BarInterval = '1d'): Promise<PriceBar> {
|
|
294
|
-
|
|
487
|
+
const b = await this.client.fetch.get<PriceBar>(ENDPOINTS.INSTRUMENT_LATEST(symbol), {
|
|
295
488
|
params: { interval },
|
|
296
489
|
});
|
|
490
|
+
return coerceBar(b);
|
|
297
491
|
}
|
|
298
492
|
|
|
299
493
|
async getAnalytics(symbol: string, params: AnalyticsParams): Promise<AnalyticsResponse> {
|
|
300
|
-
|
|
494
|
+
const res = await this.client.fetch.get<AnalyticsResponse>(ENDPOINTS.INSTRUMENT_ANALYTICS(symbol), {
|
|
301
495
|
params: params as unknown as Record<string, unknown>,
|
|
302
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;
|
|
303
503
|
}
|
|
304
504
|
|
|
305
505
|
async getNews(symbol: string, params?: NewsParams): Promise<InstrumentNewsResponse> {
|
|
@@ -313,9 +513,10 @@ export class MarketsApi {
|
|
|
313
513
|
}
|
|
314
514
|
|
|
315
515
|
async getSectorBreadth(params: SectorBreadthParams): Promise<SectorBreadthResponse> {
|
|
316
|
-
|
|
516
|
+
const res = await this.client.fetch.get<SectorBreadthResponse>(ENDPOINTS.SECTOR_BREADTH, {
|
|
317
517
|
params: params as unknown as Record<string, unknown>,
|
|
318
518
|
});
|
|
519
|
+
return { ...res, results: res.results.map(coerceBreadth) };
|
|
319
520
|
}
|
|
320
521
|
|
|
321
522
|
async getEarningsCalendar(params: EarningsCalendarParams): Promise<EarningsCalendarResponse> {
|
|
@@ -325,9 +526,69 @@ export class MarketsApi {
|
|
|
325
526
|
});
|
|
326
527
|
}
|
|
327
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
|
+
|
|
328
576
|
async getTradingSnapshots(params?: TradingSnapshotsParams): Promise<TradingSnapshotsResponse> {
|
|
329
|
-
|
|
577
|
+
const res = await this.client.fetch.get<TradingSnapshotsResponse>(ENDPOINTS.TRADING_SNAPSHOTS, {
|
|
330
578
|
params: params as Record<string, unknown> | undefined,
|
|
331
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
|
+
};
|
|
332
593
|
}
|
|
333
594
|
}
|