fundamentalsTimeSeries.js•7.24 kB
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.processResponse = exports.processQuery = void 0;
exports.default = fundamentalsTimeSeries;
const typebox_1 = require("@sinclair/typebox");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: we have to ignore this for csm output.
const timeseries_json_1 = __importDefault(require("../lib/timeseries.json"));
const yahooFinanceTypes_js_1 = require("../lib/yahooFinanceTypes.js");
const FundamentalsTimeSeries_Types = ["quarterly", "annual", "trailing"];
const FundamentalsTimeSeries_Modules = [
"financials",
"balance-sheet",
"cash-flow",
"all",
];
const FundamentalsTimeSeriesResultSchema = typebox_1.Type.Object({
date: yahooFinanceTypes_js_1.YahooFinanceDate,
}, {
additionalProperties: typebox_1.Type.Unknown(),
title: "FundamentalsTimeSeriesResult",
});
const FundamentalsTimeSeriesOptionsSchema = typebox_1.Type.Object({
period1: typebox_1.Type.Union([yahooFinanceTypes_js_1.YahooFinanceDate, yahooFinanceTypes_js_1.YahooNumber, typebox_1.Type.String()]),
period2: typebox_1.Type.Optional(typebox_1.Type.Union([yahooFinanceTypes_js_1.YahooFinanceDate, yahooFinanceTypes_js_1.YahooNumber, typebox_1.Type.String()])),
type: typebox_1.Type.Optional(typebox_1.Type.String()),
merge: typebox_1.Type.Optional(typebox_1.Type.Boolean()), // This returns a completely different format that will break the transformer
padTimeSeries: typebox_1.Type.Optional(typebox_1.Type.Boolean()), // Not exactly sure what this does, assume it pads p1 and p2???
lang: typebox_1.Type.Optional(typebox_1.Type.String()),
region: typebox_1.Type.Optional(typebox_1.Type.String()),
module: typebox_1.Type.String(),
}, {
title: "FundamentalsTimeSeriesOptions",
});
const FundamentalsTimeSeriesResultsSchema = typebox_1.Type.Array(FundamentalsTimeSeriesResultSchema);
const queryOptionsDefaults = {
merge: false,
padTimeSeries: true,
lang: "en-US",
region: "US",
type: "quarterly",
};
function fundamentalsTimeSeries(symbol, queryOptionsOverrides, moduleOptions) {
return this._moduleExec({
moduleName: "options",
query: {
assertSymbol: symbol,
url: `https://query1.finance.yahoo.com/ws/fundamentals-timeseries/v1/finance/timeseries/${symbol}`,
needsCrumb: false,
schema: FundamentalsTimeSeriesOptionsSchema,
defaults: queryOptionsDefaults,
overrides: queryOptionsOverrides,
transformWith: exports.processQuery,
},
result: {
schema: FundamentalsTimeSeriesResultsSchema,
transformWith(response) {
if (!response || !response.timeseries)
throw new Error(`Unexpected result: ${JSON.stringify(response)}`);
return (0, exports.processResponse)(response);
},
},
moduleOptions,
});
}
/**
* Transform the input options into query parameters.
* The options module defines which keys that are used in the query.
* The keys are joined together into the query parameter type and
* pre-fixed with the options type (e.g. annualTotalRevenue).
* @param queryOptions Input query options.
* @returns Query parameters.
*/
const processQuery = function (queryOptions) {
// Convert dates
if (!queryOptions.period2)
queryOptions.period2 = new Date();
const dates = ["period1", "period2"];
for (const fieldName of dates) {
const value = queryOptions[fieldName];
if (value instanceof Date)
queryOptions[fieldName] = Math.floor(value.getTime() / 1000);
else if (typeof value === "string") {
const timestamp = new Date(value).getTime();
if (isNaN(timestamp))
throw new Error("yahooFinance.fundamentalsTimeSeries() option '" +
fieldName +
"' invalid date provided: '" +
value +
"'");
queryOptions[fieldName] = Math.floor(timestamp / 1000);
}
}
// Validate query parameters.
if (queryOptions.period1 === queryOptions.period2) {
throw new Error("yahooFinance.fundamentalsTimeSeries() options `period1` and `period2` " +
"cannot share the same value.");
}
else if (!FundamentalsTimeSeries_Types.includes(queryOptions.type || "")) {
throw new Error("yahooFinance.fundamentalsTimeSeries() option type invalid.");
}
else if (!FundamentalsTimeSeries_Modules.includes(queryOptions.module || "")) {
throw new Error("yahooFinance.fundamentalsTimeSeries() option module invalid.");
}
// Join the keys for the module into query types.
const keys = Object.entries(timeseries_json_1.default).reduce((previous, [module, keys]) => {
if (queryOptions.module == "all") {
return previous.concat(keys);
}
else if (module == queryOptions.module) {
return previous.concat(keys);
}
else
return previous;
}, []);
const queryType = queryOptions.type + keys.join(`,${queryOptions.type}`);
return {
period1: queryOptions.period1,
period2: queryOptions.period2,
type: queryType,
};
};
exports.processQuery = processQuery;
/**
* Transforms the time-series into an array with reported values per period.
* Each object represents a period and its properties are the data points.
* Financial statement content variates and keys are skipped when empty.
* The query keys include the option type (e.g. annualTotalRevenue).
* In the response the type is removed (e.g. totalRevenue) for
* easier mapping by the client.
* @param response Query response.
* @returns Formatted response.
*/
const processResponse = function (response) {
const keyedByTimestamp = {};
const replace = new RegExp(FundamentalsTimeSeries_Types.join("|"));
for (let ct = 0; ct < response.timeseries.result.length; ct++) {
const result = response.timeseries.result[ct];
if (!result.timestamp || !result.timestamp.length) {
continue;
}
for (let ct = 0; ct < result.timestamp.length; ct++) {
const timestamp = result.timestamp[ct];
const dataKey = Object.keys(result)[2];
if (!keyedByTimestamp[timestamp]) {
keyedByTimestamp[timestamp] = { date: timestamp };
}
if (!result[dataKey][ct] ||
!result[dataKey][ct].reportedValue ||
!result[dataKey][ct].reportedValue.raw) {
continue;
}
const short = dataKey.replace(replace, "");
const key = short == short.toUpperCase()
? short
: short[0].toLowerCase() + short.slice(1);
keyedByTimestamp[timestamp][key] = result[dataKey][ct].reportedValue.raw;
}
}
return Object.keys(keyedByTimestamp).map((k) => keyedByTimestamp[k]);
};
exports.processResponse = processResponse;