@yuants/vendor-binance 0.10.12 → 0.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,8 @@ import { encodeHex, formatTime, HmacSHA256, newError } from '@yuants/utils';
3
3
  const MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');
4
4
  const MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');
5
5
  const terminal = Terminal.fromNodeEnv();
6
- let retryAfterUntil = null;
6
+ // 每个接口单独进行主动限流控制
7
+ const mapPathToRetryAfterUntil = {};
7
8
  export const isApiError = (value) => typeof (value === null || value === void 0 ? void 0 : value.code) === 'number' && typeof (value === null || value === void 0 ? void 0 : value.msg) === 'string';
8
9
  const appendParams = (url, params) => {
9
10
  if (!params)
@@ -47,11 +48,18 @@ const callApi = async (method, endpoint, params, credential) => {
47
48
  else {
48
49
  console.info(formatTime(Date.now()), method, url.href);
49
50
  }
51
+ const retryAfterUntil = mapPathToRetryAfterUntil[endpoint];
50
52
  if (retryAfterUntil) {
51
53
  if (Date.now() <= retryAfterUntil) {
52
- throw newError('ACTIVE_RATE_LIMIT', { retryAfterUntil, now: Date.now(), url: url.href });
54
+ // 主动限流
55
+ throw newError('ACTIVE_RATE_LIMIT', {
56
+ wait_time: `${retryAfterUntil - Date.now()}ms`,
57
+ retryAfterUntil,
58
+ url: url.href,
59
+ endpoint,
60
+ });
53
61
  }
54
- retryAfterUntil = null;
62
+ delete mapPathToRetryAfterUntil[endpoint];
55
63
  }
56
64
  MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();
57
65
  const res = await fetch(url.href, {
@@ -61,11 +69,11 @@ const callApi = async (method, endpoint, params, credential) => {
61
69
  const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');
62
70
  const retryAfter = res.headers.get('Retry-After');
63
71
  if (retryAfter) {
64
- retryAfterUntil = Date.now() + parseInt(retryAfter, 10) * 1000;
72
+ mapPathToRetryAfterUntil[endpoint] = Date.now() + parseInt(retryAfter, 10) * 1000;
65
73
  }
66
74
  console.info(formatTime(Date.now()), 'response', method, url.href, `status=${res.status}`, retryAfter ? `retryAfter=${retryAfter}` : '', `usedWeight1M=${usedWeight1M !== null && usedWeight1M !== void 0 ? usedWeight1M : 'N/A'}`);
67
75
  if (usedWeight1M) {
68
- MetricBinanceApiUsedWeight.labels({ terminal_id: terminal.terminal_id }).set(+usedWeight1M);
76
+ MetricBinanceApiUsedWeight.labels({ endpoint, terminal_id: terminal.terminal_id }).set(+usedWeight1M);
69
77
  }
70
78
  return res.json();
71
79
  };
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { encodeHex, formatTime, HmacSHA256, newError } from '@yuants/utils';\n\nconst MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');\nconst MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');\nconst terminal = Terminal.fromNodeEnv();\n\ntype HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\ntype RequestParams = Record<string, string | number | boolean | undefined>;\n\nexport interface ICredential {\n access_key: string;\n secret_key: string;\n}\n\nexport interface IApiError {\n code: number;\n msg: string;\n}\n\nlet retryAfterUntil: number | null = null;\n\nexport const isApiError = <T>(value: T | IApiError): value is IApiError =>\n typeof (value as IApiError)?.code === 'number' && typeof (value as IApiError)?.msg === 'string';\n\nconst appendParams = (url: URL, params?: RequestParams) => {\n if (!params) return;\n const entries = Object.entries(params).filter(([, value]) => value !== undefined);\n const timestampEntry = entries.find(([key]) => key === 'timestamp');\n const restEntries = entries.filter(([key]) => key !== 'timestamp');\n restEntries\n .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))\n .forEach(([key, value]) => {\n url.searchParams.set(key, String(value));\n });\n if (timestampEntry) {\n url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));\n }\n};\n\nconst callApi = async <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n credential?: ICredential,\n): Promise<T> => {\n const url = new URL(endpoint);\n const normalizedParams: RequestParams = { ...params };\n if (credential) {\n if (normalizedParams.recvWindow === undefined) {\n // FYI https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#timing-security\n normalizedParams.recvWindow = 5000;\n }\n if (normalizedParams.timestamp === undefined) {\n normalizedParams.timestamp = Date.now();\n }\n }\n appendParams(url, normalizedParams);\n\n let headers: Record<string, string> | undefined;\n if (credential) {\n const signData = url.search.slice(1);\n\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(signData), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n headers = {\n 'Content-Type': 'application/json;charset=utf-8',\n 'X-MBX-APIKEY': credential.access_key,\n };\n console.info(\n formatTime(Date.now()),\n method,\n url.href,\n JSON.stringify(headers),\n url.searchParams.toString(),\n signData,\n );\n } else {\n console.info(formatTime(Date.now()), method, url.href);\n }\n\n if (retryAfterUntil) {\n if (Date.now() <= retryAfterUntil) {\n throw newError('ACTIVE_RATE_LIMIT', { retryAfterUntil, now: Date.now(), url: url.href });\n }\n retryAfterUntil = null;\n }\n\n MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n\n const res = await fetch(url.href, {\n method,\n headers,\n });\n const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');\n const retryAfter = res.headers.get('Retry-After');\n if (retryAfter) {\n retryAfterUntil = Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n console.info(\n formatTime(Date.now()),\n 'response',\n method,\n url.href,\n `status=${res.status}`,\n retryAfter ? `retryAfter=${retryAfter}` : '',\n `usedWeight1M=${usedWeight1M ?? 'N/A'}`,\n );\n if (usedWeight1M) {\n MetricBinanceApiUsedWeight.labels({ terminal_id: terminal.terminal_id }).set(+usedWeight1M);\n }\n return res.json() as Promise<T>;\n};\n\nexport const requestPublic = <T>(method: HttpMethod, endpoint: string, params?: RequestParams) =>\n callApi<T>(method, endpoint, params);\n\nexport const requestPrivate = <T>(\n credential: ICredential,\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n) => callApi<T>(method, endpoint, params, credential);\n\nexport const getDefaultCredential = (): ICredential => {\n const access_key = process.env.ACCESS_KEY;\n const secret_key = process.env.SECRET_KEY;\n if (!access_key || !secret_key) {\n throw new Error('Missing Binance credential: ACCESS_KEY and SECRET_KEY must be set');\n }\n return { access_key, secret_key };\n};\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { encodeHex, formatTime, HmacSHA256, newError } from '@yuants/utils';\n\nconst MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');\nconst MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');\nconst terminal = Terminal.fromNodeEnv();\n\ntype HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\ntype RequestParams = Record<string, string | number | boolean | undefined>;\n\nexport interface ICredential {\n access_key: string;\n secret_key: string;\n}\n\nexport interface IApiError {\n code: number;\n msg: string;\n}\n\n// 每个接口单独进行主动限流控制\nconst mapPathToRetryAfterUntil: Record<string, number> = {};\n\nexport const isApiError = <T>(value: T | IApiError): value is IApiError =>\n typeof (value as IApiError)?.code === 'number' && typeof (value as IApiError)?.msg === 'string';\n\nconst appendParams = (url: URL, params?: RequestParams) => {\n if (!params) return;\n const entries = Object.entries(params).filter(([, value]) => value !== undefined);\n const timestampEntry = entries.find(([key]) => key === 'timestamp');\n const restEntries = entries.filter(([key]) => key !== 'timestamp');\n restEntries\n .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))\n .forEach(([key, value]) => {\n url.searchParams.set(key, String(value));\n });\n if (timestampEntry) {\n url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));\n }\n};\n\nconst callApi = async <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n credential?: ICredential,\n): Promise<T> => {\n const url = new URL(endpoint);\n const normalizedParams: RequestParams = { ...params };\n if (credential) {\n if (normalizedParams.recvWindow === undefined) {\n // FYI https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#timing-security\n normalizedParams.recvWindow = 5000;\n }\n if (normalizedParams.timestamp === undefined) {\n normalizedParams.timestamp = Date.now();\n }\n }\n appendParams(url, normalizedParams);\n\n let headers: Record<string, string> | undefined;\n if (credential) {\n const signData = url.search.slice(1);\n\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(signData), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n headers = {\n 'Content-Type': 'application/json;charset=utf-8',\n 'X-MBX-APIKEY': credential.access_key,\n };\n console.info(\n formatTime(Date.now()),\n method,\n url.href,\n JSON.stringify(headers),\n url.searchParams.toString(),\n signData,\n );\n } else {\n console.info(formatTime(Date.now()), method, url.href);\n }\n\n const retryAfterUntil = mapPathToRetryAfterUntil[endpoint];\n\n if (retryAfterUntil) {\n if (Date.now() <= retryAfterUntil) {\n // 主动限流\n throw newError('ACTIVE_RATE_LIMIT', {\n wait_time: `${retryAfterUntil - Date.now()}ms`,\n retryAfterUntil,\n url: url.href,\n endpoint,\n });\n }\n delete mapPathToRetryAfterUntil[endpoint];\n }\n\n MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n\n const res = await fetch(url.href, {\n method,\n headers,\n });\n const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');\n const retryAfter = res.headers.get('Retry-After');\n if (retryAfter) {\n mapPathToRetryAfterUntil[endpoint] = Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n console.info(\n formatTime(Date.now()),\n 'response',\n method,\n url.href,\n `status=${res.status}`,\n retryAfter ? `retryAfter=${retryAfter}` : '',\n `usedWeight1M=${usedWeight1M ?? 'N/A'}`,\n );\n if (usedWeight1M) {\n MetricBinanceApiUsedWeight.labels({ endpoint, terminal_id: terminal.terminal_id }).set(+usedWeight1M);\n }\n return res.json() as Promise<T>;\n};\n\nexport const requestPublic = <T>(method: HttpMethod, endpoint: string, params?: RequestParams) =>\n callApi<T>(method, endpoint, params);\n\nexport const requestPrivate = <T>(\n credential: ICredential,\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n) => callApi<T>(method, endpoint, params, credential);\n\nexport const getDefaultCredential = (): ICredential => {\n const access_key = process.env.ACCESS_KEY;\n const secret_key = process.env.SECRET_KEY;\n if (!access_key || !secret_key) {\n throw new Error('Missing Binance credential: ACCESS_KEY and SECRET_KEY must be set');\n }\n return { access_key, secret_key };\n};\n"]}
@@ -8,7 +8,7 @@ export const getFutureExchangeInfo = () => requestPublic('GET', 'https://fapi.bi
8
8
  /**
9
9
  * 查询资金费率历史
10
10
  *
11
- * https://binance-docs.github.io/apidocs/futures/cn/#31dbeb24c4
11
+ * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History
12
12
  */
13
13
  export const getFutureFundingRate = (params) => requestPublic('GET', 'https://fapi.binance.com/fapi/v1/fundingRate', params);
14
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAgGzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAiC,EAAE,CACtE,aAAa,CAAsB,KAAK,EAAE,+CAA+C,CAAC,CAAC;AAE7F;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAKpC,EAAsC,EAAE,CACvC,aAAa,CAA4B,KAAK,EAAE,8CAA8C,EAAE,MAAM,CAAC,CAAC;AAE1G;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAA2B,EAAuC,EAAE,CACxG,aAAa,CAA6B,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AAE5G;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA4B,EAAqC,EAAE,CACrG,aAAa,CACX,KAAK,EACL,oDAAoD,EACpD,MAAM,CACP,CAAC;AAEJ;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAA0B,EAAgC,EAAE,CAChG,aAAa,CAAsB,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AASrG;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAA4B,EAAmC,EAAE,CACjG,aAAa,CAAyB,KAAK,EAAE,kDAAkD,EAAE,MAAM,CAAC,CAAC;AAwC3G;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAA+B,EAAE,CAClE,aAAa,CAAoB,KAAK,EAAE,6CAA6C,CAAC,CAAC;AAEzF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAIlC,EASC,EAAE,CACF,aAAa,CASX,KAAK,EAAE,6CAA6C,EAAE,MAAM,CAAC,CAAC","sourcesContent":["import { requestPublic } from './client';\n\nexport interface IFutureExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface IFutureExchangeSymbol {\n symbol: string;\n pair: string;\n contractType: string;\n deliveryDate: number;\n onboardDate: number;\n status: string;\n maintMarginPercent: string;\n requiredMarginPercent: string;\n baseAsset: string;\n quoteAsset: string;\n marginAsset: string;\n pricePrecision: number;\n quantityPrecision: number;\n baseAssetPrecision: number;\n quotePrecision: number;\n underlyingType: string;\n underlyingSubType: string[];\n settlePlan: number;\n triggerProtect: string;\n liquidationFee: string;\n marketTakeBound: string;\n maxMoveOrderLimit: number;\n filters: IFutureExchangeFilter[];\n orderTypes: string[];\n timeInForce: string[];\n}\n\nexport interface IFutureExchangeInfo {\n timezone: string;\n serverTime: number;\n futuresType: string;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n assets: {\n asset: string;\n marginAvailable: boolean;\n autoAssetExchange: string;\n }[];\n symbols: IFutureExchangeSymbol[];\n}\n\nexport interface IFutureFundingRateEntry {\n symbol: string;\n fundingTime: number;\n fundingRate: string;\n markPrice: string;\n}\n\nexport interface IFuturePremiumIndexEntry {\n symbol: string;\n markPrice: string;\n indexPrice: string;\n estimatedSettlePrice: string;\n lastFundingRate: string;\n interestRate: string;\n nextFundingTime: number;\n time: number;\n}\n\nexport interface IFutureBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n time: number;\n}\n\nexport interface IFutureOpenInterest {\n openInterest: string;\n symbol: string;\n time: number;\n}\n\nexport interface IMarginPair {\n id: string;\n symbol: string;\n base: string;\n quote: string;\n isMarginTrade: boolean;\n isBuyAllowed: boolean;\n isSellAllowed: boolean;\n}\n\n/**\n * 获取交易规则和交易对\n *\n * https://binance-docs.github.io/apidocs/futures/cn/#0f3f2d5ee7\n */\nexport const getFutureExchangeInfo = (): Promise<IFutureExchangeInfo> =>\n requestPublic<IFutureExchangeInfo>('GET', 'https://fapi.binance.com/fapi/v1/exchangeInfo');\n\n/**\n * 查询资金费率历史\n *\n * https://binance-docs.github.io/apidocs/futures/cn/#31dbeb24c4\n */\nexport const getFutureFundingRate = (params: {\n symbol?: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<IFutureFundingRateEntry[]> =>\n requestPublic<IFutureFundingRateEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/fundingRate', params);\n\n/**\n * 最新标记价格和资金费率\n *\n * 采集各大交易所数据加权平均\n *\n * 权重: 带symbol为1;不带symbol为10\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price\n */\nexport const getFuturePremiumIndex = (params: { symbol?: string }): Promise<IFuturePremiumIndexEntry[]> =>\n requestPublic<IFuturePremiumIndexEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/premiumIndex', params);\n\n/**\n * 当前最优挂单\n *\n * 返回当前最优的挂单(最高买单,最低卖单)\n *\n * 权重: 单交易对2,无交易对5\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker\n */\nexport const getFutureBookTicker = (params?: { symbol?: string }): Promise<IFutureBookTickerEntry[]> =>\n requestPublic<IFutureBookTickerEntry[]>(\n 'GET',\n 'https://fapi.binance.com/fapi/v1/ticker/bookTicker',\n params,\n );\n\n/**\n * 获取未平仓合约数\n *\n * 权重: 1\n *\n * 更新速率: 3s\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest\n */\nexport const getFutureOpenInterest = (params: { symbol: string }): Promise<IFutureOpenInterest> =>\n requestPublic<IFutureOpenInterest>('GET', 'https://fapi.binance.com/fapi/v1/openInterest', params);\nexport interface ISpotBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n}\n\n/**\n * 当前最优挂单 (Spot)\n *\n * https://binance-docs.github.io/apidocs/spot/cn/#5393cd0851\n */\nexport const getSpotBookTicker = (params?: { symbol?: string }): Promise<ISpotBookTickerEntry[]> =>\n requestPublic<ISpotBookTickerEntry[]>('GET', 'https://api.binance.com/api/v3/ticker/bookTicker', params);\n\nexport interface ISpotExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface ISpotExchangeSymbol {\n symbol: string;\n status: string;\n baseAsset: string;\n baseAssetPrecision: number;\n quoteAsset: string;\n quotePrecision: number;\n baseCommissionPrecision: number;\n quoteCommissionPrecision: number;\n orderTypes: string[];\n icebergAllowed: boolean;\n ocoAllowed: boolean;\n quoteOrderQtyMarketAllowed: boolean;\n allowTrailingStop: boolean;\n cancelReplaceAllowed: boolean;\n isSpotTradingAllowed: boolean;\n isMarginTradingAllowed: boolean;\n filters: ISpotExchangeFilter[];\n permissions: string[];\n}\n\nexport interface ISpotExchangeInfo {\n timezone: string;\n serverTime: number;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n symbols: ISpotExchangeSymbol[];\n}\n\n/**\n * 获取现货交易规则和交易对\n *\n * https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/general-endpoints#%E4%BA%A4%E6%98%93%E8%A7%84%E8%8C%83%E4%BF%A1%E6%81%AF\n */\nexport const getSpotExchangeInfo = (): Promise<ISpotExchangeInfo> =>\n requestPublic<ISpotExchangeInfo>('GET', 'https://api.binance.com/api/v3/exchangeInfo');\n\n/**\n * 获取当前现货报价\n *\n *https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/market-data-endpoints#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC%E6%8E%A5%E5%8F%A3\n */\nexport const getSpotTickerPrice = (params?: {\n symbol?: string;\n symbols?: string;\n symbolStatus?: 'TRADING' | 'HALT' | 'BREAK';\n}): Promise<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n> =>\n requestPublic<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n >('GET', 'https://api.binance.com/api/v3/ticker/price', params);\n"]}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAgGzC;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAiC,EAAE,CACtE,aAAa,CAAsB,KAAK,EAAE,+CAA+C,CAAC,CAAC;AAE7F;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAKpC,EAAsC,EAAE,CACvC,aAAa,CAA4B,KAAK,EAAE,8CAA8C,EAAE,MAAM,CAAC,CAAC;AAE1G;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAA2B,EAAuC,EAAE,CACxG,aAAa,CAA6B,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AAE5G;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA4B,EAAqC,EAAE,CACrG,aAAa,CACX,KAAK,EACL,oDAAoD,EACpD,MAAM,CACP,CAAC;AAEJ;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,MAA0B,EAAgC,EAAE,CAChG,aAAa,CAAsB,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AASrG;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAA4B,EAAmC,EAAE,CACjG,aAAa,CAAyB,KAAK,EAAE,kDAAkD,EAAE,MAAM,CAAC,CAAC;AAwC3G;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAA+B,EAAE,CAClE,aAAa,CAAoB,KAAK,EAAE,6CAA6C,CAAC,CAAC;AAEzF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAIlC,EASC,EAAE,CACF,aAAa,CASX,KAAK,EAAE,6CAA6C,EAAE,MAAM,CAAC,CAAC","sourcesContent":["import { requestPublic } from './client';\n\nexport interface IFutureExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface IFutureExchangeSymbol {\n symbol: string;\n pair: string;\n contractType: string;\n deliveryDate: number;\n onboardDate: number;\n status: string;\n maintMarginPercent: string;\n requiredMarginPercent: string;\n baseAsset: string;\n quoteAsset: string;\n marginAsset: string;\n pricePrecision: number;\n quantityPrecision: number;\n baseAssetPrecision: number;\n quotePrecision: number;\n underlyingType: string;\n underlyingSubType: string[];\n settlePlan: number;\n triggerProtect: string;\n liquidationFee: string;\n marketTakeBound: string;\n maxMoveOrderLimit: number;\n filters: IFutureExchangeFilter[];\n orderTypes: string[];\n timeInForce: string[];\n}\n\nexport interface IFutureExchangeInfo {\n timezone: string;\n serverTime: number;\n futuresType: string;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n assets: {\n asset: string;\n marginAvailable: boolean;\n autoAssetExchange: string;\n }[];\n symbols: IFutureExchangeSymbol[];\n}\n\nexport interface IFutureFundingRateEntry {\n symbol: string;\n fundingTime: number;\n fundingRate: string;\n markPrice: string;\n}\n\nexport interface IFuturePremiumIndexEntry {\n symbol: string;\n markPrice: string;\n indexPrice: string;\n estimatedSettlePrice: string;\n lastFundingRate: string;\n interestRate: string;\n nextFundingTime: number;\n time: number;\n}\n\nexport interface IFutureBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n time: number;\n}\n\nexport interface IFutureOpenInterest {\n openInterest: string;\n symbol: string;\n time: number;\n}\n\nexport interface IMarginPair {\n id: string;\n symbol: string;\n base: string;\n quote: string;\n isMarginTrade: boolean;\n isBuyAllowed: boolean;\n isSellAllowed: boolean;\n}\n\n/**\n * 获取交易规则和交易对\n *\n * https://binance-docs.github.io/apidocs/futures/cn/#0f3f2d5ee7\n */\nexport const getFutureExchangeInfo = (): Promise<IFutureExchangeInfo> =>\n requestPublic<IFutureExchangeInfo>('GET', 'https://fapi.binance.com/fapi/v1/exchangeInfo');\n\n/**\n * 查询资金费率历史\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History\n */\nexport const getFutureFundingRate = (params: {\n symbol?: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<IFutureFundingRateEntry[]> =>\n requestPublic<IFutureFundingRateEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/fundingRate', params);\n\n/**\n * 最新标记价格和资金费率\n *\n * 采集各大交易所数据加权平均\n *\n * 权重: 带symbol为1;不带symbol为10\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price\n */\nexport const getFuturePremiumIndex = (params: { symbol?: string }): Promise<IFuturePremiumIndexEntry[]> =>\n requestPublic<IFuturePremiumIndexEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/premiumIndex', params);\n\n/**\n * 当前最优挂单\n *\n * 返回当前最优的挂单(最高买单,最低卖单)\n *\n * 权重: 单交易对2,无交易对5\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker\n */\nexport const getFutureBookTicker = (params?: { symbol?: string }): Promise<IFutureBookTickerEntry[]> =>\n requestPublic<IFutureBookTickerEntry[]>(\n 'GET',\n 'https://fapi.binance.com/fapi/v1/ticker/bookTicker',\n params,\n );\n\n/**\n * 获取未平仓合约数\n *\n * 权重: 1\n *\n * 更新速率: 3s\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest\n */\nexport const getFutureOpenInterest = (params: { symbol: string }): Promise<IFutureOpenInterest> =>\n requestPublic<IFutureOpenInterest>('GET', 'https://fapi.binance.com/fapi/v1/openInterest', params);\nexport interface ISpotBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n}\n\n/**\n * 当前最优挂单 (Spot)\n *\n * https://binance-docs.github.io/apidocs/spot/cn/#5393cd0851\n */\nexport const getSpotBookTicker = (params?: { symbol?: string }): Promise<ISpotBookTickerEntry[]> =>\n requestPublic<ISpotBookTickerEntry[]>('GET', 'https://api.binance.com/api/v3/ticker/bookTicker', params);\n\nexport interface ISpotExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface ISpotExchangeSymbol {\n symbol: string;\n status: string;\n baseAsset: string;\n baseAssetPrecision: number;\n quoteAsset: string;\n quotePrecision: number;\n baseCommissionPrecision: number;\n quoteCommissionPrecision: number;\n orderTypes: string[];\n icebergAllowed: boolean;\n ocoAllowed: boolean;\n quoteOrderQtyMarketAllowed: boolean;\n allowTrailingStop: boolean;\n cancelReplaceAllowed: boolean;\n isSpotTradingAllowed: boolean;\n isMarginTradingAllowed: boolean;\n filters: ISpotExchangeFilter[];\n permissions: string[];\n}\n\nexport interface ISpotExchangeInfo {\n timezone: string;\n serverTime: number;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n symbols: ISpotExchangeSymbol[];\n}\n\n/**\n * 获取现货交易规则和交易对\n *\n * https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/general-endpoints#%E4%BA%A4%E6%98%93%E8%A7%84%E8%8C%83%E4%BF%A1%E6%81%AF\n */\nexport const getSpotExchangeInfo = (): Promise<ISpotExchangeInfo> =>\n requestPublic<ISpotExchangeInfo>('GET', 'https://api.binance.com/api/v3/exchangeInfo');\n\n/**\n * 获取当前现货报价\n *\n *https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/market-data-endpoints#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC%E6%8E%A5%E5%8F%A3\n */\nexport const getSpotTickerPrice = (params?: {\n symbol?: string;\n symbols?: string;\n symbolStatus?: 'TRADING' | 'HALT' | 'BREAK';\n}): Promise<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n> =>\n requestPublic<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n >('GET', 'https://api.binance.com/api/v3/ticker/price', params);\n"]}
@@ -14,8 +14,8 @@ import { createSeriesProvider } from '@yuants/data-series';
14
14
  import { Terminal } from '@yuants/protocol';
15
15
  import { decodePath, formatTime } from '@yuants/utils';
16
16
  import { firstValueFrom, timer } from 'rxjs';
17
- import { getFutureFundingRate } from '../api/public-api';
18
17
  import { getMarginInterestRateHistory } from '../api/private-api';
18
+ import { getFutureFundingRate } from '../api/public-api';
19
19
  const terminal = Terminal.fromNodeEnv();
20
20
  createSeriesProvider(terminal, {
21
21
  tableName: 'interest_rate',
@@ -1 +1 @@
1
- {"version":3,"file":"interest_rate.js","sourceRoot":"","sources":["../../src/public-data/interest_rate.ts"],"names":[],"mappings":"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","sourcesContent":["import { IInterestRate } from '@yuants/data-interest-rate';\nimport { createSeriesProvider } from '@yuants/data-series';\nimport { Terminal } from '@yuants/protocol';\nimport { decodePath, formatTime } from '@yuants/utils';\nimport { firstValueFrom, timer } from 'rxjs';\nimport { getFutureFundingRate } from '../api/public-api';\nimport { getMarginInterestRateHistory } from '../api/private-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\ncreateSeriesProvider<IInterestRate>(terminal, {\n tableName: 'interest_rate',\n series_id_prefix_parts: ['BINANCE'],\n reversed: true,\n serviceOptions: { concurrent: 1 },\n queryFn: async function* ({ series_id, started_at, ended_at }) {\n let current_start = started_at;\n const [, instType, symbol] = decodePath(series_id);\n if (instType === 'USDT-FUTURE') {\n while (true) {\n // 向前翻页,时间降序\n const res = await getFutureFundingRate({\n symbol: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 1000,\n });\n yield res.map(\n (v): IInterestRate => ({\n series_id,\n created_at: formatTime(v.fundingTime),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: `${-v.fundingRate}`,\n short_rate: `${v.fundingRate}`,\n settlement_price: '',\n }),\n );\n if (res.length < 1000) {\n break;\n }\n current_start = +res[res.length - 1].fundingTime;\n await firstValueFrom(timer(1000));\n }\n return;\n }\n if (instType === 'MARGIN') {\n while (true) {\n const res = await getMarginInterestRateHistory({\n asset: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 100,\n });\n yield res.map(\n (v: {\n asset: string;\n dailyInterestRate: string;\n timestamp: number;\n vipLevel: number;\n }): IInterestRate => ({\n series_id,\n created_at: formatTime(v.timestamp),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: v.dailyInterestRate,\n short_rate: '0',\n settlement_price: '',\n }),\n );\n if (res.length < 100) {\n break;\n }\n current_start = +res[res.length - 1].timestamp;\n await firstValueFrom(timer(1000));\n }\n }\n return;\n },\n});\n"]}
1
+ {"version":3,"file":"interest_rate.js","sourceRoot":"","sources":["../../src/public-data/interest_rate.ts"],"names":[],"mappings":"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","sourcesContent":["import { IInterestRate } from '@yuants/data-interest-rate';\nimport { createSeriesProvider } from '@yuants/data-series';\nimport { Terminal } from '@yuants/protocol';\nimport { decodePath, formatTime } from '@yuants/utils';\nimport { firstValueFrom, timer } from 'rxjs';\nimport { getMarginInterestRateHistory } from '../api/private-api';\nimport { getFutureFundingRate } from '../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\ncreateSeriesProvider<IInterestRate>(terminal, {\n tableName: 'interest_rate',\n series_id_prefix_parts: ['BINANCE'],\n reversed: true,\n serviceOptions: { concurrent: 1 },\n queryFn: async function* ({ series_id, started_at, ended_at }) {\n let current_start = started_at;\n const [, instType, symbol] = decodePath(series_id);\n if (instType === 'USDT-FUTURE') {\n while (true) {\n // 向前翻页,时间降序\n const res = await getFutureFundingRate({\n symbol: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 1000,\n });\n yield res.map(\n (v): IInterestRate => ({\n series_id,\n created_at: formatTime(v.fundingTime),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: `${-v.fundingRate}`,\n short_rate: `${v.fundingRate}`,\n settlement_price: '',\n }),\n );\n if (res.length < 1000) {\n break;\n }\n current_start = +res[res.length - 1].fundingTime;\n await firstValueFrom(timer(1000));\n }\n return;\n }\n if (instType === 'MARGIN') {\n while (true) {\n const res = await getMarginInterestRateHistory({\n asset: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 100,\n });\n yield res.map(\n (v): IInterestRate => ({\n series_id,\n created_at: formatTime(v.timestamp),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: v.dailyInterestRate,\n short_rate: '0',\n settlement_price: '',\n }),\n );\n if (res.length < 100) {\n break;\n }\n current_start = +res[res.length - 1].timestamp;\n await firstValueFrom(timer(1000));\n }\n }\n return;\n },\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAOA,aAAK,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEpD,aAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAE3E,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAID,eAAO,MAAM,UAAU,iDAC0E,CAAC;AA6FlG,eAAO,MAAM,aAAa,cAAe,UAAU,YAAY,MAAM,WAAW,aAAa,eACvD,CAAC;AAEvC,eAAO,MAAM,cAAc,kBACb,WAAW,UACf,UAAU,YACR,MAAM,WACP,aAAa,eAC6B,CAAC;AAEtD,eAAO,MAAM,oBAAoB,QAAO,WAOvC,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAOA,aAAK,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEpD,aAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAE3E,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAKD,eAAO,MAAM,UAAU,iDAC0E,CAAC;AAqGlG,eAAO,MAAM,aAAa,cAAe,UAAU,YAAY,MAAM,WAAW,aAAa,eACvD,CAAC;AAEvC,eAAO,MAAM,cAAc,kBACb,WAAW,UACf,UAAU,YACR,MAAM,WACP,aAAa,eAC6B,CAAC;AAEtD,eAAO,MAAM,oBAAoB,QAAO,WAOvC,CAAC"}
package/lib/api/client.js CHANGED
@@ -6,7 +6,8 @@ const utils_1 = require("@yuants/utils");
6
6
  const MetricBinanceApiUsedWeight = protocol_1.GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');
7
7
  const MetricBinanceApiCounter = protocol_1.GlobalPrometheusRegistry.counter('binance_api_request_total', '');
8
8
  const terminal = protocol_1.Terminal.fromNodeEnv();
9
- let retryAfterUntil = null;
9
+ // 每个接口单独进行主动限流控制
10
+ const mapPathToRetryAfterUntil = {};
10
11
  const isApiError = (value) => typeof (value === null || value === void 0 ? void 0 : value.code) === 'number' && typeof (value === null || value === void 0 ? void 0 : value.msg) === 'string';
11
12
  exports.isApiError = isApiError;
12
13
  const appendParams = (url, params) => {
@@ -51,11 +52,18 @@ const callApi = async (method, endpoint, params, credential) => {
51
52
  else {
52
53
  console.info((0, utils_1.formatTime)(Date.now()), method, url.href);
53
54
  }
55
+ const retryAfterUntil = mapPathToRetryAfterUntil[endpoint];
54
56
  if (retryAfterUntil) {
55
57
  if (Date.now() <= retryAfterUntil) {
56
- throw (0, utils_1.newError)('ACTIVE_RATE_LIMIT', { retryAfterUntil, now: Date.now(), url: url.href });
58
+ // 主动限流
59
+ throw (0, utils_1.newError)('ACTIVE_RATE_LIMIT', {
60
+ wait_time: `${retryAfterUntil - Date.now()}ms`,
61
+ retryAfterUntil,
62
+ url: url.href,
63
+ endpoint,
64
+ });
57
65
  }
58
- retryAfterUntil = null;
66
+ delete mapPathToRetryAfterUntil[endpoint];
59
67
  }
60
68
  MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();
61
69
  const res = await fetch(url.href, {
@@ -65,11 +73,11 @@ const callApi = async (method, endpoint, params, credential) => {
65
73
  const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');
66
74
  const retryAfter = res.headers.get('Retry-After');
67
75
  if (retryAfter) {
68
- retryAfterUntil = Date.now() + parseInt(retryAfter, 10) * 1000;
76
+ mapPathToRetryAfterUntil[endpoint] = Date.now() + parseInt(retryAfter, 10) * 1000;
69
77
  }
70
78
  console.info((0, utils_1.formatTime)(Date.now()), 'response', method, url.href, `status=${res.status}`, retryAfter ? `retryAfter=${retryAfter}` : '', `usedWeight1M=${usedWeight1M !== null && usedWeight1M !== void 0 ? usedWeight1M : 'N/A'}`);
71
79
  if (usedWeight1M) {
72
- MetricBinanceApiUsedWeight.labels({ terminal_id: terminal.terminal_id }).set(+usedWeight1M);
80
+ MetricBinanceApiUsedWeight.labels({ endpoint, terminal_id: terminal.terminal_id }).set(+usedWeight1M);
73
81
  }
74
82
  return res.json();
75
83
  };
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":";;;AAAA,+CAAsE;AACtE,yCAA4E;AAE5E,MAAM,0BAA0B,GAAG,mCAAwB,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC;AACjG,MAAM,uBAAuB,GAAG,mCAAwB,CAAC,OAAO,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;AAClG,MAAM,QAAQ,GAAG,mBAAQ,CAAC,WAAW,EAAE,CAAC;AAgBxC,IAAI,eAAe,GAAkB,IAAI,CAAC;AAEnC,MAAM,UAAU,GAAG,CAAI,KAAoB,EAAsB,EAAE,CACxE,OAAO,CAAC,KAAmB,aAAnB,KAAK,uBAAL,KAAK,CAAgB,IAAI,CAAA,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAmB,aAAnB,KAAK,uBAAL,KAAK,CAAgB,GAAG,CAAA,KAAK,QAAQ,CAAC;AADrF,QAAA,UAAU,cAC2E;AAElG,MAAM,YAAY,GAAG,CAAC,GAAQ,EAAE,MAAsB,EAAE,EAAE;IACxD,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;IAClF,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC;IACnE,WAAW;SACR,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;SAClD,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACxB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACL,IAAI,cAAc,EAAE;QAClB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACpE;AACH,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,KAAK,EACnB,MAAkB,EAClB,QAAgB,EAChB,MAAsB,EACtB,UAAwB,EACZ,EAAE;IACd,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,MAAM,gBAAgB,qBAAuB,MAAM,CAAE,CAAC;IACtD,IAAI,UAAU,EAAE;QACd,IAAI,gBAAgB,CAAC,UAAU,KAAK,SAAS,EAAE;YAC7C,yGAAyG;YACzG,gBAAgB,CAAC,UAAU,GAAG,IAAI,CAAC;SACpC;QACD,IAAI,gBAAgB,CAAC,SAAS,KAAK,SAAS,EAAE;YAC5C,gBAAgB,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;SACzC;KACF;IACD,YAAY,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAEpC,IAAI,OAA2C,CAAC;IAChD,IAAI,UAAU,EAAE;QACd,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAErC,MAAM,SAAS,GAAG,IAAA,iBAAS,EACzB,MAAM,IAAA,kBAAU,EAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CACtG,CAAC;QACF,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QAC7C,OAAO,GAAG;YACR,cAAc,EAAE,gCAAgC;YAChD,cAAc,EAAE,UAAU,CAAC,UAAU;SACtC,CAAC;QACF,OAAO,CAAC,IAAI,CACV,IAAA,kBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EACtB,MAAM,EACN,GAAG,CAAC,IAAI,EACR,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EACvB,GAAG,CAAC,YAAY,CAAC,QAAQ,EAAE,EAC3B,QAAQ,CACT,CAAC;KACH;SAAM;QACL,OAAO,CAAC,IAAI,CAAC,IAAA,kBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;KACxD;IAED,IAAI,eAAe,EAAE;QACnB,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,eAAe,EAAE;YACjC,MAAM,IAAA,gBAAQ,EAAC,mBAAmB,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;SAC1F;QACD,eAAe,GAAG,IAAI,CAAC;KACxB;IAED,uBAAuB,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IAEhG,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE;QAChC,MAAM;QACN,OAAO;KACR,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAClD,IAAI,UAAU,EAAE;QACd,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC;KAChE;IACD,OAAO,CAAC,IAAI,CACV,IAAA,kBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EACtB,UAAU,EACV,MAAM,EACN,GAAG,CAAC,IAAI,EACR,UAAU,GAAG,CAAC,MAAM,EAAE,EACtB,UAAU,CAAC,CAAC,CAAC,cAAc,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAC5C,gBAAgB,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,KAAK,EAAE,CACxC,CAAC;IACF,IAAI,YAAY,EAAE;QAChB,0BAA0B,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;KAC7F;IACD,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAC;AAClC,CAAC,CAAC;AAEK,MAAM,aAAa,GAAG,CAAI,MAAkB,EAAE,QAAgB,EAAE,MAAsB,EAAE,EAAE,CAC/F,OAAO,CAAI,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAD1B,QAAA,aAAa,iBACa;AAEhC,MAAM,cAAc,GAAG,CAC5B,UAAuB,EACvB,MAAkB,EAClB,QAAgB,EAChB,MAAsB,EACtB,EAAE,CAAC,OAAO,CAAI,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AALzC,QAAA,cAAc,kBAK2B;AAE/C,MAAM,oBAAoB,GAAG,GAAgB,EAAE;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IAC1C,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,EAAE;QAC9B,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;KACtF;IACD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AACpC,CAAC,CAAC;AAPW,QAAA,oBAAoB,wBAO/B","sourcesContent":["import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { encodeHex, formatTime, HmacSHA256, newError } from '@yuants/utils';\n\nconst MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');\nconst MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');\nconst terminal = Terminal.fromNodeEnv();\n\ntype HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\ntype RequestParams = Record<string, string | number | boolean | undefined>;\n\nexport interface ICredential {\n access_key: string;\n secret_key: string;\n}\n\nexport interface IApiError {\n code: number;\n msg: string;\n}\n\nlet retryAfterUntil: number | null = null;\n\nexport const isApiError = <T>(value: T | IApiError): value is IApiError =>\n typeof (value as IApiError)?.code === 'number' && typeof (value as IApiError)?.msg === 'string';\n\nconst appendParams = (url: URL, params?: RequestParams) => {\n if (!params) return;\n const entries = Object.entries(params).filter(([, value]) => value !== undefined);\n const timestampEntry = entries.find(([key]) => key === 'timestamp');\n const restEntries = entries.filter(([key]) => key !== 'timestamp');\n restEntries\n .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))\n .forEach(([key, value]) => {\n url.searchParams.set(key, String(value));\n });\n if (timestampEntry) {\n url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));\n }\n};\n\nconst callApi = async <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n credential?: ICredential,\n): Promise<T> => {\n const url = new URL(endpoint);\n const normalizedParams: RequestParams = { ...params };\n if (credential) {\n if (normalizedParams.recvWindow === undefined) {\n // FYI https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#timing-security\n normalizedParams.recvWindow = 5000;\n }\n if (normalizedParams.timestamp === undefined) {\n normalizedParams.timestamp = Date.now();\n }\n }\n appendParams(url, normalizedParams);\n\n let headers: Record<string, string> | undefined;\n if (credential) {\n const signData = url.search.slice(1);\n\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(signData), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n headers = {\n 'Content-Type': 'application/json;charset=utf-8',\n 'X-MBX-APIKEY': credential.access_key,\n };\n console.info(\n formatTime(Date.now()),\n method,\n url.href,\n JSON.stringify(headers),\n url.searchParams.toString(),\n signData,\n );\n } else {\n console.info(formatTime(Date.now()), method, url.href);\n }\n\n if (retryAfterUntil) {\n if (Date.now() <= retryAfterUntil) {\n throw newError('ACTIVE_RATE_LIMIT', { retryAfterUntil, now: Date.now(), url: url.href });\n }\n retryAfterUntil = null;\n }\n\n MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n\n const res = await fetch(url.href, {\n method,\n headers,\n });\n const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');\n const retryAfter = res.headers.get('Retry-After');\n if (retryAfter) {\n retryAfterUntil = Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n console.info(\n formatTime(Date.now()),\n 'response',\n method,\n url.href,\n `status=${res.status}`,\n retryAfter ? `retryAfter=${retryAfter}` : '',\n `usedWeight1M=${usedWeight1M ?? 'N/A'}`,\n );\n if (usedWeight1M) {\n MetricBinanceApiUsedWeight.labels({ terminal_id: terminal.terminal_id }).set(+usedWeight1M);\n }\n return res.json() as Promise<T>;\n};\n\nexport const requestPublic = <T>(method: HttpMethod, endpoint: string, params?: RequestParams) =>\n callApi<T>(method, endpoint, params);\n\nexport const requestPrivate = <T>(\n credential: ICredential,\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n) => callApi<T>(method, endpoint, params, credential);\n\nexport const getDefaultCredential = (): ICredential => {\n const access_key = process.env.ACCESS_KEY;\n const secret_key = process.env.SECRET_KEY;\n if (!access_key || !secret_key) {\n throw new Error('Missing Binance credential: ACCESS_KEY and SECRET_KEY must be set');\n }\n return { access_key, secret_key };\n};\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { GlobalPrometheusRegistry, Terminal } from '@yuants/protocol';\nimport { encodeHex, formatTime, HmacSHA256, newError } from '@yuants/utils';\n\nconst MetricBinanceApiUsedWeight = GlobalPrometheusRegistry.gauge('binance_api_used_weight', '');\nconst MetricBinanceApiCounter = GlobalPrometheusRegistry.counter('binance_api_request_total', '');\nconst terminal = Terminal.fromNodeEnv();\n\ntype HttpMethod = 'GET' | 'POST' | 'DELETE' | 'PUT';\n\ntype RequestParams = Record<string, string | number | boolean | undefined>;\n\nexport interface ICredential {\n access_key: string;\n secret_key: string;\n}\n\nexport interface IApiError {\n code: number;\n msg: string;\n}\n\n// 每个接口单独进行主动限流控制\nconst mapPathToRetryAfterUntil: Record<string, number> = {};\n\nexport const isApiError = <T>(value: T | IApiError): value is IApiError =>\n typeof (value as IApiError)?.code === 'number' && typeof (value as IApiError)?.msg === 'string';\n\nconst appendParams = (url: URL, params?: RequestParams) => {\n if (!params) return;\n const entries = Object.entries(params).filter(([, value]) => value !== undefined);\n const timestampEntry = entries.find(([key]) => key === 'timestamp');\n const restEntries = entries.filter(([key]) => key !== 'timestamp');\n restEntries\n .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))\n .forEach(([key, value]) => {\n url.searchParams.set(key, String(value));\n });\n if (timestampEntry) {\n url.searchParams.set(timestampEntry[0], String(timestampEntry[1]));\n }\n};\n\nconst callApi = async <T>(\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n credential?: ICredential,\n): Promise<T> => {\n const url = new URL(endpoint);\n const normalizedParams: RequestParams = { ...params };\n if (credential) {\n if (normalizedParams.recvWindow === undefined) {\n // FYI https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info#timing-security\n normalizedParams.recvWindow = 5000;\n }\n if (normalizedParams.timestamp === undefined) {\n normalizedParams.timestamp = Date.now();\n }\n }\n appendParams(url, normalizedParams);\n\n let headers: Record<string, string> | undefined;\n if (credential) {\n const signData = url.search.slice(1);\n\n const signature = encodeHex(\n await HmacSHA256(new TextEncoder().encode(signData), new TextEncoder().encode(credential.secret_key)),\n );\n url.searchParams.set('signature', signature);\n headers = {\n 'Content-Type': 'application/json;charset=utf-8',\n 'X-MBX-APIKEY': credential.access_key,\n };\n console.info(\n formatTime(Date.now()),\n method,\n url.href,\n JSON.stringify(headers),\n url.searchParams.toString(),\n signData,\n );\n } else {\n console.info(formatTime(Date.now()), method, url.href);\n }\n\n const retryAfterUntil = mapPathToRetryAfterUntil[endpoint];\n\n if (retryAfterUntil) {\n if (Date.now() <= retryAfterUntil) {\n // 主动限流\n throw newError('ACTIVE_RATE_LIMIT', {\n wait_time: `${retryAfterUntil - Date.now()}ms`,\n retryAfterUntil,\n url: url.href,\n endpoint,\n });\n }\n delete mapPathToRetryAfterUntil[endpoint];\n }\n\n MetricBinanceApiCounter.labels({ path: url.pathname, terminal_id: terminal.terminal_id }).inc();\n\n const res = await fetch(url.href, {\n method,\n headers,\n });\n const usedWeight1M = res.headers.get('x-mbx-used-weight-1m');\n const retryAfter = res.headers.get('Retry-After');\n if (retryAfter) {\n mapPathToRetryAfterUntil[endpoint] = Date.now() + parseInt(retryAfter, 10) * 1000;\n }\n console.info(\n formatTime(Date.now()),\n 'response',\n method,\n url.href,\n `status=${res.status}`,\n retryAfter ? `retryAfter=${retryAfter}` : '',\n `usedWeight1M=${usedWeight1M ?? 'N/A'}`,\n );\n if (usedWeight1M) {\n MetricBinanceApiUsedWeight.labels({ endpoint, terminal_id: terminal.terminal_id }).set(+usedWeight1M);\n }\n return res.json() as Promise<T>;\n};\n\nexport const requestPublic = <T>(method: HttpMethod, endpoint: string, params?: RequestParams) =>\n callApi<T>(method, endpoint, params);\n\nexport const requestPrivate = <T>(\n credential: ICredential,\n method: HttpMethod,\n endpoint: string,\n params?: RequestParams,\n) => callApi<T>(method, endpoint, params, credential);\n\nexport const getDefaultCredential = (): ICredential => {\n const access_key = process.env.ACCESS_KEY;\n const secret_key = process.env.SECRET_KEY;\n if (!access_key || !secret_key) {\n throw new Error('Missing Binance credential: ACCESS_KEY and SECRET_KEY must be set');\n }\n return { access_key, secret_key };\n};\n"]}
@@ -93,7 +93,7 @@ export declare const getFutureExchangeInfo: () => Promise<IFutureExchangeInfo>;
93
93
  /**
94
94
  * 查询资金费率历史
95
95
  *
96
- * https://binance-docs.github.io/apidocs/futures/cn/#31dbeb24c4
96
+ * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History
97
97
  */
98
98
  export declare const getFutureFundingRate: (params: {
99
99
  symbol?: string;
@@ -12,7 +12,7 @@ exports.getFutureExchangeInfo = getFutureExchangeInfo;
12
12
  /**
13
13
  * 查询资金费率历史
14
14
  *
15
- * https://binance-docs.github.io/apidocs/futures/cn/#31dbeb24c4
15
+ * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History
16
16
  */
17
17
  const getFutureFundingRate = (params) => (0, client_1.requestPublic)('GET', 'https://fapi.binance.com/fapi/v1/fundingRate', params);
18
18
  exports.getFutureFundingRate = getFutureFundingRate;
@@ -1 +1 @@
1
- {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":";;;AAAA,qCAAyC;AAgGzC;;;;GAIG;AACI,MAAM,qBAAqB,GAAG,GAAiC,EAAE,CACtE,IAAA,sBAAa,EAAsB,KAAK,EAAE,+CAA+C,CAAC,CAAC;AADhF,QAAA,qBAAqB,yBAC2D;AAE7F;;;;GAIG;AACI,MAAM,oBAAoB,GAAG,CAAC,MAKpC,EAAsC,EAAE,CACvC,IAAA,sBAAa,EAA4B,KAAK,EAAE,8CAA8C,EAAE,MAAM,CAAC,CAAC;AAN7F,QAAA,oBAAoB,wBAMyE;AAE1G;;;;;;;;GAQG;AACI,MAAM,qBAAqB,GAAG,CAAC,MAA2B,EAAuC,EAAE,CACxG,IAAA,sBAAa,EAA6B,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AAD/F,QAAA,qBAAqB,yBAC0E;AAE5G;;;;;;;;GAQG;AACI,MAAM,mBAAmB,GAAG,CAAC,MAA4B,EAAqC,EAAE,CACrG,IAAA,sBAAa,EACX,KAAK,EACL,oDAAoD,EACpD,MAAM,CACP,CAAC;AALS,QAAA,mBAAmB,uBAK5B;AAEJ;;;;;;;;GAQG;AACI,MAAM,qBAAqB,GAAG,CAAC,MAA0B,EAAgC,EAAE,CAChG,IAAA,sBAAa,EAAsB,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AADxF,QAAA,qBAAqB,yBACmE;AASrG;;;;GAIG;AACI,MAAM,iBAAiB,GAAG,CAAC,MAA4B,EAAmC,EAAE,CACjG,IAAA,sBAAa,EAAyB,KAAK,EAAE,kDAAkD,EAAE,MAAM,CAAC,CAAC;AAD9F,QAAA,iBAAiB,qBAC6E;AAwC3G;;;;GAIG;AACI,MAAM,mBAAmB,GAAG,GAA+B,EAAE,CAClE,IAAA,sBAAa,EAAoB,KAAK,EAAE,6CAA6C,CAAC,CAAC;AAD5E,QAAA,mBAAmB,uBACyD;AAEzF;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,MAIlC,EASC,EAAE,CACF,IAAA,sBAAa,EASX,KAAK,EAAE,6CAA6C,EAAE,MAAM,CAAC,CAAC;AAvBrD,QAAA,kBAAkB,sBAuBmC","sourcesContent":["import { requestPublic } from './client';\n\nexport interface IFutureExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface IFutureExchangeSymbol {\n symbol: string;\n pair: string;\n contractType: string;\n deliveryDate: number;\n onboardDate: number;\n status: string;\n maintMarginPercent: string;\n requiredMarginPercent: string;\n baseAsset: string;\n quoteAsset: string;\n marginAsset: string;\n pricePrecision: number;\n quantityPrecision: number;\n baseAssetPrecision: number;\n quotePrecision: number;\n underlyingType: string;\n underlyingSubType: string[];\n settlePlan: number;\n triggerProtect: string;\n liquidationFee: string;\n marketTakeBound: string;\n maxMoveOrderLimit: number;\n filters: IFutureExchangeFilter[];\n orderTypes: string[];\n timeInForce: string[];\n}\n\nexport interface IFutureExchangeInfo {\n timezone: string;\n serverTime: number;\n futuresType: string;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n assets: {\n asset: string;\n marginAvailable: boolean;\n autoAssetExchange: string;\n }[];\n symbols: IFutureExchangeSymbol[];\n}\n\nexport interface IFutureFundingRateEntry {\n symbol: string;\n fundingTime: number;\n fundingRate: string;\n markPrice: string;\n}\n\nexport interface IFuturePremiumIndexEntry {\n symbol: string;\n markPrice: string;\n indexPrice: string;\n estimatedSettlePrice: string;\n lastFundingRate: string;\n interestRate: string;\n nextFundingTime: number;\n time: number;\n}\n\nexport interface IFutureBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n time: number;\n}\n\nexport interface IFutureOpenInterest {\n openInterest: string;\n symbol: string;\n time: number;\n}\n\nexport interface IMarginPair {\n id: string;\n symbol: string;\n base: string;\n quote: string;\n isMarginTrade: boolean;\n isBuyAllowed: boolean;\n isSellAllowed: boolean;\n}\n\n/**\n * 获取交易规则和交易对\n *\n * https://binance-docs.github.io/apidocs/futures/cn/#0f3f2d5ee7\n */\nexport const getFutureExchangeInfo = (): Promise<IFutureExchangeInfo> =>\n requestPublic<IFutureExchangeInfo>('GET', 'https://fapi.binance.com/fapi/v1/exchangeInfo');\n\n/**\n * 查询资金费率历史\n *\n * https://binance-docs.github.io/apidocs/futures/cn/#31dbeb24c4\n */\nexport const getFutureFundingRate = (params: {\n symbol?: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<IFutureFundingRateEntry[]> =>\n requestPublic<IFutureFundingRateEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/fundingRate', params);\n\n/**\n * 最新标记价格和资金费率\n *\n * 采集各大交易所数据加权平均\n *\n * 权重: 带symbol为1;不带symbol为10\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price\n */\nexport const getFuturePremiumIndex = (params: { symbol?: string }): Promise<IFuturePremiumIndexEntry[]> =>\n requestPublic<IFuturePremiumIndexEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/premiumIndex', params);\n\n/**\n * 当前最优挂单\n *\n * 返回当前最优的挂单(最高买单,最低卖单)\n *\n * 权重: 单交易对2,无交易对5\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker\n */\nexport const getFutureBookTicker = (params?: { symbol?: string }): Promise<IFutureBookTickerEntry[]> =>\n requestPublic<IFutureBookTickerEntry[]>(\n 'GET',\n 'https://fapi.binance.com/fapi/v1/ticker/bookTicker',\n params,\n );\n\n/**\n * 获取未平仓合约数\n *\n * 权重: 1\n *\n * 更新速率: 3s\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest\n */\nexport const getFutureOpenInterest = (params: { symbol: string }): Promise<IFutureOpenInterest> =>\n requestPublic<IFutureOpenInterest>('GET', 'https://fapi.binance.com/fapi/v1/openInterest', params);\nexport interface ISpotBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n}\n\n/**\n * 当前最优挂单 (Spot)\n *\n * https://binance-docs.github.io/apidocs/spot/cn/#5393cd0851\n */\nexport const getSpotBookTicker = (params?: { symbol?: string }): Promise<ISpotBookTickerEntry[]> =>\n requestPublic<ISpotBookTickerEntry[]>('GET', 'https://api.binance.com/api/v3/ticker/bookTicker', params);\n\nexport interface ISpotExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface ISpotExchangeSymbol {\n symbol: string;\n status: string;\n baseAsset: string;\n baseAssetPrecision: number;\n quoteAsset: string;\n quotePrecision: number;\n baseCommissionPrecision: number;\n quoteCommissionPrecision: number;\n orderTypes: string[];\n icebergAllowed: boolean;\n ocoAllowed: boolean;\n quoteOrderQtyMarketAllowed: boolean;\n allowTrailingStop: boolean;\n cancelReplaceAllowed: boolean;\n isSpotTradingAllowed: boolean;\n isMarginTradingAllowed: boolean;\n filters: ISpotExchangeFilter[];\n permissions: string[];\n}\n\nexport interface ISpotExchangeInfo {\n timezone: string;\n serverTime: number;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n symbols: ISpotExchangeSymbol[];\n}\n\n/**\n * 获取现货交易规则和交易对\n *\n * https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/general-endpoints#%E4%BA%A4%E6%98%93%E8%A7%84%E8%8C%83%E4%BF%A1%E6%81%AF\n */\nexport const getSpotExchangeInfo = (): Promise<ISpotExchangeInfo> =>\n requestPublic<ISpotExchangeInfo>('GET', 'https://api.binance.com/api/v3/exchangeInfo');\n\n/**\n * 获取当前现货报价\n *\n *https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/market-data-endpoints#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC%E6%8E%A5%E5%8F%A3\n */\nexport const getSpotTickerPrice = (params?: {\n symbol?: string;\n symbols?: string;\n symbolStatus?: 'TRADING' | 'HALT' | 'BREAK';\n}): Promise<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n> =>\n requestPublic<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n >('GET', 'https://api.binance.com/api/v3/ticker/price', params);\n"]}
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../src/api/public-api.ts"],"names":[],"mappings":";;;AAAA,qCAAyC;AAgGzC;;;;GAIG;AACI,MAAM,qBAAqB,GAAG,GAAiC,EAAE,CACtE,IAAA,sBAAa,EAAsB,KAAK,EAAE,+CAA+C,CAAC,CAAC;AADhF,QAAA,qBAAqB,yBAC2D;AAE7F;;;;GAIG;AACI,MAAM,oBAAoB,GAAG,CAAC,MAKpC,EAAsC,EAAE,CACvC,IAAA,sBAAa,EAA4B,KAAK,EAAE,8CAA8C,EAAE,MAAM,CAAC,CAAC;AAN7F,QAAA,oBAAoB,wBAMyE;AAE1G;;;;;;;;GAQG;AACI,MAAM,qBAAqB,GAAG,CAAC,MAA2B,EAAuC,EAAE,CACxG,IAAA,sBAAa,EAA6B,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AAD/F,QAAA,qBAAqB,yBAC0E;AAE5G;;;;;;;;GAQG;AACI,MAAM,mBAAmB,GAAG,CAAC,MAA4B,EAAqC,EAAE,CACrG,IAAA,sBAAa,EACX,KAAK,EACL,oDAAoD,EACpD,MAAM,CACP,CAAC;AALS,QAAA,mBAAmB,uBAK5B;AAEJ;;;;;;;;GAQG;AACI,MAAM,qBAAqB,GAAG,CAAC,MAA0B,EAAgC,EAAE,CAChG,IAAA,sBAAa,EAAsB,KAAK,EAAE,+CAA+C,EAAE,MAAM,CAAC,CAAC;AADxF,QAAA,qBAAqB,yBACmE;AASrG;;;;GAIG;AACI,MAAM,iBAAiB,GAAG,CAAC,MAA4B,EAAmC,EAAE,CACjG,IAAA,sBAAa,EAAyB,KAAK,EAAE,kDAAkD,EAAE,MAAM,CAAC,CAAC;AAD9F,QAAA,iBAAiB,qBAC6E;AAwC3G;;;;GAIG;AACI,MAAM,mBAAmB,GAAG,GAA+B,EAAE,CAClE,IAAA,sBAAa,EAAoB,KAAK,EAAE,6CAA6C,CAAC,CAAC;AAD5E,QAAA,mBAAmB,uBACyD;AAEzF;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,MAIlC,EASC,EAAE,CACF,IAAA,sBAAa,EASX,KAAK,EAAE,6CAA6C,EAAE,MAAM,CAAC,CAAC;AAvBrD,QAAA,kBAAkB,sBAuBmC","sourcesContent":["import { requestPublic } from './client';\n\nexport interface IFutureExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface IFutureExchangeSymbol {\n symbol: string;\n pair: string;\n contractType: string;\n deliveryDate: number;\n onboardDate: number;\n status: string;\n maintMarginPercent: string;\n requiredMarginPercent: string;\n baseAsset: string;\n quoteAsset: string;\n marginAsset: string;\n pricePrecision: number;\n quantityPrecision: number;\n baseAssetPrecision: number;\n quotePrecision: number;\n underlyingType: string;\n underlyingSubType: string[];\n settlePlan: number;\n triggerProtect: string;\n liquidationFee: string;\n marketTakeBound: string;\n maxMoveOrderLimit: number;\n filters: IFutureExchangeFilter[];\n orderTypes: string[];\n timeInForce: string[];\n}\n\nexport interface IFutureExchangeInfo {\n timezone: string;\n serverTime: number;\n futuresType: string;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n assets: {\n asset: string;\n marginAvailable: boolean;\n autoAssetExchange: string;\n }[];\n symbols: IFutureExchangeSymbol[];\n}\n\nexport interface IFutureFundingRateEntry {\n symbol: string;\n fundingTime: number;\n fundingRate: string;\n markPrice: string;\n}\n\nexport interface IFuturePremiumIndexEntry {\n symbol: string;\n markPrice: string;\n indexPrice: string;\n estimatedSettlePrice: string;\n lastFundingRate: string;\n interestRate: string;\n nextFundingTime: number;\n time: number;\n}\n\nexport interface IFutureBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n time: number;\n}\n\nexport interface IFutureOpenInterest {\n openInterest: string;\n symbol: string;\n time: number;\n}\n\nexport interface IMarginPair {\n id: string;\n symbol: string;\n base: string;\n quote: string;\n isMarginTrade: boolean;\n isBuyAllowed: boolean;\n isSellAllowed: boolean;\n}\n\n/**\n * 获取交易规则和交易对\n *\n * https://binance-docs.github.io/apidocs/futures/cn/#0f3f2d5ee7\n */\nexport const getFutureExchangeInfo = (): Promise<IFutureExchangeInfo> =>\n requestPublic<IFutureExchangeInfo>('GET', 'https://fapi.binance.com/fapi/v1/exchangeInfo');\n\n/**\n * 查询资金费率历史\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Get-Funding-Rate-History\n */\nexport const getFutureFundingRate = (params: {\n symbol?: string;\n startTime?: number;\n endTime?: number;\n limit?: number;\n}): Promise<IFutureFundingRateEntry[]> =>\n requestPublic<IFutureFundingRateEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/fundingRate', params);\n\n/**\n * 最新标记价格和资金费率\n *\n * 采集各大交易所数据加权平均\n *\n * 权重: 带symbol为1;不带symbol为10\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Mark-Price\n */\nexport const getFuturePremiumIndex = (params: { symbol?: string }): Promise<IFuturePremiumIndexEntry[]> =>\n requestPublic<IFuturePremiumIndexEntry[]>('GET', 'https://fapi.binance.com/fapi/v1/premiumIndex', params);\n\n/**\n * 当前最优挂单\n *\n * 返回当前最优的挂单(最高买单,最低卖单)\n *\n * 权重: 单交易对2,无交易对5\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Symbol-Order-Book-Ticker\n */\nexport const getFutureBookTicker = (params?: { symbol?: string }): Promise<IFutureBookTickerEntry[]> =>\n requestPublic<IFutureBookTickerEntry[]>(\n 'GET',\n 'https://fapi.binance.com/fapi/v1/ticker/bookTicker',\n params,\n );\n\n/**\n * 获取未平仓合约数\n *\n * 权重: 1\n *\n * 更新速率: 3s\n *\n * https://developers.binance.com/docs/zh-CN/derivatives/usds-margined-futures/market-data/rest-api/Open-Interest\n */\nexport const getFutureOpenInterest = (params: { symbol: string }): Promise<IFutureOpenInterest> =>\n requestPublic<IFutureOpenInterest>('GET', 'https://fapi.binance.com/fapi/v1/openInterest', params);\nexport interface ISpotBookTickerEntry {\n symbol: string;\n bidPrice: string;\n bidQty: string;\n askPrice: string;\n askQty: string;\n}\n\n/**\n * 当前最优挂单 (Spot)\n *\n * https://binance-docs.github.io/apidocs/spot/cn/#5393cd0851\n */\nexport const getSpotBookTicker = (params?: { symbol?: string }): Promise<ISpotBookTickerEntry[]> =>\n requestPublic<ISpotBookTickerEntry[]>('GET', 'https://api.binance.com/api/v3/ticker/bookTicker', params);\n\nexport interface ISpotExchangeFilter extends Record<string, string | number | boolean | undefined> {\n filterType: string;\n}\n\nexport interface ISpotExchangeSymbol {\n symbol: string;\n status: string;\n baseAsset: string;\n baseAssetPrecision: number;\n quoteAsset: string;\n quotePrecision: number;\n baseCommissionPrecision: number;\n quoteCommissionPrecision: number;\n orderTypes: string[];\n icebergAllowed: boolean;\n ocoAllowed: boolean;\n quoteOrderQtyMarketAllowed: boolean;\n allowTrailingStop: boolean;\n cancelReplaceAllowed: boolean;\n isSpotTradingAllowed: boolean;\n isMarginTradingAllowed: boolean;\n filters: ISpotExchangeFilter[];\n permissions: string[];\n}\n\nexport interface ISpotExchangeInfo {\n timezone: string;\n serverTime: number;\n rateLimits: {\n rateLimitType: string;\n interval: string;\n intervalNum: number;\n limit: number;\n }[];\n exchangeFilters: unknown[];\n symbols: ISpotExchangeSymbol[];\n}\n\n/**\n * 获取现货交易规则和交易对\n *\n * https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/general-endpoints#%E4%BA%A4%E6%98%93%E8%A7%84%E8%8C%83%E4%BF%A1%E6%81%AF\n */\nexport const getSpotExchangeInfo = (): Promise<ISpotExchangeInfo> =>\n requestPublic<ISpotExchangeInfo>('GET', 'https://api.binance.com/api/v3/exchangeInfo');\n\n/**\n * 获取当前现货报价\n *\n *https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/rest-api/market-data-endpoints#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC%E6%8E%A5%E5%8F%A3\n */\nexport const getSpotTickerPrice = (params?: {\n symbol?: string;\n symbols?: string;\n symbolStatus?: 'TRADING' | 'HALT' | 'BREAK';\n}): Promise<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n> =>\n requestPublic<\n | {\n symbol: string;\n price: string;\n }\n | {\n symbol: string;\n price: string;\n }[]\n >('GET', 'https://api.binance.com/api/v3/ticker/price', params);\n"]}
@@ -16,8 +16,8 @@ const data_series_1 = require("@yuants/data-series");
16
16
  const protocol_1 = require("@yuants/protocol");
17
17
  const utils_1 = require("@yuants/utils");
18
18
  const rxjs_1 = require("rxjs");
19
- const public_api_1 = require("../api/public-api");
20
19
  const private_api_1 = require("../api/private-api");
20
+ const public_api_1 = require("../api/public-api");
21
21
  const terminal = protocol_1.Terminal.fromNodeEnv();
22
22
  (0, data_series_1.createSeriesProvider)(terminal, {
23
23
  tableName: 'interest_rate',
@@ -1 +1 @@
1
- {"version":3,"file":"interest_rate.js","sourceRoot":"","sources":["../../src/public-data/interest_rate.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AACA,qDAA2D;AAC3D,+CAA4C;AAC5C,yCAAuD;AACvD,+BAA6C;AAC7C,kDAAyD;AACzD,oDAAkE;AAElE,MAAM,QAAQ,GAAG,mBAAQ,CAAC,WAAW,EAAE,CAAC;AAExC,IAAA,kCAAoB,EAAgB,QAAQ,EAAE;IAC5C,SAAS,EAAE,eAAe;IAC1B,sBAAsB,EAAE,CAAC,SAAS,CAAC;IACnC,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;IACjC,OAAO,EAAE,UAAiB,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;;YAC3D,IAAI,aAAa,GAAG,UAAU,CAAC;YAC/B,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,IAAA,kBAAU,EAAC,SAAS,CAAC,CAAC;YACnD,IAAI,QAAQ,KAAK,aAAa,EAAE;gBAC9B,OAAO,IAAI,EAAE;oBACX,YAAY;oBACZ,MAAM,GAAG,GAAG,cAAM,IAAA,iCAAoB,EAAC;wBACrC,MAAM,EAAE,MAAM;wBACd,SAAS,EAAE,aAAa;wBACxB,OAAO,EAAE,QAAQ;wBACjB,KAAK,EAAE,IAAI;qBACZ,CAAC,CAAA,CAAC;oBACH,oBAAM,GAAG,CAAC,GAAG,CACX,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC;wBACrB,SAAS;wBACT,UAAU,EAAE,IAAA,kBAAU,EAAC,CAAC,CAAC,WAAW,CAAC;wBACrC,aAAa,EAAE,SAAS;wBACxB,UAAU,EAAE,SAAS;wBACrB,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE;wBAC9B,UAAU,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE;wBAC9B,gBAAgB,EAAE,EAAE;qBACrB,CAAC,CACH,CAAA,CAAC;oBACF,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI,EAAE;wBACrB,MAAM;qBACP;oBACD,aAAa,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;oBACjD,cAAM,IAAA,qBAAc,EAAC,IAAA,YAAK,EAAC,IAAI,CAAC,CAAC,CAAA,CAAC;iBACnC;gBACD,6BAAO;aACR;YACD,IAAI,QAAQ,KAAK,QAAQ,EAAE;gBACzB,OAAO,IAAI,EAAE;oBACX,MAAM,GAAG,GAAG,cAAM,IAAA,0CAA4B,EAAC;wBAC7C,KAAK,EAAE,MAAM;wBACb,SAAS,EAAE,aAAa;wBACxB,OAAO,EAAE,QAAQ;wBACjB,KAAK,EAAE,GAAG;qBACX,CAAC,CAAA,CAAC;oBACH,oBAAM,GAAG,CAAC,GAAG,CACX,CAAC,CAKA,EAAiB,EAAE,CAAC,CAAC;wBACpB,SAAS;wBACT,UAAU,EAAE,IAAA,kBAAU,EAAC,CAAC,CAAC,SAAS,CAAC;wBACnC,aAAa,EAAE,SAAS;wBACxB,UAAU,EAAE,SAAS;wBACrB,SAAS,EAAE,CAAC,CAAC,iBAAiB;wBAC9B,UAAU,EAAE,GAAG;wBACf,gBAAgB,EAAE,EAAE;qBACrB,CAAC,CACH,CAAA,CAAC;oBACF,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;wBACpB,MAAM;qBACP;oBACD,aAAa,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC/C,cAAM,IAAA,qBAAc,EAAC,IAAA,YAAK,EAAC,IAAI,CAAC,CAAC,CAAA,CAAC;iBACnC;aACF;YACD,6BAAO;QACT,CAAC;KAAA;CACF,CAAC,CAAC","sourcesContent":["import { IInterestRate } from '@yuants/data-interest-rate';\nimport { createSeriesProvider } from '@yuants/data-series';\nimport { Terminal } from '@yuants/protocol';\nimport { decodePath, formatTime } from '@yuants/utils';\nimport { firstValueFrom, timer } from 'rxjs';\nimport { getFutureFundingRate } from '../api/public-api';\nimport { getMarginInterestRateHistory } from '../api/private-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\ncreateSeriesProvider<IInterestRate>(terminal, {\n tableName: 'interest_rate',\n series_id_prefix_parts: ['BINANCE'],\n reversed: true,\n serviceOptions: { concurrent: 1 },\n queryFn: async function* ({ series_id, started_at, ended_at }) {\n let current_start = started_at;\n const [, instType, symbol] = decodePath(series_id);\n if (instType === 'USDT-FUTURE') {\n while (true) {\n // 向前翻页,时间降序\n const res = await getFutureFundingRate({\n symbol: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 1000,\n });\n yield res.map(\n (v): IInterestRate => ({\n series_id,\n created_at: formatTime(v.fundingTime),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: `${-v.fundingRate}`,\n short_rate: `${v.fundingRate}`,\n settlement_price: '',\n }),\n );\n if (res.length < 1000) {\n break;\n }\n current_start = +res[res.length - 1].fundingTime;\n await firstValueFrom(timer(1000));\n }\n return;\n }\n if (instType === 'MARGIN') {\n while (true) {\n const res = await getMarginInterestRateHistory({\n asset: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 100,\n });\n yield res.map(\n (v: {\n asset: string;\n dailyInterestRate: string;\n timestamp: number;\n vipLevel: number;\n }): IInterestRate => ({\n series_id,\n created_at: formatTime(v.timestamp),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: v.dailyInterestRate,\n short_rate: '0',\n settlement_price: '',\n }),\n );\n if (res.length < 100) {\n break;\n }\n current_start = +res[res.length - 1].timestamp;\n await firstValueFrom(timer(1000));\n }\n }\n return;\n },\n});\n"]}
1
+ {"version":3,"file":"interest_rate.js","sourceRoot":"","sources":["../../src/public-data/interest_rate.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AACA,qDAA2D;AAC3D,+CAA4C;AAC5C,yCAAuD;AACvD,+BAA6C;AAC7C,oDAAkE;AAClE,kDAAyD;AAEzD,MAAM,QAAQ,GAAG,mBAAQ,CAAC,WAAW,EAAE,CAAC;AAExC,IAAA,kCAAoB,EAAgB,QAAQ,EAAE;IAC5C,SAAS,EAAE,eAAe;IAC1B,sBAAsB,EAAE,CAAC,SAAS,CAAC;IACnC,QAAQ,EAAE,IAAI;IACd,cAAc,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;IACjC,OAAO,EAAE,UAAiB,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;;YAC3D,IAAI,aAAa,GAAG,UAAU,CAAC;YAC/B,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,IAAA,kBAAU,EAAC,SAAS,CAAC,CAAC;YACnD,IAAI,QAAQ,KAAK,aAAa,EAAE;gBAC9B,OAAO,IAAI,EAAE;oBACX,YAAY;oBACZ,MAAM,GAAG,GAAG,cAAM,IAAA,iCAAoB,EAAC;wBACrC,MAAM,EAAE,MAAM;wBACd,SAAS,EAAE,aAAa;wBACxB,OAAO,EAAE,QAAQ;wBACjB,KAAK,EAAE,IAAI;qBACZ,CAAC,CAAA,CAAC;oBACH,oBAAM,GAAG,CAAC,GAAG,CACX,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC;wBACrB,SAAS;wBACT,UAAU,EAAE,IAAA,kBAAU,EAAC,CAAC,CAAC,WAAW,CAAC;wBACrC,aAAa,EAAE,SAAS;wBACxB,UAAU,EAAE,SAAS;wBACrB,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE;wBAC9B,UAAU,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE;wBAC9B,gBAAgB,EAAE,EAAE;qBACrB,CAAC,CACH,CAAA,CAAC;oBACF,IAAI,GAAG,CAAC,MAAM,GAAG,IAAI,EAAE;wBACrB,MAAM;qBACP;oBACD,aAAa,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;oBACjD,cAAM,IAAA,qBAAc,EAAC,IAAA,YAAK,EAAC,IAAI,CAAC,CAAC,CAAA,CAAC;iBACnC;gBACD,6BAAO;aACR;YACD,IAAI,QAAQ,KAAK,QAAQ,EAAE;gBACzB,OAAO,IAAI,EAAE;oBACX,MAAM,GAAG,GAAG,cAAM,IAAA,0CAA4B,EAAC;wBAC7C,KAAK,EAAE,MAAM;wBACb,SAAS,EAAE,aAAa;wBACxB,OAAO,EAAE,QAAQ;wBACjB,KAAK,EAAE,GAAG;qBACX,CAAC,CAAA,CAAC;oBACH,oBAAM,GAAG,CAAC,GAAG,CACX,CAAC,CAAC,EAAiB,EAAE,CAAC,CAAC;wBACrB,SAAS;wBACT,UAAU,EAAE,IAAA,kBAAU,EAAC,CAAC,CAAC,SAAS,CAAC;wBACnC,aAAa,EAAE,SAAS;wBACxB,UAAU,EAAE,SAAS;wBACrB,SAAS,EAAE,CAAC,CAAC,iBAAiB;wBAC9B,UAAU,EAAE,GAAG;wBACf,gBAAgB,EAAE,EAAE;qBACrB,CAAC,CACH,CAAA,CAAC;oBACF,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE;wBACpB,MAAM;qBACP;oBACD,aAAa,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC/C,cAAM,IAAA,qBAAc,EAAC,IAAA,YAAK,EAAC,IAAI,CAAC,CAAC,CAAA,CAAC;iBACnC;aACF;YACD,6BAAO;QACT,CAAC;KAAA;CACF,CAAC,CAAC","sourcesContent":["import { IInterestRate } from '@yuants/data-interest-rate';\nimport { createSeriesProvider } from '@yuants/data-series';\nimport { Terminal } from '@yuants/protocol';\nimport { decodePath, formatTime } from '@yuants/utils';\nimport { firstValueFrom, timer } from 'rxjs';\nimport { getMarginInterestRateHistory } from '../api/private-api';\nimport { getFutureFundingRate } from '../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\ncreateSeriesProvider<IInterestRate>(terminal, {\n tableName: 'interest_rate',\n series_id_prefix_parts: ['BINANCE'],\n reversed: true,\n serviceOptions: { concurrent: 1 },\n queryFn: async function* ({ series_id, started_at, ended_at }) {\n let current_start = started_at;\n const [, instType, symbol] = decodePath(series_id);\n if (instType === 'USDT-FUTURE') {\n while (true) {\n // 向前翻页,时间降序\n const res = await getFutureFundingRate({\n symbol: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 1000,\n });\n yield res.map(\n (v): IInterestRate => ({\n series_id,\n created_at: formatTime(v.fundingTime),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: `${-v.fundingRate}`,\n short_rate: `${v.fundingRate}`,\n settlement_price: '',\n }),\n );\n if (res.length < 1000) {\n break;\n }\n current_start = +res[res.length - 1].fundingTime;\n await firstValueFrom(timer(1000));\n }\n return;\n }\n if (instType === 'MARGIN') {\n while (true) {\n const res = await getMarginInterestRateHistory({\n asset: symbol,\n startTime: current_start,\n endTime: ended_at,\n limit: 100,\n });\n yield res.map(\n (v): IInterestRate => ({\n series_id,\n created_at: formatTime(v.timestamp),\n datasource_id: 'BINANCE',\n product_id: series_id,\n long_rate: v.dailyInterestRate,\n short_rate: '0',\n settlement_price: '',\n }),\n );\n if (res.length < 100) {\n break;\n }\n current_start = +res[res.length - 1].timestamp;\n await firstValueFrom(timer(1000));\n }\n }\n return;\n },\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuants/vendor-binance",
3
- "version": "0.10.12",
3
+ "version": "0.10.13",
4
4
  "main": "lib/index.js",
5
5
  "files": [
6
6
  "dist",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "apps/vendor-binance/AGENTS.md": "378369925bffa237cd5e8ef459fe2bc2a141337c",
3
- "apps/vendor-binance/CHANGELOG.json": "38df98c493fe9839a2db5e90c507a5e05b6aca6f",
4
- "apps/vendor-binance/CHANGELOG.md": "04ea881b65d3e42bea3d44b7cdcc841d0008b380",
3
+ "apps/vendor-binance/CHANGELOG.json": "f4782dd2ffd146a57549c2c6560e4478cd0d3597",
4
+ "apps/vendor-binance/CHANGELOG.md": "4d7c1cec45814c1f89fffb93f5a4c52883a9dc42",
5
5
  "apps/vendor-binance/README.md": "4ab94c08b3d07398aee74c3264a4f87d554d20fa",
6
6
  "apps/vendor-binance/SESSION_NOTES.md": "dd49b3364394c502fdcaf687ed5942811d89abe9",
7
7
  "apps/vendor-binance/api-extractor.json": "62f4fd324425b9a235f0c117975967aab09ced0c",
@@ -9,13 +9,13 @@
9
9
  "apps/vendor-binance/config/rig.json": "f6c7b5537dc77a3170ba9f008bae3b6c3ee11956",
10
10
  "apps/vendor-binance/config/typescript.json": "854907e8a821f2050f6533368db160c649c25348",
11
11
  "apps/vendor-binance/etc/vendor-binance.api.md": "2094b84e9b5e7503f5c42b31fffee8d7db47fe7b",
12
- "apps/vendor-binance/package.json": "9586b42d0ba83429d43652314f2ed7d67e3d25f4",
13
- "apps/vendor-binance/src/api/client.ts": "ed027929c6ab9514d246130073ec40944c89ad39",
12
+ "apps/vendor-binance/package.json": "ecc201b031c16d79126fefd36f9c104913bea46c",
13
+ "apps/vendor-binance/src/api/client.ts": "4435ec16c4ffd3e0575b0e5aee9d6a33906033f3",
14
14
  "apps/vendor-binance/src/api/private-api.ts": "982c1902b8417aba1f75df5fa63db76dfc416dac",
15
- "apps/vendor-binance/src/api/public-api.ts": "07546b44b7fc7dec67423cfd73b9175afe828ea0",
15
+ "apps/vendor-binance/src/api/public-api.ts": "70dc33214e05ec3ead45271bfae70abdb0623c1e",
16
16
  "apps/vendor-binance/src/index.ts": "285cc4bded3608d415aaa00b9df180fbbdcebac0",
17
17
  "apps/vendor-binance/src/legacy_index.ts": "a2b44c041aa7ef1f4b77249e11f84d97cde5525f",
18
- "apps/vendor-binance/src/public-data/interest_rate.ts": "ab9b57f1af6478c61d34db8b51f4fcabac971fd4",
18
+ "apps/vendor-binance/src/public-data/interest_rate.ts": "920a2d3ce2b14649a0113ca3007358bfeae80715",
19
19
  "apps/vendor-binance/src/public-data/ohlc.ts": "bb4f791f96064eca7d45d361ea36cb8ba0ab8fe0",
20
20
  "apps/vendor-binance/src/public-data/product.ts": "8879f59f4b5ee5925c5becef5fce5492e2f30f42",
21
21
  "apps/vendor-binance/src/public-data/quote.ts": "0445513ea3b36e1ba9bd05d577ac7050503168f0",