@itick/browser-sdk
Version:
Official iTick API SDK for browser. Real-time & historical data for global Stocks, Forex, Crypto, Indices, Futures, Funds, Precious Metals. REST (OHLCV/K-line) + low-latency WebSocket. Promise-based, TypeScript-ready. For quant trading & fintech
197 lines • 6.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildApiUrl = buildApiUrl;
exports.buildWebSocketUrl = buildWebSocketUrl;
exports.convertKlinePeriod = convertKlinePeriod;
exports.convertSubscribeType = convertSubscribeType;
exports.parseeSubscribeType = parseeSubscribeType;
exports.parseSymbols = parseSymbols;
exports.makeHttpRequest = makeHttpRequest;
exports.createWebSocket = createWebSocket;
const kline_1 = require("../types/kline");
const socket_1 = require("../types/socket");
const STRICT_SYMBOL_REGEX = /^[A-Za-z0-9]+\$[A-Za-z0-9]+$/;
function buildApiUrl(baseURL, path) {
return `${baseURL.replace(/\/$/, '')}${path}`;
}
function buildWebSocketUrl(wssURL, path) {
return `${wssURL.replace(/\/$/, '')}${path}`;
}
/**
* Convert K-line period string to number
*/
function convertKlinePeriod(period) {
if (period === kline_1.KlinePeriod.ONE_MIN)
return 1;
if (period === kline_1.KlinePeriod.FIVE_MIN)
return 2;
if (period === kline_1.KlinePeriod.FIFTEEN_MIN)
return 3;
if (period === kline_1.KlinePeriod.THIRTY_MIN)
return 4;
if (period === kline_1.KlinePeriod.ONE_HOUR)
return 5;
if (period === kline_1.KlinePeriod.TWO_HOUR)
return 6;
if (period === kline_1.KlinePeriod.FOUR_HOUR)
return 7;
if (period === kline_1.KlinePeriod.ONE_DAY)
return 8;
if (period === kline_1.KlinePeriod.ONE_WEEK)
return 9;
if (period === kline_1.KlinePeriod.ONE_MONTH)
return 10;
return period;
}
/**
* Convert websocket subscription type
*/
function convertSubscribeType(type) {
if (type === socket_1.SubscribeType.KLINE_1M)
return 'kline@1';
if (type === socket_1.SubscribeType.KLINE_5M)
return 'kline@2';
if (type === socket_1.SubscribeType.KLINE_15M)
return 'kline@3';
if (type === socket_1.SubscribeType.KLINE_30M)
return 'kline@4';
if (type === socket_1.SubscribeType.KLINE_1H)
return 'kline@5';
if (type === socket_1.SubscribeType.KLINE_2H)
return 'kline@6';
if (type === socket_1.SubscribeType.KLINE_4H)
return 'kline@7';
if (type === socket_1.SubscribeType.KLINE_1D)
return 'kline@8';
if (type === socket_1.SubscribeType.KLINE_1W)
return 'kline@9';
if (type === socket_1.SubscribeType.KLINE_1MTH)
return 'kline@10';
return type;
}
/**
* Convert subscription type list
*/
function parseeSubscribeType(type) {
let rawList;
if (Array.isArray(type))
rawList = type.map(String);
else
rawList = type.split(',');
const validTypes = Object.values(socket_1.SubscribeType);
const processedList = [];
const apiToEnumMap = {};
for (const enumValue of validTypes) {
// Ensure enumValue is a valid SubscribeType
if (typeof enumValue === 'string' || typeof enumValue === 'number') {
const apiFormat = convertSubscribeType(enumValue);
apiToEnumMap[apiFormat] = enumValue;
}
}
for (const item of rawList) {
const trimmed = item.trim();
if (!trimmed)
continue;
let targetEnum;
const isValid = validTypes.some(v => v === trimmed);
if (isValid)
targetEnum = trimmed;
else
targetEnum = apiToEnumMap[trimmed];
if (!targetEnum)
throw new Error(`Invalid subscribe type: "${trimmed}". Valid types are: ${validTypes.join(', ')}`);
processedList.push(convertSubscribeType(trimmed));
}
return [...new Set(processedList)].join(',');
}
/**
* Subscription product parameter formatting function
* @param input Subscription parameters
*/
function parseSymbols(input) {
const list = Array.isArray(input) ? input : input.split(',');
return [...new Set(list
.map(item => item.trim())
.filter(Boolean)
.map(item => {
if (!STRICT_SYMBOL_REGEX.test(item))
throw new Error(`Invalid symbol format: "${item}". Expected format is "SYMBOL$REGION", e.g. "AAPL$US".`);
return item.toUpperCase();
}))].join(',');
}
/**
* Build query string
*/
function buildQueryString(params) {
return Object.entries(params)
.filter(([_, value]) => value != null)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&');
}
/**
* HTTP request implementation for Browser environment (using fetch API)
*/
async function makeHttpRequest(url, options = {}) {
const { method = 'GET', headers = {}, body, params = {}, } = options;
// Build complete URL
let finalUrl = url;
// For GET requests, append params to URL
if (method === 'GET' && Object.keys(params).length > 0) {
const queryString = buildQueryString(params);
// Handle existing query parameters in the base URL
const separator = finalUrl.includes('?') ? '&' : '?';
finalUrl = `${finalUrl}${separator}${queryString}`;
}
// Prepare fetch options
const fetchOptions = {
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
};
// Attach body for non-GET methods
if (body && method !== 'GET') {
fetchOptions.body = body;
}
try {
const response = await fetch(finalUrl, fetchOptions);
// Extract headers as a plain object
const responseHeaders = {};
response.headers.forEach((value, key) => {
responseHeaders[key] = value;
});
// Parse JSON response
let data;
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
try {
data = await response.json();
}
catch (e) {
// Fallback to text if JSON parsing fails but content-type suggests JSON
data = await response.text();
}
}
else {
// For non-JSON responses, get text
data = await response.text();
}
return {
status: response.status,
data,
headers: responseHeaders,
};
}
catch (error) {
// Network errors or other fetch failures
throw new Error(`Request failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Create WebSocket instance (Browser environment)
*/
async function createWebSocket(url, token) {
return new WebSocket(`${url}?token=${token}`);
}
//# sourceMappingURL=index.js.map