diff --git a/.circleci/config.yml b/.circleci/config.yml index 79d8873f..b2566445 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,9 +42,6 @@ jobs: # Check for TypeScript errors - run: yarn test:ts - # Check that schema is up-to-date - - run: scripts/schema-check.sh - - run: yarn prettier --check src tests tests-modules # run tests! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 51fe1a1e..c3d4fbac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,11 +71,6 @@ work and submitting your PR. All PRs should be submitted against the `devel` branch (github default). -**Changing TypeScript Interfaces** - -Run `yarn generateSchema` after changing any interfaces, this will regenerate -the `schema.json` file which is used for run-time tests. - **Commit Messages** Commit messages should follow the @@ -134,12 +129,11 @@ TODO Checklist: 1. Create it in `src/modules/myAmazingModule.ts` -1. Run `yarn generateSchema` (and on any future interface changes) -1. Test it in `src/modules/myAmazingModule.spec.ts` -1. Add it to `src/index-common.ts` -1. Docs in `docs/modules/myAmazingModule.md` -1. Link these docs in `README.md` and `docs/README.md`. -1. Commit all the above and any `tests/http/*` created in your tests. +2. Test it in `src/modules/myAmazingModule.spec.ts` +3. Add it to `src/index-common.ts` +4. Docs in `docs/modules/myAmazingModule.md` +5. Link these docs in `README.md` and `docs/README.md`. +6. Commit all the above and any `tests/http/*` created in your tests. For a model example, see the [recommendationsBySymbol PR](https://github.com/gadicc/node-yahoo-finance2/pull/28) diff --git a/docs/validation.md b/docs/validation.md index d6f18de1..2f69f595 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -146,16 +146,17 @@ receive back, we'll no longer throw a new error on new unknown keys. This is important because Yahoo constantly add new fields, and this would break all existing deployments. -You can revert to the old behaviour with: +You can revert to the old behaviour by passing the `_internalThrowOnAdditionalProperties` to the global configuration like so: ```js -yahooFinance._disallowAdditionalProps(); +yahooFinance.setGlobalConfig({ validation: { _internalThrowOnAdditionalProperties: true} }); ``` which is the default when `NODE_ENV==="test"`. This means that during our development of the library itself, we make sure that we're testing against all types. + ## Help Fix Validation Errors diff --git a/package.json b/package.json index a184bc96..b589e3e0 100644 --- a/package.json +++ b/package.json @@ -37,15 +37,10 @@ "scripts": { "coverage": "yarn test --coverage", "lint": "eslint . --ext .js,.ts", - "//schema": "ts-json-schema-generator -f tsconfig.json -p 'src/{modules/**/*.ts,lib/options.ts}' -t '*' | node bin/schema-tweak.js > schema.json", - "schema": "node --loader ts-node/esm scripts/schema.js > schema.json", "timeseries": "node --loader ts-node/esm scripts/timeseries.js", - "build": "yarn run build:esm && yarn run build:cjs && yarn run build:post", + "build": "yarn run build:esm && yarn run build:cjs", "build:esm": "tsc --module es2020 --target es2019 --outDir dist/esm", "build:cjs": "tsc --module commonjs --target es2015 --outDir dist/cjs && sed 's/\"type\": \"module\",/\"type:\": \"commonjs\",/' dist/cjs/package.json > dist/cjs/package-changed.json && mv dist/cjs/package-changed.json dist/cjs/package.json", - "build:post": "scripts/json-transform.sh", - "generateSchema": "yarn schema", - "prepublishOnly": "yarn build && yarn generateSchema && yarn build:post", "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js", "test:ts": "tsc --noEmit", "test:esm": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js -c tests-modules/esm/jest.config.js tests-modules/esm/tests/*", @@ -54,13 +49,11 @@ "test:build": "yarn test:modules" }, "files": [ - "dist", - "schema.json" + "dist" ], "dependencies": { + "@sinclair/typebox": "^0.32.27", "@types/tough-cookie": "^4.0.2", - "ajv": "8.10.0", - "ajv-formats": "2.1.1", "node-fetch": "^2.6.1", "tough-cookie": "^4.1.2", "tough-cookie-file-store": "^2.0.3" @@ -83,11 +76,9 @@ "globby": "13.2.2", "jest": "29.7.0", "jest-tobetype": "1.2.3", - "oas-schema-walker": "1.1.5", "prettier": "2.8.8", "semantic-release": "19.0.5", "ts-jest": "29.1.2", - "ts-json-schema-generator": "1.5.0", "ts-node": "10.9.2", "typescript": "5.4.3" } diff --git a/schema.json b/schema.json deleted file mode 100644 index 7729a246..00000000 --- a/schema.json +++ /dev/null @@ -1,10222 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$comment": "DO NOT EDIT THIS FILE. It is generated automatically from typescript interfaces in the project. To update, run `yarn schema`.", - "definitions": { - "Logger": { - "type": "object", - "properties": { - "info": {}, - "warn": {}, - "error": {}, - "debug": {} - }, - "required": [ - "info", - "warn", - "error", - "debug" - ], - "additionalProperties": false - }, - "YahooFinanceOptions": { - "type": "object", - "properties": { - "YF_QUERY_HOST": { - "type": "string" - }, - "cookieJar": { - "$ref": "#/definitions/ExtendedCookieJar" - }, - "queue": { - "$ref": "#/definitions/QueueOptions" - }, - "validation": { - "$ref": "#/definitions/ValidationOptions" - }, - "logger": { - "$ref": "#/definitions/Logger" - } - }, - "additionalProperties": false - }, - "ExtendedCookieJar": { - "type": "object", - "additionalProperties": false, - "properties": {} - }, - "CookieJar": { - "type": "object", - "additionalProperties": false - }, - "QueueOptions": { - "type": "object", - "properties": { - "concurrency": { - "yahooFinanceType": "number" - }, - "timeout": { - "yahooFinanceType": "number" - } - }, - "additionalProperties": false - }, - "ValidationOptions": { - "type": "object", - "properties": { - "logErrors": { - "type": "boolean" - }, - "logOptionsErrors": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "additionalProperties": false - }, - "ChartResultObject": { - "type": "object", - "properties": { - "meta": { - "$ref": "#/definitions/ChartMeta" - }, - "timestamp": { - "type": "array", - "items": { - "yahooFinanceType": "number" - } - }, - "events": { - "$ref": "#/definitions/ChartEventsObject" - }, - "indicators": { - "$ref": "#/definitions/ChartIndicatorsObject" - } - }, - "required": [ - "meta", - "indicators" - ] - }, - "ChartMeta": { - "type": "object", - "properties": { - "currency": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "exchangeName": { - "type": "string" - }, - "instrumentType": { - "type": "string" - }, - "firstTradeDate": { - "yahooFinanceType": "date|null" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "gmtoffset": { - "yahooFinanceType": "number" - }, - "timezone": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "chartPreviousClose": { - "yahooFinanceType": "number" - }, - "previousClose": { - "yahooFinanceType": "number" - }, - "scale": { - "yahooFinanceType": "number" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "currentTradingPeriod": { - "type": "object", - "properties": { - "pre": { - "$ref": "#/definitions/ChartMetaTradingPeriod" - }, - "regular": { - "$ref": "#/definitions/ChartMetaTradingPeriod" - }, - "post": { - "$ref": "#/definitions/ChartMetaTradingPeriod" - } - }, - "required": [ - "pre", - "regular", - "post" - ] - }, - "tradingPeriods": { - "$ref": "#/definitions/ChartMetaTradingPeriods" - }, - "dataGranularity": { - "type": "string" - }, - "range": { - "type": "string" - }, - "validRanges": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "currency", - "symbol", - "exchangeName", - "instrumentType", - "firstTradeDate", - "regularMarketTime", - "gmtoffset", - "timezone", - "exchangeTimezoneName", - "regularMarketPrice", - "priceHint", - "currentTradingPeriod", - "dataGranularity", - "range", - "validRanges" - ] - }, - "ChartMetaTradingPeriod": { - "type": "object", - "properties": { - "timezone": { - "type": "string" - }, - "start": { - "yahooFinanceType": "date" - }, - "end": { - "yahooFinanceType": "date" - }, - "gmtoffset": { - "yahooFinanceType": "number" - } - }, - "required": [ - "timezone", - "start", - "end", - "gmtoffset" - ] - }, - "ChartMetaTradingPeriods": { - "type": "object", - "properties": { - "pre": { - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartMetaTradingPeriod" - } - } - }, - "post": { - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartMetaTradingPeriod" - } - } - }, - "regular": { - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartMetaTradingPeriod" - } - } - } - } - }, - "ChartEventsObject": { - "type": "object", - "properties": { - "dividends": { - "$ref": "#/definitions/ChartEventDividends" - }, - "splits": { - "$ref": "#/definitions/ChartEventSplits" - } - } - }, - "ChartEventDividends": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChartEventDividend" - } - }, - "ChartEventDividend": { - "type": "object", - "properties": { - "amount": { - "yahooFinanceType": "number" - }, - "date": { - "yahooFinanceType": "date" - } - }, - "required": [ - "amount", - "date" - ] - }, - "ChartEventSplits": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChartEventSplit" - } - }, - "ChartEventSplit": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "date" - }, - "numerator": { - "yahooFinanceType": "number" - }, - "denominator": { - "yahooFinanceType": "number" - }, - "splitRatio": { - "type": "string" - } - }, - "required": [ - "date", - "numerator", - "denominator", - "splitRatio" - ] - }, - "ChartIndicatorsObject": { - "type": "object", - "properties": { - "quote": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartIndicatorQuote" - } - }, - "adjclose": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartIndicatorAdjclose" - } - } - }, - "required": [ - "quote" - ] - }, - "ChartIndicatorQuote": { - "type": "object", - "properties": { - "high": { - "type": "array", - "items": { - "yahooFinanceType": "number|null" - } - }, - "low": { - "type": "array", - "items": { - "yahooFinanceType": "number|null" - } - }, - "open": { - "type": "array", - "items": { - "yahooFinanceType": "number|null" - } - }, - "close": { - "type": "array", - "items": { - "yahooFinanceType": "number|null" - } - }, - "volume": { - "type": "array", - "items": { - "yahooFinanceType": "number|null" - } - } - }, - "required": [ - "high", - "low", - "open", - "close", - "volume" - ] - }, - "ChartIndicatorAdjclose": { - "type": "object", - "properties": { - "adjclose": { - "type": "array", - "items": { - "yahooFinanceType": "number|null" - } - } - } - }, - "ChartResultArray": { - "type": "object", - "properties": { - "meta": { - "$ref": "#/definitions/ChartMeta" - }, - "events": { - "$ref": "#/definitions/ChartEventsArray" - }, - "quotes": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartResultArrayQuote" - } - } - }, - "required": [ - "meta", - "quotes" - ], - "additionalProperties": false - }, - "ChartEventsArray": { - "type": "object", - "properties": { - "dividends": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartEventDividend" - } - }, - "splits": { - "type": "array", - "items": { - "$ref": "#/definitions/ChartEventSplit" - } - } - } - }, - "ChartResultArrayQuote": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "date" - }, - "high": { - "yahooFinanceType": "number|null" - }, - "low": { - "yahooFinanceType": "number|null" - }, - "open": { - "yahooFinanceType": "number|null" - }, - "close": { - "yahooFinanceType": "number|null" - }, - "volume": { - "yahooFinanceType": "number|null" - }, - "adjclose": { - "yahooFinanceType": "number|null" - } - }, - "required": [ - "date", - "high", - "low", - "open", - "close", - "volume" - ] - }, - "ChartOptions": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "useYfid": { - "type": "boolean" - }, - "interval": { - "type": "string", - "enum": [ - "1m", - "2m", - "5m", - "15m", - "30m", - "60m", - "90m", - "1h", - "1d", - "5d", - "1wk", - "1mo", - "3mo" - ] - }, - "includePrePost": { - "type": "boolean" - }, - "events": { - "type": "string" - }, - "lang": { - "type": "string" - }, - "return": { - "type": "string", - "enum": [ - "array", - "object" - ] - } - }, - "required": [ - "period1" - ], - "additionalProperties": false - }, - "ChartOptionsWithReturnArray": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "useYfid": { - "type": "boolean" - }, - "interval": { - "type": "string", - "enum": [ - "1m", - "2m", - "5m", - "15m", - "30m", - "60m", - "90m", - "1h", - "1d", - "5d", - "1wk", - "1mo", - "3mo" - ] - }, - "includePrePost": { - "type": "boolean" - }, - "events": { - "type": "string" - }, - "lang": { - "type": "string" - }, - "return": { - "type": "string", - "const": "array" - } - }, - "additionalProperties": false, - "required": [ - "period1" - ] - }, - "ChartOptionsWithReturnObject": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "useYfid": { - "type": "boolean" - }, - "interval": { - "type": "string", - "enum": [ - "1m", - "2m", - "5m", - "15m", - "30m", - "60m", - "90m", - "1h", - "1d", - "5d", - "1wk", - "1mo", - "3mo" - ] - }, - "includePrePost": { - "type": "boolean" - }, - "events": { - "type": "string" - }, - "lang": { - "type": "string" - }, - "return": { - "type": "string", - "const": "object" - } - }, - "required": [ - "period1", - "return" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "symbol": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/ChartOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "symbol", - "queryOptionsOverrides" - ], - "additionalProperties": false - }, - "ModuleThis": { - "type": "object", - "properties": { - "_moduleExec": {} - }, - "required": [ - "_moduleExec" - ] - }, - "ModuleOptions": { - "type": "object", - "properties": { - "validateResult": { - "type": "boolean" - }, - "devel": { - "type": [ - "boolean", - "string" - ] - }, - "fetchOptions": { - "type": "object" - } - }, - "additionalProperties": false - }, - "DailyGainersResult": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "canonicalName": { - "type": "string" - }, - "criteriaMeta": { - "$ref": "#/definitions/DailyGainersCriteriaMeta" - }, - "rawCriteria": { - "type": "string" - }, - "start": { - "yahooFinanceType": "number" - }, - "count": { - "yahooFinanceType": "number" - }, - "total": { - "yahooFinanceType": "number" - }, - "quotes": { - "type": "array", - "items": { - "$ref": "#/definitions/DailyGainersQuote" - } - }, - "useRecords": { - "type": "boolean" - }, - "predefinedScr": { - "type": "boolean" - }, - "versionId": { - "yahooFinanceType": "number" - }, - "creationDate": { - "yahooFinanceType": "number" - }, - "lastUpdated": { - "yahooFinanceType": "number" - }, - "isPremium": { - "type": "boolean" - }, - "iconUrl": { - "type": "string" - } - }, - "required": [ - "id", - "title", - "description", - "canonicalName", - "criteriaMeta", - "rawCriteria", - "start", - "count", - "total", - "quotes", - "useRecords", - "predefinedScr", - "versionId", - "creationDate", - "lastUpdated", - "isPremium", - "iconUrl" - ], - "additionalProperties": false - }, - "DailyGainersCriteriaMeta": { - "type": "object", - "properties": { - "size": { - "yahooFinanceType": "number" - }, - "offset": { - "yahooFinanceType": "number" - }, - "sortField": { - "type": "string" - }, - "sortType": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "criteria": { - "type": "array", - "items": { - "$ref": "#/definitions/DailyGainersCriterum" - } - }, - "topOperator": { - "type": "string" - } - }, - "required": [ - "size", - "offset", - "sortField", - "sortType", - "quoteType", - "criteria", - "topOperator" - ], - "additionalProperties": false - }, - "DailyGainersCriterum": { - "type": "object", - "properties": { - "field": { - "type": "string" - }, - "operators": { - "type": "array", - "items": { - "type": "string" - } - }, - "values": { - "type": "array", - "items": { - "yahooFinanceType": "number" - } - }, - "labelsSelected": { - "type": "array", - "items": { - "yahooFinanceType": "number" - } - }, - "dependentValues": { - "type": "array", - "items": {} - } - }, - "required": [ - "field", - "operators", - "values", - "labelsSelected", - "dependentValues" - ], - "additionalProperties": false - }, - "DailyGainersQuote": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "lastCloseTevEbitLtm": { - "yahooFinanceType": "number" - }, - "lastClosePriceToNNWCPerShare": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "number" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "number" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "number" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "fullExchangeName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "type": "string" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "earningsTimestamp": { - "yahooFinanceType": "number" - }, - "earningsTimestampStart": { - "yahooFinanceType": "number" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "marketState": { - "type": "string" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "esgPopulated": { - "type": "boolean" - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "shortName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "symbol": { - "type": "string" - }, - "dividendDate": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "number" - }, - "dividendYield": { - "yahooFinanceType": "number" - }, - "dividendRate": { - "yahooFinanceType": "number" - } - }, - "required": [ - "language", - "region", - "quoteType", - "typeDisp", - "quoteSourceName", - "triggerable", - "customPriceAlertConfidence", - "firstTradeDateMilliseconds", - "priceHint", - "regularMarketChange", - "regularMarketTime", - "regularMarketPrice", - "regularMarketDayHigh", - "regularMarketDayRange", - "currency", - "regularMarketDayLow", - "regularMarketVolume", - "regularMarketPreviousClose", - "market", - "messageBoardId", - "fullExchangeName", - "longName", - "regularMarketOpen", - "averageDailyVolume3Month", - "averageDailyVolume10Day", - "fiftyTwoWeekLowChange", - "fiftyTwoWeekLowChangePercent", - "fiftyTwoWeekRange", - "fiftyTwoWeekHighChange", - "fiftyTwoWeekHighChangePercent", - "fiftyTwoWeekChangePercent", - "trailingAnnualDividendRate", - "trailingAnnualDividendYield", - "marketState", - "sharesOutstanding", - "fiftyDayAverage", - "fiftyDayAverageChange", - "fiftyDayAverageChangePercent", - "twoHundredDayAverage", - "twoHundredDayAverageChange", - "twoHundredDayAverageChangePercent", - "marketCap", - "sourceInterval", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "gmtOffSetMilliseconds", - "esgPopulated", - "tradeable", - "cryptoTradeable", - "exchange", - "fiftyTwoWeekLow", - "fiftyTwoWeekHigh", - "shortName", - "regularMarketChangePercent", - "symbol" - ], - "additionalProperties": false - }, - "DailyGainersOptions": { - "type": "object", - "properties": { - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "count": { - "yahooFinanceType": "number" - } - }, - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/DailyGainersOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this" - ], - "additionalProperties": false - }, - "FundamentalsTimeSeriesResults": { - "type": "array", - "items": { - "$ref": "#/definitions/FundamentalsTimeSeriesResult" - } - }, - "FundamentalsTimeSeriesResult": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "date" - } - }, - "required": [ - "date" - ], - "additionalProperties": {} - }, - "FundamentalsTimeSeriesOptions": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "yahooFinanceType": "number" - }, - { - "type": "string" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "yahooFinanceType": "number" - }, - { - "type": "string" - } - ] - }, - "type": { - "type": "string" - }, - "merge": { - "type": "boolean" - }, - "padTimeSeries": { - "type": "boolean" - }, - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "module": { - "type": "string" - } - }, - "required": [ - "period1", - "module" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "symbol": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/FundamentalsTimeSeriesOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "symbol", - "queryOptionsOverrides" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "queryOptions": { - "$ref": "#/definitions/FundamentalsTimeSeriesOptions", - "description": "Input query options." - } - }, - "required": [ - "queryOptions" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "response": { - "description": "Query response." - } - }, - "required": [ - "response" - ], - "additionalProperties": false - }, - "HistoricalHistoryResult": { - "type": "array", - "items": { - "$ref": "#/definitions/HistoricalRowHistory" - } - }, - "HistoricalRowHistory": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "date" - }, - "open": { - "yahooFinanceType": "number" - }, - "high": { - "yahooFinanceType": "number" - }, - "low": { - "yahooFinanceType": "number" - }, - "close": { - "yahooFinanceType": "number" - }, - "adjClose": { - "yahooFinanceType": "number" - }, - "volume": { - "yahooFinanceType": "number" - } - }, - "required": [ - "date", - "open", - "high", - "low", - "close", - "volume" - ] - }, - "HistoricalDividendsResult": { - "type": "array", - "items": { - "$ref": "#/definitions/HistoricalRowDividend" - } - }, - "HistoricalRowDividend": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "date" - }, - "dividends": { - "yahooFinanceType": "number" - } - }, - "required": [ - "date", - "dividends" - ], - "additionalProperties": false - }, - "HistoricalStockSplitsResult": { - "type": "array", - "items": { - "$ref": "#/definitions/HistoricalRowStockSplit" - } - }, - "HistoricalRowStockSplit": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "date" - }, - "stockSplits": { - "type": "string" - } - }, - "required": [ - "date", - "stockSplits" - ], - "additionalProperties": false - }, - "HistoricalResult": { - "anyOf": [ - { - "$ref": "#/definitions/HistoricalHistoryResult" - }, - { - "$ref": "#/definitions/HistoricalDividendsResult" - }, - { - "$ref": "#/definitions/HistoricalStockSplitsResult" - } - ] - }, - "HistoricalOptions": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "interval": { - "type": "string", - "enum": [ - "1d", - "1wk", - "1mo" - ] - }, - "events": { - "type": "string" - }, - "includeAdjustedClose": { - "type": "boolean" - } - }, - "required": [ - "period1" - ], - "additionalProperties": false - }, - "HistoricalOptionsEventsHistory": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "interval": { - "type": "string", - "enum": [ - "1d", - "1wk", - "1mo" - ] - }, - "events": { - "type": "string", - "const": "history" - }, - "includeAdjustedClose": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "period1" - ] - }, - "HistoricalOptionsEventsDividends": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "interval": { - "type": "string", - "enum": [ - "1d", - "1wk", - "1mo" - ] - }, - "events": { - "type": "string", - "const": "dividends" - }, - "includeAdjustedClose": { - "type": "boolean" - } - }, - "required": [ - "events", - "period1" - ], - "additionalProperties": false - }, - "HistoricalOptionsEventsSplit": { - "type": "object", - "properties": { - "period1": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "period2": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "type": "string" - }, - { - "yahooFinanceType": "number" - } - ] - }, - "interval": { - "type": "string", - "enum": [ - "1d", - "1wk", - "1mo" - ] - }, - "events": { - "type": "string", - "const": "split" - }, - "includeAdjustedClose": { - "type": "boolean" - } - }, - "required": [ - "events", - "period1" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "symbol": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/HistoricalOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "symbol", - "queryOptionsOverrides" - ], - "additionalProperties": false - }, - "InsightsResult": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "instrumentInfo": { - "$ref": "#/definitions/InsightsInstrumentInfo" - }, - "companySnapshot": { - "$ref": "#/definitions/InsightsCompanySnapshot" - }, - "recommendation": { - "type": "object", - "properties": { - "targetPrice": { - "yahooFinanceType": "number" - }, - "provider": { - "type": "string" - }, - "rating": { - "type": "string", - "enum": [ - "BUY", - "SELL", - "HOLD" - ] - } - }, - "required": [ - "provider", - "rating" - ], - "additionalProperties": false - }, - "events": { - "type": "array", - "items": { - "$ref": "#/definitions/InsightsEvent" - } - }, - "reports": { - "type": "array", - "items": { - "$ref": "#/definitions/InsightsReport" - } - }, - "sigDevs": { - "type": "array", - "items": { - "$ref": "#/definitions/InsightsSigDev" - } - }, - "upsell": { - "$ref": "#/definitions/InsightsUpsell" - }, - "upsellSearchDD": { - "type": "object", - "properties": { - "researchReports": { - "$ref": "#/definitions/InsightsResearchReport" - } - }, - "required": [ - "researchReports" - ], - "additionalProperties": false - }, - "secReports": { - "type": "array", - "items": { - "$ref": "#/definitions/InsightsSecReport" - } - } - }, - "required": [ - "symbol", - "sigDevs" - ] - }, - "InsightsInstrumentInfo": { - "type": "object", - "properties": { - "keyTechnicals": { - "type": "object", - "properties": { - "provider": { - "type": "string" - }, - "support": { - "yahooFinanceType": "number" - }, - "resistance": { - "yahooFinanceType": "number" - }, - "stopLoss": { - "yahooFinanceType": "number" - } - }, - "required": [ - "provider" - ] - }, - "technicalEvents": { - "type": "object", - "properties": { - "provider": { - "type": "string" - }, - "sector": { - "type": "string" - }, - "shortTermOutlook": { - "$ref": "#/definitions/InsightsOutlook" - }, - "intermediateTermOutlook": { - "$ref": "#/definitions/InsightsOutlook" - }, - "longTermOutlook": { - "$ref": "#/definitions/InsightsOutlook" - } - }, - "required": [ - "provider", - "shortTermOutlook", - "intermediateTermOutlook", - "longTermOutlook" - ] - }, - "valuation": { - "type": "object", - "properties": { - "color": { - "yahooFinanceType": "number" - }, - "description": { - "type": "string" - }, - "discount": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "relativeValue": { - "type": "string" - } - }, - "required": [ - "provider" - ] - } - }, - "required": [ - "keyTechnicals", - "technicalEvents", - "valuation" - ] - }, - "InsightsOutlook": { - "type": "object", - "properties": { - "stateDescription": { - "type": "string" - }, - "direction": { - "$ref": "#/definitions/InsightsDirection" - }, - "score": { - "yahooFinanceType": "number" - }, - "scoreDescription": { - "type": "string" - }, - "sectorDirection": { - "$ref": "#/definitions/InsightsDirection" - }, - "sectorScore": { - "yahooFinanceType": "number" - }, - "sectorScoreDescription": { - "type": "string" - }, - "indexDirection": { - "$ref": "#/definitions/InsightsDirection" - }, - "indexScore": { - "yahooFinanceType": "number" - }, - "indexScoreDescription": { - "type": "string" - } - }, - "required": [ - "stateDescription", - "direction", - "score", - "scoreDescription", - "indexDirection", - "indexScore", - "indexScoreDescription" - ] - }, - "InsightsDirection": { - "type": "string", - "enum": [ - "Bearish", - "Bullish", - "Neutral" - ] - }, - "InsightsCompanySnapshot": { - "type": "object", - "properties": { - "sectorInfo": { - "type": "string" - }, - "company": { - "type": "object", - "properties": { - "innovativeness": { - "yahooFinanceType": "number" - }, - "hiring": { - "yahooFinanceType": "number" - }, - "sustainability": { - "yahooFinanceType": "number" - }, - "insiderSentiments": { - "yahooFinanceType": "number" - }, - "earningsReports": { - "yahooFinanceType": "number" - }, - "dividends": { - "yahooFinanceType": "number" - } - } - }, - "sector": { - "type": "object", - "properties": { - "innovativeness": { - "yahooFinanceType": "number" - }, - "hiring": { - "yahooFinanceType": "number" - }, - "sustainability": { - "yahooFinanceType": "number" - }, - "insiderSentiments": { - "yahooFinanceType": "number" - }, - "earningsReports": { - "yahooFinanceType": "number" - }, - "dividends": { - "yahooFinanceType": "number" - } - }, - "required": [ - "innovativeness", - "hiring", - "insiderSentiments", - "dividends" - ] - } - }, - "required": [ - "company", - "sector" - ] - }, - "InsightsEvent": { - "type": "object", - "properties": { - "eventType": { - "type": "string" - }, - "pricePeriod": { - "type": "string" - }, - "tradingHorizon": { - "type": "string" - }, - "tradeType": { - "type": "string" - }, - "imageUrl": { - "type": "string" - }, - "startDate": { - "yahooFinanceType": "date" - }, - "endDate": { - "yahooFinanceType": "date" - } - }, - "required": [ - "eventType", - "pricePeriod", - "tradingHorizon", - "tradeType", - "imageUrl", - "startDate", - "endDate" - ] - }, - "InsightsReport": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "headHtml": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "reportDate": { - "yahooFinanceType": "date" - }, - "reportTitle": { - "type": "string" - }, - "reportType": { - "type": "string" - }, - "targetPrice": { - "yahooFinanceType": "number" - }, - "targetPriceStatus": { - "type": "string", - "enum": [ - "Increased", - "Maintained", - "Decreased", - "-" - ] - }, - "investmentRating": { - "type": "string", - "enum": [ - "Bullish", - "Neutral", - "Bearish" - ] - }, - "tickers": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "id", - "headHtml", - "provider", - "reportDate", - "reportTitle", - "reportType" - ] - }, - "InsightsSigDev": { - "type": "object", - "properties": { - "headline": { - "type": "string" - }, - "date": { - "yahooFinanceType": "date" - } - }, - "required": [ - "headline", - "date" - ] - }, - "InsightsUpsell": { - "type": "object", - "properties": { - "msBullishSummary": { - "type": "array", - "items": { - "type": "string" - } - }, - "msBearishSummary": { - "type": "array", - "items": { - "type": "string" - } - }, - "msBullishBearishSummariesPublishDate": { - "yahooFinanceType": "DateInMs" - }, - "companyName": { - "type": "string" - }, - "upsellReportType": { - "type": "string" - } - } - }, - "DateInMs": { - "yahooFinanceType": "date" - }, - "InsightsResearchReport": { - "type": "object", - "properties": { - "reportId": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "title": { - "type": "string" - }, - "reportDate": { - "yahooFinanceType": "date" - }, - "summary": { - "type": "string" - }, - "investmentRating": { - "type": "string", - "enum": [ - "Bullish", - "Neutral", - "Bearish" - ] - } - }, - "required": [ - "reportId", - "provider", - "title", - "reportDate", - "summary" - ], - "additionalProperties": false - }, - "InsightsSecReport": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "filingDate": { - "yahooFinanceType": "DateInMs" - }, - "snapshotUrl": { - "type": "string" - }, - "formType": { - "type": "string" - } - }, - "required": [ - "id", - "type", - "title", - "description", - "filingDate", - "snapshotUrl", - "formType" - ], - "additionalProperties": false - }, - "InsightsOptions": { - "type": "object", - "properties": { - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "reportsCount": { - "yahooFinanceType": "number" - } - }, - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "query": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/TrendingSymbolsOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "query" - ], - "additionalProperties": false - }, - "TrendingSymbolsOptions": { - "type": "object", - "properties": { - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "count": { - "yahooFinanceType": "number" - } - }, - "additionalProperties": false - }, - "QuoteBase": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - } - }, - "required": [ - "language", - "region", - "quoteType", - "triggerable", - "marketState", - "tradeable", - "exchange", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "gmtOffSetMilliseconds", - "market", - "esgPopulated", - "sourceInterval", - "exchangeDataDelayedBy", - "priceHint", - "fullExchangeName", - "symbol" - ] - }, - "TwoNumberRange": { - "type": "object", - "properties": { - "low": { - "yahooFinanceType": "number" - }, - "high": { - "yahooFinanceType": "number" - } - }, - "required": [ - "low", - "high" - ], - "additionalProperties": false - }, - "QuoteCryptoCurrency": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "CRYPTOCURRENCY" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "circulatingSupply": { - "yahooFinanceType": "number" - }, - "fromCurrency": { - "type": "string" - }, - "toCurrency": { - "type": "string" - }, - "lastMarket": { - "type": "string" - }, - "coinImageUrl": { - "type": "string" - }, - "volume24Hr": { - "yahooFinanceType": "number" - }, - "volumeAllCurrencies": { - "yahooFinanceType": "number" - }, - "startDate": { - "yahooFinanceType": "date" - } - }, - "required": [ - "circulatingSupply", - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "fromCurrency", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "lastMarket", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "toCurrency", - "tradeable", - "triggerable" - ] - }, - "QuoteCurrency": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "CURRENCY" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - } - }, - "required": [ - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "tradeable", - "triggerable" - ] - }, - "QuoteEtf": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "ETF" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - } - }, - "required": [ - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "tradeable", - "triggerable" - ] - }, - "QuoteEquity": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "EQUITY" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "dividendRate": { - "yahooFinanceType": "number" - }, - "dividendYield": { - "yahooFinanceType": "number" - } - }, - "required": [ - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "tradeable", - "triggerable" - ] - }, - "QuoteFuture": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "FUTURE" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "headSymbolAsString": { - "type": "string" - }, - "contractSymbol": { - "type": "boolean" - }, - "underlyingExchangeSymbol": { - "type": "string" - }, - "expireDate": { - "yahooFinanceType": "date" - }, - "expireIsoDate": { - "yahooFinanceType": "number" - } - }, - "required": [ - "contractSymbol", - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "expireDate", - "expireIsoDate", - "fullExchangeName", - "gmtOffSetMilliseconds", - "headSymbolAsString", - "language", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "tradeable", - "triggerable", - "underlyingExchangeSymbol" - ] - }, - "QuoteIndex": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "INDEX" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - } - }, - "required": [ - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "tradeable", - "triggerable" - ] - }, - "QuoteOption": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "OPTION" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "strike": { - "yahooFinanceType": "number" - }, - "expireDate": { - "yahooFinanceType": "number" - }, - "expireIsoDate": { - "yahooFinanceType": "number" - } - }, - "required": [ - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "expireDate", - "expireIsoDate", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "market", - "marketState", - "openInterest", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "strike", - "symbol", - "tradeable", - "triggerable", - "underlyingSymbol" - ] - }, - "QuoteMutualfund": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string", - "const": "MUTUALFUND" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "currency": { - "type": "string" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "marketState": { - "type": "string", - "enum": [ - "REGULAR", - "CLOSED", - "PRE", - "PREPRE", - "POST", - "POSTPOST" - ] - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "shortName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "esgPopulated": { - "type": "boolean" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "dividendDate": { - "yahooFinanceType": "date" - }, - "earningsTimestamp": { - "yahooFinanceType": "date" - }, - "earningsTimestampStart": { - "yahooFinanceType": "date" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "date" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "DateInMs" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "yahooFinanceType": "TwoNumberRange" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "fullExchangeName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthNavReturns": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "date" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "pageViewGrowthWeekly": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - } - }, - "required": [ - "esgPopulated", - "exchange", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "fullExchangeName", - "gmtOffSetMilliseconds", - "language", - "market", - "marketState", - "priceHint", - "quoteType", - "region", - "sourceInterval", - "symbol", - "tradeable", - "triggerable" - ] - }, - "Quote": { - "anyOf": [ - { - "$ref": "#/definitions/QuoteCryptoCurrency" - }, - { - "$ref": "#/definitions/QuoteCurrency" - }, - { - "$ref": "#/definitions/QuoteEtf" - }, - { - "$ref": "#/definitions/QuoteEquity" - }, - { - "$ref": "#/definitions/QuoteFuture" - }, - { - "$ref": "#/definitions/QuoteIndex" - }, - { - "$ref": "#/definitions/QuoteMutualfund" - }, - { - "$ref": "#/definitions/QuoteOption" - } - ] - }, - "QuoteField": { - "type": "string", - "enum": [ - "quoteType", - "circulatingSupply", - "fromCurrency", - "toCurrency", - "lastMarket", - "coinImageUrl", - "volume24Hr", - "volumeAllCurrencies", - "startDate", - "language", - "region", - "typeDisp", - "quoteSourceName", - "triggerable", - "currency", - "customPriceAlertConfidence", - "marketState", - "tradeable", - "cryptoTradeable", - "exchange", - "shortName", - "longName", - "messageBoardId", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "gmtOffSetMilliseconds", - "market", - "esgPopulated", - "fiftyTwoWeekLowChange", - "fiftyTwoWeekLowChangePercent", - "fiftyTwoWeekRange", - "fiftyTwoWeekHighChange", - "fiftyTwoWeekHighChangePercent", - "fiftyTwoWeekLow", - "fiftyTwoWeekHigh", - "fiftyTwoWeekChangePercent", - "dividendDate", - "earningsTimestamp", - "earningsTimestampStart", - "earningsTimestampEnd", - "trailingAnnualDividendRate", - "trailingPE", - "trailingAnnualDividendYield", - "epsTrailingTwelveMonths", - "epsForward", - "epsCurrentYear", - "priceEpsCurrentYear", - "sharesOutstanding", - "bookValue", - "fiftyDayAverage", - "fiftyDayAverageChange", - "fiftyDayAverageChangePercent", - "twoHundredDayAverage", - "twoHundredDayAverageChange", - "twoHundredDayAverageChangePercent", - "marketCap", - "forwardPE", - "priceToBook", - "sourceInterval", - "exchangeDataDelayedBy", - "firstTradeDateMilliseconds", - "priceHint", - "postMarketChangePercent", - "postMarketTime", - "postMarketPrice", - "postMarketChange", - "regularMarketChange", - "regularMarketChangePercent", - "regularMarketTime", - "regularMarketPrice", - "regularMarketDayHigh", - "regularMarketDayRange", - "regularMarketDayLow", - "regularMarketVolume", - "regularMarketPreviousClose", - "preMarketChange", - "preMarketChangePercent", - "preMarketTime", - "preMarketPrice", - "bid", - "ask", - "bidSize", - "askSize", - "fullExchangeName", - "financialCurrency", - "regularMarketOpen", - "averageDailyVolume3Month", - "averageDailyVolume10Day", - "displayName", - "symbol", - "underlyingSymbol", - "ytdReturn", - "trailingThreeMonthReturns", - "trailingThreeMonthNavReturns", - "ipoExpectedDate", - "newListingDate", - "nameChangeDate", - "prevName", - "averageAnalystRating", - "pageViewGrowthWeekly", - "openInterest", - "beta", - "dividendRate", - "dividendYield", - "headSymbolAsString", - "contractSymbol", - "underlyingExchangeSymbol", - "expireDate", - "expireIsoDate", - "strike" - ] - }, - "ResultType": { - "type": "string", - "enum": [ - "array", - "object", - "map" - ] - }, - "QuoteResponseArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Quote" - } - }, - "QuoteResponseMap": { - "type": "object", - "properties": { - "size": { - "yahooFinanceType": "number" - } - }, - "required": [ - "size" - ], - "additionalProperties": false - }, - "QuoteResponseObject": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Quote" - } - }, - "QuoteOptions": { - "type": "object", - "properties": { - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/QuoteField" - } - }, - "return": { - "$ref": "#/definitions/ResultType" - } - }, - "additionalProperties": false - }, - "QuoteOptionsWithReturnArray": { - "type": "object", - "properties": { - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/QuoteField" - } - }, - "return": { - "type": "string", - "const": "array" - } - }, - "additionalProperties": false - }, - "QuoteOptionsWithReturnMap": { - "type": "object", - "properties": { - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/QuoteField" - } - }, - "return": { - "type": "string", - "const": "map" - } - }, - "required": [ - "return" - ], - "additionalProperties": false - }, - "QuoteOptionsWithReturnObject": { - "type": "object", - "properties": { - "fields": { - "type": "array", - "items": { - "$ref": "#/definitions/QuoteField" - } - }, - "return": { - "type": "string", - "const": "object" - } - }, - "required": [ - "return" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "query": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/QuoteOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "query" - ], - "additionalProperties": false - }, - "OptionsResult": { - "type": "object", - "properties": { - "underlyingSymbol": { - "type": "string" - }, - "expirationDates": { - "type": "array", - "items": { - "yahooFinanceType": "date" - } - }, - "strikes": { - "type": "array", - "items": { - "yahooFinanceType": "number" - } - }, - "hasMiniOptions": { - "type": "boolean" - }, - "quote": { - "$ref": "#/definitions/Quote" - }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/Option" - } - } - }, - "required": [ - "underlyingSymbol", - "expirationDates", - "strikes", - "hasMiniOptions", - "quote", - "options" - ] - }, - "Option": { - "type": "object", - "properties": { - "expirationDate": { - "yahooFinanceType": "date" - }, - "hasMiniOptions": { - "type": "boolean" - }, - "calls": { - "type": "array", - "items": { - "$ref": "#/definitions/CallOrPut" - } - }, - "puts": { - "type": "array", - "items": { - "$ref": "#/definitions/CallOrPut" - } - } - }, - "required": [ - "expirationDate", - "hasMiniOptions", - "calls", - "puts" - ] - }, - "CallOrPut": { - "type": "object", - "properties": { - "contractSymbol": { - "type": "string" - }, - "strike": { - "yahooFinanceType": "number" - }, - "currency": { - "type": "string" - }, - "lastPrice": { - "yahooFinanceType": "number" - }, - "change": { - "yahooFinanceType": "number" - }, - "percentChange": { - "yahooFinanceType": "number" - }, - "volume": { - "yahooFinanceType": "number" - }, - "openInterest": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "contractSize": { - "type": "string", - "const": "REGULAR" - }, - "expiration": { - "yahooFinanceType": "date" - }, - "lastTradeDate": { - "yahooFinanceType": "date" - }, - "impliedVolatility": { - "yahooFinanceType": "number" - }, - "inTheMoney": { - "type": "boolean" - } - }, - "required": [ - "contractSymbol", - "strike", - "lastPrice", - "change", - "contractSize", - "expiration", - "lastTradeDate", - "impliedVolatility", - "inTheMoney" - ] - }, - "OptionsOptions": { - "type": "object", - "properties": { - "formatted": { - "type": "boolean" - }, - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "date": { - "anyOf": [ - { - "yahooFinanceType": "date" - }, - { - "yahooFinanceType": "number" - }, - { - "type": "string" - } - ] - } - }, - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "symbol": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/OptionsOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "symbol", - "queryOptionsOverrides" - ], - "additionalProperties": false - }, - "QuoteSummaryResult": { - "type": "object", - "properties": { - "assetProfile": { - "$ref": "#/definitions/AssetProfile" - }, - "balanceSheetHistory": { - "$ref": "#/definitions/BalanceSheetHistory" - }, - "balanceSheetHistoryQuarterly": { - "$ref": "#/definitions/BalanceSheetHistory" - }, - "calendarEvents": { - "$ref": "#/definitions/CalendarEvents" - }, - "cashflowStatementHistory": { - "$ref": "#/definitions/CashflowStatementHistory" - }, - "cashflowStatementHistoryQuarterly": { - "$ref": "#/definitions/CashflowStatementHistory" - }, - "defaultKeyStatistics": { - "$ref": "#/definitions/DefaultKeyStatistics" - }, - "earnings": { - "$ref": "#/definitions/QuoteSummaryEarnings" - }, - "earningsHistory": { - "$ref": "#/definitions/EarningsHistory" - }, - "earningsTrend": { - "$ref": "#/definitions/EarningsTrend" - }, - "financialData": { - "$ref": "#/definitions/FinancialData" - }, - "fundOwnership": { - "$ref": "#/definitions/Ownership" - }, - "fundPerformance": { - "$ref": "#/definitions/FundPerformance" - }, - "fundProfile": { - "$ref": "#/definitions/FundProfile" - }, - "incomeStatementHistory": { - "$ref": "#/definitions/IncomeStatementHistory" - }, - "incomeStatementHistoryQuarterly": { - "$ref": "#/definitions/IncomeStatementHistory" - }, - "indexTrend": { - "$ref": "#/definitions/IndexTrend" - }, - "industryTrend": { - "$ref": "#/definitions/Trend" - }, - "insiderHolders": { - "$ref": "#/definitions/Holders" - }, - "insiderTransactions": { - "$ref": "#/definitions/InsiderTransactions" - }, - "institutionOwnership": { - "$ref": "#/definitions/Ownership" - }, - "majorDirectHolders": { - "$ref": "#/definitions/Holders" - }, - "majorHoldersBreakdown": { - "$ref": "#/definitions/MajorHoldersBreakdown" - }, - "netSharePurchaseActivity": { - "$ref": "#/definitions/NetSharePurchaseActivity" - }, - "price": { - "$ref": "#/definitions/Price" - }, - "quoteType": { - "$ref": "#/definitions/QuoteType" - }, - "recommendationTrend": { - "$ref": "#/definitions/RecommendationTrend" - }, - "secFilings": { - "$ref": "#/definitions/SECFilings" - }, - "sectorTrend": { - "$ref": "#/definitions/Trend" - }, - "summaryDetail": { - "$ref": "#/definitions/SummaryDetail" - }, - "summaryProfile": { - "$ref": "#/definitions/SummaryProfile" - }, - "topHoldings": { - "$ref": "#/definitions/TopHoldings" - }, - "upgradeDowngradeHistory": { - "$ref": "#/definitions/UpgradeDowngradeHistory" - } - } - }, - "AssetProfile": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "address1": { - "type": "string" - }, - "address2": { - "type": "string" - }, - "address3": { - "type": "string" - }, - "city": { - "type": "string" - }, - "state": { - "type": "string" - }, - "zip": { - "type": "string" - }, - "country": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "fax": { - "type": "string" - }, - "website": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "industryDisp": { - "type": "string" - }, - "industryKey": { - "type": "string" - }, - "industrySymbol": { - "type": "string" - }, - "sector": { - "type": "string" - }, - "sectorDisp": { - "type": "string" - }, - "sectorKey": { - "type": "string" - }, - "longBusinessSummary": { - "type": "string" - }, - "fullTimeEmployees": { - "yahooFinanceType": "number" - }, - "companyOfficers": { - "type": "array", - "items": { - "$ref": "#/definitions/CompanyOfficer" - } - }, - "auditRisk": { - "yahooFinanceType": "number" - }, - "boardRisk": { - "yahooFinanceType": "number" - }, - "compensationRisk": { - "yahooFinanceType": "number" - }, - "shareHolderRightsRisk": { - "yahooFinanceType": "number" - }, - "overallRisk": { - "yahooFinanceType": "number" - }, - "governanceEpochDate": { - "yahooFinanceType": "date" - }, - "compensationAsOfEpochDate": { - "yahooFinanceType": "date" - }, - "name": { - "type": "string" - }, - "startDate": { - "yahooFinanceType": "date" - }, - "description": { - "type": "string" - }, - "twitter": { - "type": "string" - } - }, - "required": [ - "maxAge", - "companyOfficers" - ] - }, - "CompanyOfficer": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "name": { - "type": "string" - }, - "age": { - "yahooFinanceType": "number" - }, - "title": { - "type": "string" - }, - "yearBorn": { - "yahooFinanceType": "number" - }, - "fiscalYear": { - "yahooFinanceType": "number" - }, - "totalPay": { - "yahooFinanceType": "number" - }, - "exercisedValue": { - "yahooFinanceType": "number" - }, - "unexercisedValue": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "name", - "title" - ] - }, - "BalanceSheetHistory": { - "type": "object", - "properties": { - "balanceSheetStatements": { - "type": "array", - "items": { - "$ref": "#/definitions/BalanceSheetStatement" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "balanceSheetStatements", - "maxAge" - ] - }, - "BalanceSheetStatement": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "endDate": { - "yahooFinanceType": "date" - }, - "cash": { - "yahooFinanceType": "number" - }, - "shortTermInvestments": { - "yahooFinanceType": "number" - }, - "netReceivables": { - "yahooFinanceType": "number" - }, - "inventory": { - "yahooFinanceType": "number" - }, - "otherCurrentAssets": { - "yahooFinanceType": "number" - }, - "totalCurrentAssets": { - "yahooFinanceType": "number" - }, - "longTermInvestments": { - "yahooFinanceType": "number" - }, - "propertyPlantEquipment": { - "yahooFinanceType": "number" - }, - "otherAssets": { - "yahooFinanceType": "number" - }, - "totalAssets": { - "yahooFinanceType": "number" - }, - "accountsPayable": { - "yahooFinanceType": "number" - }, - "shortLongTermDebt": { - "yahooFinanceType": "number" - }, - "otherCurrentLiab": { - "yahooFinanceType": "number" - }, - "longTermDebt": { - "yahooFinanceType": "number" - }, - "otherLiab": { - "yahooFinanceType": "number" - }, - "totalCurrentLiabilities": { - "yahooFinanceType": "number" - }, - "totalLiab": { - "yahooFinanceType": "number" - }, - "commonStock": { - "yahooFinanceType": "number" - }, - "retainedEarnings": { - "yahooFinanceType": "number" - }, - "treasuryStock": { - "yahooFinanceType": "number" - }, - "otherStockholderEquity": { - "yahooFinanceType": "number" - }, - "totalStockholderEquity": { - "yahooFinanceType": "number" - }, - "netTangibleAssets": { - "yahooFinanceType": "number" - }, - "goodWill": { - "yahooFinanceType": "number" - }, - "intangibleAssets": { - "yahooFinanceType": "number" - }, - "deferredLongTermAssetCharges": { - "yahooFinanceType": "number" - }, - "deferredLongTermLiab": { - "yahooFinanceType": "number" - }, - "minorityInterest": { - "yahooFinanceType": "number|null" - }, - "capitalSurplus": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "endDate" - ] - }, - "CalendarEvents": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "earnings": { - "$ref": "#/definitions/CalendarEventsEarnings" - }, - "exDividendDate": { - "yahooFinanceType": "date" - }, - "dividendDate": { - "yahooFinanceType": "date" - } - }, - "required": [ - "maxAge", - "earnings" - ] - }, - "CalendarEventsEarnings": { - "type": "object", - "properties": { - "earningsDate": { - "type": "array", - "items": { - "yahooFinanceType": "date" - } - }, - "earningsAverage": { - "yahooFinanceType": "number" - }, - "earningsLow": { - "yahooFinanceType": "number" - }, - "earningsHigh": { - "yahooFinanceType": "number" - }, - "revenueAverage": { - "yahooFinanceType": "number" - }, - "revenueLow": { - "yahooFinanceType": "number" - }, - "revenueHigh": { - "yahooFinanceType": "number" - } - }, - "required": [ - "earningsDate" - ] - }, - "CashflowStatementHistory": { - "type": "object", - "properties": { - "cashflowStatements": { - "type": "array", - "items": { - "$ref": "#/definitions/CashflowStatement" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "cashflowStatements", - "maxAge" - ] - }, - "CashflowStatement": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "endDate": { - "yahooFinanceType": "date" - }, - "netIncome": { - "yahooFinanceType": "number" - }, - "depreciation": { - "yahooFinanceType": "number" - }, - "changeToNetincome": { - "yahooFinanceType": "number" - }, - "changeToAccountReceivables": { - "yahooFinanceType": "number" - }, - "changeToLiabilities": { - "yahooFinanceType": "number" - }, - "changeToInventory": { - "yahooFinanceType": "number" - }, - "changeToOperatingActivities": { - "yahooFinanceType": "number" - }, - "totalCashFromOperatingActivities": { - "yahooFinanceType": "number" - }, - "capitalExpenditures": { - "yahooFinanceType": "number" - }, - "investments": { - "yahooFinanceType": "number" - }, - "otherCashflowsFromInvestingActivities": { - "yahooFinanceType": "number" - }, - "totalCashflowsFromInvestingActivities": { - "yahooFinanceType": "number" - }, - "dividendsPaid": { - "yahooFinanceType": "number" - }, - "netBorrowings": { - "yahooFinanceType": "number" - }, - "otherCashflowsFromFinancingActivities": { - "yahooFinanceType": "number" - }, - "totalCashFromFinancingActivities": { - "yahooFinanceType": "number" - }, - "changeInCash": { - "yahooFinanceType": "number" - }, - "repurchaseOfStock": { - "yahooFinanceType": "number" - }, - "issuanceOfStock": { - "yahooFinanceType": "number" - }, - "effectOfExchangeRate": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "endDate", - "netIncome" - ] - }, - "DefaultKeyStatistics": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "enterpriseValue": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "profitMargins": { - "yahooFinanceType": "number" - }, - "floatShares": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "sharesShort": { - "yahooFinanceType": "number" - }, - "sharesShortPriorMonth": { - "yahooFinanceType": "date" - }, - "sharesShortPreviousMonthDate": { - "yahooFinanceType": "date" - }, - "dateShortInterest": { - "yahooFinanceType": "date" - }, - "sharesPercentSharesOut": { - "yahooFinanceType": "number" - }, - "heldPercentInsiders": { - "yahooFinanceType": "number" - }, - "heldPercentInstitutions": { - "yahooFinanceType": "number" - }, - "shortRatio": { - "yahooFinanceType": "number" - }, - "shortPercentOfFloat": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "impliedSharesOutstanding": { - "yahooFinanceType": "number" - }, - "category": { - "type": [ - "null", - "string" - ] - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "fundFamily": { - "type": [ - "null", - "string" - ] - }, - "legalType": { - "type": [ - "null", - "string" - ] - }, - "lastFiscalYearEnd": { - "yahooFinanceType": "date" - }, - "nextFiscalYearEnd": { - "yahooFinanceType": "date" - }, - "mostRecentQuarter": { - "yahooFinanceType": "date" - }, - "earningsQuarterlyGrowth": { - "yahooFinanceType": "number" - }, - "netIncomeToCommon": { - "yahooFinanceType": "number" - }, - "trailingEps": { - "yahooFinanceType": "number" - }, - "forwardEps": { - "yahooFinanceType": "number" - }, - "pegRatio": { - "yahooFinanceType": "number" - }, - "lastSplitFactor": { - "type": [ - "null", - "string" - ] - }, - "lastSplitDate": { - "yahooFinanceType": "number" - }, - "enterpriseToRevenue": { - "yahooFinanceType": "number" - }, - "enterpriseToEbitda": { - "yahooFinanceType": "number" - }, - "52WeekChange": { - "yahooFinanceType": "number" - }, - "SandP52WeekChange": { - "yahooFinanceType": "number" - }, - "lastDividendValue": { - "yahooFinanceType": "number" - }, - "lastDividendDate": { - "yahooFinanceType": "date" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "beta3Year": { - "yahooFinanceType": "number" - }, - "totalAssets": { - "yahooFinanceType": "number" - }, - "yield": { - "yahooFinanceType": "number" - }, - "fundInceptionDate": { - "yahooFinanceType": "date" - }, - "threeYearAverageReturn": { - "yahooFinanceType": "number" - }, - "fiveYearAverageReturn": { - "yahooFinanceType": "number" - }, - "morningStarOverallRating": { - "yahooFinanceType": "number" - }, - "morningStarRiskRating": { - "yahooFinanceType": "number" - }, - "annualReportExpenseRatio": { - "yahooFinanceType": "number" - }, - "lastCapGain": { - "yahooFinanceType": "number" - }, - "annualHoldingsTurnover": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "priceHint", - "category", - "fundFamily", - "legalType", - "lastSplitFactor" - ] - }, - "QuoteSummaryEarnings": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "earningsChart": { - "$ref": "#/definitions/EarningsChart" - }, - "financialsChart": { - "$ref": "#/definitions/FinancialsChart" - }, - "financialCurrency": { - "type": "string" - } - }, - "required": [ - "maxAge", - "earningsChart", - "financialsChart" - ] - }, - "EarningsChart": { - "type": "object", - "properties": { - "quarterly": { - "type": "array", - "items": { - "$ref": "#/definitions/EarningsChartQuarterly" - } - }, - "currentQuarterEstimate": { - "yahooFinanceType": "number" - }, - "currentQuarterEstimateDate": { - "type": "string" - }, - "currentQuarterEstimateYear": { - "yahooFinanceType": "number" - }, - "earningsDate": { - "type": "array", - "items": { - "yahooFinanceType": "date" - } - } - }, - "required": [ - "quarterly", - "earningsDate" - ] - }, - "EarningsChartQuarterly": { - "type": "object", - "properties": { - "date": { - "type": "string" - }, - "actual": { - "yahooFinanceType": "number" - }, - "estimate": { - "yahooFinanceType": "number" - } - }, - "required": [ - "date", - "actual", - "estimate" - ] - }, - "FinancialsChart": { - "type": "object", - "properties": { - "yearly": { - "type": "array", - "items": { - "$ref": "#/definitions/Yearly" - } - }, - "quarterly": { - "type": "array", - "items": { - "$ref": "#/definitions/FinancialsChartQuarterly" - } - } - }, - "required": [ - "yearly", - "quarterly" - ] - }, - "Yearly": { - "type": "object", - "properties": { - "date": { - "yahooFinanceType": "number" - }, - "revenue": { - "yahooFinanceType": "number" - }, - "earnings": { - "yahooFinanceType": "number" - } - }, - "required": [ - "date", - "revenue", - "earnings" - ] - }, - "FinancialsChartQuarterly": { - "type": "object", - "properties": { - "date": { - "type": "string" - }, - "revenue": { - "yahooFinanceType": "number" - }, - "earnings": { - "yahooFinanceType": "number" - } - }, - "required": [ - "date", - "revenue", - "earnings" - ] - }, - "EarningsHistory": { - "type": "object", - "properties": { - "history": { - "type": "array", - "items": { - "$ref": "#/definitions/EarningsHistoryHistory" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "history", - "maxAge" - ] - }, - "EarningsHistoryHistory": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "epsActual": { - "yahooFinanceType": "number|null" - }, - "epsEstimate": { - "yahooFinanceType": "number|null" - }, - "epsDifference": { - "yahooFinanceType": "number|null" - }, - "surprisePercent": { - "yahooFinanceType": "number|null" - }, - "quarter": { - "yahooFinanceType": "date|null" - }, - "period": { - "type": "string" - } - }, - "required": [ - "maxAge", - "epsActual", - "epsEstimate", - "epsDifference", - "surprisePercent", - "quarter", - "period" - ] - }, - "EarningsTrend": { - "type": "object", - "properties": { - "trend": { - "type": "array", - "items": { - "$ref": "#/definitions/EarningsTrendTrend" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "trend", - "maxAge" - ] - }, - "EarningsTrendTrend": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "period": { - "type": "string" - }, - "endDate": { - "yahooFinanceType": "date|null" - }, - "growth": { - "yahooFinanceType": "number|null" - }, - "earningsEstimate": { - "$ref": "#/definitions/EarningsEstimate" - }, - "revenueEstimate": { - "$ref": "#/definitions/RevenueEstimate" - }, - "epsTrend": { - "$ref": "#/definitions/EpsTrend" - }, - "epsRevisions": { - "$ref": "#/definitions/EpsRevisions" - } - }, - "required": [ - "maxAge", - "period", - "endDate", - "growth", - "earningsEstimate", - "revenueEstimate", - "epsTrend", - "epsRevisions" - ] - }, - "EarningsEstimate": { - "type": "object", - "properties": { - "avg": { - "yahooFinanceType": "number|null" - }, - "low": { - "yahooFinanceType": "number|null" - }, - "high": { - "yahooFinanceType": "number|null" - }, - "yearAgoEps": { - "yahooFinanceType": "number|null" - }, - "numberOfAnalysts": { - "yahooFinanceType": "number|null" - }, - "growth": { - "yahooFinanceType": "number|null" - } - }, - "required": [ - "avg", - "low", - "high", - "yearAgoEps", - "numberOfAnalysts", - "growth" - ] - }, - "RevenueEstimate": { - "type": "object", - "properties": { - "avg": { - "yahooFinanceType": "number|null" - }, - "low": { - "yahooFinanceType": "number|null" - }, - "high": { - "yahooFinanceType": "number|null" - }, - "numberOfAnalysts": { - "yahooFinanceType": "number|null" - }, - "yearAgoRevenue": { - "yahooFinanceType": "number|null" - }, - "growth": { - "yahooFinanceType": "number|null" - } - }, - "required": [ - "avg", - "low", - "high", - "numberOfAnalysts", - "yearAgoRevenue", - "growth" - ] - }, - "EpsTrend": { - "type": "object", - "properties": { - "current": { - "yahooFinanceType": "number|null" - }, - "7daysAgo": { - "yahooFinanceType": "number|null" - }, - "30daysAgo": { - "yahooFinanceType": "number|null" - }, - "60daysAgo": { - "yahooFinanceType": "number|null" - }, - "90daysAgo": { - "yahooFinanceType": "number|null" - } - }, - "required": [ - "current", - "7daysAgo", - "30daysAgo", - "60daysAgo", - "90daysAgo" - ] - }, - "EpsRevisions": { - "type": "object", - "properties": { - "upLast7days": { - "yahooFinanceType": "number|null" - }, - "upLast30days": { - "yahooFinanceType": "number|null" - }, - "downLast30days": { - "yahooFinanceType": "number|null" - }, - "downLast90days": { - "yahooFinanceType": "number|null" - } - }, - "required": [ - "upLast7days", - "upLast30days", - "downLast30days", - "downLast90days" - ] - }, - "FinancialData": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "currentPrice": { - "yahooFinanceType": "number" - }, - "targetHighPrice": { - "yahooFinanceType": "number" - }, - "targetLowPrice": { - "yahooFinanceType": "number" - }, - "targetMeanPrice": { - "yahooFinanceType": "number" - }, - "targetMedianPrice": { - "yahooFinanceType": "number" - }, - "recommendationMean": { - "yahooFinanceType": "number" - }, - "recommendationKey": { - "type": "string" - }, - "numberOfAnalystOpinions": { - "yahooFinanceType": "number" - }, - "totalCash": { - "yahooFinanceType": "number" - }, - "totalCashPerShare": { - "yahooFinanceType": "number" - }, - "ebitda": { - "yahooFinanceType": "number" - }, - "totalDebt": { - "yahooFinanceType": "number" - }, - "quickRatio": { - "yahooFinanceType": "number" - }, - "currentRatio": { - "yahooFinanceType": "number" - }, - "totalRevenue": { - "yahooFinanceType": "number" - }, - "debtToEquity": { - "yahooFinanceType": "number" - }, - "revenuePerShare": { - "yahooFinanceType": "number" - }, - "returnOnAssets": { - "yahooFinanceType": "number" - }, - "returnOnEquity": { - "yahooFinanceType": "number" - }, - "grossProfits": { - "yahooFinanceType": "number" - }, - "freeCashflow": { - "yahooFinanceType": "number" - }, - "operatingCashflow": { - "yahooFinanceType": "number" - }, - "earningsGrowth": { - "yahooFinanceType": "number" - }, - "revenueGrowth": { - "yahooFinanceType": "number" - }, - "grossMargins": { - "yahooFinanceType": "number" - }, - "ebitdaMargins": { - "yahooFinanceType": "number" - }, - "operatingMargins": { - "yahooFinanceType": "number" - }, - "profitMargins": { - "yahooFinanceType": "number" - }, - "financialCurrency": { - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "maxAge", - "recommendationKey", - "financialCurrency" - ] - }, - "Ownership": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "ownershipList": { - "type": "array", - "items": { - "$ref": "#/definitions/OwnershipList" - } - } - }, - "required": [ - "maxAge", - "ownershipList" - ] - }, - "OwnershipList": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "reportDate": { - "yahooFinanceType": "date" - }, - "organization": { - "type": "string" - }, - "pctHeld": { - "yahooFinanceType": "number" - }, - "position": { - "yahooFinanceType": "number" - }, - "value": { - "yahooFinanceType": "number" - }, - "pctChange": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "reportDate", - "organization", - "pctHeld", - "position", - "value" - ] - }, - "FundPerformance": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "loadAdjustedReturns": { - "$ref": "#/definitions/PeriodRange" - }, - "rankInCategory": { - "$ref": "#/definitions/PeriodRange" - }, - "performanceOverview": { - "$ref": "#/definitions/FundPerformancePerformanceOverview" - }, - "performanceOverviewCat": { - "$ref": "#/definitions/FundPerformancePerformanceOverviewCat" - }, - "trailingReturns": { - "$ref": "#/definitions/FundPerformanceTrailingReturns" - }, - "trailingReturnsNav": { - "$ref": "#/definitions/FundPerformanceTrailingReturns" - }, - "trailingReturnsCat": { - "$ref": "#/definitions/FundPerformanceTrailingReturns" - }, - "annualTotalReturns": { - "$ref": "#/definitions/FundPerformanceReturns" - }, - "pastQuarterlyReturns": { - "$ref": "#/definitions/FundPerformanceReturns" - }, - "riskOverviewStatistics": { - "$ref": "#/definitions/FundPerformanceRiskOverviewStats" - }, - "riskOverviewStatisticsCat": { - "$ref": "#/definitions/FundPerformanceRiskOverviewStatsCat" - } - }, - "required": [ - "maxAge", - "performanceOverview", - "performanceOverviewCat", - "trailingReturns", - "trailingReturnsNav", - "trailingReturnsCat", - "annualTotalReturns", - "pastQuarterlyReturns", - "riskOverviewStatistics", - "riskOverviewStatisticsCat" - ] - }, - "PeriodRange": { - "type": "object", - "properties": { - "asOfDate": { - "yahooFinanceType": "date" - }, - "ytd": { - "yahooFinanceType": "number" - }, - "oneMonth": { - "yahooFinanceType": "number" - }, - "threeMonth": { - "yahooFinanceType": "number" - }, - "oneYear": { - "yahooFinanceType": "number" - }, - "threeYear": { - "yahooFinanceType": "number" - }, - "fiveYear": { - "yahooFinanceType": "number" - }, - "tenYear": { - "yahooFinanceType": "number" - } - } - }, - "FundPerformancePerformanceOverview": { - "type": "object", - "properties": { - "asOfDate": { - "yahooFinanceType": "date" - }, - "ytdReturnPct": { - "yahooFinanceType": "number" - }, - "oneYearTotalReturn": { - "yahooFinanceType": "number" - }, - "threeYearTotalReturn": { - "yahooFinanceType": "number" - }, - "fiveYrAvgReturnPct": { - "yahooFinanceType": "number" - }, - "morningStarReturnRating": { - "yahooFinanceType": "number" - }, - "numYearsUp": { - "yahooFinanceType": "number" - }, - "numYearsDown": { - "yahooFinanceType": "number" - }, - "bestOneYrTotalReturn": { - "yahooFinanceType": "number" - }, - "worstOneYrTotalReturn": { - "yahooFinanceType": "number" - }, - "bestThreeYrTotalReturn": { - "yahooFinanceType": "number" - }, - "worstThreeYrTotalReturn": { - "yahooFinanceType": "number" - } - } - }, - "FundPerformancePerformanceOverviewCat": { - "type": "object", - "properties": { - "ytdReturnPct": { - "yahooFinanceType": "number" - }, - "fiveYrAvgReturnPct": { - "yahooFinanceType": "number" - } - } - }, - "FundPerformanceTrailingReturns": { - "type": "object", - "properties": { - "asOfDate": { - "yahooFinanceType": "date" - }, - "ytd": { - "yahooFinanceType": "number" - }, - "oneMonth": { - "yahooFinanceType": "number" - }, - "threeMonth": { - "yahooFinanceType": "number" - }, - "oneYear": { - "yahooFinanceType": "number" - }, - "threeYear": { - "yahooFinanceType": "number" - }, - "fiveYear": { - "yahooFinanceType": "number" - }, - "tenYear": { - "yahooFinanceType": "number" - }, - "lastBullMkt": { - "yahooFinanceType": "number" - }, - "lastBearMkt": { - "yahooFinanceType": "number" - } - } - }, - "FundPerformanceReturns": { - "type": "object", - "properties": { - "returns": { - "type": "array", - "items": { - "$ref": "#/definitions/FundPerformanceReturnsRow" - } - }, - "returnsCat": { - "type": "array", - "items": { - "$ref": "#/definitions/FundPerformanceReturnsRow" - } - } - }, - "required": [ - "returns" - ] - }, - "FundPerformanceReturnsRow": { - "type": "object", - "properties": { - "year": { - "yahooFinanceType": "number" - }, - "annualValue": { - "yahooFinanceType": "number" - }, - "q1": { - "yahooFinanceType": "number" - }, - "q2": { - "yahooFinanceType": "number" - }, - "q3": { - "yahooFinanceType": "number" - }, - "q4": { - "yahooFinanceType": "number" - } - }, - "required": [ - "year" - ] - }, - "FundPerformanceRiskOverviewStats": { - "type": "object", - "properties": { - "riskStatistics": { - "type": "array", - "items": { - "$ref": "#/definitions/FundPerformanceRiskOverviewStatsRow" - } - }, - "riskRating": { - "yahooFinanceType": "number" - } - }, - "required": [ - "riskStatistics" - ] - }, - "FundPerformanceRiskOverviewStatsRow": { - "type": "object", - "properties": { - "year": { - "type": "string" - }, - "alpha": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "meanAnnualReturn": { - "yahooFinanceType": "number" - }, - "rSquared": { - "yahooFinanceType": "number" - }, - "stdDev": { - "yahooFinanceType": "number" - }, - "sharpeRatio": { - "yahooFinanceType": "number" - }, - "treynorRatio": { - "yahooFinanceType": "number" - } - }, - "required": [ - "year", - "alpha", - "beta", - "meanAnnualReturn", - "rSquared", - "sharpeRatio", - "treynorRatio" - ] - }, - "FundPerformanceRiskOverviewStatsCat": { - "type": "object", - "properties": { - "riskStatisticsCat": { - "type": "array", - "items": { - "$ref": "#/definitions/FundPerformanceRiskOverviewStatsRow" - } - } - }, - "required": [ - "riskStatisticsCat" - ] - }, - "FundProfile": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "styleBoxUrl": { - "type": [ - "null", - "string" - ] - }, - "family": { - "type": [ - "null", - "string" - ] - }, - "categoryName": { - "type": [ - "null", - "string" - ] - }, - "legalType": { - "type": [ - "null", - "string" - ] - }, - "managementInfo": { - "$ref": "#/definitions/FundProfileManagementInfo" - }, - "feesExpensesInvestment": { - "$ref": "#/definitions/FundProfileFeesExpensesInvestment" - }, - "feesExpensesInvestmentCat": { - "$ref": "#/definitions/FundProfileFeesExpensesInvestmentCat" - }, - "brokerages": { - "type": "array", - "items": { - "$ref": "#/definitions/FundProfileBrokerage" - } - }, - "initInvestment": { - "yahooFinanceType": "number" - }, - "initIraInvestment": { - "yahooFinanceType": "number" - }, - "initAipInvestment": { - "yahooFinanceType": "number" - }, - "subseqInvestment": { - "yahooFinanceType": "number" - }, - "subseqIraInvestment": { - "yahooFinanceType": "number" - }, - "subseqAipInvestment": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "family", - "categoryName", - "legalType" - ] - }, - "FundProfileManagementInfo": { - "type": "object", - "properties": { - "managerName": { - "type": [ - "null", - "string" - ] - }, - "managerBio": { - "type": [ - "null", - "string" - ] - }, - "startdate": { - "yahooFinanceType": "date" - } - }, - "required": [ - "managerName", - "managerBio" - ] - }, - "FundProfileFeesExpensesInvestment": { - "type": "object", - "properties": { - "annualHoldingsTurnover": { - "yahooFinanceType": "number" - }, - "annualReportExpenseRatio": { - "yahooFinanceType": "number" - }, - "grossExpRatio": { - "yahooFinanceType": "number" - }, - "netExpRatio": { - "yahooFinanceType": "number" - }, - "projectionValues": { - "type": "object" - }, - "totalNetAssets": { - "yahooFinanceType": "number" - } - }, - "required": [ - "projectionValues" - ] - }, - "FundProfileFeesExpensesInvestmentCat": { - "type": "object", - "properties": { - "annualHoldingsTurnover": { - "yahooFinanceType": "number" - }, - "annualReportExpenseRatio": { - "yahooFinanceType": "number" - }, - "grossExpRatio": { - "yahooFinanceType": "number" - }, - "netExpRatio": { - "yahooFinanceType": "number" - }, - "totalNetAssets": { - "yahooFinanceType": "number" - }, - "projectionValuesCat": { - "type": "object" - } - }, - "required": [ - "projectionValuesCat" - ] - }, - "FundProfileBrokerage": { - "type": "object", - "additionalProperties": false - }, - "IncomeStatementHistory": { - "type": "object", - "properties": { - "incomeStatementHistory": { - "type": "array", - "items": { - "$ref": "#/definitions/IncomeStatementHistoryElement" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "incomeStatementHistory", - "maxAge" - ] - }, - "IncomeStatementHistoryElement": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "endDate": { - "yahooFinanceType": "date" - }, - "totalRevenue": { - "yahooFinanceType": "number" - }, - "costOfRevenue": { - "yahooFinanceType": "number" - }, - "grossProfit": { - "yahooFinanceType": "number" - }, - "researchDevelopment": { - "yahooFinanceType": "number|null" - }, - "sellingGeneralAdministrative": { - "yahooFinanceType": "number|null" - }, - "nonRecurring": { - "yahooFinanceType": "number|null" - }, - "otherOperatingExpenses": { - "yahooFinanceType": "number|null" - }, - "totalOperatingExpenses": { - "yahooFinanceType": "number" - }, - "operatingIncome": { - "yahooFinanceType": "number|null" - }, - "totalOtherIncomeExpenseNet": { - "yahooFinanceType": "number|null" - }, - "ebit": { - "yahooFinanceType": "number" - }, - "interestExpense": { - "yahooFinanceType": "number|null" - }, - "incomeBeforeTax": { - "yahooFinanceType": "number|null" - }, - "incomeTaxExpense": { - "yahooFinanceType": "number" - }, - "minorityInterest": { - "yahooFinanceType": "number|null" - }, - "netIncomeFromContinuingOps": { - "yahooFinanceType": "number|null" - }, - "discontinuedOperations": { - "yahooFinanceType": "number|null" - }, - "extraordinaryItems": { - "yahooFinanceType": "number|null" - }, - "effectOfAccountingCharges": { - "yahooFinanceType": "number|null" - }, - "otherItems": { - "yahooFinanceType": "number|null" - }, - "netIncome": { - "yahooFinanceType": "number" - }, - "netIncomeApplicableToCommonShares": { - "yahooFinanceType": "number|null" - } - }, - "required": [ - "maxAge", - "endDate", - "totalRevenue", - "costOfRevenue", - "grossProfit", - "researchDevelopment", - "sellingGeneralAdministrative", - "nonRecurring", - "otherOperatingExpenses", - "totalOperatingExpenses", - "operatingIncome", - "totalOtherIncomeExpenseNet", - "ebit", - "interestExpense", - "incomeBeforeTax", - "incomeTaxExpense", - "minorityInterest", - "netIncomeFromContinuingOps", - "discontinuedOperations", - "extraordinaryItems", - "effectOfAccountingCharges", - "otherItems", - "netIncome", - "netIncomeApplicableToCommonShares" - ] - }, - "IndexTrend": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "symbol": { - "type": "string" - }, - "peRatio": { - "yahooFinanceType": "number" - }, - "pegRatio": { - "yahooFinanceType": "number" - }, - "estimates": { - "type": "array", - "items": { - "$ref": "#/definitions/Estimate" - } - } - }, - "required": [ - "maxAge", - "symbol", - "peRatio", - "pegRatio", - "estimates" - ] - }, - "Estimate": { - "type": "object", - "properties": { - "period": { - "type": "string" - }, - "growth": { - "yahooFinanceType": "number" - } - }, - "required": [ - "period" - ] - }, - "Trend": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "symbol": { - "type": "null" - }, - "estimates": { - "type": "array", - "items": {} - } - }, - "required": [ - "maxAge", - "symbol", - "estimates" - ] - }, - "Holders": { - "type": "object", - "properties": { - "holders": { - "type": "array", - "items": { - "$ref": "#/definitions/Holder" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "holders", - "maxAge" - ] - }, - "Holder": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "name": { - "type": "string" - }, - "relation": { - "anyOf": [ - { - "$ref": "#/definitions/Relation" - }, - { - "type": "string" - } - ] - }, - "url": { - "type": "string" - }, - "transactionDescription": { - "type": "string" - }, - "latestTransDate": { - "yahooFinanceType": "date" - }, - "positionDirect": { - "yahooFinanceType": "number" - }, - "positionDirectDate": { - "yahooFinanceType": "date" - }, - "positionIndirect": { - "yahooFinanceType": "number" - }, - "positionIndirectDate": { - "yahooFinanceType": "date" - }, - "positionSummaryDate": { - "yahooFinanceType": "date" - } - }, - "required": [ - "maxAge", - "name", - "relation", - "url", - "transactionDescription", - "latestTransDate" - ] - }, - "Relation": { - "type": "string", - "enum": [ - "Chairman of the Board", - "Chief Executive Officer", - "Chief Financial Officer", - "Chief Operating Officer", - "Chief Technology Officer", - "Director", - "Director (Independent)", - "", - "General Counsel", - "Independent Non-Executive Director", - "Officer", - "President" - ] - }, - "InsiderTransactions": { - "type": "object", - "properties": { - "transactions": { - "type": "array", - "items": { - "$ref": "#/definitions/Transaction" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "transactions", - "maxAge" - ] - }, - "Transaction": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "shares": { - "yahooFinanceType": "number" - }, - "filerUrl": { - "type": "string" - }, - "transactionText": { - "type": "string" - }, - "filerName": { - "type": "string" - }, - "filerRelation": { - "anyOf": [ - { - "$ref": "#/definitions/Relation" - }, - { - "type": "string" - } - ] - }, - "moneyText": { - "type": "string" - }, - "startDate": { - "yahooFinanceType": "date" - }, - "ownership": { - "anyOf": [ - { - "$ref": "#/definitions/OwnershipEnum" - }, - { - "type": "string" - } - ] - }, - "value": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "shares", - "filerUrl", - "transactionText", - "filerName", - "filerRelation", - "moneyText", - "startDate", - "ownership" - ] - }, - "OwnershipEnum": { - "type": "string", - "enum": [ - "D", - "I" - ] - }, - "MajorHoldersBreakdown": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "insidersPercentHeld": { - "yahooFinanceType": "number" - }, - "institutionsPercentHeld": { - "yahooFinanceType": "number" - }, - "institutionsFloatPercentHeld": { - "yahooFinanceType": "number" - }, - "institutionsCount": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge" - ] - }, - "NetSharePurchaseActivity": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "period": { - "type": "string" - }, - "buyInfoCount": { - "yahooFinanceType": "number" - }, - "buyInfoShares": { - "yahooFinanceType": "number" - }, - "buyPercentInsiderShares": { - "yahooFinanceType": "number" - }, - "sellInfoCount": { - "yahooFinanceType": "number" - }, - "sellInfoShares": { - "yahooFinanceType": "number" - }, - "sellPercentInsiderShares": { - "yahooFinanceType": "number" - }, - "netInfoCount": { - "yahooFinanceType": "number" - }, - "netInfoShares": { - "yahooFinanceType": "number" - }, - "netPercentInsiderShares": { - "yahooFinanceType": "number" - }, - "totalInsiderShares": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "period", - "buyInfoCount", - "buyInfoShares", - "sellInfoCount", - "netInfoCount", - "netInfoShares", - "totalInsiderShares" - ] - }, - "Price": { - "type": "object", - "properties": { - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "exchange": { - "type": "string" - }, - "exchangeName": { - "type": "string" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "maxAge": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "date" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketSource": { - "type": "string" - }, - "preMarketChangePercent": { - "yahooFinanceType": "number" - }, - "preMarketChange": { - "yahooFinanceType": "number" - }, - "preMarketTime": { - "yahooFinanceType": "date" - }, - "preMarketPrice": { - "yahooFinanceType": "number" - }, - "preMarketSource": { - "type": "string" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "date" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "regularMarketSource": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "quoteSourceName": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": [ - "null", - "string" - ] - }, - "shortName": { - "type": [ - "null", - "string" - ] - }, - "longName": { - "type": [ - "null", - "string" - ] - }, - "lastMarket": { - "type": [ - "null", - "string" - ] - }, - "marketState": { - "type": "string" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "currency": { - "type": "string" - }, - "currencySymbol": { - "type": "string" - }, - "fromCurrency": { - "type": [ - "string", - "null" - ] - }, - "toCurrency": { - "type": [ - "string", - "null" - ] - }, - "volume24Hr": { - "yahooFinanceType": "number" - }, - "volumeAllCurrencies": { - "yahooFinanceType": "number" - }, - "circulatingSupply": { - "yahooFinanceType": "number" - }, - "expireDate": { - "yahooFinanceType": "date" - }, - "openInterest": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "priceHint", - "quoteType", - "symbol", - "underlyingSymbol", - "shortName", - "longName", - "lastMarket", - "fromCurrency" - ] - }, - "QuoteType": { - "type": "object", - "properties": { - "exchange": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "symbol": { - "type": "string" - }, - "underlyingSymbol": { - "type": "string" - }, - "shortName": { - "type": [ - "null", - "string" - ] - }, - "longName": { - "type": [ - "null", - "string" - ] - }, - "firstTradeDateEpochUtc": { - "yahooFinanceType": "date|null" - }, - "timeZoneFullName": { - "type": "string" - }, - "timeZoneShortName": { - "type": "string" - }, - "uuid": { - "type": "string" - }, - "messageBoardId": { - "type": [ - "null", - "string" - ] - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "exchange", - "quoteType", - "symbol", - "underlyingSymbol", - "shortName", - "longName", - "firstTradeDateEpochUtc", - "timeZoneFullName", - "timeZoneShortName", - "uuid", - "gmtOffSetMilliseconds", - "maxAge" - ] - }, - "RecommendationTrend": { - "type": "object", - "properties": { - "trend": { - "type": "array", - "items": { - "$ref": "#/definitions/RecommendationTrendTrend" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "trend", - "maxAge" - ] - }, - "RecommendationTrendTrend": { - "type": "object", - "properties": { - "period": { - "type": "string" - }, - "strongBuy": { - "yahooFinanceType": "number" - }, - "buy": { - "yahooFinanceType": "number" - }, - "hold": { - "yahooFinanceType": "number" - }, - "sell": { - "yahooFinanceType": "number" - }, - "strongSell": { - "yahooFinanceType": "number" - } - }, - "required": [ - "period", - "strongBuy", - "buy", - "hold", - "sell", - "strongSell" - ] - }, - "SECFilings": { - "type": "object", - "properties": { - "filings": { - "type": "array", - "items": { - "$ref": "#/definitions/Filing" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "filings", - "maxAge" - ] - }, - "Filing": { - "type": "object", - "properties": { - "date": { - "type": "string" - }, - "epochDate": { - "yahooFinanceType": "date" - }, - "type": { - "type": "string", - "enum": [ - "10-K", - "10-Q", - "8-K", - "8-K/A", - "10-K/A", - "10-Q/A", - "SD", - "PX14A6G", - "SC 13G/A", - "DEFA14A", - "25-NSE", - "S-8 POS", - "6-K", - "F-3ASR", - "SC 13D/A", - "20-F", - "425", - "SC14D9C", - "SC 13G", - "S-8", - "DEF 14A", - "F-10" - ] - }, - "title": { - "type": "string" - }, - "edgarUrl": { - "type": "string" - }, - "maxAge": { - "yahooFinanceType": "number" - }, - "url": { - "type": "string" - }, - "exhibits": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "url": { - "type": "string" - }, - "downloadUrl": { - "type": "string" - } - }, - "required": [ - "type", - "url" - ], - "additionalProperties": false - } - } - }, - "required": [ - "date", - "epochDate", - "type", - "title", - "edgarUrl", - "maxAge" - ] - }, - "SummaryDetail": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "previousClose": { - "yahooFinanceType": "number" - }, - "open": { - "yahooFinanceType": "number" - }, - "dayLow": { - "yahooFinanceType": "number" - }, - "dayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "dividendRate": { - "yahooFinanceType": "number" - }, - "dividendYield": { - "yahooFinanceType": "number" - }, - "exDividendDate": { - "yahooFinanceType": "date" - }, - "payoutRatio": { - "yahooFinanceType": "number" - }, - "fiveYearAvgDividendYield": { - "yahooFinanceType": "number" - }, - "beta": { - "yahooFinanceType": "number" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "volume": { - "yahooFinanceType": "number" - }, - "averageVolume": { - "yahooFinanceType": "number" - }, - "averageVolume10days": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "priceToSalesTrailing12Months": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "currency": { - "type": "string" - }, - "algorithm": { - "type": "null" - }, - "tradeable": { - "type": "boolean" - }, - "yield": { - "yahooFinanceType": "number" - }, - "totalAssets": { - "yahooFinanceType": "number" - }, - "navPrice": { - "yahooFinanceType": "number" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "fromCurrency": { - "type": [ - "string", - "null" - ] - }, - "toCurrency": { - "type": [ - "string", - "null" - ] - }, - "lastMarket": { - "type": [ - "string", - "null" - ] - }, - "volume24Hr": { - "yahooFinanceType": "number" - }, - "volumeAllCurrencies": { - "yahooFinanceType": "number" - }, - "circulatingSupply": { - "yahooFinanceType": "number" - }, - "startDate": { - "yahooFinanceType": "date" - }, - "coinMarketCapLink": { - "type": [ - "string", - "null" - ] - }, - "expireDate": { - "yahooFinanceType": "date" - }, - "openInterest": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "priceHint", - "currency", - "algorithm", - "tradeable", - "fromCurrency", - "lastMarket" - ] - }, - "SummaryProfile": { - "type": "object", - "properties": { - "address1": { - "type": "string" - }, - "address2": { - "type": "string" - }, - "address3": { - "type": "string" - }, - "city": { - "type": "string" - }, - "state": { - "type": "string" - }, - "zip": { - "type": "string" - }, - "country": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "fax": { - "type": "string" - }, - "website": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "industryDisp": { - "type": "string" - }, - "sector": { - "type": "string" - }, - "sectorDisp": { - "type": "string" - }, - "longBusinessSummary": { - "type": "string" - }, - "fullTimeEmployees": { - "yahooFinanceType": "number" - }, - "companyOfficers": { - "type": "array", - "items": {} - }, - "maxAge": { - "yahooFinanceType": "number" - }, - "twitter": { - "type": "string" - }, - "name": { - "type": "string" - }, - "startDate": { - "yahooFinanceType": "date" - }, - "description": { - "type": "string" - } - }, - "required": [ - "companyOfficers", - "maxAge" - ] - }, - "TopHoldings": { - "type": "object", - "properties": { - "maxAge": { - "yahooFinanceType": "number" - }, - "stockPosition": { - "yahooFinanceType": "number" - }, - "bondPosition": { - "yahooFinanceType": "number" - }, - "holdings": { - "type": "array", - "items": { - "$ref": "#/definitions/TopHoldingsHolding" - } - }, - "equityHoldings": { - "$ref": "#/definitions/TopHoldingsEquityHoldings" - }, - "bondHoldings": { - "type": "object" - }, - "bondRatings": { - "type": "array", - "items": { - "$ref": "#/definitions/TopHoldingsBondRating" - } - }, - "sectorWeightings": { - "type": "array", - "items": { - "$ref": "#/definitions/TopHoldingsSectorWeighting" - } - }, - "cashPosition": { - "yahooFinanceType": "number" - }, - "otherPosition": { - "yahooFinanceType": "number" - }, - "preferredPosition": { - "yahooFinanceType": "number" - }, - "convertiblePosition": { - "yahooFinanceType": "number" - } - }, - "required": [ - "maxAge", - "holdings", - "equityHoldings", - "bondHoldings", - "bondRatings", - "sectorWeightings" - ] - }, - "TopHoldingsHolding": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "holdingName": { - "type": "string" - }, - "holdingPercent": { - "yahooFinanceType": "number" - } - }, - "required": [ - "symbol", - "holdingName", - "holdingPercent" - ] - }, - "TopHoldingsEquityHoldings": { - "type": "object", - "properties": { - "medianMarketCap": { - "yahooFinanceType": "number" - }, - "medianMarketCapCat": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "priceToBookCat": { - "yahooFinanceType": "number" - }, - "priceToCashflow": { - "yahooFinanceType": "number" - }, - "priceToCashflowCat": { - "yahooFinanceType": "number" - }, - "priceToEarnings": { - "yahooFinanceType": "number" - }, - "priceToEarningsCat": { - "yahooFinanceType": "number" - }, - "priceToSales": { - "yahooFinanceType": "number" - }, - "priceToSalesCat": { - "yahooFinanceType": "number" - }, - "threeYearEarningsGrowth": { - "yahooFinanceType": "number" - }, - "threeYearEarningsGrowthCat": { - "yahooFinanceType": "number" - } - }, - "required": [ - "priceToBook", - "priceToCashflow", - "priceToEarnings", - "priceToSales" - ] - }, - "TopHoldingsBondRating": { - "type": "object", - "properties": { - "a": { - "yahooFinanceType": "number" - }, - "aa": { - "yahooFinanceType": "number" - }, - "aaa": { - "yahooFinanceType": "number" - }, - "other": { - "yahooFinanceType": "number" - }, - "b": { - "yahooFinanceType": "number" - }, - "bb": { - "yahooFinanceType": "number" - }, - "bbb": { - "yahooFinanceType": "number" - }, - "below_b": { - "yahooFinanceType": "number" - }, - "us_government": { - "yahooFinanceType": "number" - } - } - }, - "TopHoldingsSectorWeighting": { - "type": "object", - "properties": { - "realestate": { - "yahooFinanceType": "number" - }, - "consumer_cyclical": { - "yahooFinanceType": "number" - }, - "basic_materials": { - "yahooFinanceType": "number" - }, - "consumer_defensive": { - "yahooFinanceType": "number" - }, - "technology": { - "yahooFinanceType": "number" - }, - "communication_services": { - "yahooFinanceType": "number" - }, - "financial_services": { - "yahooFinanceType": "number" - }, - "utilities": { - "yahooFinanceType": "number" - }, - "industrials": { - "yahooFinanceType": "number" - }, - "energy": { - "yahooFinanceType": "number" - }, - "healthcare": { - "yahooFinanceType": "number" - } - } - }, - "UpgradeDowngradeHistory": { - "type": "object", - "properties": { - "history": { - "type": "array", - "items": { - "$ref": "#/definitions/UpgradeDowngradeHistoryHistory" - } - }, - "maxAge": { - "yahooFinanceType": "number" - } - }, - "required": [ - "history", - "maxAge" - ] - }, - "UpgradeDowngradeHistoryHistory": { - "type": "object", - "properties": { - "epochGradeDate": { - "yahooFinanceType": "date" - }, - "firm": { - "type": "string" - }, - "toGrade": { - "$ref": "#/definitions/Grade" - }, - "fromGrade": { - "$ref": "#/definitions/Grade" - }, - "action": { - "$ref": "#/definitions/Action" - } - }, - "required": [ - "epochGradeDate", - "firm", - "toGrade", - "action" - ] - }, - "Grade": { - "type": "string", - "enum": [ - "Accumulate", - "Add", - "Average", - "Below Average", - "Buy", - "Conviction Buy", - "", - "Equal-Weight", - "Fair Value", - "Equal-weight", - "Long-term Buy", - "Hold", - "Long-Term Buy", - "Market Outperform", - "Market Perform", - "Mixed", - "Negative", - "Neutral", - "In-Line", - "Outperform", - "Overweight", - "Peer Perform", - "Perform", - "Positive", - "Reduce", - "Sector Outperform", - "Sector Perform", - "Sector Weight", - "Sell", - "Strong Buy", - "Top Pick", - "Underperform", - "Underperformer", - "Underweight", - "Trim", - "Above Average", - "In-line", - "Outperformer", - "OVerweight", - "Cautious", - "Market Weight", - "Sector Underperform", - "Market Underperform", - "Peer perform", - "Gradually Accumulate", - "Action List Buy", - "Performer", - "Sector Performer", - "Speculative Buy", - "Strong Sell", - "Speculative Hold", - "Not Rated", - "Hold Neutral", - "Developing", - "buy", - "HOld", - "Trading Sell", - "Tender", - "market perform", - "BUy" - ] - }, - "Action": { - "type": "string", - "enum": [ - "down", - "init", - "main", - "reit", - "up" - ] - }, - "QuoteSummaryModules": { - "type": "string", - "enum": [ - "assetProfile", - "balanceSheetHistory", - "balanceSheetHistoryQuarterly", - "calendarEvents", - "cashflowStatementHistory", - "cashflowStatementHistoryQuarterly", - "defaultKeyStatistics", - "earnings", - "earningsHistory", - "earningsTrend", - "financialData", - "fundOwnership", - "fundPerformance", - "fundProfile", - "incomeStatementHistory", - "incomeStatementHistoryQuarterly", - "indexTrend", - "industryTrend", - "insiderHolders", - "insiderTransactions", - "institutionOwnership", - "majorDirectHolders", - "majorHoldersBreakdown", - "netSharePurchaseActivity", - "price", - "quoteType", - "recommendationTrend", - "secFilings", - "sectorTrend", - "summaryDetail", - "summaryProfile", - "topHoldings", - "upgradeDowngradeHistory" - ] - }, - "QuoteSummaryOptions": { - "type": "object", - "properties": { - "formatted": { - "type": "boolean" - }, - "modules": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/QuoteSummaryModules" - } - }, - { - "type": "string", - "const": "all" - } - ] - } - }, - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "symbol": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/QuoteSummaryOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "symbol" - ], - "additionalProperties": false - }, - "RecommendationsBySymbolResponse": { - "type": "object", - "properties": { - "recommendedSymbols": { - "type": "array", - "items": { - "type": "object", - "properties": { - "score": { - "yahooFinanceType": "number" - }, - "symbol": { - "type": "string" - } - }, - "required": [ - "score", - "symbol" - ] - } - }, - "symbol": { - "type": "string" - } - }, - "required": [ - "recommendedSymbols", - "symbol" - ] - }, - "RecommendationsBySymbolResponseArray": { - "type": "array", - "items": { - "$ref": "#/definitions/RecommendationsBySymbolResponse" - } - }, - "RecommendationsBySymbolOptions": { - "type": "object", - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "query": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/RecommendationsBySymbolOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "query" - ], - "additionalProperties": false - }, - "ScreenerResult": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "canonicalName": { - "type": "string" - }, - "criteriaMeta": { - "$ref": "#/definitions/ScreenerCriteriaMeta" - }, - "rawCriteria": { - "type": "string" - }, - "start": { - "yahooFinanceType": "number" - }, - "count": { - "yahooFinanceType": "number" - }, - "total": { - "yahooFinanceType": "number" - }, - "quotes": { - "type": "array", - "items": { - "$ref": "#/definitions/ScreenerQuote" - } - }, - "useRecords": { - "type": "boolean" - }, - "predefinedScr": { - "type": "boolean" - }, - "versionId": { - "yahooFinanceType": "number" - }, - "creationDate": { - "yahooFinanceType": "number" - }, - "lastUpdated": { - "yahooFinanceType": "number" - }, - "isPremium": { - "type": "boolean" - }, - "iconUrl": { - "type": "string" - } - }, - "required": [ - "id", - "title", - "description", - "canonicalName", - "criteriaMeta", - "rawCriteria", - "start", - "count", - "total", - "quotes", - "useRecords", - "predefinedScr", - "versionId", - "creationDate", - "lastUpdated", - "isPremium", - "iconUrl" - ], - "additionalProperties": false - }, - "ScreenerCriteriaMeta": { - "type": "object", - "properties": { - "size": { - "yahooFinanceType": "number" - }, - "offset": { - "yahooFinanceType": "number" - }, - "sortField": { - "type": "string" - }, - "sortType": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "criteria": { - "type": "array", - "items": { - "$ref": "#/definitions/ScreenerCriterum" - } - }, - "topOperator": { - "type": "string" - } - }, - "required": [ - "size", - "offset", - "sortField", - "sortType", - "quoteType", - "criteria", - "topOperator" - ], - "additionalProperties": false - }, - "ScreenerCriterum": { - "type": "object", - "properties": { - "field": { - "type": "string" - }, - "operators": { - "type": "array", - "items": { - "type": "string" - } - }, - "values": { - "type": "array", - "items": { - "yahooFinanceType": "number" - } - }, - "labelsSelected": { - "type": "array", - "items": { - "yahooFinanceType": "number" - } - }, - "dependentValues": { - "type": "array", - "items": {} - } - }, - "required": [ - "field", - "operators", - "values", - "labelsSelected", - "dependentValues" - ], - "additionalProperties": false - }, - "ScreenerQuote": { - "type": "object", - "properties": { - "language": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quoteType": { - "type": "string" - }, - "typeDisp": { - "type": "string" - }, - "quoteSourceName": { - "type": "string" - }, - "triggerable": { - "type": "boolean" - }, - "customPriceAlertConfidence": { - "type": "string" - }, - "lastCloseTevEbitLtm": { - "yahooFinanceType": "number" - }, - "lastClosePriceToNNWCPerShare": { - "yahooFinanceType": "number" - }, - "firstTradeDateMilliseconds": { - "yahooFinanceType": "number" - }, - "priceHint": { - "yahooFinanceType": "number" - }, - "postMarketChangePercent": { - "yahooFinanceType": "number" - }, - "postMarketTime": { - "yahooFinanceType": "number" - }, - "postMarketPrice": { - "yahooFinanceType": "number" - }, - "postMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketChange": { - "yahooFinanceType": "number" - }, - "regularMarketTime": { - "yahooFinanceType": "number" - }, - "regularMarketPrice": { - "yahooFinanceType": "number" - }, - "regularMarketDayHigh": { - "yahooFinanceType": "number" - }, - "regularMarketDayRange": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "regularMarketDayLow": { - "yahooFinanceType": "number" - }, - "regularMarketVolume": { - "yahooFinanceType": "number" - }, - "regularMarketPreviousClose": { - "yahooFinanceType": "number" - }, - "bid": { - "yahooFinanceType": "number" - }, - "ask": { - "yahooFinanceType": "number" - }, - "bidSize": { - "yahooFinanceType": "number" - }, - "askSize": { - "yahooFinanceType": "number" - }, - "market": { - "type": "string" - }, - "messageBoardId": { - "type": "string" - }, - "fullExchangeName": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "financialCurrency": { - "type": "string" - }, - "regularMarketOpen": { - "yahooFinanceType": "number" - }, - "averageDailyVolume3Month": { - "yahooFinanceType": "number" - }, - "averageDailyVolume10Day": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekLowChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekRange": { - "type": "string" - }, - "fiftyTwoWeekHighChange": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHighChangePercent": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekChangePercent": { - "yahooFinanceType": "number" - }, - "earningsTimestamp": { - "yahooFinanceType": "number" - }, - "earningsTimestampStart": { - "yahooFinanceType": "number" - }, - "earningsTimestampEnd": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendRate": { - "yahooFinanceType": "number" - }, - "trailingAnnualDividendYield": { - "yahooFinanceType": "number" - }, - "marketState": { - "type": "string" - }, - "epsTrailingTwelveMonths": { - "yahooFinanceType": "number" - }, - "epsForward": { - "yahooFinanceType": "number" - }, - "epsCurrentYear": { - "yahooFinanceType": "number" - }, - "priceEpsCurrentYear": { - "yahooFinanceType": "number" - }, - "sharesOutstanding": { - "yahooFinanceType": "number" - }, - "bookValue": { - "yahooFinanceType": "number" - }, - "fiftyDayAverage": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChange": { - "yahooFinanceType": "number" - }, - "fiftyDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverage": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChange": { - "yahooFinanceType": "number" - }, - "twoHundredDayAverageChangePercent": { - "yahooFinanceType": "number" - }, - "marketCap": { - "yahooFinanceType": "number" - }, - "forwardPE": { - "yahooFinanceType": "number" - }, - "priceToBook": { - "yahooFinanceType": "number" - }, - "sourceInterval": { - "yahooFinanceType": "number" - }, - "exchangeDataDelayedBy": { - "yahooFinanceType": "number" - }, - "exchangeTimezoneName": { - "type": "string" - }, - "exchangeTimezoneShortName": { - "type": "string" - }, - "gmtOffSetMilliseconds": { - "yahooFinanceType": "number" - }, - "esgPopulated": { - "type": "boolean" - }, - "tradeable": { - "type": "boolean" - }, - "cryptoTradeable": { - "type": "boolean" - }, - "exchange": { - "type": "string" - }, - "fiftyTwoWeekLow": { - "yahooFinanceType": "number" - }, - "fiftyTwoWeekHigh": { - "yahooFinanceType": "number" - }, - "shortName": { - "type": "string" - }, - "averageAnalystRating": { - "type": "string" - }, - "regularMarketChangePercent": { - "yahooFinanceType": "number" - }, - "symbol": { - "type": "string" - }, - "dividendDate": { - "yahooFinanceType": "number" - }, - "displayName": { - "type": "string" - }, - "trailingPE": { - "yahooFinanceType": "number" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "number" - }, - "ipoExpectedDate": { - "yahooFinanceType": "number" - }, - "dividendYield": { - "yahooFinanceType": "number" - }, - "dividendRate": { - "yahooFinanceType": "number" - }, - "yieldTTM": { - "yahooFinanceType": "number" - }, - "peTTM": { - "yahooFinanceType": "number" - }, - "annualReturnNavY3": { - "yahooFinanceType": "number" - }, - "annualReturnNavY5": { - "yahooFinanceType": "number" - }, - "ytdReturn": { - "yahooFinanceType": "number" - }, - "trailingThreeMonthReturns": { - "yahooFinanceType": "number" - }, - "netAssets": { - "yahooFinanceType": "number" - }, - "netExpenseRatio": { - "yahooFinanceType": "number" - } - }, - "required": [ - "language", - "region", - "quoteType", - "typeDisp", - "quoteSourceName", - "triggerable", - "customPriceAlertConfidence", - "firstTradeDateMilliseconds", - "priceHint", - "regularMarketChange", - "regularMarketTime", - "regularMarketPrice", - "currency", - "regularMarketPreviousClose", - "market", - "messageBoardId", - "fullExchangeName", - "longName", - "averageDailyVolume3Month", - "averageDailyVolume10Day", - "fiftyTwoWeekLowChange", - "fiftyTwoWeekLowChangePercent", - "fiftyTwoWeekRange", - "fiftyTwoWeekHighChange", - "fiftyTwoWeekHighChangePercent", - "fiftyTwoWeekChangePercent", - "marketState", - "fiftyDayAverage", - "fiftyDayAverageChange", - "fiftyDayAverageChangePercent", - "twoHundredDayAverage", - "twoHundredDayAverageChange", - "twoHundredDayAverageChangePercent", - "sourceInterval", - "exchangeDataDelayedBy", - "exchangeTimezoneName", - "exchangeTimezoneShortName", - "gmtOffSetMilliseconds", - "esgPopulated", - "tradeable", - "cryptoTradeable", - "exchange", - "fiftyTwoWeekLow", - "fiftyTwoWeekHigh", - "shortName", - "regularMarketChangePercent", - "symbol" - ], - "additionalProperties": false - }, - "PredefinedScreenerModules": { - "type": "string", - "enum": [ - "aggressive_small_caps", - "conservative_foreign_funds", - "day_gainers", - "day_losers", - "growth_technology_stocks", - "high_yield_bond", - "most_actives", - "most_shorted_stocks", - "portfolio_anchors", - "small_cap_gainers", - "solid_large_growth_funds", - "solid_midcap_growth_funds", - "top_mutual_funds", - "undervalued_growth_stocks", - "undervalued_large_caps" - ] - }, - "ScreenerOptions": { - "type": "object", - "properties": { - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "scrIds": { - "$ref": "#/definitions/PredefinedScreenerModules" - }, - "count": { - "yahooFinanceType": "number" - } - }, - "required": [ - "scrIds" - ], - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/ScreenerOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this" - ], - "additionalProperties": false - }, - "SearchQuoteYahoo": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - } - }, - "required": [ - "symbol", - "isYahooFinance", - "exchange", - "index", - "score" - ] - }, - "SearchQuoteYahooEquity": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "EQUITY" - }, - "typeDisp": { - "type": "string", - "const": "Equity" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooOption": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "OPTION" - }, - "typeDisp": { - "type": "string", - "const": "Option" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooETF": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "ETF" - }, - "typeDisp": { - "type": "string", - "const": "ETF" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooFund": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "MUTUALFUND" - }, - "typeDisp": { - "type": "string", - "const": "Fund" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooIndex": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "INDEX" - }, - "typeDisp": { - "type": "string", - "const": "Index" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooCurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "CURRENCY" - }, - "typeDisp": { - "type": "string", - "const": "Currency" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooCryptocurrency": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "CRYPTOCURRENCY" - }, - "typeDisp": { - "type": "string", - "const": "Cryptocurrency" - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteYahooFuture": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": true - }, - "exchange": { - "type": "string" - }, - "exchDisp": { - "type": "string" - }, - "shortname": { - "type": "string" - }, - "longname": { - "type": "string" - }, - "index": { - "type": "string", - "const": "quotes" - }, - "score": { - "yahooFinanceType": "number" - }, - "newListingDate": { - "yahooFinanceType": "date" - }, - "prevName": { - "type": "string" - }, - "nameChangeDate": { - "yahooFinanceType": "date" - }, - "sector": { - "type": "string" - }, - "industry": { - "type": "string" - }, - "dispSecIndFlag": { - "type": "boolean" - }, - "quoteType": { - "type": "string", - "const": "FUTURE" - }, - "typeDisp": { - "type": "string", - "enum": [ - "Future", - "Futures" - ] - } - }, - "required": [ - "exchange", - "index", - "isYahooFinance", - "quoteType", - "score", - "symbol", - "typeDisp" - ] - }, - "SearchQuoteNonYahoo": { - "type": "object", - "properties": { - "index": { - "type": "string" - }, - "name": { - "type": "string" - }, - "permalink": { - "type": "string" - }, - "isYahooFinance": { - "type": "boolean", - "const": false - } - }, - "required": [ - "index", - "name", - "permalink", - "isYahooFinance" - ] - }, - "SearchNews": { - "type": "object", - "properties": { - "uuid": { - "type": "string" - }, - "title": { - "type": "string" - }, - "publisher": { - "type": "string" - }, - "link": { - "type": "string" - }, - "providerPublishTime": { - "yahooFinanceType": "date" - }, - "type": { - "type": "string" - }, - "thumbnail": { - "type": "object", - "properties": { - "resolutions": { - "type": "array", - "items": { - "$ref": "#/definitions/SearchNewsThumbnailResolution" - } - } - }, - "required": [ - "resolutions" - ], - "additionalProperties": false - }, - "relatedTickers": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "uuid", - "title", - "publisher", - "link", - "providerPublishTime", - "type" - ] - }, - "SearchNewsThumbnailResolution": { - "type": "object", - "properties": { - "url": { - "type": "string" - }, - "width": { - "yahooFinanceType": "number" - }, - "height": { - "yahooFinanceType": "number" - }, - "tag": { - "type": "string" - } - }, - "required": [ - "url", - "width", - "height", - "tag" - ], - "additionalProperties": false - }, - "SearchResult": { - "type": "object", - "properties": { - "explains": { - "type": "array", - "items": {} - }, - "count": { - "yahooFinanceType": "number" - }, - "quotes": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/definitions/SearchQuoteYahooEquity" - }, - { - "$ref": "#/definitions/SearchQuoteYahooOption" - }, - { - "$ref": "#/definitions/SearchQuoteYahooETF" - }, - { - "$ref": "#/definitions/SearchQuoteYahooFund" - }, - { - "$ref": "#/definitions/SearchQuoteYahooIndex" - }, - { - "$ref": "#/definitions/SearchQuoteYahooCurrency" - }, - { - "$ref": "#/definitions/SearchQuoteYahooCryptocurrency" - }, - { - "$ref": "#/definitions/SearchQuoteNonYahoo" - }, - { - "$ref": "#/definitions/SearchQuoteYahooFuture" - } - ] - } - }, - "news": { - "type": "array", - "items": { - "$ref": "#/definitions/SearchNews" - } - }, - "nav": { - "type": "array", - "items": {} - }, - "lists": { - "type": "array", - "items": {} - }, - "researchReports": { - "type": "array", - "items": {} - }, - "totalTime": { - "yahooFinanceType": "number" - }, - "screenerFieldResults": { - "type": "array", - "items": {} - }, - "culturalAssets": { - "type": "array", - "items": {} - }, - "timeTakenForQuotes": { - "yahooFinanceType": "number" - }, - "timeTakenForNews": { - "yahooFinanceType": "number" - }, - "timeTakenForAlgowatchlist": { - "yahooFinanceType": "number" - }, - "timeTakenForPredefinedScreener": { - "yahooFinanceType": "number" - }, - "timeTakenForCrunchbase": { - "yahooFinanceType": "number" - }, - "timeTakenForNav": { - "yahooFinanceType": "number" - }, - "timeTakenForResearchReports": { - "yahooFinanceType": "number" - }, - "timeTakenForScreenerField": { - "yahooFinanceType": "number" - }, - "timeTakenForCulturalAssets": { - "yahooFinanceType": "number" - } - }, - "required": [ - "explains", - "count", - "quotes", - "news", - "nav", - "lists", - "researchReports", - "totalTime", - "timeTakenForQuotes", - "timeTakenForNews", - "timeTakenForAlgowatchlist", - "timeTakenForPredefinedScreener", - "timeTakenForCrunchbase", - "timeTakenForNav", - "timeTakenForResearchReports" - ] - }, - "SearchOptions": { - "type": "object", - "properties": { - "lang": { - "type": "string" - }, - "region": { - "type": "string" - }, - "quotesCount": { - "yahooFinanceType": "number" - }, - "newsCount": { - "yahooFinanceType": "number" - }, - "enableFuzzyQuery": { - "type": "boolean" - }, - "quotesQueryId": { - "type": "string" - }, - "multiQuoteQueryId": { - "type": "string" - }, - "newsQueryId": { - "type": "string" - }, - "enableCb": { - "type": "boolean" - }, - "enableNavLinks": { - "type": "boolean" - }, - "enableEnhancedTrivialQuery": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "NamedParameters": { - "type": "object", - "properties": { - "this": { - "$ref": "#/definitions/ModuleThis" - }, - "query": { - "type": "string" - }, - "queryOptionsOverrides": { - "$ref": "#/definitions/SearchOptions" - }, - "moduleOptions": { - "$ref": "#/definitions/ModuleOptions" - } - }, - "required": [ - "this", - "query" - ], - "additionalProperties": false - }, - "TrendingSymbol": { - "type": "object", - "properties": { - "symbol": { - "type": "string" - } - }, - "required": [ - "symbol" - ] - }, - "TrendingSymbolsResult": { - "type": "object", - "properties": { - "count": { - "yahooFinanceType": "number" - }, - "quotes": { - "type": "array", - "items": { - "$ref": "#/definitions/TrendingSymbol" - } - }, - "jobTimestamp": { - "yahooFinanceType": "number" - }, - "startInterval": { - "yahooFinanceType": "number" - } - }, - "required": [ - "count", - "quotes", - "jobTimestamp", - "startInterval" - ] - } - } -} \ No newline at end of file diff --git a/scripts/json-transform.sh b/scripts/json-transform.sh deleted file mode 100755 index 2d3f3e50..00000000 --- a/scripts/json-transform.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -echo "json-transform.sh" -echo "=================" -echo - -echo "ESM" -echo "---" - -cd dist/esm - -for FILE in $(find -name \*.json) ; do - echo "Converting \"$FILE\" to ESM module \"$FILE.js\""; - echo -n "export default " > $FILE.js - cat $FILE >> $FILE.js -done - -for FILE in $(find -name \*js) ; do - # Lines that start with 'import' and end with '.json";'. - if grep -qE '^(import .*)\.json";$' $FILE; then - echo - echo "Modifying imports in \"$FILE\" (backup saved to .orig)" - grep -E '^(import .*)\.json";$' $FILE - sed -Ei.orig 's/^(import .*)\.json";$/\1.json.js";/' $FILE - fi -done - -echo -echo "CJS" -echo "---" - -cd ../cjs - -for FILE in $(find -name \*.json) ; do - echo "Converting \"$FILE\" to ESM module \"$FILE.js\""; - echo -n "module.exports = " > $FILE.js - cat $FILE >> $FILE.js -done - -for FILE in $(find -name \*js) ; do - # Lines that start with 'import' and end with '.json";'. - if grep -qE '^(const .*)\.json"\)\);$' $FILE; then - echo - echo "Modifying imports in \"$FILE\" (backup saved to .orig)" - grep -E '^(const .*)\.json"\)\);$' $FILE - sed -Ei.orig 's/^(const .*)\.json"\)\);$/\1.json.js"));/' $FILE - fi -done - -echo -echo "json-transform.sh finished." diff --git a/scripts/schema-check.sh b/scripts/schema-check.sh deleted file mode 100755 index ac7750e2..00000000 --- a/scripts/schema-check.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -cp schema.json schema.json.bak -yarn schema - -MD5SUM_OLD=$(md5sum schema.json.bak | awk '{print $1}') -MD5SUM_NEW=$(md5sum schema.json | awk '{print $1}') - -if [ "$MD5SUM_OLD" = "$MD5SUM_NEW" ] ; then - echo "[OK] schema.json is up to date - good to go!"; -else - echo "[FAIL] Please commit updated schema.json."; - exit 1; -fi diff --git a/scripts/schema.ts b/scripts/schema.ts deleted file mode 100644 index 8b2d5bd9..00000000 --- a/scripts/schema.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { writeFile } from "fs"; - -import { - createProgram, - createParser, - SchemaGenerator, - createFormatter, - Config, -} from "ts-json-schema-generator"; - -// @ts-expect-error: no types -import schemaWalker from "oas-schema-walker"; -import walkerCallback from "./schema/postWalker.js"; - -import yfNumberTypeFormatter from "./schema/TypeFormatter/yfNumberTypeFormatter.js"; -import yfReferenceTypeFormatter from "./schema/TypeFormatter/yfReferenceTypeFormatter.js"; -import yfFunctionIgnorer from "./schema/TypeFormatter/yfFunctionIgnorer.js"; - -//const OUTPUT_PATH = "schema.json"; -const OUTPUT_PATH = process.stdout; - -const config: Config = { - path: "src/{modules/**/!(*spec.ts),lib/options.ts}", - tsconfig: "tsconfig.json", - type: "*", -}; - -const formatter = createFormatter( - config, - (chainTypeFormatter, circularReferenceTypeFormatter) => { - chainTypeFormatter - .addTypeFormatter( - new yfReferenceTypeFormatter( - circularReferenceTypeFormatter, - config.encodeRefs ?? true - ) - ) - .addTypeFormatter(new yfNumberTypeFormatter()) - .addTypeFormatter(new yfFunctionIgnorer()); - } -); - -const program = createProgram(config); -const parser = createParser(program, config); -const generator = new SchemaGenerator(program, parser, formatter, config); -const _schema = generator.createSchema(config.type); - -const schema = { - $schema: _schema.$schema, - $comment: - "DO NOT EDIT THIS FILE. It is generated automatically " + - "from typescript interfaces in the project. To update, run " + - "`yarn schema`.", - ..._schema, -}; - -// @ts-expect-error: no types -for (const key of Object.keys(schema.definitions)) { - // @ts-expect-error: no types - schemaWalker.walkSchema(schema.definitions[key], {}, {}, walkerCallback); -} - -const schemaString = JSON.stringify(schema, null, 2); - -function throwErr(err?: Error | null): void { - if (err) throw err; -} - -if (OUTPUT_PATH === process.stdout) { - process.stdout.write(schemaString, throwErr); -} else if (typeof OUTPUT_PATH === "string") { - writeFile(OUTPUT_PATH, schemaString, throwErr); -} else { - throw new Error("Unsupported output path"); -} diff --git a/scripts/schema/TypeFormatter/yfFunctionIgnorer.ts b/scripts/schema/TypeFormatter/yfFunctionIgnorer.ts deleted file mode 100644 index 8dc04f65..00000000 --- a/scripts/schema/TypeFormatter/yfFunctionIgnorer.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - BaseType, - FunctionType, - Definition, - SubTypeFormatter, -} from "ts-json-schema-generator"; - -export default class yfNumberTypeFormatter implements SubTypeFormatter { - public supportsType(type: FunctionType): boolean { - return type instanceof FunctionType; - } - - public getDefinition(): Definition { - return {}; - } - - public getChildren(): BaseType[] { - return []; - } -} diff --git a/scripts/schema/TypeFormatter/yfNumberTypeFormatter.ts b/scripts/schema/TypeFormatter/yfNumberTypeFormatter.ts deleted file mode 100644 index d87a851f..00000000 --- a/scripts/schema/TypeFormatter/yfNumberTypeFormatter.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { - BaseType, - Definition, - NumberType, - SubTypeFormatter, -} from "ts-json-schema-generator"; - -export default class yfNumberTypeFormatter implements SubTypeFormatter { - public supportsType(type: NumberType): boolean { - return type instanceof NumberType; - } - - public getDefinition(_type: NumberType): Definition { - return { - // @ts-ignore - yahooFinanceType: "number", - }; - } - - public getChildren(_type: NumberType): BaseType[] { - return []; - } -} diff --git a/scripts/schema/TypeFormatter/yfReferenceTypeFormatter.ts b/scripts/schema/TypeFormatter/yfReferenceTypeFormatter.ts deleted file mode 100644 index 7c44ac64..00000000 --- a/scripts/schema/TypeFormatter/yfReferenceTypeFormatter.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - Definition, - DefinitionTypeFormatter, - DefinitionType, -} from "ts-json-schema-generator"; - -export default class yfReferenceTypeFormatter extends DefinitionTypeFormatter { - public getDefinition(type: DefinitionType): Definition { - const ref = type.getName(); - - const types = ["TwoNumberRange", "DateInMs"]; - - if (types.includes(ref)) - return { - // @ts-ignore - yahooFinanceType: ref, - }; - else return super.getDefinition(type); - } -} diff --git a/scripts/schema/postWalker.js b/scripts/schema/postWalker.js deleted file mode 100644 index 4a9b207b..00000000 --- a/scripts/schema/postWalker.js +++ /dev/null @@ -1,74 +0,0 @@ -const isDate = (schema) => - schema.type === "string" && schema.format === "date-time"; - -const funcs = [ - /* - - // Now handled in TypeFormatter/yfReferenceTypeFormatter.ts - function TwoNumberRange(schema, parent, state) { - if (schema.$ref === "#/definitions/TwoNumberRange") { - const key = state.property.split("/").pop(); - parent.properties[key] = { yahooFinanceType: "TwoNumberRange" }; - return true; - } - }, - - // Now handled in TypeFormatter/yfReferenceTypeFormatter.ts - function DateInMs(schema, parent, state) { - if (schema.$ref === "#/definitions/DateInMs") { - const key = state.property.split("/").pop(); - parent.properties[key] = { yahooFinanceType: "DateInMs" }; - return true; - } - }, - - // Now handled in TypeFormatter/yfNumberTypeFormatter.ts - function Number(schema, parent, state) { - if (schema.type === "number") { - delete schema.type; - schema.yahooFinanceType = "number"; - return true; - } - }, - - */ - - function NumberNull(schema, parent, state) { - if (Array.isArray(schema.type)) { - if ( - schema.type.length === 2 && - schema.type.includes("number") && - schema.type.includes("null") - ) { - delete schema.type; - schema.yahooFinanceType = "number|null"; - return true; - } - } - }, - - function Date(schema, parent, state) { - const anyOf = schema.anyOf; - if ( - anyOf && - anyOf.length === 2 && - ((isDate(anyOf[0]) && anyOf[1].type === "null") || - (isDate(anyOf[1]) && anyOf[0].type === "null")) - ) { - delete schema.anyOf; - schema.yahooFinanceType = "date|null"; - return true; - } - - if (isDate(schema)) { - delete schema.format; - delete schema.type; - schema.yahooFinanceType = "date"; - return true; - } - }, -]; - -export default function callback(schema, parent, state) { - for (let func of funcs) if (func(schema, parent, state)) break; -} diff --git a/src/index-common.ts b/src/index-common.ts index 0cbc8bde..e0696154 100644 --- a/src/index-common.ts +++ b/src/index-common.ts @@ -1,10 +1,9 @@ // libs import yahooFinanceFetch from "./lib/yahooFinanceFetch.js"; -import moduleExec from "./lib/moduleExec.js"; import options from "./lib/options.js"; import errors from "./lib/errors.js"; import setGlobalConfig from "./lib/setGlobalConfig.js"; -import { disallowAdditionalProps } from "./lib/validateAndCoerceTypes.js"; +import moduleExec from "./lib/moduleExec.js"; // modules import autoc from "./modules/autoc.js"; @@ -30,7 +29,6 @@ export default { _fetch: yahooFinanceFetch, _moduleExec: moduleExec, _opts: options, - _disallowAdditionalProps: disallowAdditionalProps, // common errors, diff --git a/src/lib/datetime.spec.ts b/src/lib/datetime.spec.ts new file mode 100644 index 00000000..1ea1f147 --- /dev/null +++ b/src/lib/datetime.spec.ts @@ -0,0 +1,15 @@ +import { isTime } from "./datetime"; + +// There is reasonable coverage for most of this module already, so these tests aren't extensive. +// Just plugging the gaps in test coverage +describe("isTime", () => { + it("Should correctly identify a time given in the non-zulu TZ as a valid time", () => { + expect(isTime("20:20:39+00:00")).toBe(true); + }); + it("Should correctly identify a time given in a TZ behind Zulu as a valid time", () => { + expect(isTime("20:20:39-10:00")).toBe(true); + }); + it("Should correctly reject a time given in the non-zulu TZ with impossible values", () => { + expect(isTime("35:61:39+00:00")).toBe(false); + }); +}); diff --git a/src/lib/datetime.ts b/src/lib/datetime.ts new file mode 100644 index 00000000..4c4d5243 --- /dev/null +++ b/src/lib/datetime.ts @@ -0,0 +1,100 @@ +/* +The contents of this file are copied from: +* https://github.com/sinclairzx81/typebox/blob/7a42aeef5bb989c07bbfc9acdbd9d74b3febed05/example/formats/date.ts +* https://github.com/sinclairzx81/typebox/blob/7a42aeef5bb989c07bbfc9acdbd9d74b3febed05/example/formats/date-time.ts +* https://github.com/sinclairzx81/typebox/blob/7a42aeef5bb989c07bbfc9acdbd9d74b3febed05/example/formats/time.ts +* +* License info: +* +* The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; +const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; +const YEAR = /^(\d\d\d\d)$/; +const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i; +const DATE_TIME_SEPARATOR = /t|\s/i; + +function IsLeapYear(year: number): boolean { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); +} +export const isYear = (value: string): boolean => { + const matches: string[] | null = YEAR.exec(value); + return !!matches; +}; +/** + * `[ajv-formats]` ISO8601 Date component + * @example `2020-12-12` + */ +export const isDate = (value: string): boolean => { + const matches: string[] | null = DATE.exec(value); + if (!matches) return false; + const year: number = +matches[1]; + const month: number = +matches[2]; + const day: number = +matches[3]; + return ( + month >= 1 && + month <= 12 && + day >= 1 && + day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) + ); +}; + +/** + * `[ajv-formats]` ISO8601 Time component + * @example `20:20:39+00:00` + */ +export const isTime = (value: string, strictTimeZone?: boolean): boolean => { + const matches: string[] | null = TIME.exec(value); + if (!matches) return false; + const hr: number = +matches[1]; + const min: number = +matches[2]; + const sec: number = +matches[3]; + const tz: string | undefined = matches[4]; + const tzSign: number = matches[5] === "-" ? -1 : 1; + const tzH: number = +(matches[6] || 0); + const tzM: number = +(matches[7] || 0); + if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false; + if (hr <= 23 && min <= 59 && sec < 60) return true; + const utcMin = min - tzM * tzSign; + const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0); + return ( + (utcHr === 23 || utcHr === -1) && + (utcMin === 59 || utcMin === -1) && + sec < 61 + ); +}; + +/** + * `[ajv-formats]` ISO8601 DateTime + * @example `2020-12-12T20:20:40+00:00` + */ +export const isDateTime = ( + value: string, + strictTimeZone?: boolean +): boolean => { + const dateTime: string[] = value.split(DATE_TIME_SEPARATOR); + return ( + dateTime.length === 2 && + isDate(dateTime[0]) && + isTime(dateTime[1], strictTimeZone) + ); +}; diff --git a/src/lib/errors.ts b/src/lib/errors.ts index 91a8b663..89f1f26c 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -1,10 +1,15 @@ -import type { ErrorObject } from "ajv/dist/types"; +import { + TransformDecodeError, + TransformDecodeCheckError, +} from "@sinclair/typebox/build/cjs/value"; // Yahoo's servers returned an HTTP 400 for this request. export class BadRequestError extends Error { name = "BadRequestError"; } +type ValidationError = TransformDecodeError | TransformDecodeCheckError; + // Yahoo's servers returned a 'not-ok' status for this request. // https://developer.mozilla.org/en-US/docs/Web/API/Response/ok export class HTTPError extends Error { @@ -24,11 +29,11 @@ export class NoEnvironmentError extends Error { export class FailedYahooValidationError extends Error { name = "FailedYahooValidationError"; result: any; - errors?: null | ErrorObject[]; + errors?: null | ValidationError[]; constructor( message: string, - { result, errors }: { result: any; errors?: null | ErrorObject[] } + { result, errors }: { result: any; errors?: null | ValidationError[] } ) { super(message); this.result = result; diff --git a/src/lib/moduleCommon.ts b/src/lib/moduleCommon.ts index dff4b6b8..9eb99dae 100644 --- a/src/lib/moduleCommon.ts +++ b/src/lib/moduleCommon.ts @@ -1,4 +1,4 @@ -//import ModuleExec from "./moduleExec.js"; +import moduleExec from "./moduleExec"; export interface ModuleOptions { validateResult?: boolean; @@ -16,7 +16,5 @@ export interface ModuleOptionsWithValidateTrue extends ModuleOptions { export interface ModuleThis { [key: string]: any; - // TODO: should be ModuleExec function but requiring functions breaks - // schema generation because json-schema does not support functions. - _moduleExec: any; + _moduleExec: typeof moduleExec; } diff --git a/src/lib/moduleExec.spec.ts b/src/lib/moduleExec.spec.ts index cc2496db..5343f4cd 100644 --- a/src/lib/moduleExec.spec.ts +++ b/src/lib/moduleExec.spec.ts @@ -2,13 +2,15 @@ import { jest } from "@jest/globals"; import search from "../modules/search.js"; import chart from "../modules/chart.js"; -import { InvalidOptionsError } from "./errors.js"; +import { FailedYahooValidationError, InvalidOptionsError } from "./errors.js"; import testYf from "../../tests/testYf.js"; +import { Type } from "@sinclair/typebox"; const yf = testYf({ search, chart }); yf._opts.validation.logOptionsErrors = false; +yf._opts.validation.logErrors = false; -describe("moduleExec", () => { +describe("moduleExecTypebox", () => { describe("assertSymbol", () => { const periodOpts = { period1: new Date("2022-02-22"), @@ -54,7 +56,7 @@ describe("moduleExec", () => { fakeConsole.log.mock.calls.length + fakeConsole.error.mock.calls.length + fakeConsole.dir.mock.calls.length - ).toBeGreaterThan(1); + ).toBe(1); yf._opts.validation.logOptionsErrors = false; }); @@ -68,7 +70,6 @@ describe("moduleExec", () => { const rwo = (options: any) => yf.search("symbol", options); await expect(rwo({ invalid: true })).rejects.toThrow(InvalidOptionsError); console = realConsole; - expect( fakeConsole.log.mock.calls.length + fakeConsole.error.mock.calls.length + @@ -83,7 +84,7 @@ describe("moduleExec", () => { yf._opts.validation.logErrors = false; await expect( yf.search("AAPL", {}, { devel: "search-badResult.fake.json" }) - ).rejects.toThrow(/Failed Yahoo Schema/); + ).rejects.toThrow(FailedYahooValidationError); yf._opts.validation.logErrors = true; }); @@ -112,4 +113,113 @@ describe("moduleExec", () => { expect(fakeConsole.dir).not.toHaveBeenCalled(); }); }); + describe("correctly invokes callbacks when provided", () => { + it("Should invoke the query options transformWith function when one is provided", async () => { + const yf = testYf({ _fetch: jest.fn() }); + const overrides = { overrideKey: "thingy" }; + const optionsTransformWith = jest.fn((v: Record) => ({ + ...v, + overrideKey: "bobby", + })); + + await yf._moduleExec({ + query: { + transformWith: optionsTransformWith, + assertSymbol: false, + schema: Type.Any(), + overrides, + }, + result: { + schema: Type.Any(), + }, + }); + expect(optionsTransformWith).toHaveBeenCalledTimes(1); + expect(optionsTransformWith).toMatchInlineSnapshot(` + [MockFunction] { + "calls": [ + [ + { + "overrideKey": "thingy", + }, + ], + ], + "results": [ + { + "type": "return", + "value": { + "overrideKey": "bobby", + }, + }, + ], + } + `); + }); + it("Should invoke the result transformWith function when one is provided", async () => { + const yf = testYf({ _fetch: jest.fn(() => ({ statusCode: 200 })) }); + const resultTransformedWith = jest.fn((v: Record) => { + return { overrideKey: "bobby" }; + }); + + await yf._moduleExec({ + query: { + assertSymbol: false, + schema: Type.Any(), + }, + result: { + schema: Type.Any(), + transformWith: resultTransformedWith, + }, + }); + expect(resultTransformedWith).toHaveBeenCalledTimes(1); + expect(resultTransformedWith).toMatchInlineSnapshot(` + [MockFunction] { + "calls": [ + [ + { + "statusCode": 200, + }, + ], + ], + "results": [ + { + "type": "return", + "value": { + "overrideKey": "bobby", + }, + }, + ], + } + `); + }); + it("should throw when symbol assertion is enabled but a non-string symbol is provided", () => { + const yf = testYf({ _fetch: jest.fn() }); + expect( + async () => + await yf._moduleExec({ + query: { + assertSymbol: true, + schema: Type.Any(), + }, + result: { + schema: Type.Any(), + }, + }) + ).rejects.toThrow(); + }); + it("should pass a string symbol when symbol assertion is enabled", () => { + const yf = testYf({ _fetch: jest.fn() }); + expect( + async () => + await yf._moduleExec({ + query: { + assertSymbol: "AAPL", + schema: Type.Any(), + }, + result: { + schema: Type.Any(), + }, + }) + ).not.toThrow(); + }); + }); }); diff --git a/src/lib/moduleExec.ts b/src/lib/moduleExec.ts index f7cb6747..60f51463 100644 --- a/src/lib/moduleExec.ts +++ b/src/lib/moduleExec.ts @@ -15,18 +15,17 @@ * Further info below, inline. */ -import validateAndCoerceTypes from "./validateAndCoerceTypes.js"; +import { validateAndCoerceTypebox } from "./validateAndCoerceTypes.js"; import csv2json from "./csv2json.js"; +import { TSchema } from "@sinclair/typebox"; -// The consuming module itself will have a stricter return type. -type TransformFunc = (arg: any) => any; /* interface TransformFunc { (result: { [key: string]: any }): { [key: string]: any }; } */ -interface ModuleExecOptions { +interface ModuleExecOptions { /** * Name of the module, e.g. "search", "quoteSummary", etc. Used in error * reporting. @@ -44,10 +43,9 @@ interface ModuleExecOptions { */ url: string; /** - * Key of schema used to validate user-provider query options. - * e.g. yf.search('AAPL', { isThisAValidOption: "maybe" }) + * The schema to use to validate the options overrides */ - schemaKey: string; + schema: TSchema; /** * Defaults for this query, e.g. { period: '1d' } in history, * and other required options that aren't often changed { locale: 'en' }. @@ -69,7 +67,7 @@ interface ModuleExecOptions { * the query. Useful to transform options we allow but not Yahoo, e.g. * allow a "2020-01-01" date but transform this to a UNIX epoch. */ - transformWith?: TransformFunc; + transformWith?: (opts: TOpts) => unknown; /** * Default: 'json'. Can be 'text' or 'csv' (augments fetch's "text"). */ @@ -77,19 +75,19 @@ interface ModuleExecOptions { /** * Default: false. This request requires Yahoo cookies & crumb. */ - needsCrumb: boolean; + needsCrumb?: boolean; }; result: { /** - * Key of schema to validate (and coerce) the retruned result from Yahoo. + * The schema to validate (and coerce) the retruned result from Yahoo. */ - schemaKey: string; + schema: TSchema; /** * Mutate the Yahoo result *before* validating and coercion. Mostly used * to e.g. throw if no (resault.returnField) and return result.returnField. */ - transformWith?: TransformFunc; + transformWith?: (result: unknown) => TResult; }; moduleOptions?: { @@ -104,9 +102,10 @@ interface ModuleExecOptions { }; } -type ThisWithModExec = { [key: string]: any; _moduleExec: typeof moduleExec }; - -async function moduleExec(this: ThisWithModExec, opts: ModuleExecOptions) { +async function moduleExec( + this: { [key: string]: any }, + opts: ModuleExecOptions +) { const queryOpts = opts.query; const moduleOpts = opts.moduleOptions; const moduleName = opts.moduleName; @@ -122,11 +121,10 @@ async function moduleExec(this: ThisWithModExec, opts: ModuleExecOptions) { } // Check that query options passed by the user are valid for this module - validateAndCoerceTypes({ - source: moduleName, + validateAndCoerceTypebox({ type: "options", - object: queryOpts.overrides ?? {}, - schemaKey: queryOpts.schemaKey, + data: queryOpts.overrides ?? {}, + schema: queryOpts.schema, options: this._opts.validation, }); @@ -141,8 +139,9 @@ async function moduleExec(this: ThisWithModExec, opts: ModuleExecOptions) { * the query. Useful to transform options we allow but not Yahoo, e.g. * allow a "2020-01-01" date but transform this to a UNIX epoch. */ - if (queryOpts.transformWith) + if (queryOpts.transformWith) { queryOptions = queryOpts.transformWith(queryOptions); + } // this._fetch is lib/yahooFinanceFetch let result = await this._fetch( @@ -150,16 +149,20 @@ async function moduleExec(this: ThisWithModExec, opts: ModuleExecOptions) { queryOptions, moduleOpts, queryOpts.fetchType, - queryOpts.needsCrumb + queryOpts.needsCrumb ?? false ); - if (queryOpts.fetchType === "csv") result = csv2json(result); + if (queryOpts.fetchType === "csv") { + result = csv2json(result); + } /* * Mutate the Yahoo result *before* validating and coercion. Mostly used - * to e.g. throw if no (resault.returnField) and return result.returnField. + * to e.g. throw if no (result.returnField) and return result.returnField. */ - if (opts.result.transformWith) result = opts.result.transformWith(result); + if (resultOpts.transformWith) { + result = resultOpts.transformWith(result); + } const validateResult = !moduleOpts || @@ -190,11 +193,10 @@ async function moduleExec(this: ThisWithModExec, opts: ModuleExecOptions) { * database, etc. Otherwise you'll receive an error. */ try { - validateAndCoerceTypes({ - source: moduleName, + validateAndCoerceTypebox({ type: "result", - object: result, - schemaKey: resultOpts.schemaKey, + data: result, + schema: resultOpts.schema, options: validationOpts, }); } catch (error) { diff --git a/src/lib/options.ts b/src/lib/options.ts index 45454ee1..330f310b 100644 --- a/src/lib/options.ts +++ b/src/lib/options.ts @@ -1,22 +1,56 @@ -// TODO, keep defaults there too? -import type { ValidationOptions } from "./validateAndCoerceTypes.js"; -import type { QueueOptions } from "./queue.js"; import { ExtendedCookieJar } from "./cookieJar.js"; +import { Static, Type } from "@sinclair/typebox"; +import { QueueOptionsSchema } from "./queue.js"; -export interface Logger { +const LoggerSchema = Type.Object({ + info: Type.Function([], Type.Void()), + warn: Type.Function([], Type.Void()), + error: Type.Function([], Type.Void()), + debug: Type.Function([], Type.Void()), +}); + +export type Logger = { info: (...args: any[]) => void; warn: (...args: any[]) => void; error: (...args: any[]) => void; debug: (...args: any[]) => void; -} +}; + +const ValidationOptionsSchema = Type.Object({ + logErrors: Type.Optional(Type.Boolean()), + logOptionsErrors: Type.Optional(Type.Boolean()), + _internalThrowOnAdditionalProperties: Type.Optional( + Type.Boolean({ + default: process.env.NODE_ENV === "test", + description: + "Use this property to throw when properties beyond what is explicitly specified in the schema are provided. It is an internal option and subject to change, use at your own risk", + }) + ), +}); + +export type ValidationOptions = Static; -export interface YahooFinanceOptions { - YF_QUERY_HOST?: string; - cookieJar?: ExtendedCookieJar; - queue?: QueueOptions; - validation?: ValidationOptions; +export const YahooFinanceOptionsSchema = Type.Object( + { + YF_QUERY_HOST: Type.Optional(Type.String()), + cookieJar: Type.Optional(Type.Any()), + queue: Type.Optional(QueueOptionsSchema), + validation: Type.Optional(ValidationOptionsSchema), + logger: Type.Optional(LoggerSchema), + }, + { title: "YahooFinanceOptions" } +); + +/* +TODO: Ideally we'd have the typebox type be our source of truth for types here. +However, Typebox does not support type checking for instances of a particular class +in the case of the `cookieJar`, and for functions with variadic params in the case +of the `logger` (ref: https://github.com/sinclairzx81/typebox/issues/931) +*/ +export type YahooFinanceOptions = Static & { + cookieJar: ExtendedCookieJar; logger?: Logger; -} +}; const options: YahooFinanceOptions = { YF_QUERY_HOST: process.env.YF_QUERY_HOST || "query2.finance.yahoo.com", diff --git a/src/lib/queue.ts b/src/lib/queue.ts index 973e265b..f58f4547 100644 --- a/src/lib/queue.ts +++ b/src/lib/queue.ts @@ -1,15 +1,25 @@ +import { Static, Type } from "@sinclair/typebox"; + interface Job { func: () => Promise; resolve: (arg: any) => void; reject: (arg: any) => void; } -export interface QueueOptions { - // TODO: adds func type to json schema which is not supported - //_queue?: Queue; - concurrency?: number; - timeout?: number; // TODO -} +export const QueueOptionsSchema = Type.Object( + { + // TODO: adds func type to json schema which is not supported + //_queue?: Queue; + concurrency: Type.Optional(Type.Number()), + timeout: Type.Optional(Type.Number()), // TODO + }, + { + additionalProperties: false, + title: "QueueOptions", + } +); + +export type QueueOptions = Static; export default class Queue { concurrency = 1; diff --git a/src/lib/setGlobalConfig.spec.ts b/src/lib/setGlobalConfig.spec.ts index d90fb50a..42a412fe 100644 --- a/src/lib/setGlobalConfig.spec.ts +++ b/src/lib/setGlobalConfig.spec.ts @@ -28,10 +28,52 @@ describe("setGlobalConfig", () => { it("should throw on invalid config", () => { consoleSilent(); - expect(() => yf.setGlobalConfig({ queue: { abc: "" } })).toThrow( - /yahooFinance.setGlobalConfig called with invalid options\./ + expect(() => yf.setGlobalConfig({ queue: { concurrency: "" } })).toThrow( + /Validation called with invalid options/ ); consoleRestore(); }); + it("should throw on an invalid logger", () => { + consoleSilent(); + + expect(() => + yf.setGlobalConfig({ + logger: { + info() {}, + debug() {}, + error() {}, + warn: "yeh this won't work", + }, + }) + ).toThrow(/Validation called with invalid options/); + + expect(() => + yf.setGlobalConfig({ + logger: { + info() {}, + debug() {}, + error() {}, + }, + }) + ).toThrow(/Validation called with invalid options/); + + expect(() => + yf.setGlobalConfig({ + logger: {}, + }) + ).toThrow(/Validation called with invalid options/); + + consoleRestore(); + }); + it("should throw on an invalid cookie jar", () => { + consoleSilent(); + + expect(() => + yf.setGlobalConfig({ + cookieJar: "not a cookie jar", + }) + ).toThrow(/cookieJar must be an instance of ExtendedCookieJar/); + consoleRestore(); + }); }); diff --git a/src/lib/setGlobalConfig.ts b/src/lib/setGlobalConfig.ts index 8d023007..6882e310 100644 --- a/src/lib/setGlobalConfig.ts +++ b/src/lib/setGlobalConfig.ts @@ -1,4 +1,7 @@ -import type { YahooFinanceOptions } from "./options.js"; +import { + YahooFinanceOptionsSchema, + type YahooFinanceOptions, +} from "./options.js"; import type { ModuleThis } from "./moduleCommon.js"; import validateAndCoerceTypes from "./validateAndCoerceTypes.js"; import { ExtendedCookieJar } from "./cookieJar.js"; @@ -7,16 +10,16 @@ export default function setGlobalConfig( this: ModuleThis, _config: YahooFinanceOptions ): void { - // Instances (e.g. cookieJar) don't validate well :) - const { cookieJar, logger, ...config } = _config; - - validateAndCoerceTypes({ - object: config, - source: "setGlobalConfig", + const parsed = validateAndCoerceTypes({ + data: _config, type: "options", options: this._opts.validation, - schemaKey: "#/definitions/YahooFinanceOptions", + schema: YahooFinanceOptionsSchema, }); + + // Instances (e.g. cookieJar) don't validate well :) + const { cookieJar, ...config } = parsed; + mergeObjects(this._opts, config); if (cookieJar) { @@ -24,17 +27,6 @@ export default function setGlobalConfig( throw new Error("cookieJar must be an instance of ExtendedCookieJar"); this._opts.cookieJar = cookieJar; } - if (logger) { - if (typeof logger.info !== "function") - throw new Error("logger.info must be a function"); - if (typeof logger.warn !== "function") - throw new Error("logger.warn must be a function"); - if (typeof logger.error !== "function") - throw new Error("logger.error must be a function"); - if (typeof logger.debug !== "function") - throw new Error("logger.debug must be a function"); - this._opts.logger = logger; - } } type Obj = Record; diff --git a/src/lib/validateAndCoerceTypes.spec.ts b/src/lib/validateAndCoerceTypes.spec.ts index 4a836b20..b9e94b9a 100644 --- a/src/lib/validateAndCoerceTypes.spec.ts +++ b/src/lib/validateAndCoerceTypes.spec.ts @@ -1,406 +1,455 @@ import { jest } from "@jest/globals"; -import validateAndCoerceTypes, { ajv } from "./validateAndCoerceTypes.js"; -import type { ValidateParams } from "./validateAndCoerceTypes.js"; -import { InvalidOptionsError, FailedYahooValidationError } from "./errors.js"; - -ajv.addSchema({ - $id: "testSchema", - $schema: "http://json-schema.org/draft-07/schema#", - properties: { - date: { yahooFinanceType: "date" }, - dateNull: { yahooFinanceType: "date|null" }, - dateInMs: { yahooFinanceType: "DateInMs" }, - twoNumberRange: { yahooFinanceType: "TwoNumberRange" }, - number: { yahooFinanceType: "number" }, - numberNull: { yahooFinanceType: "number|null" }, - requiredRequired: { - type: "object", - properties: { required: { type: "boolean" } }, - required: ["required"], - }, - noAdditional: { - type: "object", - additionalProperties: false, - properties: {}, - }, - }, - type: "object", -}); - -// Default. Use to show (unexpected) errors during tests. -const defLogParams: ValidateParams = { - source: "validateAndCoerceTypes.spec.js", - schemaKey: "testSchema", - //schemaKey: "#/definitions/QuoteSummaryResult", - type: "result", - object: {}, - options: { - logErrors: true, - logOptionsErrors: true, - }, -}; - -// If we're purposefully testing failed validation, don't log it. -// i.e. Use to hide (expected) errors during tests. -const defNoLogParams = { - ...defLogParams, - options: { - ...defLogParams.options, - logErrors: false, - }, -}; - -describe("validateAndCoerceTypes", () => { - describe("coersion", () => { - describe("numbers", () => { - it("passes regular numbers", () => { - const object = { number: 2 }; - validateAndCoerceTypes({ ...defLogParams, object }); - expect(object.number).toBe(2); - }); +import { validateAndCoerceTypebox } from "./validateAndCoerceTypes.js"; +import { Type } from "@sinclair/typebox"; +import { YahooFinanceDate, YahooNumber } from "./yahooFinanceTypes.js"; +import { FailedYahooValidationError } from "./errors.js"; + +describe("validateAndCoerceTypebox", () => { + afterEach(() => { + jest.clearAllMocks(); + }); - it("corerces rawNumberObjs", () => { - const object = { number: { raw: 0.006599537, fmt: "6.5%" } }; - validateAndCoerceTypes({ ...defLogParams, object }); - expect(object.number).toBe(0.006599537); - }); + it("Should throw a sensible error on failure", () => { + const testCase = { date: { weird: 123 } }; + const testSchema = Type.Object({ + date: YahooFinanceDate, + }); - it("passes if data is null and type IS number|null", () => { - const object = { numberNull: null }; - expect(() => - validateAndCoerceTypes({ ...defLogParams, object }) - ).not.toThrow(); + let error: FailedYahooValidationError | undefined; + try { + validateAndCoerceTypebox({ + type: "result", + data: testCase, + schema: testSchema, + options: {}, }); - - it("fails if data is null and type IS NOT number|null", () => { - const object = { number: null }; - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ ...defNoLogParams, object }); - } catch (e) { - error = e; + } catch (e) { + error = e as unknown as FailedYahooValidationError; + } + + expect(error).toMatchInlineSnapshot( + `[FailedYahooValidationError: Failed Yahoo Schema validation]` + ); + + expect(JSON.stringify(error?.errors?.[0], null, 2)).toMatchInlineSnapshot(` + "{ + "schema": { + "type": "object", + "properties": { + "date": { + "title": "YahooFinanceDate", + "anyOf": [ + { + "type": "Date" + }, + { + "type": "number" + }, + { + "title": "RawDateObject", + "type": "object", + "properties": { + "raw": { + "type": "number" + } + }, + "required": [ + "raw" + ] + }, + { + "title": "ISOStringDate", + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "format": "year", + "type": "string" + }, + { + "format": "date-time", + "type": "string" + } + ] + } + ] + } + }, + "required": [ + "date" + ] + }, + "value": { + "date": { + "weird": 123 + } + }, + "error": { + "type": 62, + "schema": { + "title": "YahooFinanceDate", + "anyOf": [ + { + "type": "Date" + }, + { + "type": "number" + }, + { + "title": "RawDateObject", + "type": "object", + "properties": { + "raw": { + "type": "number" + } + }, + "required": [ + "raw" + ] + }, + { + "title": "ISOStringDate", + "anyOf": [ + { + "format": "date", + "type": "string" + }, + { + "format": "year", + "type": "string" + }, + { + "format": "date-time", + "type": "string" + } + ] + } + ] + }, + "path": "/date", + "value": { + "weird": 123 + }, + "message": "Expected union value" } - // @ts-ignore - expect(error).toBeDefined(); - expect(error.errors[0].message).toMatch(/Expecting number/); - }); - - it("passes and coerces {} to null if type IS number|null", () => { - const object = { numberNull: {} }; - expect(() => - validateAndCoerceTypes({ ...defLogParams, object }) - ).not.toThrow(); - expect(object.numberNull).toBe(null); - }); - - it("fails when receiving {} if type IS NOT number|null", () => { - const object = { number: {} }; - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ ...defNoLogParams, object }); - } catch (e) { - error = e; + }" + `); + + expect(JSON.stringify(error?.errors?.[0]?.value, null, 2)) + .toMatchInlineSnapshot(` + "{ + "date": { + "weird": 123 } - expect(error).toBeDefined(); - expect(error.errors[0].message).toMatch(/number \| null/); - }); - - it("fails if data is not a number nor object", () => { - const object = { number: true }; - expect(() => - validateAndCoerceTypes({ ...defNoLogParams, object }) - ).toThrow(/Failed Yahoo Schema/); - }); - - it("fails if data.raw is not a number", () => { - const object = { number: { raw: "a string" } }; - expect(() => - validateAndCoerceTypes({ ...defNoLogParams, object }) - ).toThrow(/Failed Yahoo Schema/); - }); + }" + `); + }); + it("Should log errors when logErrors = true", () => { + const logSpy = jest.spyOn(console, "log"); + const logSpyFn = jest.fn(() => undefined); + logSpy.mockImplementation(logSpyFn); - it("fails if string returns a NaN", () => { - const object = { number: "not-a-number" }; - expect(() => - validateAndCoerceTypes({ ...defNoLogParams, object }) - ).toThrow(/Failed Yahoo Schema/); - }); + const testSchema = Type.Object({ + aNumber: YahooNumber, }); - - describe("dates", () => { - it("coerces rawNumberObjs", () => { - const dateInMs = 1612313997; - const object = { date: { raw: dateInMs } }; - validateAndCoerceTypes({ ...defLogParams, object }); - expect(object.date).toBeInstanceOf(Date); - // @ts-ignore - expect(object.date.getTime()).toBe(dateInMs * 1000); - }); - - it("coerces epochs", () => { - const dateInMs = 1612313997; - const object = { date: dateInMs }; - validateAndCoerceTypes({ ...defLogParams, object }); - // @ts-ignore - expect(object.date.getTime()).toBe(new Date(dateInMs * 1000).getTime()); - }); - - it("coerces recognizable date string", () => { - const dateStr = "2021-02-02T21:00:01.000Z"; - const object = { date: dateStr }; - validateAndCoerceTypes({ ...defLogParams, object }); - // @ts-ignore - expect(object.date.getTime()).toBe(new Date(dateStr).getTime()); - }); - - it("throws on non-matching strings", () => { - const object = { date: "clearly not a date" }; - expect(() => - validateAndCoerceTypes({ ...defNoLogParams, object }) - ).toThrow(/Failed Yahoo Schema/); - }); - - it("passes through Date objects", () => { - const date = new Date(); - const object = { date }; - validateAndCoerceTypes({ ...defLogParams, object }); - expect(object.date).toBe(date); - }); - - it("passes if data is null and type IS date|null", () => { - const object = { dateNull: null }; - expect(() => - validateAndCoerceTypes({ ...defLogParams, object }) - ).not.toThrow(); - }); - - it("fails if data is null and type IS NOT date|null", () => { - const object = { date: null }; - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ ...defNoLogParams, object }); - } catch (e) { - error = e; - } - // @ts-ignore - expect(error).toBeDefined(); - expect(error.errors[0].message).toMatch(/Expecting date/); - }); - - it("passes and coerces {} to null if type IS Date|null", () => { - const object = { dateNull: {} }; - expect(() => - validateAndCoerceTypes({ ...defLogParams, object }) - ).not.toThrow(); - expect(object.dateNull).toBe(null); + const testCase = { aNumber: "foo" }; + expect(() => { + validateAndCoerceTypebox({ + type: "result", + data: testCase, + schema: testSchema, + options: { + logErrors: true, + }, }); - - it("fails when receiving {} if type IS NOT date|null", () => { - const object = { date: {} }; - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ ...defNoLogParams, object }); - } catch (e) { - error = e; + }).toThrow(); + expect(logSpy).toHaveBeenCalledTimes(2); + expect(logSpyFn).toMatchInlineSnapshot(` + [MockFunction] { + "calls": [ + [ + "{ + "schema": { + "type": "object", + "properties": { + "aNumber": { + "title": "YahooNumber", + "anyOf": [ + { + "title": "RawNumber", + "type": "object", + "properties": { + "raw": { + "type": "number" + } + }, + "required": [ + "raw" + ] + }, + { + "type": "number" + } + ] + } + }, + "required": [ + "aNumber" + ] + }, + "value": { + "aNumber": "foo" + }, + "error": { + "type": 62, + "schema": { + "title": "YahooNumber", + "anyOf": [ + { + "title": "RawNumber", + "type": "object", + "properties": { + "raw": { + "type": "number" + } + }, + "required": [ + "raw" + ] + }, + { + "type": "number" + } + ] + }, + "path": "/aNumber", + "value": "foo", + "message": "Expected union value" } - expect(error).toBeDefined(); - expect(error.errors[0].message).toMatch(/date \| null/); - }); - }); + }", + ], + [ + " + This may happen intermittently and you should catch errors appropriately. + However: 1) if this recently started happening on every request for a symbol + that used to work, Yahoo may have changed their API. 2) If this happens on + every request for a symbol you've never used before, but not for other + symbols, you've found an edge-case (OR, we may just be protecting you from + "bad" data sometimes stored for e.g. misspelt symbols on Yahoo's side). + Please see if anyone has reported this previously: + + https://github.com/gadicc/node-yahoo-finance2/issues?q=is%3Aissue+undefined + + or open a new issue (and mention the symbol): yahoo-finance2 v0.0.1 + + https://github.com/gadicc/node-yahoo-finance2/issues/new?labels=bug%2C+validation&template=validation.md&title=undefined + + For information on how to turn off the above logging or skip these errors, + see https://github.com/gadicc/node-yahoo-finance2/tree/devel/docs/validation.md. + + At the end of the doc, there's also a section on how to + [Help Fix Validation Errors](https://github.com/gadicc/node-yahoo-finance2/blob/devel/docs/validation.md#help-fix) + in case you'd like to contribute to the project. Most of the time, these + fixes are very quick and easy; it's just hard for our small core team to keep up, + so help is always appreciated! + ", + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + { + "type": "return", + "value": undefined, + }, + ], + } + `); + }); + it("Should not log errors when logErrors = false", () => { + const logSpy = jest.spyOn(console, "log"); + const logSpyFn = jest.fn(() => undefined); + logSpy.mockImplementation(logSpyFn); - describe("DateInMs", () => { - it("works with date in milliseconds", () => { - const object = { dateInMs: 917015400000 }; - validateAndCoerceTypes({ ...defLogParams, object }); - expect(object.dateInMs).toBeInstanceOf(Date); - }); + const testSchema = Type.Object({ + aNumber: YahooNumber, }); - - describe("TwoNumberRange", () => { - it("works with valid input", () => { - const object = { twoNumberRange: "541.867 - 549.19" }; - validateAndCoerceTypes({ ...defLogParams, object }); - expect(object.twoNumberRange).toMatchObject({ - low: 541.867, - high: 549.19, - }); + const testCase = { aNumber: "foo" }; + expect(() => { + validateAndCoerceTypebox({ + type: "result", + data: testCase, + schema: testSchema, + options: { + logErrors: false, + }, }); + }).toThrow(); + expect(logSpy).toHaveBeenCalledTimes(0); + }); - it("throws on invalid input", () => { - const object = { twoNumberRange: "X - 549.19" }; - expect(() => - validateAndCoerceTypes({ ...defNoLogParams, object }) - ).toThrow(/^Failed Yahoo/); - }); + it("Should log options errors when logOptionsErrors = true", () => { + const logSpy = jest.spyOn(console, "error"); + const logSpyFn = jest.fn(() => undefined); + logSpy.mockImplementation(logSpyFn); - it("throws no matching type on weird input", () => { - const object = { twoNumberRange: 12 }; - expect(() => - validateAndCoerceTypes({ ...defNoLogParams, object }) - ).toThrow(/^Failed Yahoo/); - }); + const testSchema = Type.Object({ + aNumber: YahooNumber, }); - - describe("failures", () => { - it("fails on invalid options usage", () => { - const options = { period1: true }; - expect(() => - validateAndCoerceTypes({ - ...defNoLogParams, - object: options, - type: "options", - schemaKey: "#/definitions/HistoricalOptions", - source: "historical-in-validate.spec", - options: { ...defNoLogParams.options, logOptionsErrors: false }, - }) - ).toThrow(InvalidOptionsError); - }); - - it("fails on error", () => { - const object = { date: { weird: 123 } }; - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ ...defNoLogParams, object }); - } catch (e) { - error = e; - } - - /* @ts-ignore */ - expect(error).toBeDefined(); - - /* @ts-ignore */ - if (!error) return; - expect(error.message).toMatch(/Failed Yahoo Schema/); - - /* @ts-ignore */ - const error0 = error.errors[0]; - expect(error0).toBeDefined(); - expect(error0.keyword).toBe("yahooFinanceType"); - expect(error0.message).toBe("No matching type"); - expect(error0.params).toBeDefined(); - - if (!error0.params) return; - expect(error0.params.schema).toBe("date"); - expect(error0.params.data).toBe(object.date); - expect(error0.instancePath).toBe("/date"); - expect(error0.schemaPath).toBe("#/properties/date/yahooFinanceType"); - }); - - it("fails on invalid schema key", () => { - expect(() => - validateAndCoerceTypes({ - ...defNoLogParams, - schemaKey: "SOME_MISSING_KEY", - }) - ).toThrow(/No such schema/); - }); - - // i.e. on output not from bin/modify-schema - it('fails when yahooFinanceType is not "date"|"number"', () => { - const schema = { yahooFinanceType: "impossible" }; - const validate = ajv.compile(schema); - expect(() => validate({})).toThrow(/No such yahooFinanceType/); + const testCase = { aNumber: "foo" }; + expect(() => { + validateAndCoerceTypebox({ + type: "options", + data: testCase, + schema: testSchema, + options: { + // @ts-ignore + logErrors: "bananas", + logOptionsErrors: true, + }, }); + }).toThrow(); + expect(logSpy).toHaveBeenCalledTimes(1); + expect(logSpyFn).toMatchInlineSnapshot(` + [MockFunction] { + "calls": [ + [ + "[yahooFinance] Invalid options ("{ + "type": 62, + "schema": { + "title": "YahooNumber", + "anyOf": [ + { + "title": "RawNumber", + "type": "object", + "properties": { + "raw": { + "type": "number" + } + }, + "required": [ + "raw" + ] + }, + { + "type": "number" + } + ] + }, + "path": "/aNumber", + "value": "foo", + "message": "Expected union value" + }")", + ], + ], + "results": [ + { + "type": "return", + "value": undefined, + }, + ], + } + `); + }); - it("logs errors when logErrors=true", () => { - const origConsole = console; - const fakeConsole = { - error: jest.fn(), - log: jest.fn(), - dir: jest.fn(), - }; - - /* @ts-ignore */ - console = fakeConsole; - const object = { requiredRequired: {} }; - expect(() => - validateAndCoerceTypes({ - ...defLogParams, - object, - }) - ).toThrow("Failed Yahoo Schema validation"); - console = origConsole; + it("Should not log options errors when logOptionsErrors = false", () => { + const logSpy = jest.spyOn(console, "error"); + const logSpyFn = jest.fn(() => undefined); + logSpy.mockImplementation(logSpyFn); - expect(fakeConsole.log).toHaveBeenCalled(); + const testSchema = Type.Object({ + aNumber: YahooNumber, + }); + const testCase = { aNumber: "foo" }; + expect(() => { + validateAndCoerceTypebox({ + type: "options", + data: testCase, + schema: testSchema, + options: { + // @ts-ignore + logErrors: "bananas", + logOptionsErrors: false, + }, }); + }).toThrow(); + expect(logSpy).toHaveBeenCalledTimes(0); + }); - it("does not log errors when logErrors=false", () => { - const origConsole = console; - const fakeConsole = { - error: jest.fn(), - log: jest.fn(), - dir: jest.fn(), - }; - - /* @ts-ignore */ - console = fakeConsole; - const object = { requiredRequired: {} }; - expect(() => - validateAndCoerceTypes({ - ...defNoLogParams, - object, - }) - ).toThrow("Failed Yahoo Schema validation"); - console = origConsole; + it("Should not log errors when logErrors = true and validation succeeds", () => { + const logSpy = jest.spyOn(console, "log"); + const logSpyFn = jest.fn(() => undefined); + logSpy.mockImplementation(logSpyFn); - expect(fakeConsole.log).not.toHaveBeenCalled(); - expect(fakeConsole.error).not.toHaveBeenCalled(); - expect(fakeConsole.dir).not.toHaveBeenCalled(); + const testSchema = Type.Object({ + aNumber: YahooNumber, + }); + const testCase = { aNumber: 10 }; + expect(() => { + validateAndCoerceTypebox({ + type: "result", + data: testCase, + schema: testSchema, + options: { logErrors: true }, }); + }).not.toThrow(); + expect(logSpy).toHaveBeenCalledTimes(0); + }); - it("returns results/errors in error object", () => { - const object = { noAdditional: { additional: true } }; - - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ - ...defNoLogParams, - object, - }); - } catch (e) { - error = e; - } - - expect(error).toBeDefined(); - expect(error.message).toMatch(/Failed Yahoo/); - expect(error.result).toBe(object); - expect(error.errors).toBeType("array"); + it("Should still throw if an unexpected error occurs", () => { + const testCase = { aNumber: 10 }; + expect(() => { + validateAndCoerceTypebox({ + type: "result", + data: testCase, + // Create a TypeError to ensure that we still crash hard even + // if it's not an error we expect + // @ts-ignore + schema: undefined, }); + }).toThrow(TypeError); + }); +}); - it("returns ref to problem data in error object", () => { - const object = { noAdditional: { additional: true }, number: "str" }; - - let error: FailedYahooValidationError | any; - try { - validateAndCoerceTypes({ - ...defNoLogParams, - object, - }); - } catch (e) { - error = e; - } - - expect(error).toBeDefined(); - expect(error.message).toMatch(/Failed Yahoo/); - - let e; - - e = error.errors[0]; - expect(e.params).toMatchObject({ - data: "str", - schema: "number", - }); - expect(e.instancePath).toBe("/number"); - expect(e.data).toBe("str"); +describe("_internalThrowOnAddtionalProperties", () => { + const schema = Type.Object( + { name: Type.String() }, + { additionalProperties: Type.Any() } + ); + + const testCase = { name: "Gadi", aTotallyUnrelatedKey: "foo" }; + it("Should allow additional properties if a schema does when _throwOnAdditionalProperties is false", () => { + const result = validateAndCoerceTypebox({ + type: "result", + data: testCase, + options: { + _internalThrowOnAdditionalProperties: false, + }, + schema, + }); - e = error.errors[1]; - expect(e.instancePath).toBe("/noAdditional"); - expect(e.params).toMatchObject({ - additionalProperty: "additional", - }); - expect(e.data).toBe(object.noAdditional); + expect(result).toMatchInlineSnapshot(` + { + "aTotallyUnrelatedKey": "foo", + "name": "Gadi", + } + `); + }); + it("Should not allow additional properties even if a schema does when _throwOnAdditionalProperties is true", () => { + expect(() => { + validateAndCoerceTypebox({ + type: "options", + data: testCase, + schema: schema, + options: { + _internalThrowOnAdditionalProperties: true, + }, }); - }); + }).toThrow(); }); }); diff --git a/src/lib/validateAndCoerceTypes.ts b/src/lib/validateAndCoerceTypes.ts index 17e7956b..b48105a5 100644 --- a/src/lib/validateAndCoerceTypes.ts +++ b/src/lib/validateAndCoerceTypes.ts @@ -1,366 +1,95 @@ -import Ajv from "ajv"; -import type { SchemaValidateFunction } from "ajv/dist/types"; -import addFormats from "ajv-formats"; - -//import schema from '../../schema.json'; -import schema from "../../schema.json"; import pkg from "../../package.json"; -import { InvalidOptionsError, FailedYahooValidationError } from "./errors.js"; - -// https://ajv.js.org/docs/api.html#options -export const ajv = new Ajv({ - // All rules, all errors. Don't end early after first error. - allErrors: true, - // Allow multiple non-null types, like in TypeSript. - allowUnionTypes: true, -}); -addFormats(ajv); - -ajv.addKeyword({ - keyword: "yahooFinanceType", - modifying: true, - errors: true, - schema: true, - compile(schema /*, parentSchema, it */) { - const validate: SchemaValidateFunction = (data, dataCtx) => { - const { parentData, parentDataProperty } = dataCtx; - - function set(value: any) { - parentData[parentDataProperty] = value; - return true; - } - - if (schema === "number" || schema === "number|null") { - if (typeof data === "number") return true; - - if (typeof data === "string") { - let float = Number.parseFloat(data); - if (Number.isNaN(float)) { - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: "Number.parseFloat returned NaN", - params: { schema, data }, - }); - return false; - } - return set(float); - } - - if (data === null) { - if (schema === "number|null") { - return true; - } else { - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: "Expecting number'ish but got null", - params: { schema, data }, - }); - return false; - } - } - - if (typeof data === "object") { - if (Object.keys(data).length === 0) { - // Value of {} becomes null - // Note, TypeScript types should be "number | null" - if (schema === "number|null") { - return set(null); - } else { - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: - "Got {}->null for 'number', did you want 'number | null' ?", - params: { schema, data }, - }); - return false; - } - } - if (typeof data.raw === "number") return set(data.raw); - } - } else if (schema === "date" || schema === "date|null") { - if (data instanceof Date) { - // Validate existing date objects. - // Generally we receive JSON but in the case of "historical", the - // csv parser does the date conversion, and we want to validate - // afterwards. - return true; - } - - if (typeof data === "number") return set(new Date(data * 1000)); - - if (data === null) { - if (schema === "date|null") { - return true; - } else { - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: "Expecting date'ish but got null", - params: { schema, data }, - }); - return false; - } - } - - if (typeof data === "object") { - if (Object.keys(data).length === 0) { - // Value of {} becomes null - // Note, TypeScript types should be "data | null" - if (schema === "date|null") { - return set(null); - } else { - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: - "Got {}->null for 'date', did you want 'date | null' ?", - params: { schema, data }, - }); - return false; - } - } - if (typeof data.raw === "number") - return set(new Date(data.raw * 1000)); - } - - if (typeof data === "string") { - if ( - data.match(/^\d{4,4}-\d{2,2}-\d{2,2}$/) || - data.match( - /^\d{4,4}-\d{2,2}-\d{2,2}T\d{2,2}:\d{2,2}:\d{2,2}(\.\d{3,3})?Z$/ - ) - ) - return set(new Date(data)); - } - } else if (schema === "DateInMs") { - return set(new Date(data)); - } else if (schema === "TwoNumberRange") { - if ( - typeof data === "object" && - typeof data.low === "number" && - typeof data.high === "number" - ) - return true; - if (typeof data === "string") { - const parts = data.split("-").map(parseFloat); - if (Number.isNaN(parts[0]) || Number.isNaN(parts[1])) { - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: - "Number.parseFloat returned NaN: [" + parts.join(",") + "]", - params: { schema, data }, - }); - return false; - } - return set({ low: parts[0], high: parts[1] }); - } - } else { - throw new Error("No such yahooFinanceType: " + schema); - } - - validate.errors = validate.errors || []; - validate.errors.push({ - keyword: "yahooFinanceType", - message: "No matching type", - params: { schema, data }, - }); - return false; - }; - - return validate; - }, -}); - -ajv.addSchema(schema); - -/* istanbul ignore next */ -const logObj = - typeof process !== "undefined" && process?.stdout?.isTTY - ? (obj: any) => console.dir(obj, { depth: 4, colors: true }) - : (obj: any) => console.log(JSON.stringify(obj, null, 2)); - -export function resolvePath(obj: any, instancePath: string) { - const path = instancePath.split("/"); - let ref = obj; - for (let i = 1; i < path.length; i++) ref = ref[path[i]]; - return ref; -} - -export interface ValidationOptions { - logErrors?: boolean; - logOptionsErrors?: boolean; -} - -export interface ValidateParams { - source: string; - type: "options" | "result"; - object: object; - schemaKey: string; - options: ValidationOptions; -} - -function disallowAdditionalProps(show = false) { - const disallowed = new Set(); - // @ts-ignore: this can cause a breaking catch-22 on schema generation - for (let key of Object.keys(schema.definitions)) { - if (key.match(/Options$/)) { - continue; - } - // @ts-ignore - const def = schema.definitions[key]; - if (def.type === "object" && def.additionalProperties === undefined) { - def.additionalProperties = false; - disallowed.add(key); - } +import { FailedYahooValidationError, InvalidOptionsError } from "./errors.js"; +import { StaticDecode, type TSchema } from "@sinclair/typebox"; +import { + TransformDecodeCheckError, + TransformDecodeError, + Value, +} from "@sinclair/typebox/value"; +import { ValidationOptions } from "./options"; + +const handleResultError = ( + e: TransformDecodeError | TransformDecodeCheckError, + options: ValidationOptions +) => { + const title = e.schema.title; + if (options.logErrors) { + console.log(JSON.stringify(e, null, 2)); + console.log(` + This may happen intermittently and you should catch errors appropriately. + However: 1) if this recently started happening on every request for a symbol + that used to work, Yahoo may have changed their API. 2) If this happens on + every request for a symbol you've never used before, but not for other + symbols, you've found an edge-case (OR, we may just be protecting you from + "bad" data sometimes stored for e.g. misspelt symbols on Yahoo's side). + Please see if anyone has reported this previously: + + ${pkg.repository}/issues?q=is%3Aissue+${title} + + or open a new issue (and mention the symbol): ${pkg.name} v${pkg.version} + + ${pkg.repository}/issues/new?labels=bug%2C+validation&template=validation.md&title=${title} + + For information on how to turn off the above logging or skip these errors, + see https://github.com/gadicc/node-yahoo-finance2/tree/devel/docs/validation.md. + + At the end of the doc, there's also a section on how to + [Help Fix Validation Errors](https://github.com/gadicc/node-yahoo-finance2/blob/devel/docs/validation.md#help-fix) + in case you'd like to contribute to the project. Most of the time, these + fixes are very quick and easy; it's just hard for our small core team to keep up, + so help is always appreciated! + `); } - /* istanbul ignore next */ - if (show) - console.log( - "Disallowed additional props in " + Array.from(disallowed).join(", ") + throw new FailedYahooValidationError("Failed Yahoo Schema validation", { + result: e.value, + errors: [e], + }); +}; + +const handleOptionsError = ( + e: TransformDecodeCheckError | TransformDecodeError, + { logOptionsErrors }: ValidationOptions +) => { + if (logOptionsErrors) { + console.error( + `[yahooFinance] Invalid options ("${JSON.stringify(e.error, null, 2)}")` ); -} - -if (process.env.NODE_ENV === "test") disallowAdditionalProps(); + } + throw new InvalidOptionsError("Validation called with invalid options"); +}; -function validate({ - source, +export const validateAndCoerceTypebox = ({ type, - object, - schemaKey, + data, + schema, options, -}: ValidateParams): void { - const validator = ajv.getSchema(schemaKey); - if (!validator) throw new Error("No such schema with key: " + schemaKey); - - const valid = validator(object); - if (valid) return; - - if (type === "result") { - /* istanbul ignore else */ - if (validator.errors) { - let origData: any = false; - - validator.errors.forEach((error) => { - // For now let's ignore the base object which could be huge. - /* istanbul ignore else */ - if (error.instancePath !== "") - // Note, not the regular ajv data value from verbose:true - error.data = resolvePath(object, error.instancePath); - - if (error.schemaPath === "#/anyOf") { - if (origData === false) { - origData = error.data; - } else if (origData === error.data) { - error.data = "[shortened by validateAndCoerceTypes]"; - } - } - }); - - // Becaue of the "anyOf" in quote, errors are huuuuge and mostly - // irrelevant... so let's filter out (some of) the latter - validator.errors = validator.errors.filter((error) => { - if (error.schemaPath.startsWith("#/definitions/Quote")) { - const schemaQuoteType = error.schemaPath - .substring(19) - .split("/")[0] - .toUpperCase(); - - /* - * Filter out entries for non-matching schema type, i.e. - * { - * schemaPath: '#/definitions/QuoteCryptoCurrency/properties...' - * data: { - * quoteType: "EQUITY" - * } - * } - */ - if ( - typeof error.data === "object" && - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: Properrty "quoteType" does not exist on type "object" - error.data?.quoteType !== schemaQuoteType - ) - return false; - - /* - * Filter out the non-matching "const" for the above. - * { - * schemaPath: '#/definitions/QuoteCryptoCurrency/properties/quoteType/const' - * keyword: "const", - * params: { allowedValue: "CRYPTOCURRENCY"}}, - * data: "EQUITY" - * } - */ - if ( - typeof error.data === "string" && - error.params?.allowedValue === schemaQuoteType - ) - return false; - } - return true; - }); - - // In the case of there being NO match in #anyOf, bring back the data - if ( - validator.errors.length === 1 && - validator.errors[0].schemaPath === "#/anyOf" - ) - validator.errors[0].data = origData; - } - - if (options.logErrors === true) { - const title = encodeURIComponent("Failed validation: " + schemaKey); - console.log( - "The following result did not validate with schema: " + schemaKey - ); - logObj(validator.errors); - // logObj(object); - console.log(` -This may happen intermittently and you should catch errors appropriately. -However: 1) if this recently started happening on every request for a symbol -that used to work, Yahoo may have changed their API. 2) If this happens on -every request for a symbol you've never used before, but not for other -symbols, you've found an edge-case (OR, we may just be protecting you from -"bad" data sometimes stored for e.g. misspelt symbols on Yahoo's side). -Please see if anyone has reported this previously: - - ${pkg.repository}/issues?q=is%3Aissue+${title} - -or open a new issue (and mention the symbol): ${pkg.name} v${pkg.version} - - ${pkg.repository}/issues/new?labels=bug%2C+validation&template=validation.md&title=${title} - -For information on how to turn off the above logging or skip these errors, -see https://github.com/gadicc/node-yahoo-finance2/tree/devel/docs/validation.md. - -At the end of the doc, there's also a section on how to -[Help Fix Validation Errors](https://github.com/gadicc/node-yahoo-finance2/blob/devel/docs/validation.md#help-fix) -in case you'd like to contribute to the project. Most of the time, these -fixes are very quick and easy; it's just hard for our small core team to keep up, -so help is always appreciated! -`); - } /* if (logErrors) */ - - throw new FailedYahooValidationError("Failed Yahoo Schema validation", { - result: object, - errors: validator.errors, - }); - } /* if (type === 'options') */ else { - if (options.logOptionsErrors === true) { - console.error( - `[yahooFinance.${source}] Invalid options ("${schemaKey}")` - ); - logObj({ errors: validator.errors, input: object }); +}: { + type?: "result" | "options"; + data: unknown; + schema: T; + options: ValidationOptions; +}): StaticDecode => { + try { + const validationSchema = options._internalThrowOnAdditionalProperties + ? { ...schema, additionalProperties: false } + : schema; + return Value.Decode(validationSchema, data); + } catch (e) { + if ( + e instanceof TransformDecodeError || + e instanceof TransformDecodeCheckError + ) { + // TODO: The existing implementation of 'validate' assumes that the `type` parameter may not be provided + // and defaults to validating the options if it is not. + // We should probably explore validating this further up in the call chain. + // It'd be nice to do this in the body of a module (e.g. search) so that we can avoid + // polluting core code with type checks and edge cases + type === "result" + ? handleResultError(e, options) + : handleOptionsError(e, options); } - throw new InvalidOptionsError( - `yahooFinance.${source} called with invalid options.` - ); + throw e; } -} +}; -export { disallowAdditionalProps }; -export default validate; +export default validateAndCoerceTypebox; diff --git a/src/lib/yahooFinanceTypes.spec.ts b/src/lib/yahooFinanceTypes.spec.ts new file mode 100644 index 00000000..abfcf60b --- /dev/null +++ b/src/lib/yahooFinanceTypes.spec.ts @@ -0,0 +1,239 @@ +import { Value } from "@sinclair/typebox/value"; +import { + EmptyObjectCoerceToNull, + EpochTimestamp, + ISOStringDate, + NullableYahooFinanceDate, + NullableYahooNumber, + RawDateObject, + RawNumber, + TwoNumberRangeString, + YahooDateInMs, + YahooFinanceDate, + YahooNumber, + YahooTwoNumberRange, +} from "./yahooFinanceTypes"; + +describe("NullableYahooNumber", () => { + it("Should pass regular numbers", () => { + const testCase = 2; + expect(Value.Check(NullableYahooNumber, testCase)).toBe(true); + expect(Value.Decode(NullableYahooNumber, testCase)).toBe(2); + }); + it("Should coerce raw number objects", () => { + const testCase = { raw: 0.006599537, fmt: "6.5%" }; + expect(Value.Check(NullableYahooNumber, testCase)).toBe(true); + expect(Value.Decode(NullableYahooNumber, testCase)).toBe(0.006599537); + }); + it("Should pass nulls through", () => { + const testCase = null; + expect(Value.Check(NullableYahooNumber, testCase)).toBe(true); + expect(Value.Decode(NullableYahooNumber, testCase)).toBe(null); + }); + it("Should reject invalid objects", () => { + const testCase = { number: 10 }; + expect(Value.Check(NullableYahooNumber, testCase)).toBe(false); + }); + + it("Should coerce an empty object to null", () => { + const testCase = {}; + expect(Value.Check(NullableYahooNumber, testCase)).toBe(true); + expect(Value.Decode(NullableYahooNumber, testCase)).toBe(null); + }); + + it("Should fail if data.raw is not a number", () => { + const testCase = { raw: true, fmt: "bananas" }; + expect(Value.Check(NullableYahooNumber, testCase)).toBe(false); + }); +}); +describe("YahooNumber", () => { + it("Should not accept empty objects as valid inputs", () => { + const testCase = {}; + expect(Value.Check(YahooNumber, testCase)).toBe(false); + }); +}); + +describe("YahooTwoNumberRange", () => { + it("Should correctly parse a string two number range", () => { + const testCase = "-549.867 - -541.19"; + expect(Value.Check(YahooTwoNumberRange, testCase)).toBe(true); + expect(Value.Decode(YahooTwoNumberRange, testCase)).toMatchInlineSnapshot(` + { + "high": -541.19, + "low": -549.867, + } + `); + }); + + it("Should throw for invalid string ranges", () => { + const testCase = "X - 523.12"; + expect(Value.Check(YahooTwoNumberRange, testCase)).toBe(false); + }); + it("Should reject null", () => { + const testCase = null; + expect(Value.Check(YahooTwoNumberRange, testCase)).toBe(false); + }); + + it("Should pass through a valid object range", () => { + const testCase = { low: 10.24, high: 100.453 }; + expect(Value.Check(YahooTwoNumberRange, testCase)).toBe(true); + expect(Value.Decode(YahooTwoNumberRange, testCase)).toMatchInlineSnapshot(` + { + "high": 100.453, + "low": 10.24, + } + `); + }); + + it("Should fail if an invalid type is provided for an expected object key", () => { + const testCase = { low: true, high: undefined }; + expect(Value.Check(YahooTwoNumberRange, testCase)).toBe(false); + }); + + it("Should fail if an object is malformed", () => { + const testCase = { africa: "by Toto" }; + expect(Value.Check(YahooTwoNumberRange, testCase)).toBe(false); + }); +}); + +describe("YahooFinanceDate", () => { + it("Should coerce a raw date object correctly", () => { + const testCase = { raw: 1612313997 }; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(true); + const decoded = Value.Decode(YahooFinanceDate, testCase); + expect(decoded).toMatchInlineSnapshot(`2021-02-03T00:59:57.000Z`); + expect(+decoded).toBe(testCase.raw * 1000); + }); + + it("Should coerce timestamps from the UNIX epoch to the date using MS", () => { + const testCase = 1612313997; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(true); + const decoded = Value.Decode(YahooFinanceDate, testCase); + expect(+decoded).toBe(testCase * 1000); + }); + + it("Should parse an ISO8601 date correctly", () => { + const testCase = "2024-02-29"; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(true); + expect(Value.Decode(YahooFinanceDate, testCase)).toMatchInlineSnapshot( + `2024-02-29T00:00:00.000Z` + ); + }); + + it("Should parse an ISO8601 datetime correctly", () => { + const testCase = "2024-05-04T13:24:41.100Z"; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(true); + expect(Value.Decode(YahooFinanceDate, testCase)).toMatchInlineSnapshot( + `2024-05-04T13:24:41.100Z` + ); + }); + + it("Should parse an ISO8601 year correctly", () => { + const testCase = "2024-05-04"; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(true); + expect(Value.Decode(YahooFinanceDate, testCase)).toMatchInlineSnapshot( + `2024-05-04T00:00:00.000Z` + ); + }); + + it("Should reject obviously garbled strings", () => { + const testCase = "fdsfsdfsd"; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(false); + }); + it("Should reject null", () => { + const testCase = null; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(false); + }); + + it("Should reject an empty object", () => { + const testCase = {}; + expect(Value.Check(YahooFinanceDate, testCase)).toBe(false); + }); +}); + +describe("NullableYahooFinanceDate", () => { + it("Should accept null", () => { + const testCase = null; + expect(Value.Check(NullableYahooFinanceDate, testCase)).toBe(true); + expect(Value.Decode(NullableYahooFinanceDate, testCase)).toBe(null); + }); + it("Should coerce an empty object to null", () => { + const testCase = {}; + expect(Value.Check(NullableYahooFinanceDate, testCase)).toBe(true); + expect(Value.Decode(NullableYahooFinanceDate, testCase)).toBe(null); + }); +}); + +describe("YahooDateInMs", () => { + it("Should coerce a date in ms to a date object", () => { + const testCase = 1612313997000; + expect(Value.Check(YahooDateInMs, testCase)).toBe(true); + expect(Value.Decode(YahooDateInMs, testCase)).toMatchInlineSnapshot( + `2021-02-03T00:59:57.000Z` + ); + }); +}); + +/* +This test suite isn't super essential, it's just here for the sake of completeness. + +To generate decoded types for Typebox an `encode` function must also be provided. Given that +those functions may be called we should unit test them even though in most cases they are trivial. +*/ +describe("Test building block type encoding", () => { + it("Should encode EmptyObjectCoerceToNull correctly", () => { + const testCase = {}; + expect( + Value.Encode( + EmptyObjectCoerceToNull, + Value.Decode(EmptyObjectCoerceToNull, testCase) + ) + ).toMatchInlineSnapshot(`{}`); + }); + it("Should encode RawNumber correctly", () => { + const testCase = { raw: 10, fmt: "0.06f" }; + expect(Value.Encode(RawNumber, Value.Decode(RawNumber, testCase))) + .toMatchInlineSnapshot(` + { + "raw": 10, + } + `); + }); + it("Should encode TwoNumberRangeString correctly", () => { + const testCase = "10 - 20"; + expect( + Value.Encode( + TwoNumberRangeString, + Value.Decode(TwoNumberRangeString, testCase) + ) + ).toBe("10 - 20"); + }); + it("Should encode the epoch timestamp back to seconds", () => { + const testCase = 1000; + expect( + Value.Encode(EpochTimestamp, Value.Decode(EpochTimestamp, testCase)) + ).toBe(testCase); + }); + + it("Should encode the raw date back to an object", () => { + const testCase = { raw: 1000 }; + expect(Value.Encode(RawDateObject, Value.Decode(RawDateObject, testCase))) + .toMatchInlineSnapshot(` + { + "raw": 1000, + } + `); + }); + it("Should encode the ISOStringDate back to a valid ISOString", () => { + const testCase = new Date(1000).toISOString(); + expect( + Value.Encode(ISOStringDate, Value.Decode(ISOStringDate, testCase)) + ).toBe(testCase); + }); + it("Should encode the YahooDateInMs back to MS", () => { + const testCase = +new Date(1000); + expect( + Value.Encode(YahooDateInMs, Value.Decode(YahooDateInMs, testCase)) + ).toBe(testCase); + }); +}); diff --git a/src/lib/yahooFinanceTypes.ts b/src/lib/yahooFinanceTypes.ts new file mode 100644 index 00000000..3cef2f5b --- /dev/null +++ b/src/lib/yahooFinanceTypes.ts @@ -0,0 +1,161 @@ +import { Type } from "@sinclair/typebox"; +import { Value } from "@sinclair/typebox/value"; +import { FormatRegistry } from "@sinclair/typebox"; +import { isDate, isDateTime, isYear } from "./datetime"; + +FormatRegistry.Set("date", isDate); +FormatRegistry.Set("date-time", isDateTime); +FormatRegistry.Set("year", isYear); + +// Strictly must be empty +export const EmptyObjectCoerceToNull = Type.Transform( + Type.Object({}, { maxProperties: 0, title: "EmptyObjectCoerceToNull" }) +) + .Decode(() => null) + .Encode(() => ({})); + +// Technically this will also contain a string 'fmt' key but we don't care because we don't use it +export const RawNumber = Type.Transform( + Type.Object( + { + raw: Type.Number(), + }, + { + title: "RawNumber", + } + ) +) + .Decode((v) => v.raw) + .Encode((v) => ({ raw: v })); + +export const TwoNumberRangeString = Type.Transform( + Type.RegExp(/^(-?\d+(?:\.\d+)?) - (-?\d+(?:\.\d+)?)$/g, { + title: "TwoNumberRangeString", + }) +) + .Decode((value) => { + // Split the two numbers allowing for negatives on either side + const validatedNumbers = value.match(/-?\d+(?:\.\d+)?/g); + + if (!validatedNumbers) { + throw new Error(`Unable to decode number range from: ${value}`); + } + + const [low, high] = validatedNumbers.map((number) => parseFloat(number)); + + if (isNaN(low) || isNaN(high)) { + throw new Error( + `Unable to decode number range from: ${value}. Decoded value for low is: ${low}, decoded value for high is: ${high}` + ); + } + return { low, high }; + }) + .Encode(({ low, high }) => `${low} - ${high}`); + +const TwoNumberRange = Type.Object( + { + low: Type.Number(), + high: Type.Number(), + }, + { title: "TwoNumberRange" } +); + +export const EpochTimestamp = Type.Transform(Type.Number()) + .Decode((v) => new Date(v * 1000)) + .Encode((v) => +v / 1000); + +export const RawDateObject = Type.Transform( + Type.Object( + { + raw: EpochTimestamp, + }, + { title: "RawDateObject" } + ) +) + .Decode((v) => v.raw) + .Encode((v) => ({ + raw: Value.Encode(EpochTimestamp, v), + })); + +export const ISOStringDate = Type.Transform( + Type.Union( + [ + Type.String({ format: "date" }), + Type.String({ format: "year" }), + Type.String({ format: "date-time" }), + ], + { title: "ISOStringDate" } + ) +) + .Decode((v) => new Date(v)) + .Encode((v) => v.toISOString()); + +export const YahooFinanceDate = Type.Union( + [Type.Date(), EpochTimestamp, RawDateObject, ISOStringDate], + { title: "YahooFinanceDate" } +); + +/** + * Validates and decodes all nullable date representations produced by Yahoo + * e.g. accepted inputs include: + * - 1612313997 + * - { raw: 1612313997 } + * - "2024-02-29" + * - "2024-05-04T13:24:41.100Z" + * - {} (coerces to null) + */ +export const NullableYahooFinanceDate = Type.Union( + [YahooFinanceDate, Type.Null(), EmptyObjectCoerceToNull], + { + title: "NullableYahooFinanceDate", + } +); + +/** + * Validates and decodes all number types and coerces to a number + * e.g. accepted inputs include: + * - 10.54 + * - {raw: 10.54, fmt: "%6f"} + */ + +export const YahooNumber = Type.Union([RawNumber, Type.Number()], { + title: "YahooNumber", +}); + +/** + * Validates and decodes dates represented as milliseconds since the unix epoch to Date objects + * e.g. accepted inputs include: + * - 1612313997000 + */ +export const YahooDateInMs = Type.Transform( + Type.Number({ title: "YahooDateInMs" }) +) + .Decode((v) => new Date(v)) + .Encode((v) => +v); +/** + * Validates and decodes all nullable number types and coerces to a number or null + * e.g. accepted inputs include: + * - 10.54 + * - {raw: 10.54, fmt: "%6f"} + * - null + * - {} (coerces to null) + */ +export const NullableYahooNumber = Type.Union( + [RawNumber, EmptyObjectCoerceToNull, Type.Number(), Type.Null()], + { + title: "NullableYahooNumber", + } +); + +/** + * Validates and decodes 2 number ranges to a consistent object format of { low: , high: } + * e.g. accepted inputs include: + * - { low: 103, high: 10043 } + * - "-32432 - 453" + */ +export const YahooTwoNumberRange = Type.Union( + [TwoNumberRange, TwoNumberRangeString], + { + title: "YahooTwoNumberRange", + } +); diff --git a/src/modules/chart.ts b/src/modules/chart.ts index 712f63fa..d1f87279 100644 --- a/src/modules/chart.ts +++ b/src/modules/chart.ts @@ -1,157 +1,276 @@ // Co-authored by @gadicc, @PythonCreator27 and @huned. +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { YahooFinanceDate, YahooNumber } from "../lib/yahooFinanceTypes.js"; + +const ChartMetaTradingPeriod = Type.Object( + { + timezone: Type.String(), // "EST", + start: YahooFinanceDate, // new Date(1637355600 * 1000), + end: YahooFinanceDate, // new Date(1637370000 * 10000), + gmtoffset: YahooNumber, // -18000 + }, + { + additionalProperties: Type.Any(), + title: "ChartMetaTradingPeriod", + } +); + +const ChartMetaTradingPeriods = Type.Object( + { + pre: Type.Optional(Type.Array(Type.Array(ChartMetaTradingPeriod))), + post: Type.Optional(Type.Array(Type.Array(ChartMetaTradingPeriod))), + regular: Type.Optional(Type.Array(Type.Array(ChartMetaTradingPeriod))), + }, + { + additionalProperties: Type.Any(), + title: "ChartMetaTradingPeriods", + } +); + +const ChartResultArrayQuote = Type.Object( + { + date: YahooFinanceDate, + high: Type.Union([YahooNumber, Type.Null()]), + low: Type.Union([YahooNumber, Type.Null()]), + open: Type.Union([YahooNumber, Type.Null()]), + close: Type.Union([YahooNumber, Type.Null()]), + volume: Type.Union([YahooNumber, Type.Null()]), + adjclose: Type.Optional(Type.Union([YahooNumber, Type.Null()])), + }, + { + additionalProperties: Type.Any(), + title: "ChartResultArrayQuote", + } +); + +const ChartEventDividend = Type.Object( + { + amount: YahooNumber, + date: YahooFinanceDate, + }, + { + additionalProperties: Type.Any(), + title: "ChartEventDividend", + } +); -export interface ChartResultObject { - [key: string]: any; - meta: ChartMeta; - timestamp?: Array; - events?: ChartEventsObject; - indicators: ChartIndicatorsObject; -} - -export interface ChartResultArray { - meta: ChartMeta; - events?: ChartEventsArray; - quotes: Array; -} - -export interface ChartResultArrayQuote { - [key: string]: any; - date: Date; - high: number | null; - low: number | null; - open: number | null; - close: number | null; - volume: number | null; - adjclose?: number | null; -} - -export interface ChartMeta { - [key: string]: any; - currency: string; // "USD" - symbol: string; // "AAPL", - exchangeName: string; // "NMS", - instrumentType: string; // "EQUITY", - firstTradeDate: Date | null; // new Date(345479400 * 1000); null in e.g. "APS.AX" - regularMarketTime: Date; // new Date(1637355602 * 1000), - gmtoffset: number; // -18000, - timezone: string; /// "EST", - exchangeTimezoneName: string; // "America/New_York", - regularMarketPrice: number; // 160.55, - chartPreviousClose?: number; // 79.75; missing in e.g. "APS.AX" - previousClose?: number; // 1137.06 - scale?: number; // 3, - priceHint: number; // 2, - currentTradingPeriod: { - [key: string]: any; - pre: ChartMetaTradingPeriod; - regular: ChartMetaTradingPeriod; - post: ChartMetaTradingPeriod; - }; - tradingPeriods?: ChartMetaTradingPeriods; - dataGranularity: string; // "1d", - range: string; // "", - validRanges: Array; // ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"] -} - -export interface ChartMetaTradingPeriod { - [key: string]: any; - timezone: string; // "EST", - start: Date; // new Date(1637355600 * 1000), - end: Date; // new Date(1637370000 * 10000), - gmtoffset: number; // -18000 -} - -export interface ChartMetaTradingPeriods { - [key: string]: any; - pre?: Array>; - post?: Array>; - regular?: Array>; -} - -export interface ChartEventsObject { - [key: string]: any; - dividends?: ChartEventDividends; - splits?: ChartEventSplits; -} - -export interface ChartEventsArray { - [key: string]: any; - dividends?: Array; - splits?: Array; -} - -export interface ChartEventDividends { - [key: string]: ChartEventDividend; -} - -export interface ChartEventDividend { - [key: string]: any; - amount: number; - date: Date; -} - -export interface ChartEventSplits { - [key: string]: ChartEventSplit; -} - -export interface ChartEventSplit { - [key: string]: any; - date: Date; // new Date(1598880600 * 1000) - numerator: number; // 4 - denominator: number; // 1 - splitRatio: string; // "4:1" -} - -export interface ChartIndicatorsObject { - [key: string]: any; - quote: Array; - adjclose?: Array; -} - -export interface ChartIndicatorQuote { - [key: string]: any; - high: Array; - low: Array; - open: Array; - close: Array; - volume: Array; -} - -export interface ChartIndicatorAdjclose { - [key: string]: any; - adjclose?: Array; // Missing in e.g. "APS.AX" -} - -export interface ChartOptions { - period1: Date | string | number; - period2?: Date | string | number; - useYfid?: boolean; // true - interval?: - | "1m" - | "2m" - | "5m" - | "15m" - | "30m" - | "60m" - | "90m" - | "1h" - | "1d" - | "5d" - | "1wk" - | "1mo" - | "3mo"; - includePrePost?: boolean; // true - events?: string; // 'history', - lang?: string; // "en-US" - return?: "array" | "object"; -} +const ChartEventDividends = Type.Object( + {}, + { + additionalProperties: ChartEventDividend, + title: "ChartEventDividends", + } +); + +const ChartEventSplit = Type.Object( + { + date: YahooFinanceDate, // new Date(1598880600 * 1000) + numerator: YahooNumber, // 4 + denominator: YahooNumber, // 1 + splitRatio: Type.String(), // "4:1" + }, + { + additionalProperties: Type.Any(), + } +); +const ChartEventsArray = Type.Object( + { + dividends: Type.Optional(Type.Array(ChartEventDividend)), + splits: Type.Optional(Type.Array(ChartEventSplit)), + }, + { + additionalProperties: Type.Any(), + title: "ChartEventsArray", + } +); + +const ChartMeta = Type.Object( + { + currency: Type.String(), // "USD" + symbol: Type.String(), // "AAPL", + exchangeName: Type.String(), // "NMS", + instrumentType: Type.String(), // "EQUITY", + firstTradeDate: Type.Union([YahooFinanceDate, Type.Null()]), // new Date(345479400 * 1000); null in e.g. "APS.AX" + regularMarketTime: YahooFinanceDate, // new Date(1637355602 * 1000), + gmtoffset: YahooNumber, // -18000, + timezone: Type.String(), /// "EST", + exchangeTimezoneName: Type.String(), // "America/New_York", + regularMarketPrice: YahooNumber, // 160.55, + chartPreviousClose: Type.Optional(YahooNumber), // 79.75; missing in e.g. "APS.AX" + previousClose: Type.Optional(YahooNumber), // 1137.06 + scale: Type.Optional(YahooNumber), // 3, + priceHint: YahooNumber, // 2, + currentTradingPeriod: Type.Object( + { + pre: ChartMetaTradingPeriod, + regular: ChartMetaTradingPeriod, + post: ChartMetaTradingPeriod, + }, + { + additionalProperties: Type.Any(), + } + ), + tradingPeriods: Type.Optional(ChartMetaTradingPeriods), + dataGranularity: Type.String(), // "1d", + range: Type.String(), // "" + validRanges: Type.Array(Type.String()), // ["1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max"] + }, + { + additionalProperties: Type.Any(), + title: "ChartMeta", + } +); + +const ChartResultArraySchema = Type.Object( + { + meta: ChartMeta, + events: Type.Optional(ChartEventsArray), + quotes: Type.Array(ChartResultArrayQuote), + }, + { title: "ChartResultArray" } +); + +const ChartEventSplits = Type.Object( + {}, + { + additionalProperties: ChartEventSplit, + title: "ChartEventSplits", + } +); + +const ChartIndicatorQuote = Type.Object( + { + high: Type.Array(Type.Union([YahooNumber, Type.Null()])), + low: Type.Array(Type.Union([YahooNumber, Type.Null()])), + open: Type.Array(Type.Union([YahooNumber, Type.Null()])), + close: Type.Array(Type.Union([YahooNumber, Type.Null()])), + volume: Type.Array(Type.Union([YahooNumber, Type.Null()])), + }, + { + additionalProperties: Type.Any(), + title: "ChartIndicatorQuote", + } +); + +const ChartIndicatorAdjclose = Type.Object( + { + adjclose: Type.Optional(Type.Array(Type.Union([YahooNumber, Type.Null()]))), // Missing in e.g. "APS.AX" + }, + { + additionalProperties: Type.Any(), + title: "ChartIndicatorAdjClose", + } +); + +const ChartEventsObject = Type.Object( + { + dividends: Type.Optional(ChartEventDividends), + splits: Type.Optional(ChartEventSplits), + }, + { + additionalProperties: Type.Any(), + } +); + +const ChartIndicatorsObject = Type.Object( + { + quote: Type.Array(ChartIndicatorQuote), + adjclose: Type.Optional(Type.Array(ChartIndicatorAdjclose)), + }, + { + additionalProperties: Type.Any(), + title: "ChartIndicatorObject", + } +); +const ChartResultObjectSchema = Type.Object( + { + meta: ChartMeta, + timestamp: Type.Optional(Type.Array(YahooNumber)), + events: Type.Optional(ChartEventsObject), + indicators: ChartIndicatorsObject, + }, + { + additionalProperties: Type.Any(), + title: "ChartResultObject", + } +); + +const ChartOptionsSchema = Type.Object( + { + period1: Type.Union([Type.Date(), Type.String(), YahooNumber]), + period2: Type.Optional( + Type.Union([Type.Date(), Type.String(), YahooNumber]) + ), + useYfid: Type.Optional(Type.Boolean()), // true + interval: Type.Optional( + Type.Union([ + Type.Literal("1m"), + Type.Literal("2m"), + Type.Literal("5m"), + Type.Literal("15m"), + Type.Literal("30m"), + Type.Literal("60m"), + Type.Literal("90m"), + Type.Literal("1h"), + Type.Literal("1d"), + Type.Literal("5d"), + Type.Literal("1wk"), + Type.Literal("1mo"), + Type.Literal("3mo"), + ]) + ), + includePrePost: Type.Optional(Type.Boolean()), // true + events: Type.Optional(Type.String()), // 'history', + lang: Type.Optional(Type.String()), // "en-US" + return: Type.Optional( + Type.Union([Type.Literal("array"), Type.Literal("object")]) + ), + }, + { + title: "ChartOptions", + } +); + +const ChartOptionsWithReturnArraySchema = Type.Composite( + [ + ChartOptionsSchema, + Type.Object({ + return: Type.Optional(Type.Literal("array")), + }), + ], + { + title: "ChartOptionsWithReturnArray", + } +); + +const ChartOptionsWithReturnObjectSchema = Type.Composite( + [ + ChartOptionsSchema, + Type.Object({ + return: Type.Literal("object"), + }), + ], + { + title: "ChartOptionsWithReturnObject", + } +); + +type ChartOptions = Static; +type ChartOptionsWithReturnObject = Static< + typeof ChartOptionsWithReturnObjectSchema +>; +type ChartResultObject = Static; +type ChartOptionsWithReturnArray = Static< + typeof ChartOptionsWithReturnArraySchema +>; +type ChartResultArray = Static; const queryOptionsDefaults: Omit = { useYfid: true, @@ -161,14 +280,6 @@ const queryOptionsDefaults: Omit = { lang: "en-US", return: "array", }; - -export interface ChartOptionsWithReturnArray extends ChartOptions { - return?: "array"; -} -export interface ChartOptionsWithReturnObject extends ChartOptions { - return: "object"; -} - /* --- array input, typed output, honor "return" param --- */ // TODO: make this a deprecration passthrough @@ -203,13 +314,13 @@ export default async function chart( ): Promise { const returnAs = queryOptionsOverrides?.return || "array"; - const result = (await this._moduleExec({ + const result = (await this._moduleExec({ moduleName: "chart", query: { assertSymbol: symbol, url: "https://${YF_QUERY_HOST}/v8/finance/chart/" + symbol, - schemaKey: "#/definitions/ChartOptions", + schema: ChartOptionsSchema, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, transformWith(queryOptions: ChartOptions) { @@ -241,7 +352,7 @@ export default async function chart( }, result: { - schemaKey: "#/definitions/ChartResultObject", + schema: ChartResultObjectSchema, transformWith(result: any) { if (!result.chart) throw new Error("Unexpected result: " + JSON.stringify(result)); @@ -315,6 +426,7 @@ export default async function chart( if (timestamp) for (let i = 0; i < timestamp.length; i++) { result2.quotes[i] = { + // @ts-expect-error (eatkinson): clean this up with type in followup date: new Date(timestamp[i] * 1000), high: result.indicators.quote[0].high[i], volume: result.indicators.quote[0].volume[i], @@ -329,7 +441,9 @@ export default async function chart( result2.events = {}; for (const event of ["dividends", "splits"]) { + // @ts-expect-error (eatkinson): Fix up type in follow up if (result.events[event]) + // @ts-expect-error (eatkinson): Fix up type in follow up result2.events[event] = Object.values(result.events[event]); } } diff --git a/src/modules/dailyGainers.spec.ts b/src/modules/dailyGainers.spec.ts index d7aee67c..7220ccd0 100644 --- a/src/modules/dailyGainers.spec.ts +++ b/src/modules/dailyGainers.spec.ts @@ -7,9 +7,7 @@ describe("dailyGainers", () => { if (process.env.FETCH_DEVEL !== "nocache") it("returns expected result", () => { const devel = "dailyGainers.json"; - return expect( - yf.dailyGainers({}, { devel: `dailyGainers.json` }) - ).resolves.toBeDefined(); + return expect(yf.dailyGainers({}, { devel })).resolves.toBeDefined(); }); it("throws on weird result", () => { diff --git a/src/modules/dailyGainers.ts b/src/modules/dailyGainers.ts index e4556a78..308588e3 100644 --- a/src/modules/dailyGainers.ts +++ b/src/modules/dailyGainers.ts @@ -1,136 +1,162 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { YahooNumber } from "../lib/yahooFinanceTypes.js"; -export interface DailyGainersResult { - id: string; - title: string; - description: string; - canonicalName: string; - criteriaMeta: DailyGainersCriteriaMeta; - rawCriteria: string; - start: number; - count: number; - total: number; - quotes: DailyGainersQuote[]; - useRecords: boolean; - predefinedScr: boolean; - versionId: number; - creationDate: number; - lastUpdated: number; - isPremium: boolean; - iconUrl: string; -} +const DailyGainersCriterum = Type.Object( + { + field: Type.String(), + operators: Type.Array(Type.String()), + values: Type.Array(YahooNumber), + labelsSelected: Type.Array(YahooNumber), + dependentValues: Type.Array(Type.Any()), + }, + { title: "DailyGainersCriterium" } +); -export interface DailyGainersCriteriaMeta { - size: number; - offset: number; - sortField: string; - sortType: string; - quoteType: string; - criteria: DailyGainersCriterum[]; - topOperator: string; -} +const DailyGainersQuote = Type.Object( + { + language: Type.String(), + region: Type.String(), + quoteType: Type.String(), + typeDisp: Type.String(), + quoteSourceName: Type.String(), + triggerable: Type.Boolean(), + customPriceAlertConfidence: Type.String(), + lastCloseTevEbitLtm: Type.Optional(YahooNumber), + lastClosePriceToNNWCPerShare: Type.Optional(YahooNumber), + firstTradeDateMilliseconds: YahooNumber, + priceHint: YahooNumber, + postMarketChangePercent: Type.Optional(YahooNumber), + postMarketTime: Type.Optional(YahooNumber), + postMarketPrice: Type.Optional(YahooNumber), + postMarketChange: Type.Optional(YahooNumber), + regularMarketChange: YahooNumber, + regularMarketTime: YahooNumber, + regularMarketPrice: YahooNumber, + regularMarketDayHigh: YahooNumber, + regularMarketDayRange: Type.String(), + currency: Type.String(), + regularMarketDayLow: YahooNumber, + regularMarketVolume: YahooNumber, + regularMarketPreviousClose: YahooNumber, + bid: Type.Optional(YahooNumber), + ask: Type.Optional(YahooNumber), + bidSize: Type.Optional(YahooNumber), + askSize: Type.Optional(YahooNumber), + market: Type.String(), + messageBoardId: Type.String(), + fullExchangeName: Type.String(), + longName: Type.String(), + financialCurrency: Type.Optional(Type.String()), + regularMarketOpen: YahooNumber, + averageDailyVolume3Month: YahooNumber, + averageDailyVolume10Day: YahooNumber, + fiftyTwoWeekLowChange: YahooNumber, + fiftyTwoWeekLowChangePercent: YahooNumber, + fiftyTwoWeekRange: Type.String(), + fiftyTwoWeekHighChange: YahooNumber, + fiftyTwoWeekHighChangePercent: YahooNumber, + fiftyTwoWeekChangePercent: YahooNumber, + earningsTimestamp: Type.Optional(YahooNumber), + earningsTimestampStart: Type.Optional(YahooNumber), + earningsTimestampEnd: Type.Optional(YahooNumber), + trailingAnnualDividendRate: YahooNumber, + trailingAnnualDividendYield: YahooNumber, + marketState: Type.String(), + epsTrailingTwelveMonths: Type.Optional(YahooNumber), + epsForward: Type.Optional(YahooNumber), + epsCurrentYear: Type.Optional(YahooNumber), + priceEpsCurrentYear: Type.Optional(YahooNumber), + sharesOutstanding: YahooNumber, + bookValue: Type.Optional(YahooNumber), + fiftyDayAverage: YahooNumber, + fiftyDayAverageChange: YahooNumber, + fiftyDayAverageChangePercent: YahooNumber, + twoHundredDayAverage: YahooNumber, + twoHundredDayAverageChange: YahooNumber, + twoHundredDayAverageChangePercent: YahooNumber, + marketCap: YahooNumber, + forwardPE: Type.Optional(YahooNumber), + priceToBook: Type.Optional(YahooNumber), + sourceInterval: YahooNumber, + exchangeDataDelayedBy: YahooNumber, + exchangeTimezoneName: Type.String(), + exchangeTimezoneShortName: Type.String(), + gmtOffSetMilliseconds: YahooNumber, + esgPopulated: Type.Boolean(), + tradeable: Type.Boolean(), + cryptoTradeable: Type.Boolean(), + exchange: Type.String(), + fiftyTwoWeekLow: YahooNumber, + fiftyTwoWeekHigh: YahooNumber, + shortName: Type.String(), + averageAnalystRating: Type.Optional(Type.String()), + regularMarketChangePercent: YahooNumber, + symbol: Type.String(), + dividendDate: Type.Optional(YahooNumber), + displayName: Type.Optional(Type.String()), + trailingPE: Type.Optional(YahooNumber), + prevName: Type.Optional(Type.String()), + nameChangeDate: Type.Optional(YahooNumber), + ipoExpectedDate: Type.Optional(YahooNumber), + dividendYield: Type.Optional(YahooNumber), + dividendRate: Type.Optional(YahooNumber), + }, + { title: "DailyGainersQuote" } +); -export interface DailyGainersCriterum { - field: string; - operators: string[]; - values: number[]; - labelsSelected: number[]; - dependentValues: any[]; -} +const DailyGainersOptionsSchema = Type.Object( + { + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + count: Type.Optional(YahooNumber), + }, + { title: "DailyGainersOptions" } +); -export interface DailyGainersQuote { - language: string; - region: string; - quoteType: string; - typeDisp: string; - quoteSourceName: string; - triggerable: boolean; - customPriceAlertConfidence: string; - lastCloseTevEbitLtm?: number; - lastClosePriceToNNWCPerShare?: number; - firstTradeDateMilliseconds: number; - priceHint: number; - postMarketChangePercent?: number; - postMarketTime?: number; - postMarketPrice?: number; - postMarketChange?: number; - regularMarketChange: number; - regularMarketTime: number; - regularMarketPrice: number; - regularMarketDayHigh: number; - regularMarketDayRange: string; - currency: string; - regularMarketDayLow: number; - regularMarketVolume: number; - regularMarketPreviousClose: number; - bid?: number; - ask?: number; - bidSize?: number; - askSize?: number; - market: string; - messageBoardId: string; - fullExchangeName: string; - longName: string; - financialCurrency?: string; - regularMarketOpen: number; - averageDailyVolume3Month: number; - averageDailyVolume10Day: number; - fiftyTwoWeekLowChange: number; - fiftyTwoWeekLowChangePercent: number; - fiftyTwoWeekRange: string; - fiftyTwoWeekHighChange: number; - fiftyTwoWeekHighChangePercent: number; - fiftyTwoWeekChangePercent: number; - earningsTimestamp?: number; - earningsTimestampStart?: number; - earningsTimestampEnd?: number; - trailingAnnualDividendRate: number; - trailingAnnualDividendYield: number; - marketState: string; - epsTrailingTwelveMonths?: number; - epsForward?: number; - epsCurrentYear?: number; - priceEpsCurrentYear?: number; - sharesOutstanding: number; - bookValue?: number; - fiftyDayAverage: number; - fiftyDayAverageChange: number; - fiftyDayAverageChangePercent: number; - twoHundredDayAverage: number; - twoHundredDayAverageChange: number; - twoHundredDayAverageChangePercent: number; - marketCap: number; - forwardPE?: number; - priceToBook?: number; - sourceInterval: number; - exchangeDataDelayedBy: number; - exchangeTimezoneName: string; - exchangeTimezoneShortName: string; - gmtOffSetMilliseconds: number; - esgPopulated: boolean; - tradeable: boolean; - cryptoTradeable: boolean; - exchange: string; - fiftyTwoWeekLow: number; - fiftyTwoWeekHigh: number; - shortName: string; - averageAnalystRating?: string; - regularMarketChangePercent: number; - symbol: string; - dividendDate?: number; - displayName?: string; - trailingPE?: number; - prevName?: string; - nameChangeDate?: number; - ipoExpectedDate?: number; - dividendYield?: number; - dividendRate?: number; -} +const DailyGainersCriteriaMeta = Type.Object( + { + size: YahooNumber, + offset: YahooNumber, + sortField: Type.String(), + sortType: Type.String(), + quoteType: Type.String(), + criteria: Type.Array(DailyGainersCriterum), + topOperator: Type.String(), + }, + { title: "DailyGainersCriteriaMeta" } +); + +const DailyGainersResultSchema = Type.Object( + { + id: Type.String(), + title: Type.String(), + description: Type.String(), + canonicalName: Type.String(), + criteriaMeta: DailyGainersCriteriaMeta, + rawCriteria: Type.String(), + start: YahooNumber, + count: YahooNumber, + total: YahooNumber, + quotes: Type.Array(DailyGainersQuote), + useRecords: Type.Boolean(), + predefinedScr: Type.Boolean(), + versionId: YahooNumber, + creationDate: YahooNumber, + lastUpdated: YahooNumber, + isPremium: Type.Boolean(), + iconUrl: Type.String(), + }, + { title: "DailyGainersResult" } +); + +type DailyGainersResult = Static; +type DailyGainersOptions = Static; const queryOptionsDefaults = { lang: "en-US", @@ -139,12 +165,6 @@ const queryOptionsDefaults = { count: 5, }; -export interface DailyGainersOptions { - lang?: string; - region?: string; - count?: number; -} - export default function dailyGainers( this: ModuleThis, queryOptionsOverrides?: DailyGainersOptions, @@ -166,15 +186,14 @@ export default function dailyGainers( moduleName: "dailyGainers", query: { url: "https://${YF_QUERY_HOST}/v1/finance/screener/predefined/saved", - schemaKey: "#/definitions/DailyGainersOptions", + schema: DailyGainersOptionsSchema, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, needsCrumb: true, }, result: { - schemaKey: "#/definitions/DailyGainersResult", + schema: DailyGainersResultSchema, transformWith(result: any) { - // console.log(result); if (!result.finance) throw new Error("Unexpected result: " + JSON.stringify(result)); return result.finance.result[0]; diff --git a/src/modules/fundamentalsTimeSeries.ts b/src/modules/fundamentalsTimeSeries.ts index 0afdce41..29e946ef 100644 --- a/src/modules/fundamentalsTimeSeries.ts +++ b/src/modules/fundamentalsTimeSeries.ts @@ -1,3 +1,4 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, @@ -5,33 +6,56 @@ import type { ModuleThis, } from "../lib/moduleCommon.js"; import Timeseries_Keys from "../lib/timeseries.json"; +import { YahooFinanceDate, YahooNumber } from "../lib/yahooFinanceTypes.js"; -export const FundamentalsTimeSeries_Types = ["quarterly", "annual", "trailing"]; +const FundamentalsTimeSeries_Types = ["quarterly", "annual", "trailing"]; -export const FundamentalsTimeSeries_Modules = [ +const FundamentalsTimeSeries_Modules = [ "financials", "balance-sheet", "cash-flow", "all", ]; -export type FundamentalsTimeSeriesResults = Array; +const FundamentalsTimeSeriesResultSchema = Type.Object( + { + date: YahooFinanceDate, + }, + { + additionalProperties: Type.Unknown(), + title: "FundamentalsTimeSeriesResult", + } +); + +const FundamentalsTimeSeriesOptionsSchema = Type.Object( + { + period1: Type.Union([YahooFinanceDate, YahooNumber, Type.String()]), + period2: Type.Optional( + Type.Union([YahooFinanceDate, YahooNumber, Type.String()]) + ), + type: Type.Optional(Type.String()), + merge: Type.Optional(Type.Boolean()), // This returns a completely different format that will break the transformer + padTimeSeries: Type.Optional(Type.Boolean()), // Not exactly sure what this does, assume it pads p1 and p2??? + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + module: Type.String(), + }, + { + title: "FundamentalsTimeSeriesOptions", + } +); -export interface FundamentalsTimeSeriesResult { - [key: string]: unknown; - date: Date; -} +type FundamentalsTimeSeriesOptions = Static< + typeof FundamentalsTimeSeriesOptionsSchema +>; -export interface FundamentalsTimeSeriesOptions { - period1: Date | number | string; - period2?: Date | number | string; - type?: string; - merge?: boolean; // This returns a completely different format that will break the transformer - padTimeSeries?: boolean; // Not exactly sure what this does, assume it pads p1 and p2??? - lang?: string; - region?: string; - module: string; -} +const FundamentalsTimeSeriesResultsSchema = Type.Array( + FundamentalsTimeSeriesResultSchema +); + +type FundamentalsTimeSeriesResult = Static< + typeof FundamentalsTimeSeriesResultSchema +>; const queryOptionsDefaults: Omit< FundamentalsTimeSeriesOptions, @@ -71,14 +95,14 @@ export default function fundamentalsTimeSeries( assertSymbol: symbol, url: `https://query1.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/${symbol}`, needsCrumb: false, - schemaKey: "#/definitions/FundamentalsTimeSeriesOptions", + schema: FundamentalsTimeSeriesOptionsSchema, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, transformWith: processQuery, }, result: { - schemaKey: "#/definitions/FundamentalsTimeSeriesResults", + schema: FundamentalsTimeSeriesResultsSchema, transformWith(response: any) { if (!response || !response.timeseries) throw new Error(`Unexpected result: ${JSON.stringify(response)}`); diff --git a/src/modules/historical.ts b/src/modules/historical.ts index 16236f61..f43628db 100644 --- a/src/modules/historical.ts +++ b/src/modules/historical.ts @@ -1,58 +1,115 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { YahooFinanceDate, YahooNumber } from "../lib/yahooFinanceTypes.js"; -export type HistoricalHistoryResult = Array; -export type HistoricalDividendsResult = Array; -export type HistoricalStockSplitsResult = Array; -export type HistoricalResult = - | HistoricalHistoryResult - | HistoricalDividendsResult - | HistoricalStockSplitsResult; - -export interface HistoricalRowHistory { - [key: string]: any; - date: Date; - open: number; - high: number; - low: number; - close: number; - adjClose?: number; - volume: number; -} +const HistoricalRowHistory = Type.Object( + { + date: YahooFinanceDate, + open: YahooNumber, + high: YahooNumber, + low: YahooNumber, + close: YahooNumber, + adjClose: Type.Optional(YahooNumber), + volume: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "HistoricalRowHistory", + } +); -export interface HistoricalRowDividend { - date: Date; - dividends: number; -} +const HistoricalRowDividend = Type.Object( + { + date: YahooFinanceDate, + dividends: YahooNumber, + }, + { title: "HistoricalRowDividend" } +); -export interface HistoricalRowStockSplit { - date: Date; - stockSplits: string; -} +const HistoricalRowStockSplit = Type.Object( + { + date: YahooFinanceDate, + stockSplits: Type.String(), + }, + { title: "HistoricalRowStockSplit" } +); -export interface HistoricalOptions { - period1: Date | string | number; - period2?: Date | string | number; - interval?: "1d" | "1wk" | "1mo"; // '1d', TODO: all | types - events?: string; // 'history', - includeAdjustedClose?: boolean; // true, -} +const HistoricalOptionsSchema = Type.Object( + { + period1: Type.Union([Type.Date(), Type.String(), Type.Number()]), + period2: Type.Optional( + Type.Union([Type.Date(), Type.String(), Type.Number()]) + ), + interval: Type.Optional( + Type.Union([Type.Literal("1d"), Type.Literal("1wk"), Type.Literal("1mo")]) + ), + events: Type.Optional(Type.String()), + includeAdjustedClose: Type.Optional(Type.Boolean()), + }, + { title: "HistoricalOptions" } +); -export interface HistoricalOptionsEventsHistory extends HistoricalOptions { - events?: "history"; -} +const HistoricalOptionsEventsHistorySchema = Type.Composite( + [ + HistoricalOptionsSchema, + Type.Object({ + events: Type.Optional(Type.Literal("history")), + }), + ], + { title: "HistoricalOptionsEventsHistory" } +); -export interface HistoricalOptionsEventsDividends extends HistoricalOptions { - events: "dividends"; -} +const HistoricalOptionsEventsDividendsSchema = Type.Composite( + [ + HistoricalOptionsSchema, + Type.Object({ + events: Type.Literal("dividends"), + }), + ], + { title: "HistoricalOptionsEventsDividends" } +); -export interface HistoricalOptionsEventsSplit extends HistoricalOptions { - events: "split"; -} +const HistoricalOptionsEventsSplitSchema = Type.Composite( + [ + HistoricalOptionsSchema, + Type.Object({ + events: Type.Literal("split"), + }), + ], + { title: "HistoricalOptionsEventsSplit" } +); + +const HistoricalHistoryResultSchema = Type.Array(HistoricalRowHistory, { + title: "HistoricalHistoryResult", +}); +const HistoricalDividendsResultSchema = Type.Array(HistoricalRowDividend, { + title: "HistoricalDividendsResult", +}); +const HistoricalStockSplitsResultSchema = Type.Array(HistoricalRowStockSplit, { + title: "HistoricalRowStockSplit", +}); + +type HistoricalOptions = Static; +type HistoricalOptionsEventsHistory = Static< + typeof HistoricalOptionsEventsHistorySchema +>; + +type HistoricalHistoryResult = Static; +type HistoricalDividendsResult = Static; +type HistoricalOptionsEventsDividends = Static< + typeof HistoricalOptionsEventsDividendsSchema +>; +type HistoricalOptionsEventsSplit = Static< + typeof HistoricalOptionsEventsSplitSchema +>; +type HistoricalStockSplitsResult = Static< + typeof HistoricalStockSplitsResultSchema +>; const queryOptionsDefaults: Omit = { interval: "1d", @@ -94,16 +151,16 @@ export default function historical( queryOptionsOverrides: HistoricalOptions, moduleOptions?: ModuleOptions ): Promise { - let schemaKey; + let schema; if ( !queryOptionsOverrides.events || queryOptionsOverrides.events === "history" ) - schemaKey = "#/definitions/HistoricalHistoryResult"; + schema = HistoricalHistoryResultSchema; else if (queryOptionsOverrides.events === "dividends") - schemaKey = "#/definitions/HistoricalDividendsResult"; + schema = HistoricalDividendsResultSchema; else if (queryOptionsOverrides.events === "split") - schemaKey = "#/definitions/HistoricalStockSplitsResult"; + schema = HistoricalStockSplitsResultSchema; else throw new Error("No such event type:" + queryOptionsOverrides.events); return this._moduleExec({ @@ -112,7 +169,7 @@ export default function historical( query: { assertSymbol: symbol, url: "https://${YF_QUERY_HOST}/v7/finance/download/" + symbol, - schemaKey: "#/definitions/HistoricalOptions", + schema: HistoricalOptionsSchema, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, fetchType: "csv", @@ -152,7 +209,7 @@ export default function historical( }, result: { - schemaKey, + schema, transformWith(result: any) { if (result.length === 0) return result; @@ -160,7 +217,10 @@ export default function historical( const fieldCount = Object.keys(result[0]).length; // Count number of null values in object (1-level deep) - function nullFieldCount(object: Object) { + function nullFieldCount(object: unknown) { + if (object == null) { + return; + } let nullCount = 0; for (const val of Object.values(object)) if (val === null) nullCount++; diff --git a/src/modules/insights.ts b/src/modules/insights.ts index 40c52289..0ad36a46 100644 --- a/src/modules/insights.ts +++ b/src/modules/insights.ts @@ -4,78 +4,240 @@ import type { ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { Type, Static } from "@sinclair/typebox"; -import type { DateInMs } from "../lib/commonTypes.js"; +import { + YahooDateInMs, + YahooFinanceDate, + YahooNumber, +} from "../lib/yahooFinanceTypes.js"; -export interface InsightsResult { - [key: string]: any; - symbol: string; - instrumentInfo?: InsightsInstrumentInfo; - companySnapshot?: InsightsCompanySnapshot; - recommendation?: { - targetPrice?: number; - provider: string; - rating: "BUY" | "SELL" | "HOLD"; - }; - events?: InsightsEvent[]; - reports?: InsightsReport[]; - sigDevs: InsightsSigDev[]; - upsell?: InsightsUpsell; - upsellSearchDD?: { - researchReports: InsightsResearchReport; - }; - secReports?: InsightsSecReport[]; -} +const InsightsDirection = Type.Union( + [Type.Literal("Bearish"), Type.Literal("Bullish"), Type.Literal("Neutral")], + { title: "InsightsDirection" } +); -export interface InsightsSigDev { - [key: string]: any; - headline: string; - date: Date; -} +const InsightsOutlookSchema = Type.Object( + { + stateDescription: Type.String(), + direction: InsightsDirection, + score: YahooNumber, + scoreDescription: Type.String(), + sectorDirection: Type.Optional(InsightsDirection), + sectorScore: Type.Optional(YahooNumber), + sectorScoreDescription: Type.Optional(Type.String()), + indexDirection: InsightsDirection, + indexScore: YahooNumber, + indexScoreDescription: Type.String(), + }, + { + additionalProperties: Type.Any(), + title: "InsightsOutlook", + } +); -export interface InsightsReport { - [key: string]: any; - id: string; - headHtml: string; - provider: string; - reportDate: Date; - reportTitle: string; - reportType: string; - targetPrice?: number; - targetPriceStatus?: "Increased" | "Maintained" | "Decreased" | "-"; - investmentRating?: "Bullish" | "Neutral" | "Bearish"; - tickers?: string[]; -} +const InsightsInstrumentInfo = Type.Object( + { + keyTechnicals: Type.Object( + { + provider: Type.String(), + support: Type.Optional(YahooNumber), + resistance: Type.Optional(YahooNumber), + stopLoss: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + } + ), + technicalEvents: Type.Object( + { + provider: Type.String(), + sector: Type.Optional(Type.String()), + shortTermOutlook: InsightsOutlookSchema, + intermediateTermOutlook: InsightsOutlookSchema, + longTermOutlook: InsightsOutlookSchema, + }, + { + additionalProperties: Type.Any(), + } + ), + valuation: Type.Object( + { + color: Type.Optional(YahooNumber), + description: Type.Optional(Type.String()), + discount: Type.Optional(Type.String()), + provider: Type.String(), + relativeValue: Type.Optional(Type.String()), + }, + { + additionalProperties: Type.Any(), + } + ), + }, + { + additionalProperties: Type.Any(), + title: "InsightsInstrumentInfo", + } +); -export interface InsightsResearchReport { - reportId: string; - provider: string; - title: string; - reportDate: Date; - summary: string; - investmentRating?: "Bullish" | "Neutral" | "Bearish"; -} +const InsightsCompanySnapshot = Type.Object( + { + sectorInfo: Type.Optional(Type.String()), + company: Type.Object( + { + innovativeness: Type.Optional(YahooNumber), + hiring: Type.Optional(YahooNumber), + sustainability: Type.Optional(YahooNumber), + insiderSentiments: Type.Optional(YahooNumber), + earningsReports: Type.Optional(YahooNumber), + dividends: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + } + ), + sector: Type.Object( + { + innovativeness: YahooNumber, + hiring: YahooNumber, + sustainability: Type.Optional(YahooNumber), + insiderSentiments: YahooNumber, + earningsReports: Type.Optional(YahooNumber), + dividends: YahooNumber, + }, + { + additionalProperties: Type.Any(), + } + ), + }, + { title: "InsightsCompanySnapshot", additionalProperties: Type.Any() } +); -export interface InsightsSecReport { - id: string; - type: string; - title: string; - description: string; - filingDate: DateInMs; - snapshotUrl: string; - formType: string; -} +const InsightsEventSchema = Type.Object( + { + eventType: Type.String(), + pricePeriod: Type.String(), + tradingHorizon: Type.String(), + tradeType: Type.String(), + imageUrl: Type.String(), + startDate: YahooFinanceDate, + endDate: YahooFinanceDate, + }, + { title: "InsightsEvent", additionalProperties: Type.Any() } +); +const InsightsReport = Type.Object( + { + id: Type.String(), + headHtml: Type.String(), + provider: Type.String(), + reportDate: YahooFinanceDate, + reportTitle: Type.String(), + reportType: Type.String(), + targetPrice: Type.Optional(YahooNumber), + targetPriceStatus: Type.Optional( + Type.Union([ + Type.Literal("Increased"), + Type.Literal("Maintained"), + Type.Literal("Decreased"), + Type.Literal("-"), + ]) + ), + investmentRating: Type.Optional( + Type.Union([ + Type.Literal("Bullish"), + Type.Literal("Neutral"), + Type.Literal("Bearish"), + ]) + ), + tickers: Type.Optional(Type.Array(Type.String())), + }, + { title: "InsightsReport", additionalProperties: Type.Any() } +); +const InsightsSigDev = Type.Object( + { + headline: Type.String(), + date: YahooFinanceDate, + }, + { title: "InsightsSigDev", additionalProperties: Type.Any() } +); +const InsightsUpsell = Type.Object( + { + msBullishSummary: Type.Optional(Type.Array(Type.String())), + msBearishSummary: Type.Optional(Type.Array(Type.String())), + msBullishBearishSummariesPublishDate: Type.Optional(YahooDateInMs), + companyName: Type.Optional(Type.String()), + upsellReportType: Type.Optional(Type.String()), + }, + { title: "InsightsUpsell", additionalProperties: Type.Any() } +); +const InsightsResearchReport = Type.Object( + { + reportId: Type.String(), + provider: Type.String(), + title: Type.String(), + reportDate: YahooFinanceDate, + summary: Type.String(), + investmentRating: Type.Optional( + Type.Union([ + Type.Literal("Bullish"), + Type.Literal("Neutral"), + Type.Literal("Bearish"), + ]) + ), + }, + { title: "InsightsResearchReport" } +); +const InsightsSecReport = Type.Object( + { + id: Type.String(), + type: Type.String(), + title: Type.String(), + description: Type.String(), + filingDate: YahooDateInMs, + snapshotUrl: Type.String(), + formType: Type.String(), + }, + { + title: "InsightsSecReport", + additionalProperties: Type.Any(), + } +); -export interface InsightsEvent { - [key: string]: any; - eventType: string; - pricePeriod: string; - tradingHorizon: string; - tradeType: string; - imageUrl: string; - startDate: Date; - endDate: Date; -} +const InsightsResultSchema = Type.Object( + { + symbol: Type.String(), + instrumentInfo: Type.Optional(InsightsInstrumentInfo), + companySnapshot: Type.Optional(InsightsCompanySnapshot), + recommendation: Type.Optional( + Type.Object({ + targetPrice: Type.Optional(YahooNumber), + provider: Type.String(), + rating: Type.Union([ + Type.Literal("BUY"), + Type.Literal("SELL"), + Type.Literal("HOLD"), + ]), + }) + ), + events: Type.Optional(Type.Array(InsightsEventSchema)), + reports: Type.Optional(Type.Array(InsightsReport)), + sigDevs: Type.Optional(Type.Array(InsightsSigDev)), + upsell: Type.Optional(InsightsUpsell), + upsellSearchDD: Type.Optional( + Type.Object({ + researchReports: InsightsResearchReport, + }) + ), + secReports: Type.Optional(Type.Array(InsightsSecReport)), + }, + { + additionalProperties: Type.Any(), + title: "InsightsResult", + } +); + +type InsightsResult = Static; +type InsightsOutlook = Static; export interface InsightsInstrumentInfo { [key: string]: any; @@ -127,36 +289,16 @@ export interface InsightsCompanySnapshot { }; } -export type InsightsDirection = "Bearish" | "Bullish" | "Neutral"; +const InsightsOptionsSchema = Type.Object( + { + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + reportsCount: Type.Optional(YahooNumber), + }, + { title: "InsightsOptions" } +); -export interface InsightsOutlook { - [key: string]: any; - stateDescription: string; - direction: InsightsDirection; - score: number; - scoreDescription: string; - sectorDirection?: InsightsDirection; - sectorScore?: number; - sectorScoreDescription?: string; - indexDirection: InsightsDirection; - indexScore: number; - indexScoreDescription: string; -} - -export interface InsightsUpsell { - [key: string]: any; - msBullishSummary?: Array; - msBearishSummary?: Array; - msBullishBearishSummariesPublishDate?: DateInMs; - companyName?: string; // Missing in e.g. APS.AX - upsellReportType?: string; -} - -export interface InsightsOptions { - lang?: string; - region?: string; - reportsCount?: number; -} +type InsightsOptions = Static; const queryOptionsDefaults = { lang: "en-US", @@ -190,13 +332,13 @@ export default function trendingSymbols( query: { assertSymbol: symbol, url: "https://${YF_QUERY_HOST}/ws/insights/v2/finance/insights", - schemaKey: "#/definitions/InsightsOptions", + schema: InsightsOptionsSchema, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, runtime: { symbol }, }, result: { - schemaKey: "#/definitions/InsightsResult", + schema: InsightsResultSchema, transformWith(result: any) { if (!result.finance) throw new Error("Unexpected result: " + JSON.stringify(result)); diff --git a/src/modules/options.spec.ts b/src/modules/options.spec.ts index 123f4bae..8a89c6fe 100644 --- a/src/modules/options.spec.ts +++ b/src/modules/options.spec.ts @@ -57,7 +57,7 @@ describe("options", () => { it("throws on invalid", () => { return expect( yf.options("AAPL", { date: "something yfDate can't parse" }) - ).rejects.toThrow(/^Unsupported date type/); + ).rejects.toThrow("Validation called with invalid options"); }); }); }); diff --git a/src/modules/options.ts b/src/modules/options.ts index e1add34c..5b12bbb5 100644 --- a/src/modules/options.ts +++ b/src/modules/options.ts @@ -4,52 +4,216 @@ import type { ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { Type, Static } from "@sinclair/typebox"; -import { Quote } from "./quote.js"; +import { YahooFinanceDate, YahooNumber } from "../lib/yahooFinanceTypes.js"; +import { QuoteBase } from "./quote.js"; +import { Value } from "@sinclair/typebox/value"; -export interface OptionsResult { - [key: string]: any; - underlyingSymbol: string; - expirationDates: Date[]; - strikes: number[]; - hasMiniOptions: boolean; - quote: Quote; - options: Option[]; -} +/* + * [TODO] Fields seen in a query but not in this module yet: + * + * - extendedMarketChange + * - extendedMarketChangePercent + * - extendedMarketPrice + * - extendedMarketTime + * - dayHigh (separate to regularMarketDayHigh, etc) + * - dayLow (separate to regularMarketDayLow, etc) + * - volume (separate to regularMarketVolume, etc) + * + * i.e. on yahoo site, with ?fields=dayHigh,dayLow,etc. + */ -export interface Option { - [key: string]: any; - expirationDate: Date; - hasMiniOptions: boolean; - calls: CallOrPut[]; - puts: CallOrPut[]; -} +/* + * Guaranteed fields, even we don't ask for them + */ +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) + }), + ], + { title: "QuoteCryptoCurrency" } +); -export interface CallOrPut { - [key: string]: any; - contractSymbol: string; - strike: number; - currency?: string; - lastPrice: number; - change: number; - percentChange?: number; - volume?: number; - openInterest?: number; - bid?: number; - ask?: number; - contractSize: "REGULAR"; - expiration: Date; - lastTradeDate: Date; - impliedVolatility: number; - inTheMoney: boolean; -} +const QuoteCurrency = Type.Composite( + [ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("CURRENCY"), + }), + ], + { title: "QuoteCurrency" } +); -export interface OptionsOptions { - formatted?: boolean; - lang?: string; - region?: string; - date?: Date | number | string; -} +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()), + }), + ], + { title: "QuoteEquity" } +); + +const QuoteFuture = Type.Composite( + [ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("FUTURE"), + headSymbolAsString: Type.String(), + contractSymbol: Type.Boolean(), + underlyingExchangeSymbol: Type.String(), + expireDate: YahooFinanceDate, + expireIsoDate: YahooFinanceDate, + }), + ], + { + title: "QuoteFuture", + } +); + +const QuoteIndex = Type.Composite( + [ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("INDEX"), + }), + ], + { + title: "QuoteIndex", + } +); + +const QuoteOption = Type.Composite( + [ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("OPTION"), + strike: YahooNumber, + openInterest: YahooNumber, + expireDate: YahooNumber, + expireIsoDate: YahooNumber, + underlyingSymbol: Type.String(), + }), + ], + { + title: "QuoteOption", + } +); + +const QuoteMutualfund = Type.Composite( + [ + QuoteBase, + Type.Object({ + quoteType: Type.Literal("MUTUALFUND"), + }), + ], + { + title: "QuoteMutualFund", + } +); + +const QuoteSchema = Type.Union( + [ + QuoteCryptoCurrency, + QuoteCurrency, + QuoteEtf, + QuoteEquity, + QuoteFuture, + QuoteIndex, + QuoteMutualfund, + QuoteOption, + ], + { + title: "Quote", + } +); + +const CallOrPut = Type.Object( + { + contractSymbol: Type.String(), + strike: YahooNumber, + currency: Type.Optional(Type.String()), + lastPrice: YahooNumber, + change: YahooNumber, + percentChange: Type.Optional(YahooNumber), + volume: Type.Optional(YahooNumber), + openInterest: Type.Optional(YahooNumber), + bid: Type.Optional(YahooNumber), + ask: Type.Optional(YahooNumber), + contractSize: Type.Literal("REGULAR"), + expiration: YahooFinanceDate, + lastTradeDate: YahooFinanceDate, + impliedVolatility: YahooNumber, + inTheMoney: Type.Boolean(), + }, + { + additionalProperties: Type.Any(), + title: "CallOrPut", + } +); + +const Option = Type.Object( + { + expirationDate: YahooFinanceDate, + hasMiniOptions: Type.Boolean(), + calls: Type.Array(CallOrPut), + puts: Type.Array(CallOrPut), + }, + { + additionalProperties: Type.Any(), + title: "Option", + } +); + +const OptionsResultSchema = Type.Object( + { + underlyingSymbol: Type.String(), + expirationDates: Type.Array(YahooFinanceDate), + strikes: Type.Array(YahooNumber), + hasMiniOptions: Type.Boolean(), + quote: QuoteSchema, + options: Type.Array(Option), + }, + { + additionalProperties: Type.Any(), + title: "OptionsResult", + } +); + +const OptionsOptionsSchema = Type.Object( + { + formatted: Type.Optional(Type.Boolean()), + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + date: Type.Optional(YahooFinanceDate), + }, + { + title: "OptionsOptions", + } +); + +type OptionsOptions = Static; +type OptionsResult = Static; const queryOptionsDefaults: OptionsOptions = { formatted: false, @@ -77,34 +241,29 @@ export default function options( queryOptionsOverrides: OptionsOptions, moduleOptions?: ModuleOptions ): Promise { - return this._moduleExec({ + return this._moduleExec({ moduleName: "options", query: { assertSymbol: symbol, url: "https://${YF_QUERY_HOST}/v7/finance/options/" + symbol, needsCrumb: true, - schemaKey: "#/definitions/OptionsOptions", + schema: OptionsOptionsSchema, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, transformWith(queryOptions: OptionsOptions) { - const date = queryOptions.date; - if (date) { - // yfDate will convert valid number/string to Date. - if (date instanceof Date) { - // now we convert back to unix epoch in seconds for query - queryOptions.date = Math.floor(date.getTime() / 1000); - } else { - // yfDate didn't recognize it as a date. - throw new Error("Unsupported date type: " + date); - } + // This is honestly the easiest way to coerce the date properly + const parsed = Value.Decode(OptionsOptionsSchema, queryOptions); + + if (parsed.date) { + queryOptions.date = Math.floor(parsed.date.getTime() / 1000); } return queryOptions; }, }, result: { - schemaKey: "#/definitions/OptionsResult", + schema: OptionsResultSchema, transformWith(result: any) { if (!result.optionChain) throw new Error("Unexpected result: " + JSON.stringify(result)); diff --git a/src/modules/quote.ts b/src/modules/quote.ts index 2b96bd7d..64607c67 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._moduleExec({ 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/modules/quoteSummary-iface.ts b/src/modules/quoteSummary-iface.ts index 867c4a8f..07464cb9 100644 --- a/src/modules/quoteSummary-iface.ts +++ b/src/modules/quoteSummary-iface.ts @@ -1,1013 +1,25 @@ +import { Type } from "@sinclair/typebox"; +import { + NullableYahooFinanceDate, + NullableYahooNumber, + YahooFinanceDate, + YahooNumber, +} from "../lib/yahooFinanceTypes"; + /* * To generate the initial file, we took the output of all submodules for * 'AAPL', 'OCDO.L', '0700.HK' and '^IXIC' and ran the results through - * the awesome https://app.quicktype.io/. + * the awesome https://app.quicktype.io/ + * and then the smashing https://sinclairzx81.github.io/typebox-workbench * * Manual cleanup afterwards: * * 1) Spaces: 4 to 2 * ~~2) Wrapped in a module~~ <--- undid this after tooling issues. * 3) Alphabeticalize QuoteSummaryResult - * 4) RawNumberObj type to Date|number for coersion */ -export interface QuoteSummaryResult { - [key: string]: any; - assetProfile?: AssetProfile; - balanceSheetHistory?: BalanceSheetHistory; - balanceSheetHistoryQuarterly?: BalanceSheetHistory; - calendarEvents?: CalendarEvents; - cashflowStatementHistory?: CashflowStatementHistory; - cashflowStatementHistoryQuarterly?: CashflowStatementHistory; - defaultKeyStatistics?: DefaultKeyStatistics; - earnings?: QuoteSummaryEarnings; - earningsHistory?: EarningsHistory; - earningsTrend?: EarningsTrend; - financialData?: FinancialData; - fundOwnership?: Ownership; - fundPerformance?: FundPerformance; - fundProfile?: FundProfile; - incomeStatementHistory?: IncomeStatementHistory; - incomeStatementHistoryQuarterly?: IncomeStatementHistory; - indexTrend?: IndexTrend; - industryTrend?: Trend; - insiderHolders?: Holders; - insiderTransactions?: InsiderTransactions; - institutionOwnership?: Ownership; - majorDirectHolders?: Holders; - majorHoldersBreakdown?: MajorHoldersBreakdown; - netSharePurchaseActivity?: NetSharePurchaseActivity; - price?: Price; - quoteType?: QuoteType; - recommendationTrend?: RecommendationTrend; - secFilings?: SECFilings; - sectorTrend?: Trend; - summaryDetail?: SummaryDetail; - summaryProfile?: SummaryProfile; - topHoldings?: TopHoldings; - upgradeDowngradeHistory?: UpgradeDowngradeHistory; -} - -export interface AssetProfile { - [key: string]: any; - maxAge: number; - address1?: string; - address2?: string; - address3?: string; - city?: string; - state?: string; - zip?: string; - country?: string; - phone?: string; - fax?: string; - website?: string; - industry?: string; - industryDisp?: string; - industryKey?: string; - industrySymbol?: string; - sector?: string; - sectorDisp?: string; - sectorKey?: string; - longBusinessSummary?: string; - fullTimeEmployees?: number; - companyOfficers: CompanyOfficer[]; - auditRisk?: number; - boardRisk?: number; - compensationRisk?: number; - shareHolderRightsRisk?: number; - overallRisk?: number; - governanceEpochDate?: Date; - compensationAsOfEpochDate?: Date; - name?: string; // 'Bitcoin'; - startDate?: Date; // new Date('2013-04-28') - description?: string; // 'Bitcoin (BTC) is a cryptocurrency...' - twitter?: string; // in e.g. "ADA-USD" (#418) -} - -export interface CompanyOfficer { - [key: string]: any; - maxAge: number; - name: string; - age?: number; - title: string; - yearBorn?: number; - fiscalYear?: number; - totalPay?: number; - exercisedValue?: number; - unexercisedValue?: number; -} - -export interface BalanceSheetHistory { - [key: string]: any; - balanceSheetStatements: BalanceSheetStatement[]; - maxAge: number; -} - -export interface BalanceSheetStatement { - [key: string]: any; - maxAge: number; - endDate: Date; - cash?: number; - shortTermInvestments?: number; - netReceivables?: number; - inventory?: number; - otherCurrentAssets?: number; - totalCurrentAssets?: number; - longTermInvestments?: number; - propertyPlantEquipment?: number; - otherAssets?: number; - totalAssets?: number; - accountsPayable?: number; - shortLongTermDebt?: number; - otherCurrentLiab?: number; - longTermDebt?: number; - otherLiab?: number; - totalCurrentLiabilities?: number; - totalLiab?: number; - commonStock?: number; - retainedEarnings?: number; - treasuryStock?: number; - otherStockholderEquity?: number; - totalStockholderEquity?: number; - netTangibleAssets?: number; - goodWill?: number; - intangibleAssets?: number; - deferredLongTermAssetCharges?: number; - deferredLongTermLiab?: number; - minorityInterest?: number | null; - capitalSurplus?: number; -} - -export interface CalendarEvents { - [key: string]: any; - maxAge: number; - earnings: CalendarEventsEarnings; - exDividendDate?: Date; - dividendDate?: Date; -} - -export interface CalendarEventsEarnings { - [key: string]: any; - earningsDate: Date[]; - earningsAverage?: number; - earningsLow?: number; - earningsHigh?: number; - revenueAverage?: number; - revenueLow?: number; - revenueHigh?: number; -} - -export interface CashflowStatementHistory { - [key: string]: any; - cashflowStatements: CashflowStatement[]; - maxAge: number; -} - -export interface CashflowStatement { - [key: string]: any; - maxAge: number; - endDate: Date; - netIncome: number; - depreciation?: number; - changeToNetincome?: number; - changeToAccountReceivables?: number; - changeToLiabilities?: number; - changeToInventory?: number; - changeToOperatingActivities?: number; - totalCashFromOperatingActivities?: number; - capitalExpenditures?: number; - investments?: number; - otherCashflowsFromInvestingActivities?: number; - totalCashflowsFromInvestingActivities?: number; - dividendsPaid?: number; - netBorrowings?: number; - otherCashflowsFromFinancingActivities?: number; - totalCashFromFinancingActivities?: number; - changeInCash?: number; - repurchaseOfStock?: number; - issuanceOfStock?: number; - effectOfExchangeRate?: number; -} - -export interface DefaultKeyStatistics { - [key: string]: any; - maxAge: number; - priceHint: number; - enterpriseValue?: number; - forwardPE?: number; - profitMargins?: number; - floatShares?: number; - sharesOutstanding?: number; - sharesShort?: number; - sharesShortPriorMonth?: Date; - sharesShortPreviousMonthDate?: Date; - dateShortInterest?: Date; - sharesPercentSharesOut?: number; - heldPercentInsiders?: number; - heldPercentInstitutions?: number; - shortRatio?: number; - shortPercentOfFloat?: number; - beta?: number; - impliedSharesOutstanding?: number; - category: null | string; - bookValue?: number; - priceToBook?: number; - fundFamily: null | string; - legalType: null | string; - lastFiscalYearEnd?: Date; - nextFiscalYearEnd?: Date; - mostRecentQuarter?: Date; - earningsQuarterlyGrowth?: number; - netIncomeToCommon?: number; - trailingEps?: number; - forwardEps?: number; - pegRatio?: number; - lastSplitFactor: null | string; - lastSplitDate?: number; - enterpriseToRevenue?: number; - enterpriseToEbitda?: number; - "52WeekChange"?: number; - SandP52WeekChange?: number; - lastDividendValue?: number; - lastDividendDate?: Date; - ytdReturn?: number; - beta3Year?: number; - totalAssets?: number; - yield?: number; - fundInceptionDate?: Date; - threeYearAverageReturn?: number; - fiveYearAverageReturn?: number; - morningStarOverallRating?: number; - morningStarRiskRating?: number; - annualReportExpenseRatio?: number; - lastCapGain?: number; - annualHoldingsTurnover?: number; -} - -export interface QuoteSummaryEarnings { - [key: string]: any; - maxAge: number; - earningsChart: EarningsChart; - financialsChart: FinancialsChart; - financialCurrency?: string; -} - -export interface EarningsChart { - [key: string]: any; - quarterly: EarningsChartQuarterly[]; - currentQuarterEstimate?: number; - currentQuarterEstimateDate?: string; - currentQuarterEstimateYear?: number; - earningsDate: Date[]; -} - -export interface EarningsChartQuarterly { - [key: string]: any; - date: string; - actual: number; - estimate: number; -} - -export interface FinancialsChart { - [key: string]: any; - yearly: Yearly[]; - quarterly: FinancialsChartQuarterly[]; -} - -export interface FinancialsChartQuarterly { - [key: string]: any; - date: string; - revenue: number; - earnings: number; -} - -export interface Yearly { - [key: string]: any; - date: number; - revenue: number; - earnings: number; -} - -export interface EarningsHistory { - [key: string]: any; - history: EarningsHistoryHistory[]; - maxAge: number; -} - -export interface EarningsHistoryHistory { - [key: string]: any; - maxAge: number; - epsActual: number | null; - epsEstimate: number | null; - epsDifference: number | null; - surprisePercent: number | null; - quarter: Date | null; - period: string; -} - -export interface EarningsTrend { - [key: string]: any; - trend: EarningsTrendTrend[]; - maxAge: number; -} - -export interface EarningsTrendTrend { - [key: string]: any; - maxAge: number; - period: string; - endDate: Date | null; - growth: number | null; - earningsEstimate: EarningsEstimate; - revenueEstimate: RevenueEstimate; - epsTrend: EpsTrend; - epsRevisions: EpsRevisions; -} - -export interface EarningsEstimate { - [key: string]: any; - avg: number | null; - low: number | null; - high: number | null; - yearAgoEps: number | null; - numberOfAnalysts: number | null; - growth: number | null; -} - -export interface EpsRevisions { - [key: string]: any; - upLast7days: number | null; - upLast30days: number | null; - downLast30days: number | null; - downLast90days: number | null; -} - -export interface EpsTrend { - [key: string]: any; - current: number | null; - "7daysAgo": number | null; - "30daysAgo": number | null; - "60daysAgo": number | null; - "90daysAgo": number | null; -} - -export interface RevenueEstimate { - [key: string]: any; - avg: number | null; - low: number | null; - high: number | null; - numberOfAnalysts: number | null; - yearAgoRevenue: number | null; - growth: number | null; -} - -export interface FinancialData { - [key: string]: any; - maxAge: number; - currentPrice?: number; - targetHighPrice?: number; - targetLowPrice?: number; - targetMeanPrice?: number; - targetMedianPrice?: number; - recommendationMean?: number; - recommendationKey: string; - numberOfAnalystOpinions?: number; - totalCash?: number; - totalCashPerShare?: number; - ebitda?: number; - totalDebt?: number; - quickRatio?: number; - currentRatio?: number; - totalRevenue?: number; - debtToEquity?: number; - revenuePerShare?: number; - returnOnAssets?: number; - returnOnEquity?: number; - grossProfits?: number; - freeCashflow?: number; - operatingCashflow?: number; - earningsGrowth?: number; - revenueGrowth?: number; - grossMargins?: number; - ebitdaMargins?: number; - operatingMargins?: number; - profitMargins?: number; - financialCurrency: string | null; -} - -export interface Ownership { - [key: string]: any; - maxAge: number; - ownershipList: OwnershipList[]; -} - -export interface OwnershipList { - [key: string]: any; - maxAge: number; - reportDate: Date; - organization: string; - pctHeld: number; - position: number; - value: number; - pctChange?: number; -} - -export interface FundPerformance { - [key: string]: any; - maxAge: number; - loadAdjustedReturns?: PeriodRange; - rankInCategory?: PeriodRange; - performanceOverview: FundPerformancePerformanceOverview; - performanceOverviewCat: FundPerformancePerformanceOverviewCat; - trailingReturns: FundPerformanceTrailingReturns; - trailingReturnsNav: FundPerformanceTrailingReturns; - trailingReturnsCat: FundPerformanceTrailingReturns; - annualTotalReturns: FundPerformanceReturns; - pastQuarterlyReturns: FundPerformanceReturns; - riskOverviewStatistics: FundPerformanceRiskOverviewStats; - riskOverviewStatisticsCat: FundPerformanceRiskOverviewStatsCat; -} - -export interface PeriodRange { - [key: string]: any; - asOfDate?: Date; - ytd?: number; - oneMonth?: number; - threeMonth?: number; - oneYear?: number; - threeYear?: number; - fiveYear?: number; - tenYear?: number; -} - -export interface FundPerformanceTrailingReturns extends PeriodRange { - [key: string]: any; - lastBullMkt?: number; - lastBearMkt?: number; -} - -export interface FundPerformancePerformanceOverview { - [key: string]: any; - asOfDate?: Date; - ytdReturnPct?: number; - oneYearTotalReturn?: number; - threeYearTotalReturn?: number; - fiveYrAvgReturnPct?: number; - morningStarReturnRating?: number; - numYearsUp?: number; - numYearsDown?: number; - bestOneYrTotalReturn?: number; - worstOneYrTotalReturn?: number; - bestThreeYrTotalReturn?: number; - worstThreeYrTotalReturn?: number; -} - -export interface FundPerformancePerformanceOverviewCat { - [key: string]: any; - ytdReturnPct?: number; - fiveYrAvgReturnPct?: number; -} - -export interface FundPerformanceReturns { - [key: string]: any; - returns: FundPerformanceReturnsRow[]; - returnsCat?: FundPerformanceReturnsRow[]; -} - -export interface FundPerformanceReturnsRow { - [key: string]: any; - year: number; // coerce to number from string "2020" - annualValue?: number; - q1?: number; - q2?: number; - q3?: number; - q4?: number; -} - -export interface FundPerformanceRiskOverviewStats { - [key: string]: any; - riskStatistics: FundPerformanceRiskOverviewStatsRow[]; - riskRating?: number; -} - -export interface FundPerformanceRiskOverviewStatsCat { - [key: string]: any; - riskStatisticsCat: FundPerformanceRiskOverviewStatsRow[]; -} - -export interface FundPerformanceRiskOverviewStatsRow { - [key: string]: any; - year: string; // "5y" | "3y" | "10y" | anything else? - alpha: number; // 7.76 - beta: number; // 1.04 - meanAnnualReturn: number; // 2.05 - rSquared: number; // 84.03 - stdDev?: number; // 17.12 - sharpeRatio: number; // 1.37 - treynorRatio: number; // 23.61 -} - -export interface FundProfile { - [key: string]: any; - maxAge: number; - styleBoxUrl?: null | string; - family: null | string; - categoryName: null | string; - legalType: null | string; - managementInfo?: FundProfileManagementInfo; - feesExpensesInvestment?: FundProfileFeesExpensesInvestment; - feesExpensesInvestmentCat?: FundProfileFeesExpensesInvestmentCat; - brokerages?: FundProfileBrokerage[]; - initInvestment?: number; - initIraInvestment?: number; - initAipInvestment?: number; - subseqInvestment?: number; - subseqIraInvestment?: number; - subseqAipInvestment?: number; -} - -export interface FundProfileManagementInfo { - [key: string]: any; - managerName: null | string; - managerBio: null | string; - startdate?: Date; -} - -export interface FundProfileFeesExpensesInvestment { - [key: string]: any; - annualHoldingsTurnover?: number; - annualReportExpenseRatio?: number; - grossExpRatio?: number; - netExpRatio?: number; - projectionValues: object; - totalNetAssets?: number; -} - -export interface FundProfileFeesExpensesInvestmentCat - extends Omit { - [key: string]: any; - projectionValuesCat: object; -} - -export interface FundProfileBrokerage {} - -export interface IncomeStatementHistory { - [key: string]: any; - incomeStatementHistory: IncomeStatementHistoryElement[]; - maxAge: number; -} - -export interface IncomeStatementHistoryElement { - [key: string]: any; - maxAge: number; - endDate: Date; - totalRevenue: number; - costOfRevenue: number; - grossProfit: number; - researchDevelopment: number | null; - sellingGeneralAdministrative: number | null; - nonRecurring: number | null; - otherOperatingExpenses: number | null; - totalOperatingExpenses: number; - operatingIncome: number | null; // Example of null in EREGL.IS (#517) - totalOtherIncomeExpenseNet: number | null; // null since Since Feb 22 (#734) - ebit: number; - interestExpense: number | null; - incomeBeforeTax: number | null; - incomeTaxExpense: number; - minorityInterest: number | null; - netIncomeFromContinuingOps: number | null; - discontinuedOperations: number | null; - extraordinaryItems: number | null; - effectOfAccountingCharges: number | null; - otherItems: number | null; - netIncome: number; - netIncomeApplicableToCommonShares: number | null; -} - -export interface IndexTrend { - [key: string]: any; - maxAge: number; - symbol: string; - peRatio: number; - pegRatio: number; - estimates: Estimate[]; -} - -export interface Estimate { - [key: string]: any; - period: string; - growth?: number; -} - -export interface Trend { - [key: string]: any; - maxAge: number; - symbol: null; - estimates: any[]; -} - -export interface Holders { - [key: string]: any; - holders: Holder[]; - maxAge: number; -} - -export interface Holder { - [key: string]: any; - maxAge: number; - name: string; - relation: Relation | string; - url: string; - transactionDescription: string; - latestTransDate: Date; - positionDirect?: number; - positionDirectDate?: Date; - positionIndirect?: number; - positionIndirectDate?: Date; - positionSummaryDate?: Date; -} - -export enum Relation { - ChairmanOfTheBoard = "Chairman of the Board", - ChiefExecutiveOfficer = "Chief Executive Officer", - ChiefFinancialOfficer = "Chief Financial Officer", - ChiefOperatingOfficer = "Chief Operating Officer", - ChiefTechnologyOfficer = "Chief Technology Officer", - Director = "Director", - DirectorIndependent = "Director (Independent)", - Empty = "", - GeneralCounsel = "General Counsel", - IndependentNonExecutiveDirector = "Independent Non-Executive Director", - Officer = "Officer", - President = "President", -} - -export interface InsiderTransactions { - [key: string]: any; - transactions: Transaction[]; - maxAge: number; -} - -export interface Transaction { - [key: string]: any; - maxAge: number; - shares: number; - filerUrl: string; - transactionText: string; - filerName: string; - filerRelation: Relation | string; - moneyText: string; - startDate: Date; - ownership: OwnershipEnum | string; - value?: number; -} - -export enum OwnershipEnum { - D = "D", - I = "I", -} - -export interface MajorHoldersBreakdown { - [key: string]: any; - maxAge: number; - insidersPercentHeld?: number; - institutionsPercentHeld?: number; - institutionsFloatPercentHeld?: number; - institutionsCount?: number; -} -export interface NetSharePurchaseActivity { - [key: string]: any; - maxAge: number; - period: string; - buyInfoCount: number; - buyInfoShares: number; - buyPercentInsiderShares?: number; - sellInfoCount: number; - sellInfoShares?: number; - sellPercentInsiderShares?: number; - netInfoCount: number; - netInfoShares: number; - netPercentInsiderShares?: number; - totalInsiderShares: number; -} - -/* - * Dates are usually epoch numbers, but including other modules can change - * result to include an ISODate. - */ -export interface Price { - [key: string]: any; - averageDailyVolume10Day?: number; - averageDailyVolume3Month?: number; - exchange?: string; - exchangeName?: string; - exchangeDataDelayedBy?: number; - maxAge: number; - postMarketChangePercent?: number; - postMarketChange?: number; - postMarketTime?: Date; - postMarketPrice?: number; - postMarketSource?: string; - preMarketChangePercent?: number; - preMarketChange?: number; - preMarketTime?: Date; - preMarketPrice?: number; - preMarketSource?: string; - priceHint: number; - regularMarketChangePercent?: number; - regularMarketChange?: number; - regularMarketTime?: Date; - regularMarketPrice?: number; - regularMarketDayHigh?: number; - regularMarketDayLow?: number; - regularMarketVolume?: number; - regularMarketPreviousClose?: number; - regularMarketSource?: string; - regularMarketOpen?: number; - - quoteSourceName?: string; - quoteType: string; - - symbol: string; - underlyingSymbol: null | string; // "GCM22.CMX" (from GC=F future) - shortName: null | string; - longName: null | string; - - lastMarket: null | string; - marketState?: string; - marketCap?: number; - - // Crypto only? Is Price actually Quote? TODO after - currency?: string; - currencySymbol?: string; - fromCurrency: string | null; - toCurrency?: string | null; - volume24Hr?: number; - volumeAllCurrencies?: number; - circulatingSupply?: number; - - // futures - expireDate?: Date; // 1656374400, - openInterest?: number; // 444411, -} - -export interface QuoteType { - [key: string]: any; - exchange: string; - quoteType: string; - symbol: string; - underlyingSymbol: string; - shortName: null | string; - longName: null | string; - firstTradeDateEpochUtc: null | Date; - timeZoneFullName: string; - timeZoneShortName: string; - uuid: string; - messageBoardId?: null | string; - gmtOffSetMilliseconds: number; - maxAge: number; -} - -export interface RecommendationTrend { - [key: string]: any; - trend: RecommendationTrendTrend[]; - maxAge: number; -} - -export interface RecommendationTrendTrend { - [key: string]: any; - period: string; - strongBuy: number; - buy: number; - hold: number; - sell: number; - strongSell: number; -} - -export interface SECFilings { - [key: string]: any; - filings: Filing[]; - maxAge: number; -} - -export interface Filing { - [key: string]: any; - date: string; // TODO: check the format - epochDate: Date; - type: FilingType; - title: string; - edgarUrl: string; - maxAge: number; - url?: string; - exhibits?: { type: string; url: string; downloadUrl?: string }[]; -} - -// May consider switching this to string, as we keep finding more and more. -type FilingType = - | "10-K" - | "10-Q" - | "8-K" - | "8-K/A" - | "10-K/A" - | "10-Q/A" - | "SD" - | "PX14A6G" - | "SC 13G/A" - | "DEFA14A" - | "25-NSE" - | "S-8 POS" - | "6-K" - | "F-3ASR" - | "SC 13D/A" - | "20-F" - | "425" - | "SC14D9C" - | "SC 13G" - | "S-8" - | "DEF 14A" - | "F-10"; - -export interface SummaryDetail { - [key: string]: any; - maxAge: number; - priceHint: number; - previousClose?: number; // missing in e.g. "APS.AX" - open?: number; - dayLow?: number; - dayHigh?: number; - regularMarketPreviousClose?: number; // missing in e.g. "APS.AX" - regularMarketOpen?: number; - regularMarketDayLow?: number; - regularMarketDayHigh?: number; - regularMarketVolume?: number; - dividendRate?: number; - dividendYield?: number; - exDividendDate?: Date; - payoutRatio?: number; - fiveYearAvgDividendYield?: number; - beta?: number; - trailingPE?: number; - forwardPE?: number; - volume?: number; - averageVolume?: number; - averageVolume10days?: number; - averageDailyVolume10Day?: number; - bid?: number; - ask?: number; - bidSize?: number; - askSize?: number; - marketCap?: number; - fiftyDayAverage?: number; - fiftyTwoWeekLow?: number; - fiftyTwoWeekHigh?: number; - twoHundredDayAverage?: number; - priceToSalesTrailing12Months?: number; - trailingAnnualDividendRate?: number; - trailingAnnualDividendYield?: number; - currency: string; - algorithm: null; - tradeable: boolean; - yield?: number; - totalAssets?: number; - navPrice?: number; - ytdReturn?: number; - - // crypto only (optional, or null in other types) - // TODO: how does Price / SummaryDetail compare? common base? - fromCurrency: string | null; // 'BTC' - toCurrency?: string | null; // 'USD-X' - lastMarket: string | null; // 'CoinMarketCap' - volume24Hr?: number; // 62650314752 - volumeAllCurrencies?: number; // 62650314752 - circulatingSupply?: number; // 18638932 - startDate?: Date; // new Date(1367107200 * 1000) - coinMarketCapLink?: string | null; // "https://coinmarketcap.com/currencies/cardano" - - // futures - expireDate?: Date; // 1656374400, - openInterest?: number; // 444411, -} - -export interface SummaryProfile { - [key: string]: any; - address1?: string; - address2?: string; - address3?: string; - city?: string; - state?: string; - zip?: string; - country?: string; - phone?: string; - fax?: string; - website?: string; - industry?: string; - industryDisp?: string; - sector?: string; - sectorDisp?: string; - longBusinessSummary?: string; - fullTimeEmployees?: number; - companyOfficers: any[]; - maxAge: number; - twitter?: string; // in e.g. "ADA-USD" (#418) - - // seems like for cryptocurency only - // TODO: how does this relate to Quote type. Common base? - name?: string; // 'Bitcoin' - startDate?: Date; // new Date('2013-04-28') - description?: string; // 'Bitcoin (BTC) is a cryptocurrency...' -} - -export interface TopHoldings { - [key: string]: any; - maxAge: number; - stockPosition?: number; - bondPosition?: number; - holdings: TopHoldingsHolding[]; - equityHoldings: TopHoldingsEquityHoldings; - bondHoldings: object; - bondRatings: TopHoldingsBondRating[]; - sectorWeightings: TopHoldingsSectorWeighting[]; - cashPosition?: number; - otherPosition?: number; - preferredPosition?: number; - convertiblePosition?: number; -} - -export interface TopHoldingsHolding { - [key: string]: any; - symbol: string; - holdingName: string; - holdingPercent: number; -} - -export interface TopHoldingsEquityHoldings { - [key: string]: any; - medianMarketCap?: number; - medianMarketCapCat?: number; - priceToBook: number; - priceToBookCat?: number; - priceToCashflow: number; - priceToCashflowCat?: number; - priceToEarnings: number; - priceToEarningsCat?: number; - priceToSales: number; - priceToSalesCat?: number; - threeYearEarningsGrowth?: number; - threeYearEarningsGrowthCat?: number; -} - -export interface TopHoldingsBondRating { - [key: string]: any; - a?: number; - aa?: number; - aaa?: number; - other?: number; - b?: number; - bb?: number; - bbb?: number; - below_b?: number; - us_government?: number; -} - -export interface TopHoldingsSectorWeighting { - [key: string]: any; - realestate?: number; - consumer_cyclical?: number; - basic_materials?: number; - consumer_defensive?: number; - technology?: number; - communication_services?: number; - financial_services?: number; - utilities?: number; - industrials?: number; - energy?: number; - healthcare?: number; -} - -export interface UpgradeDowngradeHistory { - [key: string]: any; - history: UpgradeDowngradeHistoryHistory[]; - maxAge: number; -} - -export interface UpgradeDowngradeHistoryHistory { - [key: string]: any; - epochGradeDate: Date; - firm: string; - toGrade: Grade; - fromGrade?: Grade; - action: Action; -} - -export enum Action { - Down = "down", - Init = "init", - Main = "main", - Reit = "reit", - Up = "up", -} - -export enum Grade { +enum EnumGrade { Accumulate = "Accumulate", Add = "Add", Average = "Average", @@ -1046,7 +58,7 @@ export enum Grade { AboveAverage = "Above Average", Inline = "In-line", Outperformer = "Outperformer", - OVerweight = "OVerweight", // Not a typo, how it was returned from API + OVerweight = "OVerweight", Cautious = "Cautious", MarketWeight = "Market Weight", SectorUnderperform = "Sector Underperform", @@ -1069,3 +81,1359 @@ export enum Grade { marketperform = "market perform", BUy = "BUy", // Not a typo, how it was returned from API } + +enum Action { + Down = "down", + Init = "init", + Main = "main", + Reit = "reit", + Up = "up", +} + +const Grade = Type.Enum(EnumGrade, { title: "QuoteSummaryEnumGrade" }); +const ActionSchema = Type.Enum(Action, { title: "QuoteSummaryAction" }); + +const UpgradeDowngradeHistoryHistory = Type.Object( + { + epochGradeDate: YahooFinanceDate, + firm: Type.String(), + toGrade: Grade, + fromGrade: Type.Optional(Grade), + action: ActionSchema, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryUpgradeDowngradeHistoryHistory", + } +); + +const UpgradeDowngradeHistory = Type.Object( + { + history: Type.Array(UpgradeDowngradeHistoryHistory), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryUpgradeDowngradeHistory", + } +); + +const TopHoldingsSectorWeighting = Type.Object( + { + realestate: Type.Optional(YahooNumber), + consumer_cyclical: Type.Optional(YahooNumber), + basic_materials: Type.Optional(YahooNumber), + consumer_defensive: Type.Optional(YahooNumber), + technology: Type.Optional(YahooNumber), + communication_services: Type.Optional(YahooNumber), + financial_services: Type.Optional(YahooNumber), + utilities: Type.Optional(YahooNumber), + industrials: Type.Optional(YahooNumber), + energy: Type.Optional(YahooNumber), + healthcare: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTopHoldingsSectorWeighting", + } +); + +const TopHoldingsBondRating = Type.Object( + { + a: Type.Optional(YahooNumber), + aa: Type.Optional(YahooNumber), + aaa: Type.Optional(YahooNumber), + other: Type.Optional(YahooNumber), + b: Type.Optional(YahooNumber), + bb: Type.Optional(YahooNumber), + bbb: Type.Optional(YahooNumber), + below_b: Type.Optional(YahooNumber), + us_government: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTopHoldingsBondRating", + } +); + +const TopHoldingsEquityHoldings = Type.Object( + { + medianMarketCap: Type.Optional(YahooNumber), + medianMarketCapCat: Type.Optional(YahooNumber), + priceToBook: YahooNumber, + priceToBookCat: Type.Optional(YahooNumber), + priceToCashflow: YahooNumber, + priceToCashflowCat: Type.Optional(YahooNumber), + priceToEarnings: YahooNumber, + priceToEarningsCat: Type.Optional(YahooNumber), + priceToSales: YahooNumber, + priceToSalesCat: Type.Optional(YahooNumber), + threeYearEarningsGrowth: Type.Optional(YahooNumber), + threeYearEarningsGrowthCat: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTopHoldingsEquityHoldings", + } +); + +const TopHoldingsHolding = Type.Object( + { + symbol: Type.String(), + holdingName: Type.String(), + holdingPercent: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTopHoldingsHolding", + } +); + +const TopHoldings = Type.Object( + { + maxAge: YahooNumber, + stockPosition: Type.Optional(YahooNumber), + bondPosition: Type.Optional(YahooNumber), + holdings: Type.Array(TopHoldingsHolding), + equityHoldings: TopHoldingsEquityHoldings, + bondHoldings: Type.Object({}), + bondRatings: Type.Array(TopHoldingsBondRating), + sectorWeightings: Type.Array(TopHoldingsSectorWeighting), + cashPosition: Type.Optional(YahooNumber), + otherPosition: Type.Optional(YahooNumber), + preferredPosition: Type.Optional(YahooNumber), + convertiblePosition: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTopHoldings", + } +); + +const SummaryProfile = Type.Object( + { + address1: Type.Optional(Type.String()), + address2: Type.Optional(Type.String()), + address3: Type.Optional(Type.String()), + city: Type.Optional(Type.String()), + state: Type.Optional(Type.String()), + zip: Type.Optional(Type.String()), + country: Type.Optional(Type.String()), + phone: Type.Optional(Type.String()), + fax: Type.Optional(Type.String()), + website: Type.Optional(Type.String()), + industry: Type.Optional(Type.String()), + industryDisp: Type.Optional(Type.String()), + sector: Type.Optional(Type.String()), + sectorDisp: Type.Optional(Type.String()), + longBusinessSummary: Type.Optional(Type.String()), + fullTimeEmployees: Type.Optional(YahooNumber), + companyOfficers: Type.Array(Type.Any()), + maxAge: YahooNumber, + twitter: Type.Optional(Type.String()), // in e.g. "ADA-USD" (#418) + + // seems like for cryptocurency only + // TODO: how does this relate to Quote type. Common base? + + name: Type.Optional(Type.String()), // 'Bitcoin' + startDate: Type.Optional(YahooFinanceDate), // new Date('2013-04-28') + description: Type.Optional(Type.String()), // 'Bitcoin (BTC) is a cryptocurrency...' + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummarySummaryProfile", + } +); + +const SummaryDetail = Type.Object( + { + maxAge: YahooNumber, + priceHint: YahooNumber, + previousClose: Type.Optional(YahooNumber), // missing in e.g. "APS.AX" + open: Type.Optional(YahooNumber), + dayLow: Type.Optional(YahooNumber), + dayHigh: Type.Optional(YahooNumber), + regularMarketPreviousClose: Type.Optional(YahooNumber), // missing in e.g. "APS.AX" + regularMarketOpen: Type.Optional(YahooNumber), + regularMarketDayLow: Type.Optional(YahooNumber), + regularMarketDayHigh: Type.Optional(YahooNumber), + regularMarketVolume: Type.Optional(YahooNumber), + dividendRate: Type.Optional(YahooNumber), + dividendYield: Type.Optional(YahooNumber), + exDividendDate: Type.Optional(YahooFinanceDate), + payoutRatio: Type.Optional(YahooNumber), + fiveYearAvgDividendYield: Type.Optional(YahooNumber), + beta: Type.Optional(YahooNumber), + trailingPE: Type.Optional(YahooNumber), + forwardPE: Type.Optional(YahooNumber), + volume: Type.Optional(YahooNumber), + averageVolume: Type.Optional(YahooNumber), + averageVolume10days: Type.Optional(YahooNumber), + averageDailyVolume10Day: Type.Optional(YahooNumber), + bid: Type.Optional(YahooNumber), + ask: Type.Optional(YahooNumber), + bidSize: Type.Optional(YahooNumber), + askSize: Type.Optional(YahooNumber), + marketCap: Type.Optional(YahooNumber), + fiftyDayAverage: Type.Optional(YahooNumber), + fiftyTwoWeekLow: Type.Optional(YahooNumber), + fiftyTwoWeekHigh: Type.Optional(YahooNumber), + twoHundredDayAverage: Type.Optional(YahooNumber), + priceToSalesTrailing12Months: Type.Optional(YahooNumber), + trailingAnnualDividendRate: Type.Optional(YahooNumber), + trailingAnnualDividendYield: Type.Optional(YahooNumber), + currency: Type.String(), + algorithm: Type.Null(), + tradeable: Type.Boolean(), + yield: Type.Optional(YahooNumber), + totalAssets: Type.Optional(YahooNumber), + navPrice: Type.Optional(YahooNumber), + ytdReturn: Type.Optional(YahooNumber), + + // crypto only (optional, or null in other types) + // TODO: how does Price / SummaryDetail compare? common base? + + fromCurrency: Type.Union([Type.String(), Type.Null()]), // 'BTC' + toCurrency: Type.Optional(Type.Union([Type.String(), Type.Null()])), // 'USD-X' + lastMarket: Type.Union([Type.String(), Type.Null()]), // 'CoinMarketCap' + volume24Hr: Type.Optional(YahooNumber), // 62650314752 + volumeAllCurrencies: Type.Optional(YahooNumber), // 62650314752 + circulatingSupply: Type.Optional(YahooNumber), // 18638932 + startDate: Type.Optional(YahooFinanceDate), // new Date(1367107200 * 1000) + coinMarketCapLink: Type.Optional(Type.Union([Type.String(), Type.Null()])), // "https://coinmarketcap.com/currencies/cardano" + + // futures + expireDate: Type.Optional(YahooFinanceDate), // 1656374400, + openInterest: Type.Optional(YahooNumber), // 444411, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummarySummaryDetail", + } +); + +// May consider switching this to string, as we keep finding more and more. +const FilingType = Type.Union( + [ + Type.Literal("10-K"), + Type.Literal("10-Q"), + Type.Literal("8-K"), + Type.Literal("8-K/A"), + Type.Literal("10-K/A"), + Type.Literal("10-Q/A"), + Type.Literal("SD"), + Type.Literal("PX14A6G"), + Type.Literal("SC 13G/A"), + Type.Literal("DEFA14A"), + Type.Literal("25-NSE"), + Type.Literal("S-8 POS"), + Type.Literal("6-K"), + Type.Literal("F-3ASR"), + Type.Literal("SC 13D/A"), + Type.Literal("20-F"), + Type.Literal("425"), + Type.Literal("SC14D9C"), + Type.Literal("SC 13G"), + Type.Literal("S-8"), + Type.Literal("DEF 14A"), + Type.Literal("F-10"), + ], + { + title: "QuoteSummaryFilingType", + } +); + +const Filing = Type.Object( + { + date: Type.String(), + epochDate: YahooFinanceDate, + type: FilingType, + title: Type.String(), + edgarUrl: Type.String(), + maxAge: YahooNumber, + url: Type.Optional(Type.String()), + exhibits: Type.Optional( + Type.Array( + Type.Object({ + type: Type.String(), + url: Type.String(), + downloadUrl: Type.Optional(Type.String()), + }) + ) + ), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFiling", + } +); + +const SECFilings = Type.Object( + { + filings: Type.Array(Filing), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummarySECFilings", + } +); + +const RecommendationTrendTrend = Type.Object( + { + period: Type.String(), + strongBuy: YahooNumber, + buy: YahooNumber, + hold: YahooNumber, + sell: YahooNumber, + strongSell: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryRecommendationTrendTrend", + } +); + +const RecommendationTrend = Type.Object( + { + trend: Type.Array(RecommendationTrendTrend), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryRecommendationTrend", + } +); + +const QuoteType = Type.Object( + { + exchange: Type.String(), + quoteType: Type.String(), + symbol: Type.String(), + underlyingSymbol: Type.String(), + shortName: Type.Union([Type.Null(), Type.String()]), + longName: Type.Union([Type.Null(), Type.String()]), + firstTradeDateEpochUtc: NullableYahooFinanceDate, + timeZoneFullName: Type.String(), + timeZoneShortName: Type.String(), + uuid: Type.String(), + messageBoardId: Type.Optional(Type.Union([Type.Null(), Type.String()])), + gmtOffSetMilliseconds: YahooNumber, + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryQuoteType", + } +); + +const Price = Type.Object( + { + averageDailyVolume10Day: Type.Optional(YahooNumber), + averageDailyVolume3Month: Type.Optional(YahooNumber), + exchange: Type.Optional(Type.String()), + exchangeName: Type.Optional(Type.String()), + exchangeDataDelayedBy: Type.Optional(YahooNumber), + maxAge: YahooNumber, + postMarketChangePercent: Type.Optional(YahooNumber), + postMarketChange: Type.Optional(YahooNumber), + postMarketTime: Type.Optional(YahooFinanceDate), + postMarketPrice: Type.Optional(YahooNumber), + postMarketSource: Type.Optional(Type.String()), + preMarketChangePercent: Type.Optional(YahooNumber), + preMarketChange: Type.Optional(YahooNumber), + preMarketTime: Type.Optional(YahooFinanceDate), + preMarketPrice: Type.Optional(YahooNumber), + preMarketSource: Type.Optional(Type.String()), + priceHint: YahooNumber, + regularMarketChangePercent: Type.Optional(YahooNumber), + regularMarketChange: Type.Optional(YahooNumber), + regularMarketTime: Type.Optional(YahooFinanceDate), + regularMarketPrice: Type.Optional(YahooNumber), + regularMarketDayHigh: Type.Optional(YahooNumber), + regularMarketDayLow: Type.Optional(YahooNumber), + regularMarketVolume: Type.Optional(YahooNumber), + regularMarketPreviousClose: Type.Optional(YahooNumber), + regularMarketSource: Type.Optional(Type.String()), + regularMarketOpen: Type.Optional(YahooNumber), + quoteSourceName: Type.Optional(Type.String()), + quoteType: Type.String(), + symbol: Type.String(), + underlyingSymbol: Type.Union([Type.Null(), Type.String()]), + shortName: Type.Union([Type.Null(), Type.String()]), + longName: Type.Union([Type.Null(), Type.String()]), + lastMarket: Type.Union([Type.Null(), Type.String()]), + marketState: Type.Optional(Type.String()), + marketCap: Type.Optional(YahooNumber), + currency: Type.Optional(Type.String()), + currencySymbol: Type.Optional(Type.String()), + fromCurrency: Type.Union([Type.String(), Type.Null()]), + toCurrency: Type.Optional(Type.Union([Type.String(), Type.Null()])), + volume24Hr: Type.Optional(YahooNumber), + volumeAllCurrencies: Type.Optional(YahooNumber), + circulatingSupply: Type.Optional(YahooNumber), + expireDate: Type.Optional(YahooFinanceDate), + openInterest: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryPrice", + } +); + +const NetSharePurchaseActivity = Type.Object( + { + maxAge: YahooNumber, + period: Type.String(), + buyInfoCount: YahooNumber, + buyInfoShares: YahooNumber, + buyPercentInsiderShares: Type.Optional(YahooNumber), + sellInfoCount: YahooNumber, + sellInfoShares: Type.Optional(YahooNumber), + sellPercentInsiderShares: Type.Optional(YahooNumber), + netInfoCount: YahooNumber, + netInfoShares: YahooNumber, + netPercentInsiderShares: Type.Optional(YahooNumber), + totalInsiderShares: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryNetSharePurchaseActivity", + } +); + +const MajorHoldersBreakdown = Type.Object( + { + maxAge: YahooNumber, + insidersPercentHeld: Type.Optional(YahooNumber), + institutionsPercentHeld: Type.Optional(YahooNumber), + institutionsFloatPercentHeld: Type.Optional(YahooNumber), + institutionsCount: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryMajorHoldersBreakdown", + } +); + +enum EnumOwnership { + D = "D", + I = "I", +} + +enum EnumRelation { + ChairmanOfTheBoard = "Chairman of the Board", + ChiefExecutiveOfficer = "Chief Executive Officer", + ChiefFinancialOfficer = "Chief Financial Officer", + ChiefOperatingOfficer = "Chief Operating Officer", + ChiefTechnologyOfficer = "Chief Technology Officer", + Director = "Director", + DirectorIndependent = "Director (Independent)", + Empty = "", + GeneralCounsel = "General Counsel", + IndependentNonExecutiveDirector = "Independent Non-Executive Director", + Officer = "Officer", + President = "President", +} + +const Relation = Type.Enum(EnumRelation, { title: "QuoteSummaryRelation" }); + +const OwnershipSchema = Type.Enum(EnumOwnership, { + title: "QuoteSummaryOwnership", +}); + +const Transaction = Type.Object( + { + maxAge: YahooNumber, + shares: YahooNumber, + filerUrl: Type.String(), + transactionText: Type.String(), + filerName: Type.String(), + filerRelation: Type.Union([Relation, Type.String()]), + moneyText: Type.String(), + startDate: YahooFinanceDate, + ownership: Type.Union([OwnershipSchema, Type.String()]), + value: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTransaction", + } +); + +const InsiderTransactions = Type.Object( + { + transactions: Type.Array(Transaction), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryInsiderTransactions", + } +); + +const Holder = Type.Object( + { + maxAge: YahooNumber, + name: Type.String(), + relation: Type.Union([Relation, Type.String()]), + url: Type.String(), + transactionDescription: Type.String(), + latestTransDate: YahooFinanceDate, + positionDirect: Type.Optional(YahooNumber), + positionDirectDate: Type.Optional(YahooFinanceDate), + positionIndirect: Type.Optional(YahooNumber), + positionIndirectDate: Type.Optional(YahooFinanceDate), + positionSummaryDate: Type.Optional(YahooFinanceDate), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryHolder", + } +); + +const Holders = Type.Object( + { + holders: Type.Array(Holder), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryHolders", + } +); + +const Trend = Type.Object( + { + maxAge: YahooNumber, + symbol: Type.Null(), + estimates: Type.Array(Type.Any()), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryTrend", + } +); + +const Estimate = Type.Object( + { + period: Type.String(), + growth: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEstimate", + } +); + +const IndexTrend = Type.Object( + { + maxAge: YahooNumber, + symbol: Type.String(), + peRatio: YahooNumber, + pegRatio: YahooNumber, + estimates: Type.Array(Estimate), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryIndexTrend", + } +); + +const IncomeStatementHistoryElement = Type.Object( + { + maxAge: NullableYahooNumber, + endDate: YahooFinanceDate, + totalRevenue: NullableYahooNumber, + costOfRevenue: NullableYahooNumber, + grossProfit: NullableYahooNumber, + researchDevelopment: NullableYahooNumber, + sellingGeneralAdministrative: NullableYahooNumber, + nonRecurring: NullableYahooNumber, + otherOperatingExpenses: NullableYahooNumber, + totalOperatingExpenses: NullableYahooNumber, + operatingIncome: NullableYahooNumber, + totalOtherIncomeExpenseNet: NullableYahooNumber, + ebit: NullableYahooNumber, + interestExpense: NullableYahooNumber, + incomeBeforeTax: NullableYahooNumber, + incomeTaxExpense: NullableYahooNumber, + minorityInterest: NullableYahooNumber, + netIncomeFromContinuingOps: NullableYahooNumber, + discontinuedOperations: NullableYahooNumber, + extraordinaryItems: NullableYahooNumber, + effectOfAccountingCharges: NullableYahooNumber, + otherItems: NullableYahooNumber, + netIncome: NullableYahooNumber, + netIncomeApplicableToCommonShares: NullableYahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryIncomeStatementHistoryElement", + } +); + +const IncomeStatementHistory = Type.Object( + { + incomeStatementHistory: Type.Array(IncomeStatementHistoryElement), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryIncomeStatementHistory", + } +); + +const FundProfileBrokerage = Type.Object( + {}, + { + title: "QuoteSummaryFundProfileBrokerage", + } +); + +const FundProfileFeesExpensesInvestment = Type.Object( + { + annualHoldingsTurnover: Type.Optional(YahooNumber), + annualReportExpenseRatio: Type.Optional(YahooNumber), + grossExpRatio: Type.Optional(YahooNumber), + netExpRatio: Type.Optional(YahooNumber), + projectionValues: Type.Object({}), + totalNetAssets: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundProfileFeesExpensesInvestment", + } +); + +const FundProfileFeesExpensesInvestmentCat = Type.Composite( + [ + Type.Omit(FundProfileFeesExpensesInvestment, ["projectionValues"]), + Type.Object({ + projectionValuesCat: Type.Object({}), + }), + ], + { + title: "QuoteSummaryFundProfileFeesExpensesInvestmentCat", + additionalProperties: Type.Any(), + } +); + +const FundProfileManagementInfo = Type.Object( + { + managerName: Type.Union([Type.Null(), Type.String()]), + managerBio: Type.Union([Type.Null(), Type.String()]), + startdate: Type.Optional(YahooFinanceDate), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundProfileManagementInfo", + } +); + +const FundProfile = Type.Object( + { + maxAge: YahooNumber, + styleBoxUrl: Type.Optional(Type.Union([Type.Null(), Type.String()])), + family: Type.Union([Type.Null(), Type.String()]), + categoryName: Type.Union([Type.Null(), Type.String()]), + legalType: Type.Union([Type.Null(), Type.String()]), + managementInfo: Type.Optional(FundProfileManagementInfo), + feesExpensesInvestment: Type.Optional(FundProfileFeesExpensesInvestment), + feesExpensesInvestmentCat: Type.Optional( + FundProfileFeesExpensesInvestmentCat + ), + brokerages: Type.Optional(Type.Array(FundProfileBrokerage)), + initInvestment: Type.Optional(YahooNumber), + initIraInvestment: Type.Optional(YahooNumber), + initAipInvestment: Type.Optional(YahooNumber), + subseqInvestment: Type.Optional(YahooNumber), + subseqIraInvestment: Type.Optional(YahooNumber), + subseqAipInvestment: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundProfile", + } +); + +const FundPerformanceRiskOverviewStatsRow = Type.Object( + { + year: Type.String(), // "5y" | "3y" | "10y" | anything else? + alpha: YahooNumber, // 7.76 + beta: YahooNumber, // 1.04 + meanAnnualReturn: YahooNumber, // 2.05 + rSquared: YahooNumber, // 84.03 + stdDev: Type.Optional(YahooNumber), // 17.12 + sharpeRatio: YahooNumber, // 1.37 + treynorRatio: YahooNumber, // 23.61 + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformanceRiskOverviewStatsRow", + } +); + +const FundPerformanceRiskOverviewStatsCat = Type.Object( + { + riskStatisticsCat: Type.Array(FundPerformanceRiskOverviewStatsRow), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformanceRiskOverviewStatsCat", + } +); + +const FundPerformanceRiskOverviewStats = Type.Object( + { + riskStatistics: Type.Array(FundPerformanceRiskOverviewStatsRow), + riskRating: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformanceRiskOverviewStats", + } +); + +const FundPerformanceReturnsRow = Type.Object( + { + year: YahooFinanceDate, + annualValue: Type.Optional(YahooNumber), + q1: Type.Optional(YahooNumber), + q2: Type.Optional(YahooNumber), + q3: Type.Optional(YahooNumber), + q4: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformanceReturnsRow", + } +); + +const FundPerformanceReturns = Type.Object( + { + returns: Type.Array(FundPerformanceReturnsRow), + returnsCat: Type.Optional(Type.Array(FundPerformanceReturnsRow)), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformanceReturns", + } +); + +const FundPerformancePerformanceOverviewCat = Type.Object( + { + ytdReturnPct: Type.Optional(YahooNumber), + fiveYrAvgReturnPct: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformancePerformanceOverviewCat", + } +); + +const FundPerformancePerformanceOverview = Type.Object( + { + asOfDate: Type.Optional(YahooFinanceDate), + ytdReturnPct: Type.Optional(YahooNumber), + oneYearTotalReturn: Type.Optional(YahooNumber), + threeYearTotalReturn: Type.Optional(YahooNumber), + fiveYrAvgReturnPct: Type.Optional(YahooNumber), + morningStarReturnRating: Type.Optional(YahooNumber), + numYearsUp: Type.Optional(YahooNumber), + numYearsDown: Type.Optional(YahooNumber), + bestOneYrTotalReturn: Type.Optional(YahooNumber), + worstOneYrTotalReturn: Type.Optional(YahooNumber), + bestThreeYrTotalReturn: Type.Optional(YahooNumber), + worstThreeYrTotalReturn: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformancePerformanceOverview", + } +); + +const PeriodRange = Type.Object( + { + asOfDate: Type.Optional(YahooFinanceDate), + ytd: Type.Optional(YahooNumber), + oneMonth: Type.Optional(YahooNumber), + threeMonth: Type.Optional(YahooNumber), + oneYear: Type.Optional(YahooNumber), + threeYear: Type.Optional(YahooNumber), + fiveYear: Type.Optional(YahooNumber), + tenYear: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryPeriodRange", + } +); + +const FundPerformanceTrailingReturns = Type.Composite( + [ + PeriodRange, + Type.Object( + { + lastBullMkt: Type.Optional(YahooNumber), + lastBearMkt: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + } + ), + ], + { + title: "QuoteSummaryFundPerformanceTrailingReturns", + } +); + +const FundPerformance = Type.Object( + { + maxAge: YahooNumber, + loadAdjustedReturns: Type.Optional(PeriodRange), + rankInCategory: Type.Optional(PeriodRange), + performanceOverview: FundPerformancePerformanceOverview, + performanceOverviewCat: FundPerformancePerformanceOverviewCat, + trailingReturns: FundPerformanceTrailingReturns, + trailingReturnsNav: FundPerformanceTrailingReturns, + trailingReturnsCat: FundPerformanceTrailingReturns, + annualTotalReturns: FundPerformanceReturns, + pastQuarterlyReturns: FundPerformanceReturns, + riskOverviewStatistics: FundPerformanceRiskOverviewStats, + riskOverviewStatisticsCat: FundPerformanceRiskOverviewStatsCat, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFundPerformance", + } +); + +const OwnershipList = Type.Object( + { + maxAge: YahooNumber, + reportDate: YahooFinanceDate, + organization: Type.String(), + pctHeld: YahooNumber, + position: YahooNumber, + value: YahooNumber, + pctChange: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryOwnershipList", + } +); + +const Ownership = Type.Object( + { + maxAge: YahooNumber, + ownershipList: Type.Array(OwnershipList), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryOwnership", + } +); + +const FinancialData = Type.Object( + { + maxAge: YahooNumber, + currentPrice: Type.Optional(YahooNumber), + targetHighPrice: Type.Optional(YahooNumber), + targetLowPrice: Type.Optional(YahooNumber), + targetMeanPrice: Type.Optional(YahooNumber), + targetMedianPrice: Type.Optional(YahooNumber), + recommendationMean: Type.Optional(YahooNumber), + recommendationKey: Type.String(), + numberOfAnalystOpinions: Type.Optional(YahooNumber), + totalCash: Type.Optional(YahooNumber), + totalCashPerShare: Type.Optional(YahooNumber), + ebitda: Type.Optional(YahooNumber), + totalDebt: Type.Optional(YahooNumber), + quickRatio: Type.Optional(YahooNumber), + currentRatio: Type.Optional(YahooNumber), + totalRevenue: Type.Optional(YahooNumber), + debtToEquity: Type.Optional(YahooNumber), + revenuePerShare: Type.Optional(YahooNumber), + returnOnAssets: Type.Optional(YahooNumber), + returnOnEquity: Type.Optional(YahooNumber), + grossProfits: Type.Optional(YahooNumber), + freeCashflow: Type.Optional(YahooNumber), + operatingCashflow: Type.Optional(YahooNumber), + earningsGrowth: Type.Optional(YahooNumber), + revenueGrowth: Type.Optional(YahooNumber), + grossMargins: Type.Optional(YahooNumber), + ebitdaMargins: Type.Optional(YahooNumber), + operatingMargins: Type.Optional(YahooNumber), + profitMargins: Type.Optional(YahooNumber), + financialCurrency: Type.Union([Type.String(), Type.Null()]), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFinancialData", + } +); + +const RevenueEstimate = Type.Object( + { + avg: NullableYahooNumber, + low: NullableYahooNumber, + high: NullableYahooNumber, + numberOfAnalysts: NullableYahooNumber, + yearAgoRevenue: NullableYahooNumber, + growth: NullableYahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryRevenueEstimate", + } +); + +const EpsTrend = Type.Object( + { + current: NullableYahooNumber, + "7daysAgo": NullableYahooNumber, + "30daysAgo": NullableYahooNumber, + "60daysAgo": NullableYahooNumber, + "90daysAgo": NullableYahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEpsTrend", + } +); + +const EpsRevisions = Type.Object( + { + upLast7days: NullableYahooNumber, + upLast30days: NullableYahooNumber, + downLast30days: NullableYahooNumber, + downLast90days: NullableYahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEpsRevisions", + } +); + +const EarningsEstimate = Type.Object( + { + avg: NullableYahooNumber, + low: NullableYahooNumber, + high: NullableYahooNumber, + yearAgoEps: NullableYahooNumber, + numberOfAnalysts: NullableYahooNumber, + growth: NullableYahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsEstimate", + } +); + +const EarningsTrendTrend = Type.Object( + { + maxAge: YahooNumber, + period: Type.String(), + endDate: NullableYahooFinanceDate, + growth: NullableYahooNumber, + earningsEstimate: EarningsEstimate, + revenueEstimate: RevenueEstimate, + epsTrend: EpsTrend, + epsRevisions: EpsRevisions, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsTrendTrend", + } +); + +const EarningsTrend = Type.Object( + { + trend: Type.Array(EarningsTrendTrend), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsTrend", + } +); + +const EarningsHistoryHistory = Type.Object( + { + maxAge: YahooNumber, + epsActual: NullableYahooNumber, + epsEstimate: NullableYahooNumber, + epsDifference: NullableYahooNumber, + surprisePercent: NullableYahooNumber, + quarter: NullableYahooFinanceDate, + period: Type.String(), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsHistoryHistory", + } +); + +const EarningsHistory = Type.Object( + { + history: Type.Array(EarningsHistoryHistory), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsHistory", + } +); + +const Yearly = Type.Object( + { + date: YahooNumber, + revenue: YahooNumber, + earnings: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryYearly", + } +); + +const FinancialsChartQuarterly = Type.Object( + { + date: Type.String(), + revenue: YahooNumber, + earnings: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFinancialsChartQuarterly", + } +); + +const FinancialsChart = Type.Object( + { + yearly: Type.Array(Yearly), + quarterly: Type.Array(FinancialsChartQuarterly), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryFinancialsChart", + } +); + +const EarningsChartQuarterly = Type.Object( + { + date: Type.String(), + actual: YahooNumber, + estimate: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsChartQuarterly", + } +); + +const EarningsChart = Type.Object( + { + quarterly: Type.Array(EarningsChartQuarterly), + currentQuarterEstimate: Type.Optional(YahooNumber), + currentQuarterEstimateDate: Type.Optional(Type.String()), + currentQuarterEstimateYear: Type.Optional(YahooNumber), + earningsDate: Type.Array(YahooFinanceDate), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarningsChart", + } +); + +const QuoteSummaryEarnings = Type.Object( + { + maxAge: YahooNumber, + earningsChart: EarningsChart, + financialsChart: FinancialsChart, + financialCurrency: Type.Optional(Type.String()), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryEarnings", + } +); + +const DefaultKeyStatistics = Type.Object( + { + maxAge: YahooNumber, + priceHint: YahooNumber, + enterpriseValue: Type.Optional(YahooNumber), + forwardPE: Type.Optional(YahooNumber), + profitMargins: Type.Optional(YahooNumber), + floatShares: Type.Optional(YahooNumber), + sharesOutstanding: Type.Optional(YahooNumber), + sharesShort: Type.Optional(YahooNumber), + sharesShortPriorMonth: Type.Optional(YahooFinanceDate), + sharesShortPreviousMonthDate: Type.Optional(YahooFinanceDate), + dateShortInterest: Type.Optional(YahooNumber), + sharesPercentSharesOut: Type.Optional(YahooNumber), + heldPercentInsiders: Type.Optional(YahooNumber), + heldPercentInstitutions: Type.Optional(YahooNumber), + shortRatio: Type.Optional(YahooNumber), + shortPercentOfFloat: Type.Optional(YahooNumber), + beta: Type.Optional(YahooNumber), + impliedSharesOutstanding: Type.Optional(YahooNumber), + category: Type.Union([Type.Null(), Type.String()]), + bookValue: Type.Optional(YahooNumber), + priceToBook: Type.Optional(YahooNumber), + fundFamily: Type.Union([Type.Null(), Type.String()]), + legalType: Type.Union([Type.Null(), Type.String()]), + lastFiscalYearEnd: Type.Optional(YahooFinanceDate), + nextFiscalYearEnd: Type.Optional(YahooFinanceDate), + mostRecentQuarter: Type.Optional(YahooFinanceDate), + earningsQuarterlyGrowth: Type.Optional(YahooNumber), + netIncomeToCommon: Type.Optional(YahooNumber), + trailingEps: Type.Optional(YahooNumber), + forwardEps: Type.Optional(YahooNumber), + pegRatio: Type.Optional(YahooNumber), + lastSplitFactor: Type.Union([Type.Null(), Type.String()]), + lastSplitDate: Type.Optional(YahooNumber), + enterpriseToRevenue: Type.Optional(YahooNumber), + enterpriseToEbitda: Type.Optional(YahooNumber), + "52WeekChange": Type.Optional(YahooNumber), + SandP52WeekChange: Type.Optional(YahooNumber), + lastDividendValue: Type.Optional(YahooNumber), + lastDividendDate: Type.Optional(YahooFinanceDate), + ytdReturn: Type.Optional(YahooNumber), + beta3Year: Type.Optional(YahooNumber), + totalAssets: Type.Optional(YahooNumber), + yield: Type.Optional(YahooNumber), + fundInceptionDate: Type.Optional(YahooFinanceDate), + threeYearAverageReturn: Type.Optional(YahooNumber), + fiveYearAverageReturn: Type.Optional(YahooNumber), + morningStarOverallRating: Type.Optional(YahooNumber), + morningStarRiskRating: Type.Optional(YahooNumber), + annualReportExpenseRatio: Type.Optional(YahooNumber), + lastCapGain: Type.Optional(YahooNumber), + annualHoldingsTurnover: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryDefaultKeyStatistics", + } +); + +const CashflowStatement = Type.Object( + { + maxAge: YahooNumber, + endDate: YahooFinanceDate, + netIncome: YahooNumber, + depreciation: Type.Optional(YahooNumber), + changeToNetincome: Type.Optional(YahooNumber), + changeToAccountReceivables: Type.Optional(YahooNumber), + changeToLiabilities: Type.Optional(YahooNumber), + changeToInventory: Type.Optional(YahooNumber), + changeToOperatingActivities: Type.Optional(YahooNumber), + totalCashFromOperatingActivities: Type.Optional(YahooNumber), + capitalExpenditures: Type.Optional(YahooNumber), + investments: Type.Optional(YahooNumber), + otherCashflowsFromInvestingActivities: Type.Optional(YahooNumber), + totalCashflowsFromInvestingActivities: Type.Optional(YahooNumber), + dividendsPaid: Type.Optional(YahooNumber), + netBorrowings: Type.Optional(YahooNumber), + otherCashflowsFromFinancingActivities: Type.Optional(YahooNumber), + totalCashFromFinancingActivities: Type.Optional(YahooNumber), + changeInCash: Type.Optional(YahooNumber), + repurchaseOfStock: Type.Optional(YahooNumber), + issuanceOfStock: Type.Optional(YahooNumber), + effectOfExchangeRate: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryCashflowStatement", + } +); +const CashflowStatementHistory = Type.Object( + { + cashflowStatements: Type.Array(CashflowStatement), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryCashflowStatementHistory", + } +); + +const CalendarEventsEarnings = Type.Object( + { + earningsDate: Type.Array(YahooFinanceDate), + earningsAverage: Type.Optional(YahooNumber), + earningsLow: Type.Optional(YahooNumber), + earningsHigh: Type.Optional(YahooNumber), + revenueAverage: Type.Optional(YahooNumber), + revenueLow: Type.Optional(YahooNumber), + revenueHigh: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSumamryCalendarEventsEarnings", + } +); + +const CalendarEvents = Type.Object( + { + maxAge: YahooNumber, + earnings: CalendarEventsEarnings, + exDividendDate: Type.Optional(YahooFinanceDate), + dividendDate: Type.Optional(YahooFinanceDate), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryCalendarEvents", + } +); + +const BalanceSheetStatement = Type.Object( + { + maxAge: YahooNumber, + endDate: YahooFinanceDate, + cash: Type.Optional(YahooNumber), + shortTermInvestments: Type.Optional(YahooNumber), + netReceivables: Type.Optional(YahooNumber), + inventory: Type.Optional(YahooNumber), + otherCurrentAssets: Type.Optional(YahooNumber), + totalCurrentAssets: Type.Optional(YahooNumber), + longTermInvestments: Type.Optional(YahooNumber), + propertyPlantEquipment: Type.Optional(YahooNumber), + otherAssets: Type.Optional(YahooNumber), + totalAssets: Type.Optional(YahooNumber), + accountsPayable: Type.Optional(YahooNumber), + shortLongTermDebt: Type.Optional(YahooNumber), + otherCurrentLiab: Type.Optional(YahooNumber), + longTermDebt: Type.Optional(YahooNumber), + otherLiab: Type.Optional(YahooNumber), + totalCurrentLiabilities: Type.Optional(YahooNumber), + totalLiab: Type.Optional(YahooNumber), + commonStock: Type.Optional(YahooNumber), + retainedEarnings: Type.Optional(YahooNumber), + treasuryStock: Type.Optional(YahooNumber), + otherStockholderEquity: Type.Optional(YahooNumber), + totalStockholderEquity: Type.Optional(YahooNumber), + netTangibleAssets: Type.Optional(YahooNumber), + goodWill: Type.Optional(YahooNumber), + intangibleAssets: Type.Optional(YahooNumber), + deferredLongTermAssetCharges: Type.Optional(YahooNumber), + deferredLongTermLiab: Type.Optional(YahooNumber), + minorityInterest: Type.Optional(NullableYahooNumber), + capitalSurplus: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryBalanceSheetStatement", + } +); + +const BalanceSheetHistory = Type.Object( + { + balanceSheetStatements: Type.Array(BalanceSheetStatement), + maxAge: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryBalanceSheetHistory", + } +); + +const CompanyOfficer = Type.Object( + { + maxAge: YahooNumber, + name: Type.String(), + age: Type.Optional(YahooNumber), + title: Type.String(), + yearBorn: Type.Optional(YahooNumber), + fiscalYear: Type.Optional(YahooNumber), + totalPay: Type.Optional(YahooNumber), + exercisedValue: Type.Optional(YahooNumber), + unexercisedValue: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryCompanyOfficer", + } +); + +const AssetProfile = Type.Object( + { + maxAge: YahooNumber, + address1: Type.Optional(Type.String()), + address2: Type.Optional(Type.String()), + address3: Type.Optional(Type.String()), + city: Type.Optional(Type.String()), + state: Type.Optional(Type.String()), + zip: Type.Optional(Type.String()), + country: Type.Optional(Type.String()), + phone: Type.Optional(Type.String()), + fax: Type.Optional(Type.String()), + website: Type.Optional(Type.String()), + industry: Type.Optional(Type.String()), + industryDisp: Type.Optional(Type.String()), + industryKey: Type.Optional(Type.String()), + industrySymbol: Type.Optional(Type.String()), + sector: Type.Optional(Type.String()), + sectorDisp: Type.Optional(Type.String()), + sectorKey: Type.Optional(Type.String()), + longBusinessSummary: Type.Optional(Type.String()), + fullTimeEmployees: Type.Optional(YahooNumber), + companyOfficers: Type.Array(CompanyOfficer), + auditRisk: Type.Optional(YahooNumber), + boardRisk: Type.Optional(YahooNumber), + compensationRisk: Type.Optional(YahooNumber), + shareHolderRightsRisk: Type.Optional(YahooNumber), + overallRisk: Type.Optional(YahooNumber), + governanceEpochDate: Type.Optional(YahooFinanceDate), + compensationAsOfEpochDate: Type.Optional(YahooFinanceDate), + + name: Type.Optional(Type.String()), // 'Bitcoin'; + startDate: Type.Optional(YahooFinanceDate), // new Date('2013-04-28') + description: Type.Optional(Type.String()), // 'Bitcoin (BTC) is a cryptocurrency...' + twitter: Type.Optional(Type.String()), // in e.g. "ADA-USD" (#418) + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryAssetProfile", + } +); + +export const QuoteSummaryResult = Type.Object( + { + assetProfile: Type.Optional(AssetProfile), + balanceSheetHistory: Type.Optional(BalanceSheetHistory), + balanceSheetHistoryQuarterly: Type.Optional(BalanceSheetHistory), + calendarEvents: Type.Optional(CalendarEvents), + cashflowStatementHistory: Type.Optional(CashflowStatementHistory), + cashflowStatementHistoryQuarterly: Type.Optional(CashflowStatementHistory), + defaultKeyStatistics: Type.Optional(DefaultKeyStatistics), + earnings: Type.Optional(QuoteSummaryEarnings), + earningsHistory: Type.Optional(EarningsHistory), + earningsTrend: Type.Optional(EarningsTrend), + financialData: Type.Optional(FinancialData), + fundOwnership: Type.Optional(Ownership), + fundPerformance: Type.Optional(FundPerformance), + fundProfile: Type.Optional(FundProfile), + institutionOwnership: Type.Optional(Ownership), + majorDirectHolders: Type.Optional(Holders), + majorHoldersBreakdown: Type.Optional(MajorHoldersBreakdown), + netSharePurchaseActivity: Type.Optional(NetSharePurchaseActivity), + price: Type.Optional(Price), + quoteType: Type.Optional(QuoteType), + recommendationTrend: Type.Optional(RecommendationTrend), + secFilings: Type.Optional(SECFilings), + sectorTrend: Type.Optional(Trend), + summaryDetail: Type.Optional(SummaryDetail), + summaryProfile: Type.Optional(SummaryProfile), + topHoldings: Type.Optional(TopHoldings), + upgradeDowngradeHistory: Type.Optional(UpgradeDowngradeHistory), + }, + { + additionalProperties: Type.Any(), + title: "QuoteSummaryResult", + } +); diff --git a/src/modules/quoteSummary.spec.ts b/src/modules/quoteSummary.spec.ts index e4b31e3b..b193b83d 100644 --- a/src/modules/quoteSummary.spec.ts +++ b/src/modules/quoteSummary.spec.ts @@ -1,5 +1,4 @@ -import quoteSummary, { QuoteSummaryModules } from "./quoteSummary.js"; -import { InvalidOptionsError } from "../lib/errors.js"; +import quoteSummary from "./quoteSummary.js"; import testSymbols from "../../tests/testSymbols.js"; import testYf from "../../tests/testYf.js"; @@ -10,10 +9,7 @@ interface itValidatesOpts { skip?: Array; } -function itValidates( - name: QuoteSummaryModules | "all", - opts: itValidatesOpts = {} -) { +function itValidates(name: string | "all", opts: itValidatesOpts = {}) { let symbols = testSymbols({ add: [ // incomeStatementHistory/sellingGeneralAdministrative is null (#258) diff --git a/src/modules/quoteSummary.ts b/src/modules/quoteSummary.ts index 4774cf71..dc5292e4 100644 --- a/src/modules/quoteSummary.ts +++ b/src/modules/quoteSummary.ts @@ -1,15 +1,51 @@ -// /// -// import QuoteSummaryResult from "QuoteSummaryIfaces"; -import { QuoteSummaryResult } from "./quoteSummary-iface.js"; - import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { Static, Type } from "@sinclair/typebox"; +import { QuoteSummaryResult } from "./quoteSummary-iface.js"; + +const QuoteSummaryModules = Type.Union([ + Type.Literal("assetProfile"), + Type.Literal("balanceSheetHistory"), + Type.Literal("balanceSheetHistoryQuarterly"), + Type.Literal("calendarEvents"), + Type.Literal("cashflowStatementHistory"), + Type.Literal("cashflowStatementHistoryQuarterly"), + Type.Literal("defaultKeyStatistics"), + Type.Literal("earnings"), + Type.Literal("earningsHistory"), + Type.Literal("earningsTrend"), + Type.Literal("financialData"), + Type.Literal("fundOwnership"), + Type.Literal("fundPerformance"), + Type.Literal("fundProfile"), + Type.Literal("incomeStatementHistory"), + Type.Literal("incomeStatementHistoryQuarterly"), + Type.Literal("indexTrend"), + Type.Literal("industryTrend"), + Type.Literal("insiderHolders"), + Type.Literal("insiderTransactions"), + Type.Literal("institutionOwnership"), + Type.Literal("majorDirectHolders"), + Type.Literal("majorHoldersBreakdown"), + Type.Literal("netSharePurchaseActivity"), + Type.Literal("price"), + Type.Literal("quoteType"), + Type.Literal("recommendationTrend"), + Type.Literal("secFilings"), + Type.Literal("sectorTrend"), + Type.Literal("summaryDetail"), + Type.Literal("summaryProfile"), + Type.Literal("topHoldings"), + Type.Literal("upgradeDowngradeHistory"), +]); -export const quoteSummary_modules = [ +type QuoteSummaryModulesLiteral = Static; + +const quoteSummaryModules = [ "assetProfile", "balanceSheetHistory", "balanceSheetHistoryQuarterly", @@ -45,47 +81,17 @@ export const quoteSummary_modules = [ "upgradeDowngradeHistory", ]; -export type QuoteSummaryModules = - | "assetProfile" - | "balanceSheetHistory" - | "balanceSheetHistoryQuarterly" - | "calendarEvents" - | "cashflowStatementHistory" - | "cashflowStatementHistoryQuarterly" - | "defaultKeyStatistics" - | "earnings" - | "earningsHistory" - | "earningsTrend" - | "financialData" - | "fundOwnership" - | "fundPerformance" - | "fundProfile" - | "incomeStatementHistory" - | "incomeStatementHistoryQuarterly" - | "indexTrend" - | "industryTrend" - | "insiderHolders" - | "insiderTransactions" - | "institutionOwnership" - | "majorDirectHolders" - | "majorHoldersBreakdown" - | "netSharePurchaseActivity" - | "price" - | "quoteType" - | "recommendationTrend" - | "secFilings" - | "sectorTrend" - | "summaryDetail" - | "summaryProfile" - | "topHoldings" - | "upgradeDowngradeHistory"; +type QuoteSummaryOptions = Static; +type QuoteSummaryResult = Static; -export interface QuoteSummaryOptions { - formatted?: boolean; - modules?: Array | "all"; -} +const QuoteSummaryOptions = Type.Object({ + formatted: Type.Optional(Type.Boolean()), + modules: Type.Optional( + Type.Union([Type.Array(QuoteSummaryModules), Type.Literal("all")]) + ), +}); -const queryOptionsDefaults = { +const queryOptionsDefaults: QuoteSummaryOptions = { formatted: false, modules: ["price", "summaryDetail"], }; @@ -112,23 +118,28 @@ export default function quoteSummary( ): Promise { return this._moduleExec({ moduleName: "quoteSummary", - query: { assertSymbol: symbol, url: "https://${YF_QUERY_HOST}/v10/finance/quoteSummary/" + symbol, needsCrumb: true, - schemaKey: "#/definitions/QuoteSummaryOptions", + schema: QuoteSummaryOptions, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, - transformWith(options: QuoteSummaryOptions) { - if (options.modules === "all") - options.modules = quoteSummary_modules as Array; + transformWith(options: unknown) { + if ( + typeof options === "object" && + options != null && + "modules" in options && + options.modules === "all" + ) + options.modules = + quoteSummaryModules as Array; return options; }, }, result: { - schemaKey: "#/definitions/QuoteSummaryResult", + schema: QuoteSummaryResult, transformWith(result: any) { if (!result.quoteSummary) throw new Error("Unexpected result: " + JSON.stringify(result)); diff --git a/src/modules/recommendationsBySymbol.ts b/src/modules/recommendationsBySymbol.ts index 0d50b77b..a1ba1b98 100644 --- a/src/modules/recommendationsBySymbol.ts +++ b/src/modules/recommendationsBySymbol.ts @@ -1,26 +1,51 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateFalse, ModuleOptionsWithValidateTrue, ModuleThis, } from "../lib/moduleCommon.js"; +import { YahooNumber } from "../lib/yahooFinanceTypes.js"; -export interface RecommendationsBySymbolResponse { - [key: string]: any; - recommendedSymbols: Array<{ - [key: string]: any; - score: number; // 0.1927 - symbol: string; // "BMW.DE" - }>; - symbol: string; -} +const RecommendationsBySymbolResponse = Type.Object( + { + recommendedSymbols: Type.Array( + Type.Object( + { + score: YahooNumber, // 0.1927 + symbol: Type.String(), // "BMW.DE" + }, + { + additionalProperties: Type.Any(), + } + ) + ), + symbol: Type.String(), + }, + { + additionalProperties: Type.Any(), + } +); + +const RecommendationsBySymbolResponseArray = Type.Array( + RecommendationsBySymbolResponse +); + +const RecommendationsBySymbolOptions = Type.Object({}); + +type RecommendationsBySymbolResponse = Static< + typeof RecommendationsBySymbolResponse +>; -export type RecommendationsBySymbolResponseArray = - RecommendationsBySymbolResponse[]; +type RecommendationsBySymbolOptions = Static< + typeof RecommendationsBySymbolOptions +>; -export interface RecommendationsBySymbolOptions {} +type RecommendationsBySymbolResponseArray = Static< + typeof RecommendationsBySymbolResponseArray +>; -const queryOptionsDefaults = {}; +const queryOptionsDefaults: RecommendationsBySymbolOptions = {}; export default function recommendationsBySymbol( this: ModuleThis, @@ -58,13 +83,13 @@ export default function recommendationsBySymbol( url: "https://${YF_QUERY_HOST}/v6/finance/recommendationsbysymbol/" + symbols, - schemaKey: "#/definitions/RecommendationsBySymbolOptions", + schema: RecommendationsBySymbolOptions, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, }, result: { - schemaKey: "#/definitions/RecommendationsBySymbolResponseArray", + schema: RecommendationsBySymbolResponseArray, transformWith(result: any) { if (!result.finance) throw new Error("Unexpected result: " + JSON.stringify(result)); diff --git a/src/modules/screener.ts b/src/modules/screener.ts index 7e958a57..5dad6017 100644 --- a/src/modules/screener.ts +++ b/src/modules/screener.ts @@ -1,175 +1,211 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { + YahooFinanceDate, + YahooNumber, + YahooTwoNumberRange, +} from "../lib/yahooFinanceTypes.js"; -export interface ScreenerResult { - id: string; - title: string; - description: string; - canonicalName: string; - criteriaMeta: ScreenerCriteriaMeta; - rawCriteria: string; - start: number; - count: number; - total: number; - quotes: ScreenerQuote[]; - useRecords: boolean; - predefinedScr: boolean; - versionId: number; - creationDate: number; - lastUpdated: number; - isPremium: boolean; - iconUrl: string; -} +const ScreenerCriterum = Type.Object( + { + field: Type.String(), + operators: Type.Array(Type.String()), + values: Type.Array(YahooNumber), + labelsSelected: Type.Array(YahooNumber), + dependentValues: Type.Array(Type.Any()), + }, + { + title: "ScreenerCriterum", + } +); -export interface ScreenerCriteriaMeta { - size: number; - offset: number; - sortField: string; - sortType: string; - quoteType: string; - criteria: ScreenerCriterum[]; - topOperator: string; -} +const ScreenerCriteriaMeta = Type.Object( + { + size: YahooNumber, + offset: YahooNumber, + sortField: Type.String(), + sortType: Type.String(), + quoteType: Type.String(), + criteria: Type.Array(ScreenerCriterum), + topOperator: Type.String(), + }, + { + title: "ScreenerCriteriaMeta", + } +); -export interface ScreenerCriterum { - field: string; - operators: string[]; - values: number[]; - labelsSelected: number[]; - dependentValues: any[]; -} +const ScreenerQuote = Type.Object( + { + language: Type.String(), + region: Type.String(), + quoteType: Type.String(), + typeDisp: Type.String(), + quoteSourceName: Type.String(), + triggerable: Type.Boolean(), + customPriceAlertConfidence: Type.String(), + lastCloseTevEbitLtm: Type.Optional(YahooNumber), + lastClosePriceToNNWCPerShare: Type.Optional(YahooNumber), + firstTradeDateMilliseconds: YahooNumber, + priceHint: YahooNumber, + postMarketChangePercent: Type.Optional(YahooNumber), + postMarketTime: Type.Optional(YahooNumber), + postMarketPrice: Type.Optional(YahooNumber), + postMarketChange: Type.Optional(YahooNumber), + regularMarketChange: YahooNumber, + regularMarketTime: YahooNumber, + regularMarketPrice: YahooNumber, + regularMarketDayHigh: Type.Optional(YahooNumber), + regularMarketDayRange: YahooTwoNumberRange, + currency: Type.String(), + regularMarketDayLow: Type.Optional(YahooNumber), + regularMarketVolume: Type.Optional(YahooNumber), + regularMarketPreviousClose: YahooNumber, + bid: Type.Optional(YahooNumber), + ask: Type.Optional(YahooNumber), + bidSize: Type.Optional(YahooNumber), + askSize: Type.Optional(YahooNumber), + market: Type.String(), + messageBoardId: Type.String(), + fullExchangeName: Type.String(), + longName: Type.String(), + financialCurrency: Type.Optional(Type.String()), + regularMarketOpen: Type.Optional(YahooNumber), + averageDailyVolume3Month: YahooNumber, + averageDailyVolume10Day: YahooNumber, + fiftyTwoWeekLowChange: YahooNumber, + fiftyTwoWeekLowChangePercent: YahooNumber, + fiftyTwoWeekRange: YahooTwoNumberRange, + fiftyTwoWeekHighChange: YahooNumber, + fiftyTwoWeekHighChangePercent: YahooNumber, + fiftyTwoWeekChangePercent: YahooNumber, + earningsTimestamp: Type.Optional(YahooNumber), + earningsTimestampStart: Type.Optional(YahooNumber), + earningsTimestampEnd: Type.Optional(YahooNumber), + trailingAnnualDividendRate: Type.Optional(YahooNumber), + trailingAnnualDividendYield: Type.Optional(YahooNumber), + marketState: Type.String(), + epsTrailingTwelveMonths: Type.Optional(YahooNumber), + epsForward: Type.Optional(YahooNumber), + epsCurrentYear: Type.Optional(YahooNumber), + priceEpsCurrentYear: Type.Optional(YahooNumber), + sharesOutstanding: Type.Optional(YahooNumber), + bookValue: Type.Optional(YahooNumber), + fiftyDayAverage: YahooNumber, + fiftyDayAverageChange: YahooNumber, + fiftyDayAverageChangePercent: YahooNumber, + twoHundredDayAverage: YahooNumber, + twoHundredDayAverageChange: YahooNumber, + twoHundredDayAverageChangePercent: YahooNumber, + marketCap: Type.Optional(YahooNumber), + forwardPE: Type.Optional(YahooNumber), + priceToBook: Type.Optional(YahooNumber), + sourceInterval: YahooNumber, + exchangeDataDelayedBy: YahooNumber, + exchangeTimezoneName: Type.String(), + exchangeTimezoneShortName: Type.String(), + gmtOffSetMilliseconds: YahooNumber, + esgPopulated: Type.Boolean(), + tradeable: Type.Boolean(), + cryptoTradeable: Type.Boolean(), + exchange: Type.String(), + fiftyTwoWeekLow: YahooNumber, + fiftyTwoWeekHigh: YahooNumber, + shortName: Type.String(), + averageAnalystRating: Type.Optional(Type.String()), + regularMarketChangePercent: YahooNumber, + symbol: Type.String(), + dividendDate: Type.Optional(YahooFinanceDate), + displayName: Type.Optional(Type.String()), + trailingPE: Type.Optional(YahooNumber), + prevName: Type.Optional(Type.String()), + nameChangeDate: Type.Optional(YahooFinanceDate), + ipoExpectedDate: Type.Optional(YahooFinanceDate), + dividendYield: Type.Optional(YahooNumber), + dividendRate: Type.Optional(YahooNumber), + yieldTTM: Type.Optional(YahooNumber), + peTTM: Type.Optional(YahooNumber), + annualReturnNavY3: Type.Optional(YahooNumber), + annualReturnNavY5: Type.Optional(YahooNumber), + ytdReturn: Type.Optional(YahooNumber), + trailingThreeMonthReturns: Type.Optional(YahooNumber), + netAssets: Type.Optional(YahooNumber), + netExpenseRatio: Type.Optional(YahooNumber), + }, + { + title: "ScreenerQuote", + } +); -export interface ScreenerQuote { - language: string; - region: string; - quoteType: string; - typeDisp: string; - quoteSourceName: string; - triggerable: boolean; - customPriceAlertConfidence: string; - lastCloseTevEbitLtm?: number; - lastClosePriceToNNWCPerShare?: number; - firstTradeDateMilliseconds: number; - priceHint: number; - postMarketChangePercent?: number; - postMarketTime?: number; - postMarketPrice?: number; - postMarketChange?: number; - regularMarketChange: number; - regularMarketTime: number; - regularMarketPrice: number; - regularMarketDayHigh?: number; - regularMarketDayRange?: string; - currency: string; - regularMarketDayLow?: number; - regularMarketVolume?: number; - regularMarketPreviousClose: number; - bid?: number; - ask?: number; - bidSize?: number; - askSize?: number; - market: string; - messageBoardId: string; - fullExchangeName: string; - longName: string; - financialCurrency?: string; - regularMarketOpen?: number; - averageDailyVolume3Month: number; - averageDailyVolume10Day: number; - fiftyTwoWeekLowChange: number; - fiftyTwoWeekLowChangePercent: number; - fiftyTwoWeekRange: string; - fiftyTwoWeekHighChange: number; - fiftyTwoWeekHighChangePercent: number; - fiftyTwoWeekChangePercent: number; - earningsTimestamp?: number; - earningsTimestampStart?: number; - earningsTimestampEnd?: number; - trailingAnnualDividendRate?: number; - trailingAnnualDividendYield?: number; - marketState: string; - epsTrailingTwelveMonths?: number; - epsForward?: number; - epsCurrentYear?: number; - priceEpsCurrentYear?: number; - sharesOutstanding?: number; - bookValue?: number; - fiftyDayAverage: number; - fiftyDayAverageChange: number; - fiftyDayAverageChangePercent: number; - twoHundredDayAverage: number; - twoHundredDayAverageChange: number; - twoHundredDayAverageChangePercent: number; - marketCap?: number; - forwardPE?: number; - priceToBook?: number; - sourceInterval: number; - exchangeDataDelayedBy: number; - exchangeTimezoneName: string; - exchangeTimezoneShortName: string; - gmtOffSetMilliseconds: number; - esgPopulated: boolean; - tradeable: boolean; - cryptoTradeable: boolean; - exchange: string; - fiftyTwoWeekLow: number; - fiftyTwoWeekHigh: number; - shortName: string; - averageAnalystRating?: string; - regularMarketChangePercent: number; - symbol: string; - dividendDate?: number; - displayName?: string; - trailingPE?: number; - prevName?: string; - nameChangeDate?: number; - ipoExpectedDate?: number; - dividendYield?: number; - dividendRate?: number; - yieldTTM?: number; - peTTM?: number; - annualReturnNavY3?: number; - annualReturnNavY5?: number; - ytdReturn?: number; - trailingThreeMonthReturns?: number; - netAssets?: number; - netExpenseRatio?: number; -} +const ScreenerResult = Type.Object( + { + id: Type.String(), + title: Type.String(), + description: Type.String(), + canonicalName: Type.String(), + criteriaMeta: ScreenerCriteriaMeta, + rawCriteria: Type.String(), + start: YahooNumber, + count: YahooNumber, + total: YahooNumber, + quotes: Type.Array(ScreenerQuote), + useRecords: Type.Boolean(), + predefinedScr: Type.Boolean(), + versionId: YahooNumber, + creationDate: YahooFinanceDate, + lastUpdated: YahooFinanceDate, + isPremium: Type.Boolean(), + iconUrl: Type.String(), + }, + { + title: "ScreenerResult", + } +); -export type PredefinedScreenerModules = - | "aggressive_small_caps" - | "conservative_foreign_funds" - | "day_gainers" - | "day_losers" - | "growth_technology_stocks" - | "high_yield_bond" - | "most_actives" - | "most_shorted_stocks" - | "portfolio_anchors" - | "small_cap_gainers" - | "solid_large_growth_funds" - | "solid_midcap_growth_funds" - | "top_mutual_funds" - | "undervalued_growth_stocks" - | "undervalued_large_caps"; +const PredefinedScreenerModules = Type.Union( + [ + Type.Literal("aggressive_small_caps"), + Type.Literal("conservative_foreign_funds"), + Type.Literal("day_gainers"), + Type.Literal("day_losers"), + Type.Literal("growth_technology_stocks"), + Type.Literal("high_yield_bond"), + Type.Literal("most_actives"), + Type.Literal("most_shorted_stocks"), + Type.Literal("portfolio_anchors"), + Type.Literal("small_cap_gainers"), + Type.Literal("solid_large_growth_funds"), + Type.Literal("solid_midcap_growth_funds"), + Type.Literal("top_mutual_funds"), + Type.Literal("undervalued_growth_stocks"), + Type.Literal("undervalued_large_caps"), + ], + { + title: "ScreenerPredefinedScreenerModules", + } +); -const queryOptionsDefaults = { +type ScreenerResult = Static; + +type ScreenerOptions = Static; + +const queryOptionsDefaults: ScreenerOptions = { lang: "en-US", region: "US", scrIds: "day_gainers", count: 5, }; -export interface ScreenerOptions { - lang?: string; - region?: string; - scrIds: PredefinedScreenerModules; - count?: number; -} +const ScreenerOptions = Type.Object({ + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + scrIds: PredefinedScreenerModules, + count: Type.Optional(Type.Number()), +}); export default function screener( this: ModuleThis, @@ -192,13 +228,13 @@ export default function screener( moduleName: "screener", query: { url: "https://${YF_QUERY_HOST}/v1/finance/screener/predefined/saved", - schemaKey: "#/definitions/ScreenerOptions", + schema: ScreenerOptions, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, needsCrumb: true, }, result: { - schemaKey: "#/definitions/ScreenerResult", + schema: ScreenerResult, transformWith(result: any) { // console.log(result); if (!result.finance) @@ -209,19 +245,3 @@ export default function screener( moduleOptions, }); } - -// aggressive_small_caps -// conservative_foreign_funds -// day_gainers -// day_losers -// growth_technology_stocks -// high_yield_bond -// most_actives -// most_shorted_stocks -// portfolio_anchors -// small_cap_gainers -// solid_large_growth_funds -// solid_midcap_growth_funds -// top_mutual_funds -// undervalued_growth_stocks -// undervalued_large_caps diff --git a/src/modules/search.spec.ts b/src/modules/search.spec.ts index 805f83a3..b8e1bef8 100644 --- a/src/modules/search.spec.ts +++ b/src/modules/search.spec.ts @@ -1,14 +1,13 @@ import search from "./search.js"; -import { InvalidOptionsError } from "../lib/errors.js"; import testSymbols from "../../tests/testSymbols.js"; import testYf from "../../tests/testYf.js"; const yf = testYf({ search }); +yf._opts.validation.logErrors = true; describe("search", () => { // See also common module tests in moduleExec.spec.js - const testSearches = testSymbols({ add: [ "Evolution Gaming Group", // STO @@ -18,7 +17,6 @@ describe("search", () => { "BJ0CDD2", // additionalProperty: { exchDisp: "London" } ], }); - // validate different searches it.each(testSearches)( "passed validation for search '%s'", diff --git a/src/modules/search.ts b/src/modules/search.ts index a56dd474..608959e4 100644 --- a/src/modules/search.ts +++ b/src/modules/search.ts @@ -1,148 +1,255 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { YahooFinanceDate, YahooNumber } from "../lib/yahooFinanceTypes.js"; -export interface SearchQuoteYahoo { - [key: string]: any; - symbol: string; // "BABA" - isYahooFinance: true; // true - exchange: string; // "NYQ" - exchDisp?: string; // "London" e.g. with BJ0CDD2 - shortname?: string; // "Alibaba Group Holding Limited" - longname?: string; // "Alibaba Group Holding Limited" - index: "quotes"; // "quotes" - score: number; // 1111958.0 - newListingDate?: Date; // "2021-02-16" - prevName?: string; - nameChangeDate?: Date; - sector?: string; // "Industrials" - industry?: string; // "Building Products & Equipment" - dispSecIndFlag?: boolean; // true -} -export interface SearchQuoteYahooEquity extends SearchQuoteYahoo { - quoteType: "EQUITY"; - typeDisp: "Equity"; -} -export interface SearchQuoteYahooOption extends SearchQuoteYahoo { - quoteType: "OPTION"; - typeDisp: "Option"; -} -export interface SearchQuoteYahooETF extends SearchQuoteYahoo { - quoteType: "ETF"; - typeDisp: "ETF"; // "Option" -} -export interface SearchQuoteYahooFund extends SearchQuoteYahoo { - quoteType: "MUTUALFUND"; - typeDisp: "Fund"; -} -export interface SearchQuoteYahooIndex extends SearchQuoteYahoo { - quoteType: "INDEX"; - typeDisp: "Index"; -} -export interface SearchQuoteYahooCurrency extends SearchQuoteYahoo { - quoteType: "CURRENCY"; - typeDisp: "Currency"; -} -export interface SearchQuoteYahooCryptocurrency extends SearchQuoteYahoo { - quoteType: "CRYPTOCURRENCY"; - typeDisp: "Cryptocurrency"; -} +const SearchQuoteYahoo = Type.Object( + { + symbol: Type.String(), // "BABA" + isYahooFinance: Type.Literal(true), // true + exchange: Type.String(), // "NYQ" + exchDisp: Type.Optional(Type.String()), // "London", e.g. with BJ0CDD2 + shortname: Type.Optional(Type.String()), // "Alibaba Group Holding Limited" + longname: Type.Optional(Type.String()), // "Alibaba Group Holding Limited" + index: Type.Literal("quotes"), // "quotes" + score: YahooNumber, // 1111958.0 + newListingDate: Type.Optional(YahooFinanceDate), // "2021-02-16" + prevName: Type.Optional(Type.String()), + nameChangeDate: Type.Optional(YahooFinanceDate), + sector: Type.Optional(Type.String()), // "Industrials" + industry: Type.Optional(Type.String()), // "Building Products & Equipment" + dispSecIndFlag: Type.Optional(Type.Boolean()), // true + }, + { + additionalProperties: Type.Any(), + } +); -export interface SearchQuoteYahooFuture extends SearchQuoteYahoo { - quoteType: "FUTURE"; - typeDisp: "Future" | "Futures"; -} +const SearchQuoteYahooEquity = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("EQUITY"), + typeDisp: Type.Literal("Equity"), + }), + ], + { + title: "SearchQuoteYahooEntity", + } +); -export interface SearchQuoteNonYahoo { - [key: string]: any; - index: string; // '78ddc07626ff4bbcae663e88514c23a0' - name: string; // 'AAPlasma' - permalink: string; // 'aaplasma', - isYahooFinance: false; // false -} +const SearchQuoteYahooOption = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("OPTION"), + typeDisp: Type.Literal("Option"), + }), + ], + { + title: "SearchQuoteYahooOption", + } +); -export interface SearchNews { - [key: string]: any; - uuid: string; // "9aff624a-e84c-35f3-9c23-db39852006dc" - title: string; // "Analyst Report: Alibaba Group Holding Limited" - publisher: string; // "Morningstar Research" - link: string; // "https://finance.yahoo.com/m/9aff624a-e84c-35f3-9c23-db39852006dc/analyst-report%3A-alibaba-group.html" - providerPublishTime: Date; // coerced to new Date(1611286342 * 1000) - type: string; // "STORY" TODO "STORY" | ??? - thumbnail?: { resolutions: SearchNewsThumbnailResolution[] }; - relatedTickers?: string[]; // [ "AAPL" ] -} +const SearchQuoteYahooETF = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("ETF"), + typeDisp: Type.Literal("ETF"), + }), + ], + { + title: "SearchQuoteYahooETF", + } +); -export interface SearchNewsThumbnailResolution { - url: string; - width: number; - height: number; - tag: string; -} +const SearchQuoteYahooFund = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("MUTUALFUND"), + typeDisp: Type.Literal("Fund"), + }), + ], + { + title: "SearchQuoteYahooFund", + } +); -export interface SearchResult { - [key: string]: any; - explains: Array; - count: number; - quotes: Array< - | SearchQuoteYahooEquity - | SearchQuoteYahooOption - | SearchQuoteYahooETF - | SearchQuoteYahooFund - | SearchQuoteYahooIndex - | SearchQuoteYahooCurrency - | SearchQuoteYahooCryptocurrency - | SearchQuoteNonYahoo - | SearchQuoteYahooFuture - >; - news: Array; - nav: Array; - lists: Array; - researchReports: Array; - totalTime: number; - // ALWAYS present, but TEMPORARILY marked optional ("?") since its - // sudden appearance, let's make sure it doesn't get suddenly removed. - // Array until we can find some examples of what it actually looks - // like (#255). - screenerFieldResults?: Array; - // ALWAYS present, but TEMPORARILY marked optional ("?") since its - // sudden appearance, let's make sure it doesn't get suddenly removed. - // Array until we can find some examples of what it actually looks - // like (#399). - culturalAssets?: Array; - timeTakenForQuotes: number; // 26 - timeTakenForNews: number; // 419 - timeTakenForAlgowatchlist: number; // 700 - timeTakenForPredefinedScreener: number; // 400 - timeTakenForCrunchbase: number; // 400 - timeTakenForNav: number; // 400 - timeTakenForResearchReports: number; // 0 - // ALWAYS present, but TEMPORARILY marked optional ("?") since its - // sudden appearance, let's make sure it doesn't get suddenly removed. - timeTakenForScreenerField?: number; - // ALWAYS present, but TEMPORARILY marked optional ("?") since its - // sudden appearance, let's make sure it doesn't get suddenly removed. - timeTakenForCulturalAssets?: number; -} +const SearchQuoteYahooIndex = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("INDEX"), + typeDisp: Type.Literal("Index"), + }), + ], + { + title: "SearchQuoteYahooIndex", + } +); -export interface SearchOptions { - lang?: string; - region?: string; - quotesCount?: number; - newsCount?: number; - enableFuzzyQuery?: boolean; - quotesQueryId?: string; - multiQuoteQueryId?: string; - newsQueryId?: string; - enableCb?: boolean; - enableNavLinks?: boolean; - enableEnhancedTrivialQuery?: boolean; -} +const SearchQuoteYahooCurrency = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("CURRENCY"), + typeDisp: Type.Literal("Currency"), + }), + ], + { + title: "SearchQuoteYahooCurrency", + } +); + +const SearchQuoteYahooCryptocurrency = Type.Composite([ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("CRYPTOCURRENCY"), + typeDisp: Type.Literal("Cryptocurrency"), + }), +]); + +const SearchQuoteYahooFuture = Type.Composite( + [ + SearchQuoteYahoo, + Type.Object({ + quoteType: Type.Literal("FUTURE"), + typeDisp: Type.Union([Type.Literal("Future"), Type.Literal("Futures")]), + }), + ], + { + title: "SearchQuoteYahooFuture", + } +); + +const SearchQuoteNonYahoo = Type.Object( + { + index: Type.String(), // '78ddc07626ff4bbcae663e88514c23a0' + name: Type.String(), // 'AAPlasma' + permalink: Type.String(), // 'aaplasma' + isYahooFinance: Type.Literal(false), // false + }, + { + additionalProperties: Type.Any(), + title: "SearchQuoteNonYahoo", + } +); + +const SearchNewsThumbnailResolution = Type.Object( + { + url: Type.String(), + width: YahooNumber, + height: YahooNumber, + tag: Type.String(), + }, + { + title: "SearchNewsThumbnailResolution", + } +); + +const SearchNews = Type.Object( + { + uuid: Type.String(), // "9aff624a-e84c-35f3-9c23-db39852006dc" + title: Type.String(), // "Analyst Report: Alibaba Group Holding Limited" + publisher: Type.String(), // "Morningstar Research" + link: Type.String(), // "https://finance.yahoo.com/m/9aff624a-e84c-35f3-9c23-db39852006dc/analyst-report%3A-alibaba-group.html" + providerPublishTime: YahooFinanceDate, // coerced to New Date(1611285342 * 1000) + type: Type.String(), // "STORY" TODO "STORY" | ??? + thumbnail: Type.Optional( + Type.Object({ + resolutions: Type.Array(SearchNewsThumbnailResolution), + }) + ), + relatedTickers: Type.Optional(Type.Array(Type.String())), // [ "AAPL" ] + }, + { + additionalProperties: Type.Any(), + title: "SearchNews", + } +); + +type SearchResult = Static; +const SearchResultSchema = Type.Object( + { + explains: Type.Array(Type.Any()), + count: YahooNumber, + quotes: Type.Array( + Type.Union([ + SearchQuoteYahooEquity, + SearchQuoteYahooOption, + SearchQuoteYahooETF, + SearchQuoteYahooFund, + SearchQuoteYahooIndex, + SearchQuoteYahooCurrency, + SearchQuoteYahooCryptocurrency, + SearchQuoteNonYahoo, + SearchQuoteYahooFuture, + ]) + ), + news: Type.Array(SearchNews), + nav: Type.Array(Type.Any()), + lists: Type.Array(Type.Any()), + researchReports: Type.Array(Type.Any()), + totalTime: YahooNumber, + // ALWAYS present, but TEMPORARILY marked optional ("?") since its + // sudden appearance, let's make sure it doesn't get suddenly removed. + // Array until we can find some examples of what it actually looks + // like (#255). + screenerFieldResults: Type.Optional(Type.Array(Type.Any())), + // ALWAYS present, but TEMPORARILY marked optional ("?") since its + // sudden appearance, let's make sure it doesn't get suddenly removed. + // Array until we can find some examples of what it actually looks + // like (#399). + culturalAssets: Type.Optional(Type.Array(Type.Any())), + timeTakenForQuotes: YahooNumber, // 26 + timeTakenForNews: YahooNumber, // 419 + timeTakenForAlgowatchlist: YahooNumber, // 700 + timeTakenForPredefinedScreener: YahooNumber, // 400 + timeTakenForCrunchbase: YahooNumber, // 400 + timeTakenForNav: YahooNumber, // 400 + timeTakenForResearchReports: YahooNumber, // 0 + // ALWAYS present, but TEMPORARILY marked optional ("?") since its + // sudden appearance, let's make sure it doesn't get suddenly removed. + timeTakenForScreenerField: Type.Optional(YahooNumber), + // ALWAYS present, but TEMPORARILY marked optional ("?") since its + // sudden appearance, let's make sure it doesn't get suddenly removed. + timeTakenForCulturalAssets: Type.Optional(YahooNumber), + }, + { + additionalProperties: Type.Any(), + title: "SearchResults", + } +); + +const SearchOptionsSchema = Type.Object( + { + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + quotesCount: Type.Optional(YahooNumber), + newsCount: Type.Optional(YahooNumber), + enableFuzzyQuery: Type.Optional(Type.Boolean()), + quotesQueryId: Type.Optional(Type.String()), + multiQuoteQueryId: Type.Optional(Type.String()), + newsQueryId: Type.Optional(Type.String()), + enableCb: Type.Optional(Type.Boolean()), + enableNavLinks: Type.Optional(Type.Boolean()), + enableEnhancedTrivialQuery: Type.Optional(Type.Boolean()), + }, + { + title: "SearchOptions", + additionalProperties: false, + } +); + +type SearchOptions = Static; -const queryOptionsDefaults = { +const queryOptionsDefaults: SearchOptions = { lang: "en-US", region: "US", quotesCount: 6, @@ -177,18 +284,19 @@ export default function search( moduleOptions?: ModuleOptions ): Promise { return this._moduleExec({ - moduleName: "search", + moduleName: "searchTypebox", query: { url: "https://${YF_QUERY_HOST}/v1/finance/search", - schemaKey: "#/definitions/SearchOptions", + schema: SearchOptionsSchema, defaults: queryOptionsDefaults, runtime: { q: query }, overrides: queryOptionsOverrides, + needsCrumb: false, }, result: { - schemaKey: "#/definitions/SearchResult", + schema: SearchResultSchema, }, moduleOptions, diff --git a/src/modules/trendingSymbols.ts b/src/modules/trendingSymbols.ts index 19588124..ca24a814 100644 --- a/src/modules/trendingSymbols.ts +++ b/src/modules/trendingSymbols.ts @@ -1,30 +1,51 @@ +import { Static, Type } from "@sinclair/typebox"; import type { ModuleOptions, ModuleOptionsWithValidateTrue, ModuleOptionsWithValidateFalse, ModuleThis, } from "../lib/moduleCommon.js"; +import { NullableYahooNumber, YahooNumber } from "../lib/yahooFinanceTypes.js"; -export interface TrendingSymbol { - [key: string]: any; - symbol: string; -} +const TrendingSymbol = Type.Object( + { + symbol: Type.String(), + }, + { + additionalProperties: Type.Any(), + } +); -export interface TrendingSymbolsResult { - [key: string]: any; - count: number; - quotes: TrendingSymbol[]; - jobTimestamp: number; - startInterval: number; -} +const TrendingSymbolsResult = Type.Object( + { + count: YahooNumber, + quotes: Type.Array(TrendingSymbol), + jobTimestamp: YahooNumber, + startInterval: YahooNumber, + }, + { + additionalProperties: Type.Any(), + title: "TrendingSymbolsResult", + } +); -export interface TrendingSymbolsOptions { - lang?: string; - region?: string; - count?: number; -} +const TrendingSymbolsOptions = Type.Optional( + Type.Object( + { + lang: Type.Optional(Type.String()), + region: Type.Optional(Type.String()), + count: Type.Optional(YahooNumber), + }, + { + title: "TrendingSymbolsOptions", + } + ) +); + +type TrendingSymbolsResult = Static; +type TrendingSymbolsOptions = Static; -const queryOptionsDefaults = { +const queryOptionsDefaults: TrendingSymbolsOptions = { lang: "en-US", count: 5, }; @@ -53,12 +74,12 @@ export default function trendingSymbols( moduleName: "trendingSymbols", query: { url: "https://${YF_QUERY_HOST}/v1/finance/trending/" + query, - schemaKey: "#/definitions/TrendingSymbolsOptions", + schema: TrendingSymbolsOptions, defaults: queryOptionsDefaults, overrides: queryOptionsOverrides, }, result: { - schemaKey: "#/definitions/TrendingSymbolsResult", + schema: TrendingSymbolsResult, transformWith(result: any) { if (!result.finance) throw new Error("Unexpected result: " + JSON.stringify(result)); 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); }); diff --git a/yarn.lock b/yarn.lock index d3a57ce5..0f039811 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1510,6 +1510,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@sinclair/typebox@^0.32.27": + version "0.32.27" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.32.27.tgz#4cf1c777318c482da530cc667a1eeaaefcd2c521" + integrity sha512-JHRrubCKiXi6VKlbBTpTQnExkUFasPMIaXCJYJhqVBGLliQVt1yBZZgiZo3/uSmvAdXlIIdGoTAT6RB09L0QqA== + "@sinonjs/commons@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" @@ -1639,11 +1644,6 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/json-schema@^7.0.12": - version "7.0.13" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" - integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== - "@types/json-schema@^7.0.9": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -1878,23 +1878,6 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -ajv-formats@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv@8.10.0, ajv@^8.0.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" - integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2146,13 +2129,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - braces@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -2462,11 +2438,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-11.0.0.tgz#43e19c25dbedc8256203538e8d7e9346877a6f67" - integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== - common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" @@ -3317,17 +3288,6 @@ glob@^7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.3: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -4294,11 +4254,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -4763,13 +4718,6 @@ minimatch@^3.0.5, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -5227,11 +5175,6 @@ npmlog@^6.0.0, npmlog@^6.0.1: gauge "^4.0.0" set-blocking "^2.0.0" -oas-schema-walker@1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz#74c3cd47b70ff8e0b19adada14455b5d3ac38a22" - integrity sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ== - once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5764,11 +5707,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -5844,11 +5782,6 @@ safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-stable-stringify@^2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== - "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -6392,19 +6325,6 @@ ts-jest@29.1.2: semver "^7.5.3" yargs-parser "^21.0.1" -ts-json-schema-generator@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/ts-json-schema-generator/-/ts-json-schema-generator-1.5.0.tgz#9f5cea606c27ebcf13060157542ac1d3b225430f" - integrity sha512-RkiaJ6YxGc5EWVPfyHxszTmpGxX8HC2XBvcFlAl1zcvpOG4tjjh+eXioStXJQYTvr9MoK8zCOWzAUlko3K0DiA== - dependencies: - "@types/json-schema" "^7.0.12" - commander "^11.0.0" - glob "^8.0.3" - json5 "^2.2.3" - normalize-path "^3.0.0" - safe-stable-stringify "^2.4.3" - typescript "~5.3.2" - ts-node@10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" @@ -6488,11 +6408,6 @@ typescript@5.4.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== -typescript@~5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" - integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== - uglify-js@^3.1.4: version "3.12.5" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.5.tgz#83241496087c640efe9dfc934832e71725aba008"