@yash101/schwab-api-client
Version:
A TypeScript client library for interacting with the Charles Schwab Brokerage APIs.
285 lines • 12.7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataApi = void 0;
const assert_1 = __importDefault(require("assert"));
const Assert_1 = require("./util/Assert");
const dataapi_types_1 = require("./api-types/dataapi.types");
const CONTENT_TYPE = 'Content-Type';
const APPLICATION_JSON = 'application/json';
const APPLICATION_FORM_URLENCODED = 'application/x-www-form-urlencoded';
const GET = 'GET';
class DataApi {
tokens;
apiOptions;
async doApiFetch(path, method, { queryParams, body = null, headers = {}, } = {}) {
(0, assert_1.default)(this.tokens.getAccessToken(), 'Access token is required');
(0, Assert_1.assert_warn)(this.apiOptions.getBaseUri(), 'Recommended to specify base URL');
const url = new URL(path, this.apiOptions.getBaseUri() || 'https://api.schwabapi.com');
const mimeType = headers[CONTENT_TYPE] || APPLICATION_JSON;
queryParams?.forEach((value, key) => url.searchParams.append(key, value));
const options = {
method,
headers: {
...headers,
Authorization: this.tokens.getAuthHeader(),
Accept: APPLICATION_JSON,
},
};
if (body) {
if (typeof body !== 'string') {
switch (mimeType) {
case 'application/json':
body = JSON.stringify(body);
break;
case 'application/x-www-form-urlencoded':
body = new URLSearchParams(body).toString();
break;
default:
body = JSON.stringify(body);
break;
}
}
options.body = body;
options.headers[CONTENT_TYPE] = mimeType;
}
try {
return await fetch(url, options);
}
catch (e) {
throw new Error(`Error fetching data: ${e.message}`);
}
}
constructor(tokens, apiOptions) {
this.tokens = tokens;
this.apiOptions = apiOptions;
}
setTokens(tokens) {
this.tokens = tokens;
}
setApiOptions(apiOptions) {
this.apiOptions = apiOptions;
}
async getQuotes(request) {
(0, assert_1.default)(request.symbols, 'Symbols are required');
(0, assert_1.default)(request.symbols.length > 0, 'At least one symbol is required');
const path = '/marketdata/v1/quotes';
const queryParams = new URLSearchParams({
symbols: request.symbols.join(','),
indicative: request.indicative ? 'true' : 'false',
});
if (request.fields) {
queryParams.append('fields', request.fields.join(','));
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getSingleQuote(request) {
(0, assert_1.default)(request.symbol, 'Symbols are required');
(0, assert_1.default)(request.symbol.length === 1, 'Only one symbol is allowed');
const path = `/marketdata/v1/${request.symbol}/quotes`;
const queryParams = new URLSearchParams({
symbols: request.symbol,
});
if (request.fields) {
queryParams.append('fields', request.fields.join(','));
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getOptionChains(request) {
(0, assert_1.default)(request.symbol, 'Symbol is required');
if (request.fromDate instanceof Date) {
request.fromDate = request.fromDate.toISOString();
}
if (request.toDate instanceof Date) {
request.toDate = request.toDate.toISOString();
}
const path = `/marketdata/v1/chains`;
const queryParams = new URLSearchParams();
for (const [key, value] of Object.entries(request)) {
if (value !== undefined && value !== null) {
if (value instanceof Date) {
queryParams.append(key, value.toISOString());
}
else if (typeof value === 'boolean') {
queryParams.append(key, value ? 'true' : 'false');
}
else {
queryParams.append(key, value.toString());
}
}
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getOptionExpirationChain(request) {
(0, assert_1.default)(typeof request === 'string', 'Symbol is required');
(0, assert_1.default)(request.length > 0, 'Symbol is required');
const path = `/marketdata/v1/expirationchain`;
const queryParams = new URLSearchParams({
symbol: request,
});
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getPriceHistory(request, validate = true) {
// Validate the request
(0, assert_1.default)(request.symbol, 'Symbol is required');
if (validate) {
const validPeriods = {
[dataapi_types_1.PeriodTypeEnum.DAY]: [1, 2, 3, 4, 5, 10],
[dataapi_types_1.PeriodTypeEnum.MONTH]: [1, 2, 3, 6],
[dataapi_types_1.PeriodTypeEnum.YEAR]: [1, 2, 3, 5, 10, 15, 20],
[dataapi_types_1.PeriodTypeEnum.YTD]: [1],
};
if (request.periodType && request.period) {
(0, assert_1.default)(validPeriods[request.periodType], `Invalid period type: ${request.periodType}`);
(0, assert_1.default)(validPeriods[request.periodType].includes(request.period), `Invalid period: ${request.period} for period type: ${request.periodType}`);
}
const validFrequencyTypes = {
[dataapi_types_1.PeriodTypeEnum.DAY]: [dataapi_types_1.FrequencyTypeEnum.MINUTE],
[dataapi_types_1.PeriodTypeEnum.MONTH]: [
dataapi_types_1.FrequencyTypeEnum.DAILY, dataapi_types_1.FrequencyTypeEnum.WEEKLY
],
[dataapi_types_1.PeriodTypeEnum.YEAR]: [
dataapi_types_1.FrequencyTypeEnum.DAILY, dataapi_types_1.FrequencyTypeEnum.WEEKLY,
dataapi_types_1.FrequencyTypeEnum.MONTHLY
],
[dataapi_types_1.PeriodTypeEnum.YTD]: [dataapi_types_1.FrequencyTypeEnum.DAILY,
dataapi_types_1.FrequencyTypeEnum.WEEKLY, dataapi_types_1.FrequencyTypeEnum.MONTHLY],
};
if (request.frequencyType && request.frequency) {
(0, assert_1.default)(validFrequencyTypes[request.periodType || dataapi_types_1.PeriodTypeEnum.DAY], `Invalid frequency type: ${request.frequencyType}`);
(0, assert_1.default)(validFrequencyTypes[request.periodType || dataapi_types_1.PeriodTypeEnum.DAY]
.includes(request.frequencyType), `Invalid frequency: ${request.frequency} for frequency type: ${request.frequencyType}`);
}
const validFrequencies = {
[dataapi_types_1.FrequencyTypeEnum.MINUTE]: [1, 5, 10, 15, 30],
[dataapi_types_1.FrequencyTypeEnum.DAILY]: [1],
[dataapi_types_1.FrequencyTypeEnum.WEEKLY]: [1],
[dataapi_types_1.FrequencyTypeEnum.MONTHLY]: [1],
};
if (request.frequencyType && request.frequency) {
(0, assert_1.default)(validFrequencies[request.frequencyType], `Invalid frequency: ${request.frequency} for frequency type: ${request.frequencyType}`);
(0, assert_1.default)(validFrequencies[request.frequencyType].includes(request.frequency), `Invalid frequency: ${request.frequency} for frequency type: ${request.frequencyType}`);
}
}
const path = `/marketdata/v1/pricehistory`;
const queryParams = new URLSearchParams();
for (const [key, value] of Object.entries(request)) {
if (value !== undefined && value !== null) {
if (value instanceof Date) {
queryParams.append(key, value.toISOString());
}
else if (typeof value === 'boolean') {
queryParams.append(key, value ? 'true' : 'false');
}
else {
queryParams.append(key, value.toString());
}
}
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getMoversForIndex(request) {
(0, assert_1.default)(request.symbol_id, 'Symbol ID is required');
const path = `/marketdata/v1/movers`;
const queryParams = new URLSearchParams();
for (const [key, value] of Object.entries(request)) {
if (value !== undefined && value !== null) {
if (value instanceof Date) {
queryParams.append(key, value.toISOString());
}
else if (typeof value === 'boolean') {
queryParams.append(key, value ? 'true' : 'false');
}
else {
queryParams.append(key, value.toString());
}
}
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getMarketHours(request) {
(0, assert_1.default)(request.markets, 'Market is required');
(0, assert_1.default)(request.markets.length > 0, 'At least one market is required');
const path = `/marketdata/v1/markets`;
const queryParams = new URLSearchParams();
if (request.date instanceof Date) {
queryParams.append('date', request.date.toISOString().split('T')[0]);
}
else if (typeof request.date === 'string') {
queryParams.append('date', request.date);
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getSingleMarketHours(request) {
(0, assert_1.default)(request.market, 'Market is required');
const path = `/marketdata/v1/markets/${request.market}`;
const queryParams = new URLSearchParams();
if (request.date instanceof Date) {
queryParams.append('date', request.date.toISOString().split('T')[0]);
}
else if (typeof request.date === 'string') {
queryParams.append('date', request.date);
}
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getInstruments(request) {
(0, assert_1.default)(request.symbol, 'Symbol is required');
(0, assert_1.default)(request.projection, 'Projection is required');
const path = `/marketdata/v1/instruments`;
const queryParams = new URLSearchParams({
symbol: request.symbol,
projection: request.projection,
});
const response = await this.doApiFetch(path, GET, { queryParams });
const json = await response.json();
return (response.ok)
? json
: json;
}
async getInstrumentByCUSIPId(request) {
(0, assert_1.default)(request, 'CUSIP is required');
(0, assert_1.default)(typeof request === 'string', 'CUSIP is required');
const path = `/marketdata/v1/instruments/${request}`;
const response = await this.doApiFetch(path, GET);
const json = await response.json();
return (response.ok)
? json
: json;
}
}
exports.DataApi = DataApi;
//# sourceMappingURL=data.api.js.map