From 663789607596b58da91955261a2e03072893d5d8 Mon Sep 17 00:00:00 2001 From: Eddie Atkinson <45678264+eddie-atkinson@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:02:58 +1000 Subject: [PATCH] Add quote typebox module, migrate quoteCombine and move over options type --- src/modules/options.ts | 122 +--------- src/modules/quote.ts | 462 ++++++++++++++++++++++---------------- src/other/quoteCombine.ts | 26 +-- 3 files changed, 282 insertions(+), 328 deletions(-) diff --git a/src/modules/options.ts b/src/modules/options.ts index 01f2b1cc..6f7bd111 100644 --- a/src/modules/options.ts +++ b/src/modules/options.ts @@ -6,128 +6,10 @@ import type { } from "../lib/moduleCommon.js"; import { Type, Static } from "@sinclair/typebox"; -import { - YahooDateInMs, - YahooFinanceDate, - YahooNumber, - YahooTwoNumberRange, -} from "../lib/yahooFinanceTypes.js"; - +import { YahooFinanceDate, YahooNumber } from "../lib/yahooFinanceTypes.js"; +import { QuoteBase } from "./quote.js"; import { Value } from "@sinclair/typebox/value"; -const QuoteBase = Type.Object( - { - language: Type.String(), // "en-US", - region: Type.String(), // "US", - quoteType: Type.String(), // "EQUITY" | "ETF" | "MUTUALFUND"; - typeDisp: Type.Optional(Type.String()), // "Equity", not always present. - quoteSourceName: Type.Optional(Type.String()), // "Delayed Quote", - triggerable: Type.Boolean(), // true, - currency: Type.Optional(Type.String()), // "USD", - // Seems to appear / disappear based not on symbol but network load (#445) - customPriceAlertConfidence: Type.Optional(Type.String()), // "HIGH" | "LOW"; TODO: anything else? - marketState: Type.Union([ - Type.Literal("REGULAR"), - Type.Literal("CLOSED"), - Type.Literal("PRE"), - Type.Literal("PREPRE"), - Type.Literal("POST"), - Type.Literal("POSTPOST"), - ]), - tradeable: Type.Boolean(), // false, - cryptoTradeable: Type.Optional(Type.Boolean()), // false - exchange: Type.String(), // "NMS", - shortName: Type.Optional(Type.String()), // "NVIDIA Corporation", - longName: Type.Optional(Type.String()), // "NVIDIA Corporation", - messageBoardId: Type.Optional(Type.String()), // "finmb_32307", - exchangeTimezoneName: Type.String(), // "America/New_York", - exchangeTimezoneShortName: Type.String(), // "EST", - gmtOffSetMilliseconds: YahooNumber, // -18000000, - market: Type.String(), // "us_market", - esgPopulated: Type.Boolean(), // false, - fiftyTwoWeekLowChange: Type.Optional(YahooNumber), // 362.96002, - fiftyTwoWeekLowChangePercent: Type.Optional(YahooNumber), // 2.0088556, - fiftyTwoWeekRange: Type.Optional(YahooTwoNumberRange), // "180.68 - 589.07" -> { low, high } - fiftyTwoWeekHighChange: Type.Optional(YahooNumber), // -45.429993, - fiftyTwoWeekHighChangePercent: Type.Optional(YahooNumber), // -0.07712155, - fiftyTwoWeekLow: Type.Optional(YahooNumber), // 180.68, - fiftyTwoWeekHigh: Type.Optional(YahooNumber), // 589.07, - fiftyTwoWeekChangePercent: Type.Optional(YahooNumber), // 22.604025 - dividendDate: Type.Optional(YahooFinanceDate), // 1609200000, - // maybe always present on EQUITY? - earningsTimestamp: Type.Optional(YahooFinanceDate), // 1614200400, - earningsTimestampStart: Type.Optional(YahooFinanceDate), // 1614200400, - earningsTimestampEnd: Type.Optional(YahooFinanceDate), // 1614200400, - trailingAnnualDividendRate: Type.Optional(YahooNumber), // 0.64, - trailingPE: Type.Optional(YahooNumber), // 88.873634, - trailingAnnualDividendYield: Type.Optional(YahooNumber), // 0.0011709387, - epsTrailingTwelveMonths: Type.Optional(YahooNumber), // 6.117, - epsForward: Type.Optional(YahooNumber), // 11.68, - epsCurrentYear: Type.Optional(YahooNumber), // 9.72, - priceEpsCurrentYear: Type.Optional(YahooNumber), // 55.930042, - sharesOutstanding: Type.Optional(YahooNumber), // 619000000, - bookValue: Type.Optional(YahooNumber), // 24.772, - fiftyDayAverage: Type.Optional(YahooNumber), // 530.8828, - fiftyDayAverageChange: Type.Optional(YahooNumber), // 12.757202, - fiftyDayAverageChangePercent: Type.Optional(YahooNumber), // 0.024030166, - twoHundredDayAverage: Type.Optional(YahooNumber), // 515.8518, - twoHundredDayAverageChange: Type.Optional(YahooNumber), // 27.788208, - twoHundredDayAverageChangePercent: Type.Optional(YahooNumber), // 0.053868588, - marketCap: Type.Optional(YahooNumber), // 336513171456, - forwardPE: Type.Optional(YahooNumber), // 46.54452, - priceToBook: Type.Optional(YahooNumber), // 21.945745, - sourceInterval: YahooNumber, // 15, - exchangeDataDelayedBy: YahooNumber, // 0, - firstTradeDateMilliseconds: Type.Optional(YahooDateInMs), // 917015400000 -> Date - priceHint: YahooNumber, // 2, - postMarketChangePercent: Type.Optional(YahooNumber), // 0.093813874, - postMarketTime: Type.Optional(YahooFinanceDate), // 1612573179 -> new Date() - postMarketPrice: Type.Optional(YahooNumber), // 544.15, - postMarketChange: Type.Optional(YahooNumber), // 0.51000977, - regularMarketChange: Type.Optional(YahooNumber), // -2.9299927, - regularMarketChangePercent: Type.Optional(YahooNumber), // -0.53606904, - regularMarketTime: Type.Optional(YahooFinanceDate), // 1612558802 -> new Date() - regularMarketPrice: Type.Optional(YahooNumber), // 543.64, - regularMarketDayHigh: Type.Optional(YahooNumber), // 549.19, - regularMarketDayRange: Type.Optional(YahooTwoNumberRange), // "541.867 - 549.19" -> { low, high } - regularMarketDayLow: Type.Optional(YahooNumber), // 541.867, - regularMarketVolume: Type.Optional(YahooNumber), // 4228841, - regularMarketPreviousClose: Type.Optional(YahooNumber), // 546.57, - preMarketChange: Type.Optional(YahooNumber), // -2.9299927, - preMarketChangePercent: Type.Optional(YahooNumber), // -0.53606904, - preMarketTime: Type.Optional(YahooFinanceDate), // 1612558802 -> new Date() - preMarketPrice: Type.Optional(YahooNumber), // 543.64, - bid: Type.Optional(YahooNumber), // 543.84, - ask: Type.Optional(YahooNumber), // 544.15, - bidSize: Type.Optional(YahooNumber), // 18, - askSize: Type.Optional(YahooNumber), // 8, - fullExchangeName: Type.String(), // "NasdaqGS", - financialCurrency: Type.Optional(Type.String()), // "USD", - regularMarketOpen: Type.Optional(YahooNumber), // 549.0, - averageDailyVolume3Month: Type.Optional(YahooNumber), // 7475022, - averageDailyVolume10Day: Type.Optional(YahooNumber), // 5546385, - displayName: Type.Optional(Type.String()), // "NVIDIA", - symbol: Type.String(), // "NVDA" - underlyingSymbol: Type.Optional(Type.String()), // "LD.MI" (for LDO.MI, #363) - // only on ETF? not on EQUITY? - ytdReturn: Type.Optional(YahooNumber), // 0.31 - trailingThreeMonthReturns: Type.Optional(YahooNumber), // 16.98 - trailingThreeMonthNavReturns: Type.Optional(YahooNumber), // 17.08 - ipoExpectedDate: Type.Optional(YahooFinanceDate), // "2020-08-13", - newListingDate: Type.Optional(YahooFinanceDate), // "2021-02-16", - nameChangeDate: Type.Optional(YahooFinanceDate), - prevName: Type.Optional(Type.String()), - averageAnalystRating: Type.Optional(Type.String()), - pageViewGrowthWeekly: Type.Optional(YahooNumber), // Since 2021-11-11 (#326) - openInterest: Type.Optional(YahooNumber), // SOHO (#248) - beta: Type.Optional(YahooNumber), - }, - { - additionalProperties: Type.Any(), - title: "QuoteBase", - } -); - /* * [TODO] Fields seen in a query but not in this module yet: * diff --git a/src/modules/quote.ts b/src/modules/quote.ts index 2b96bd7d..86451d71 100644 --- a/src/modules/quote.ts +++ b/src/modules/quote.ts @@ -5,108 +5,125 @@ import type { ModuleThis, } from "../lib/moduleCommon.js"; -import type { DateInMs, TwoNumberRange } from "../lib/commonTypes.js"; - -export interface QuoteBase { - [key: string]: any; - language: string; // "en-US", - region: string; // "US", - quoteType: string; // "EQUITY" | "ETF" | "MUTUALFUND"; - typeDisp?: string; // "Equity", not always present. - quoteSourceName?: string; // "Delayed Quote", - triggerable: boolean; // true, - currency?: string; // "USD", - // Seems to appear / disappear based not on symbol but network load (#445) - customPriceAlertConfidence?: string; // "HIGH" | "LOW"; TODO: anything else? - marketState: "REGULAR" | "CLOSED" | "PRE" | "PREPRE" | "POST" | "POSTPOST"; - tradeable: boolean; // false, - cryptoTradeable?: boolean; // false - exchange: string; // "NMS", - shortName?: string; // "NVIDIA Corporation", - longName?: string; // "NVIDIA Corporation", - messageBoardId?: string; // "finmb_32307", - exchangeTimezoneName: string; // "America/New_York", - exchangeTimezoneShortName: string; // "EST", - gmtOffSetMilliseconds: number; // -18000000, - market: string; // "us_market", - esgPopulated: boolean; // false, - fiftyTwoWeekLowChange?: number; // 362.96002, - fiftyTwoWeekLowChangePercent?: number; // 2.0088556, - fiftyTwoWeekRange?: TwoNumberRange; // "180.68 - 589.07" -> { low, high } - fiftyTwoWeekHighChange?: number; // -45.429993, - fiftyTwoWeekHighChangePercent?: number; // -0.07712155, - fiftyTwoWeekLow?: number; // 180.68, - fiftyTwoWeekHigh?: number; // 589.07, - fiftyTwoWeekChangePercent?: number; // 22.604025 - dividendDate?: Date; // 1609200000, - // maybe always present on EQUITY? - earningsTimestamp?: Date; // 1614200400, - earningsTimestampStart?: Date; // 1614200400, - earningsTimestampEnd?: Date; // 1614200400, - trailingAnnualDividendRate?: number; // 0.64, - trailingPE?: number; // 88.873634, - trailingAnnualDividendYield?: number; // 0.0011709387, - epsTrailingTwelveMonths?: number; // 6.117, - epsForward?: number; // 11.68, - epsCurrentYear?: number; // 9.72, - priceEpsCurrentYear?: number; // 55.930042, - sharesOutstanding?: number; // 619000000, - bookValue?: number; // 24.772, - fiftyDayAverage?: number; // 530.8828, - fiftyDayAverageChange?: number; // 12.757202, - fiftyDayAverageChangePercent?: number; // 0.024030166, - twoHundredDayAverage?: number; // 515.8518, - twoHundredDayAverageChange?: number; // 27.788208, - twoHundredDayAverageChangePercent?: number; // 0.053868588, - marketCap?: number; // 336513171456, - forwardPE?: number; // 46.54452, - priceToBook?: number; // 21.945745, - sourceInterval: number; // 15, - exchangeDataDelayedBy: number; // 0, - firstTradeDateMilliseconds?: DateInMs; // 917015400000 -> Date - priceHint: number; // 2, - postMarketChangePercent?: number; // 0.093813874, - postMarketTime?: Date; // 1612573179 -> new Date() - postMarketPrice?: number; // 544.15, - postMarketChange?: number; // 0.51000977, - regularMarketChange?: number; // -2.9299927, - regularMarketChangePercent?: number; // -0.53606904, - regularMarketTime?: Date; // 1612558802 -> new Date() - regularMarketPrice?: number; // 543.64, - regularMarketDayHigh?: number; // 549.19, - regularMarketDayRange?: TwoNumberRange; // "541.867 - 549.19" -> { low, high } - regularMarketDayLow?: number; // 541.867, - regularMarketVolume?: number; // 4228841, - regularMarketPreviousClose?: number; // 546.57, - preMarketChange?: number; // -2.9299927, - preMarketChangePercent?: number; // -0.53606904, - preMarketTime?: Date; // 1612558802 -> new Date() - preMarketPrice?: number; // 543.64, - bid?: number; // 543.84, - ask?: number; // 544.15, - bidSize?: number; // 18, - askSize?: number; // 8, - fullExchangeName: string; // "NasdaqGS", - financialCurrency?: string; // "USD", - regularMarketOpen?: number; // 549.0, - averageDailyVolume3Month?: number; // 7475022, - averageDailyVolume10Day?: number; // 5546385, - displayName?: string; // "NVIDIA", - symbol: string; // "NVDA" - underlyingSymbol?: string; // "LD.MI" (for LDO.MI, #363) - // only on ETF? not on EQUITY? - ytdReturn?: number; // 0.31 - trailingThreeMonthReturns?: number; // 16.98 - trailingThreeMonthNavReturns?: number; // 17.08 - ipoExpectedDate?: Date; // "2020-08-13", - newListingDate?: Date; // "2021-02-16", - nameChangeDate?: Date; - prevName?: string; - averageAnalystRating?: string; - pageViewGrowthWeekly?: number; // Since 2021-11-11 (#326) - openInterest?: number; // SOHO (#248) - beta?: number; -} +import { Static, Type } from "@sinclair/typebox"; +import { + YahooDateInMs, + YahooFinanceDate, + YahooNumber, + YahooTwoNumberRange, +} from "../lib/yahooFinanceTypes.js"; + +export const QuoteBase = Type.Object( + { + language: Type.String(), // "en-US", + region: Type.String(), // "US", + quoteType: Type.String(), // "EQUITY" | "ETF" | "MUTUALFUND"; + typeDisp: Type.Optional(Type.String()), // "Equity", not always present. + quoteSourceName: Type.Optional(Type.String()), // "Delayed Quote", + triggerable: Type.Boolean(), // true, + currency: Type.Optional(Type.String()), // "USD", + // Seems to appear / disappear based not on symbol but network load (#445) + customPriceAlertConfidence: Type.Optional(Type.String()), // "HIGH" | "LOW"; TODO: anything else? + marketState: Type.Union([ + Type.Literal("REGULAR"), + Type.Literal("CLOSED"), + Type.Literal("PRE"), + Type.Literal("PREPRE"), + Type.Literal("POST"), + Type.Literal("POSTPOST"), + ]), + tradeable: Type.Boolean(), // false, + cryptoTradeable: Type.Optional(Type.Boolean()), // false + exchange: Type.String(), // "NMS", + shortName: Type.Optional(Type.String()), // "NVIDIA Corporation", + longName: Type.Optional(Type.String()), // "NVIDIA Corporation", + messageBoardId: Type.Optional(Type.String()), // "finmb_32307", + exchangeTimezoneName: Type.String(), // "America/New_York", + exchangeTimezoneShortName: Type.String(), // "EST", + gmtOffSetMilliseconds: YahooNumber, // -18000000, + market: Type.String(), // "us_market", + esgPopulated: Type.Boolean(), // false, + fiftyTwoWeekLowChange: Type.Optional(YahooNumber), // 362.96002, + fiftyTwoWeekLowChangePercent: Type.Optional(YahooNumber), // 2.0088556, + fiftyTwoWeekRange: Type.Optional(YahooTwoNumberRange), // "180.68 - 589.07" -> { low, high } + fiftyTwoWeekHighChange: Type.Optional(YahooNumber), // -45.429993, + fiftyTwoWeekHighChangePercent: Type.Optional(YahooNumber), // -0.07712155, + fiftyTwoWeekLow: Type.Optional(YahooNumber), // 180.68, + fiftyTwoWeekHigh: Type.Optional(YahooNumber), // 589.07, + fiftyTwoWeekChangePercent: Type.Optional(YahooNumber), // 22.604025 + dividendDate: Type.Optional(YahooFinanceDate), // 1609200000, + // maybe always present on EQUITY? + earningsTimestamp: Type.Optional(YahooFinanceDate), // 1614200400, + earningsTimestampStart: Type.Optional(YahooFinanceDate), // 1614200400, + earningsTimestampEnd: Type.Optional(YahooFinanceDate), // 1614200400, + trailingAnnualDividendRate: Type.Optional(YahooNumber), // 0.64, + trailingPE: Type.Optional(YahooNumber), // 88.873634, + trailingAnnualDividendYield: Type.Optional(YahooNumber), // 0.0011709387, + epsTrailingTwelveMonths: Type.Optional(YahooNumber), // 6.117, + epsForward: Type.Optional(YahooNumber), // 11.68, + epsCurrentYear: Type.Optional(YahooNumber), // 9.72, + priceEpsCurrentYear: Type.Optional(YahooNumber), // 55.930042, + sharesOutstanding: Type.Optional(YahooNumber), // 619000000, + bookValue: Type.Optional(YahooNumber), // 24.772, + fiftyDayAverage: Type.Optional(YahooNumber), // 530.8828, + fiftyDayAverageChange: Type.Optional(YahooNumber), // 12.757202, + fiftyDayAverageChangePercent: Type.Optional(YahooNumber), // 0.024030166, + twoHundredDayAverage: Type.Optional(YahooNumber), // 515.8518, + twoHundredDayAverageChange: Type.Optional(YahooNumber), // 27.788208, + twoHundredDayAverageChangePercent: Type.Optional(YahooNumber), // 0.053868588, + marketCap: Type.Optional(YahooNumber), // 336513171456, + forwardPE: Type.Optional(YahooNumber), // 46.54452, + priceToBook: Type.Optional(YahooNumber), // 21.945745, + sourceInterval: YahooNumber, // 15, + exchangeDataDelayedBy: YahooNumber, // 0, + firstTradeDateMilliseconds: Type.Optional(YahooDateInMs), // 917015400000 -> Date + priceHint: YahooNumber, // 2, + postMarketChangePercent: Type.Optional(YahooNumber), // 0.093813874, + postMarketTime: Type.Optional(YahooFinanceDate), // 1612573179 -> new Date() + postMarketPrice: Type.Optional(YahooNumber), // 544.15, + postMarketChange: Type.Optional(YahooNumber), // 0.51000977, + regularMarketChange: Type.Optional(YahooNumber), // -2.9299927, + regularMarketChangePercent: Type.Optional(YahooNumber), // -0.53606904, + regularMarketTime: Type.Optional(YahooFinanceDate), // 1612558802 -> new Date() + regularMarketPrice: Type.Optional(YahooNumber), // 543.64, + regularMarketDayHigh: Type.Optional(YahooNumber), // 549.19, + regularMarketDayRange: Type.Optional(YahooTwoNumberRange), // "541.867 - 549.19" -> { low, high } + regularMarketDayLow: Type.Optional(YahooNumber), // 541.867, + regularMarketVolume: Type.Optional(YahooNumber), // 4228841, + regularMarketPreviousClose: Type.Optional(YahooNumber), // 546.57, + preMarketChange: Type.Optional(YahooNumber), // -2.9299927, + preMarketChangePercent: Type.Optional(YahooNumber), // -0.53606904, + preMarketTime: Type.Optional(YahooFinanceDate), // 1612558802 -> new Date() + preMarketPrice: Type.Optional(YahooNumber), // 543.64, + bid: Type.Optional(YahooNumber), // 543.84, + ask: Type.Optional(YahooNumber), // 544.15, + bidSize: Type.Optional(YahooNumber), // 18, + askSize: Type.Optional(YahooNumber), // 8, + fullExchangeName: Type.String(), // "NasdaqGS", + financialCurrency: Type.Optional(Type.String()), // "USD", + regularMarketOpen: Type.Optional(YahooNumber), // 549.0, + averageDailyVolume3Month: Type.Optional(YahooNumber), // 7475022, + averageDailyVolume10Day: Type.Optional(YahooNumber), // 5546385, + displayName: Type.Optional(Type.String()), // "NVIDIA", + symbol: Type.String(), // "NVDA" + underlyingSymbol: Type.Optional(Type.String()), // "LD.MI" (for LDO.MI, #363) + // only on ETF? not on EQUITY? + ytdReturn: Type.Optional(YahooNumber), // 0.31 + trailingThreeMonthReturns: Type.Optional(YahooNumber), // 16.98 + trailingThreeMonthNavReturns: Type.Optional(YahooNumber), // 17.08 + ipoExpectedDate: Type.Optional(YahooFinanceDate), // "2020-08-13", + newListingDate: Type.Optional(YahooFinanceDate), // "2021-02-16", + nameChangeDate: Type.Optional(YahooFinanceDate), + prevName: Type.Optional(Type.String()), + averageAnalystRating: Type.Optional(Type.String()), + pageViewGrowthWeekly: Type.Optional(YahooNumber), // Since 2021-11-11 (#326) + openInterest: Type.Optional(YahooNumber), // SOHO (#248) + beta: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + } +); /* * [TODO] Fields seen in a query but not in this module yet: @@ -125,91 +142,145 @@ export interface QuoteBase { /* * Guaranteed fields, even we don't ask for them */ - -export interface QuoteCryptoCurrency extends QuoteBase { - quoteType: "CRYPTOCURRENCY"; - circulatingSupply: number; - fromCurrency: string; // 'BTC' - toCurrency: string; // 'USD=X' - lastMarket: string; // 'CoinMarketCap' - coinImageUrl?: string; // 'https://s.yimg.com/uc/fin/img/reports-thumbnails/1.png' - volume24Hr?: number; // 62631043072 - volumeAllCurrencies?: number; // 62631043072 - startDate?: Date; // new Date(1367103600 * 1000) -} - -export interface QuoteCurrency extends QuoteBase { - quoteType: "CURRENCY"; -} - -export interface QuoteEtf extends QuoteBase { - quoteType: "ETF"; -} - -export interface QuoteEquity extends QuoteBase { - quoteType: "EQUITY"; - dividendRate?: number; // 0.96 - dividendYield?: number; // 0.51, -} - -export interface QuoteFuture extends QuoteBase { - quoteType: "FUTURE"; - headSymbolAsString: string; // "GC=F" - contractSymbol: boolean; // false - underlyingExchangeSymbol: string; // "GCM22.CMX" - expireDate: Date; // 1656374400 - expireIsoDate: number; // 2022 -} - -export interface QuoteIndex extends QuoteBase { - quoteType: "INDEX"; -} - -export interface QuoteOption extends QuoteBase { - quoteType: "OPTION"; - strike: number; - openInterest: number; - expireDate: number; - expireIsoDate: number; - underlyingSymbol: string; -} - -export interface QuoteMutualfund extends QuoteBase { - quoteType: "MUTUALFUND"; -} - -export type Quote = - | QuoteCryptoCurrency - | QuoteCurrency - | QuoteEtf - | QuoteEquity - | QuoteFuture - | QuoteIndex - | QuoteMutualfund - | QuoteOption; - -export type QuoteField = keyof Quote; - -export type ResultType = "array" | "object" | "map"; - -export type QuoteResponseArray = Quote[]; -export type QuoteResponseMap = Map; -export type QuoteResponseObject = { [key: string]: Quote }; - -export interface QuoteOptions { - fields?: QuoteField[]; - return?: ResultType; -} - -export interface QuoteOptionsWithReturnArray extends QuoteOptions { - return?: "array"; -} -export interface QuoteOptionsWithReturnMap extends QuoteOptions { - return: "map"; -} -export interface QuoteOptionsWithReturnObject extends QuoteOptions { - return: "object"; -} +const QuoteCryptoCurrency = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("CRYPTOCURRENCY"), + circulatingSupply: YahooNumber, + fromCurrency: Type.String(), // 'BTC' + toCurrency: Type.String(), // 'USD=X' + lastMarket: Type.String(), // 'CoinMarketCap' + coinImageUrl: Type.Optional(Type.String()), // 'https://s.yimg.com/uc/fin/img/reports-thumbnails/1.png' + volume24Hr: Type.Optional(YahooNumber), // 62631043072 + volumeAllCurrencies: Type.Optional(YahooNumber), // 62631043072 + startDate: Type.Optional(YahooFinanceDate), // new Date(1367103600 * 1000) + }), +]); + +const QuoteCurrency = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("CURRENCY"), + }), +]); + +const QuoteEtf = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("ETF"), + }), +]); + +const QuoteEquity = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("EQUITY"), + dividendRate: Type.Optional(Type.Number()), + dividendYield: Type.Optional(Type.Number()), + }), +]); + +const QuoteFuture = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("FUTURE"), + headSymbolAsString: Type.String(), + contractSymbol: Type.Boolean(), + underlyingExchangeSymbol: Type.String(), + expireDate: YahooFinanceDate, + expireIsoDate: YahooFinanceDate, + }), +]); + +const QuoteIndex = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("INDEX"), + }), +]); + +const QuoteOption = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("OPTION"), + strike: YahooNumber, + openInterest: YahooNumber, + expireDate: YahooNumber, + expireIsoDate: YahooNumber, + underlyingSymbol: Type.String(), + }), +]); + +const QuoteMutualfund = Type.Composite([ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("MUTUALFUND"), + }), +]); + +const QuoteSchema = Type.Union([ + QuoteCryptoCurrency, + QuoteCurrency, + QuoteEtf, + QuoteEquity, + QuoteFuture, + QuoteIndex, + QuoteMutualfund, + QuoteOption, +]); + +export type Quote = Static; + +const QuoteField = Type.KeyOf(QuoteSchema); + +const ResultType = Type.Union([ + Type.Literal("array"), + Type.Literal("object"), + Type.Literal("map"), +]); + +const QuoteResponseArraySchema = Type.Array(QuoteSchema); + +type QuoteResponseArray = Quote[]; +type QuoteResponseMap = Map; +type QuoteResponseObject = { [key: string]: Quote }; + +export const QuoteOptionsSchema = Type.Object({ + fields: Type.Optional(Type.Array(QuoteField)), + return: Type.Optional(ResultType), +}); + +const QuoteOptionsWithReturnArraySchema = Type.Composite([ + QuoteOptionsSchema, + Type.Object({ + return: Type.Optional(Type.Literal("array")), + }), +]); +const QuoteOptionsWithReturnMapSchema = Type.Composite([ + QuoteOptionsSchema, + Type.Object({ + return: Type.Literal("map"), + }), +]); + +const QuoteOptionsWithReturnObjectSchema = Type.Composite([ + QuoteOptionsSchema, + Type.Object({ + return: Type.Literal("object"), + }), +]); + +type QuoteOptionsWithReturnArray = Static< + typeof QuoteOptionsWithReturnArraySchema +>; + +type QuoteOptionsWithReturnMap = Static; + +type QuoteOptionsWithReturnObject = Static< + typeof QuoteOptionsWithReturnObjectSchema +>; + +export type QuoteOptions = Static; const queryOptionsDefaults = {}; @@ -261,13 +332,13 @@ export default async function quote( const symbols = typeof query === "string" ? query : query.join(","); const returnAs = queryOptionsOverrides && queryOptionsOverrides.return; - const results: Quote[] = await this._moduleExec({ + const results: Quote[] = await this._moduleExecTypebox({ moduleName: "quote", query: { url: "https://${YF_QUERY_HOST}/v7/finance/quote", needsCrumb: true, - schemaKey: "#/definitions/QuoteOptions", + schema: QuoteOptionsSchema, defaults: queryOptionsDefaults, runtime: { symbols }, overrides: queryOptionsOverrides, @@ -283,8 +354,9 @@ export default async function quote( }, result: { - schemaKey: "#/definitions/QuoteResponseArray", + schema: QuoteResponseArraySchema, transformWith(rawResult: any) { + console.log({ rawResult: JSON.stringify(rawResult, null, 2) }); let results = rawResult?.quoteResponse?.result; if (!results || !Array.isArray(results)) @@ -305,14 +377,16 @@ export default async function quote( switch (returnAs) { case "array": return results as Quote[]; - case "object": + case "object": { const object = {} as any; - for (let result of results) object[result.symbol] = result; + for (const result of results) object[result.symbol] = result; return object; // TODO: type - case "map": + } + case "map": { const map = new Map(); - for (let result of results) map.set(result.symbol, result); + for (const result of results) map.set(result.symbol, result); return map; // TODO: type + } } } else { // By default, match the query input shape (string or string[]). diff --git a/src/other/quoteCombine.ts b/src/other/quoteCombine.ts index 957d7002..2096dd06 100644 --- a/src/other/quoteCombine.ts +++ b/src/other/quoteCombine.ts @@ -4,11 +4,10 @@ import type { ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { validateAndCoerceTypebox } from "../lib/validateAndCoerceTypes.js"; import type { QuoteOptions, Quote } from "../modules/quote.js"; -import quote from "../modules/quote.js"; - -import validateAndCoerceTypes from "../lib/validateAndCoerceTypes.js"; +import quote, { QuoteOptionsSchema } from "../modules/quote.js"; const DEBOUNCE_TIME = 50; @@ -42,11 +41,10 @@ export default function quoteCombine( JSON.stringify(symbol, null, 2) ); - validateAndCoerceTypes({ - source: "quoteCombine", + validateAndCoerceTypebox({ type: "options", - object: queryOptionsOverrides, - schemaKey: "#/definitions/QuoteOptions", + data: queryOptionsOverrides, + schema: QuoteOptionsSchema, options: this._opts.validation, }); @@ -84,17 +82,17 @@ export default function quoteCombine( // @ts-ignore thisQuote(symbols, queryOptionsOverrides, moduleOptions) .then((results) => { - for (let result of results) { - for (let promise of entry.symbols.get(result.symbol)) { + for (const result of results) { + for (const promise of entry.symbols.get(result.symbol)) { promise.resolve(result); promise.resolved = true; } } // Check for symbols we asked for and didn't get back, - // e.g. non-existant symbols (#150) - for (let [symbol, promises] of entry.symbols) { - for (let promise of promises) { + // e.g. non-existent symbols (#150) + for (const [_, promises] of entry.symbols) { + for (const promise of promises) { if (!promise.resolved) { promise.resolve(undefined); } @@ -102,8 +100,8 @@ export default function quoteCombine( } }) .catch((error) => { - for (let symbolPromiseCallbacks of entry.symbols.values()) - for (let promise of symbolPromiseCallbacks) promise.reject(error); + for (const symbolPromiseCallbacks of entry.symbols.values()) + for (const promise of symbolPromiseCallbacks) promise.reject(error); }); }, DEBOUNCE_TIME); });