@yuants/vendor-gate 0.9.7 → 0.9.9

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.
@@ -37,7 +37,7 @@ const limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);
37
37
  */
38
38
  export const getEarningAccountInfo = async (credential) => {
39
39
  const balances = await getEarnBalance(credential, {});
40
- const positions = await Promise.all(balances.map(async (balance) => {
40
+ const positions = await Promise.all((balances !== null && balances !== void 0 ? balances : []).map(async (balance) => {
41
41
  // 过滤零余额条目
42
42
  if (+balance.amount <= 0)
43
43
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"earning.js","sourceRoot":"","sources":["../../../src/services/accounts/earning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAe,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAC/B,WAAmB,EACnB,EAAiC,EACA,EAAE;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,OAAO,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzD,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC5B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,IAAU,EAAE,EAAE;QACvB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,EAAE,CAAC,GAAG,IAAI,CAAC;qBACR,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;qBACrB,OAAO,CAAC,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACP,CAAC,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,4BAA4B;AAC5B,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,UAAuB,EAAwB,EAAE;IAC3F,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC7B,UAAU;QACV,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAE3C,8BAA8B;QAC9B,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAEzD,6CAA6C;QAC7C,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAElE,OAAO,gBAAgB,CAAC;YACtB,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,WAAW,OAAO,CAAC,QAAQ,EAAE;YAC1C,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC3D,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM;YACvB,WAAW,EAAE,UAAU;YACvB,cAAc,EAAE,aAAa;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IAEF,kBAAkB;IAClB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAoB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC,CAAC","sourcesContent":["import { makeSpotPosition, type IPosition } from '@yuants/data-account';\nimport { encodePath } from '@yuants/utils';\nimport { getEarnBalance, ICredential } from '../../api/private-api';\nimport { getSpotPrice } from '../../api/public-api';\n\n/**\n * 简单的并发限制器(支持带参数的函数)\n */\nconst createConcurrencyLimiter = <T, Args extends any[]>(\n concurrency: number,\n fn: (...args: Args) => Promise<T>,\n): ((...args: Args) => Promise<T>) => {\n let running = 0;\n const queue: Array<() => void> = [];\n\n const runNext = () => {\n if (running >= concurrency || queue.length === 0) return;\n running++;\n const task = queue.shift()!;\n task();\n };\n\n return (...args: Args) => {\n return new Promise<T>((resolve, reject) => {\n const task = () => {\n fn(...args)\n .then(resolve, reject)\n .finally(() => {\n running--;\n runNext();\n });\n };\n queue.push(task);\n runNext();\n });\n };\n};\n\n// 限制现货价格查询的并发数(避免触发 API 限流)\nconst limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);\n\n/**\n * 获取理财账户信息\n */\nexport const getEarningAccountInfo = async (credential: ICredential): Promise<IPosition[]> => {\n const balances = await getEarnBalance(credential, {});\n\n const positions = await Promise.all(\n balances.map(async (balance) => {\n // 过滤零余额条目\n if (+balance.amount <= 0) return undefined;\n\n // 计算可用余额:理财总数量减去已申请赎回未到账的冻结部分\n const frozen = +balance.frozen_amount || 0;\n const freeVolume = Math.max(0, +balance.amount - frozen);\n\n // 获取币种对 USDT 的现货价格(getSpotPrice 已处理特殊映射和默认值)\n const closablePrice = await limitedGetSpotPrice(balance.currency);\n\n return makeSpotPosition({\n datasource_id: 'GATE',\n position_id: `earning/${balance.currency}`,\n product_id: encodePath('GATE', 'EARNING', balance.currency),\n volume: +balance.amount,\n free_volume: freeVolume,\n closable_price: closablePrice,\n });\n }),\n );\n\n // 过滤 undefined 条目\n return positions.filter((pos): pos is IPosition => !!pos);\n};\n"]}
1
+ {"version":3,"file":"earning.js","sourceRoot":"","sources":["../../../src/services/accounts/earning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAe,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAC/B,WAAmB,EACnB,EAAiC,EACA,EAAE;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,OAAO,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzD,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC5B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,IAAU,EAAE,EAAE;QACvB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,EAAE,CAAC,GAAG,IAAI,CAAC;qBACR,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;qBACrB,OAAO,CAAC,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACP,CAAC,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,4BAA4B;AAC5B,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,UAAuB,EAAwB,EAAE;IAC3F,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,CAAC,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACrC,UAAU;QACV,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAE3C,8BAA8B;QAC9B,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAEzD,6CAA6C;QAC7C,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAElE,OAAO,gBAAgB,CAAC;YACtB,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,WAAW,OAAO,CAAC,QAAQ,EAAE;YAC1C,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC3D,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM;YACvB,WAAW,EAAE,UAAU;YACvB,cAAc,EAAE,aAAa;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IAEF,kBAAkB;IAClB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAoB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC,CAAC","sourcesContent":["import { makeSpotPosition, type IPosition } from '@yuants/data-account';\nimport { encodePath } from '@yuants/utils';\nimport { getEarnBalance, ICredential } from '../../api/private-api';\nimport { getSpotPrice } from '../../api/public-api';\n\n/**\n * 简单的并发限制器(支持带参数的函数)\n */\nconst createConcurrencyLimiter = <T, Args extends any[]>(\n concurrency: number,\n fn: (...args: Args) => Promise<T>,\n): ((...args: Args) => Promise<T>) => {\n let running = 0;\n const queue: Array<() => void> = [];\n\n const runNext = () => {\n if (running >= concurrency || queue.length === 0) return;\n running++;\n const task = queue.shift()!;\n task();\n };\n\n return (...args: Args) => {\n return new Promise<T>((resolve, reject) => {\n const task = () => {\n fn(...args)\n .then(resolve, reject)\n .finally(() => {\n running--;\n runNext();\n });\n };\n queue.push(task);\n runNext();\n });\n };\n};\n\n// 限制现货价格查询的并发数(避免触发 API 限流)\nconst limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);\n\n/**\n * 获取理财账户信息\n */\nexport const getEarningAccountInfo = async (credential: ICredential): Promise<IPosition[]> => {\n const balances = await getEarnBalance(credential, {});\n\n const positions = await Promise.all(\n (balances ?? []).map(async (balance) => {\n // 过滤零余额条目\n if (+balance.amount <= 0) return undefined;\n\n // 计算可用余额:理财总数量减去已申请赎回未到账的冻结部分\n const frozen = +balance.frozen_amount || 0;\n const freeVolume = Math.max(0, +balance.amount - frozen);\n\n // 获取币种对 USDT 的现货价格(getSpotPrice 已处理特殊映射和默认值)\n const closablePrice = await limitedGetSpotPrice(balance.currency);\n\n return makeSpotPosition({\n datasource_id: 'GATE',\n position_id: `earning/${balance.currency}`,\n product_id: encodePath('GATE', 'EARNING', balance.currency),\n volume: +balance.amount,\n free_volume: freeVolume,\n closable_price: closablePrice,\n });\n }),\n );\n\n // 过滤 undefined 条目\n return positions.filter((pos): pos is IPosition => !!pos);\n};\n"]}
@@ -6,7 +6,13 @@ import { catchError, defer, EMPTY, filter, groupBy, map, merge, mergeMap, repeat
6
6
  import { getFuturesContracts, getFuturesTickers, getSpotTickers } from '../../api/public-api';
7
7
  const terminal = Terminal.fromNodeEnv();
8
8
  // 获取所有USDT永续合约
9
- const usdtFutureContracts$ = defer(() => getFuturesContracts('usdt', {})).pipe(repeat({ delay: 3600000 }), retry({ delay: 60000 }), shareReplay(1));
9
+ const usdtFutureContracts$ = defer(() => getFuturesContracts('usdt', {})).pipe(mergeMap((contracts) => contracts), map((contract) => ({
10
+ interest_rate_next_settled_at: contract.funding_next_apply
11
+ ? `${contract.funding_next_apply * 1000}`
12
+ : undefined,
13
+ datasource_id: 'GATE',
14
+ product_id: encodePath('GATE', 'FUTURE', contract.name),
15
+ })), repeat({ delay: 3600000 }), retry({ delay: 60000 }), shareReplay(1));
10
16
  // 从tickers获取价格和交易量数据
11
17
  const quoteFromTickers$ = defer(() => getFuturesTickers('usdt', {})).pipe(mergeMap((tickers) => tickers), map((ticker) => ({
12
18
  datasource_id: 'GATE',
@@ -39,7 +45,7 @@ const quoteFromSpotTickers$ = defer(() => getSpotTickers({})).pipe(mergeMap((tic
39
45
  open_interest: '0',
40
46
  });
41
47
  }), repeat({ delay: 5000 }), retry({ delay: 1000 }));
42
- const quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$];
48
+ const quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$, usdtFutureContracts$];
43
49
  const quote$ = defer(() => merge(...quoteSources$.map((x$) => defer(() => x$).pipe(
44
50
  // 防止单个流关闭导致整体关闭
45
51
  catchError(() => EMPTY))))).pipe(groupBy((x) => encodePath(x.datasource_id, x.product_id)), mergeMap((group$) => {
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote, setMetricsQuoteState } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n defer,\n EMPTY,\n filter,\n groupBy,\n map,\n merge,\n mergeMap,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n} from 'rxjs';\nimport { getFuturesContracts, getFuturesTickers, getSpotTickers } from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\n// 获取所有USDT永续合约\nconst usdtFutureContracts$ = defer(() => getFuturesContracts('usdt', {})).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 60_000 }),\n shareReplay(1),\n);\n\n// 从tickers获取价格和交易量数据\nconst quoteFromTickers$ = defer(() => getFuturesTickers('usdt', {})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'FUTURE', ticker.contract),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n // GATE API doesn't provide bid/ask volumes in tickers endpoint\n // We'll need to use order book for that if required\n ask_volume: '0',\n bid_volume: '0',\n // total_size is the open interest\n open_interest: ticker.total_size,\n // funding_rate is the current funding rate\n interest_rate_long: ticker.funding_rate ? `${-parseFloat(ticker.funding_rate)}` : undefined,\n interest_rate_short: ticker.funding_rate,\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\n// 从现货tickers获取价格和交易量数据\nconst quoteFromSpotTickers$ = defer(() => getSpotTickers({})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'SPOT', ticker.currency_pair),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n ask_volume: ticker.lowest_size ?? '0',\n bid_volume: ticker.highest_size ?? '0',\n // 现货没有持仓量的概念,设置为0\n open_interest: '0',\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$];\n\nconst quote$ = defer(() =>\n merge(\n ...quoteSources$.map((x$) =>\n defer(() => x$).pipe(\n // 防止单个流关闭导致整体关闭\n catchError(() => EMPTY),\n ),\n ),\n ),\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 写入数据库\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n terminal.channel.publishChannel('quote', { pattern: `^GATE/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n"]}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote, setMetricsQuoteState } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n defer,\n EMPTY,\n filter,\n groupBy,\n map,\n merge,\n mergeMap,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n} from 'rxjs';\nimport { getFuturesContracts, getFuturesTickers, getSpotTickers } from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\n// 获取所有USDT永续合约\nconst usdtFutureContracts$ = defer(() => getFuturesContracts('usdt', {})).pipe(\n mergeMap((contracts) => contracts),\n map(\n (contract): Partial<IQuote> => ({\n interest_rate_next_settled_at: contract.funding_next_apply\n ? `${contract.funding_next_apply * 1000}`\n : undefined,\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'FUTURE', contract.name),\n }),\n ),\n repeat({ delay: 3600_000 }),\n retry({ delay: 60_000 }),\n shareReplay(1),\n);\n\n// 从tickers获取价格和交易量数据\nconst quoteFromTickers$ = defer(() => getFuturesTickers('usdt', {})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'FUTURE', ticker.contract),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n // GATE API doesn't provide bid/ask volumes in tickers endpoint\n // We'll need to use order book for that if required\n ask_volume: '0',\n bid_volume: '0',\n // total_size is the open interest\n open_interest: ticker.total_size,\n // funding_rate is the current funding rate\n interest_rate_long: ticker.funding_rate ? `${-parseFloat(ticker.funding_rate)}` : undefined,\n interest_rate_short: ticker.funding_rate,\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\n// 从现货tickers获取价格和交易量数据\nconst quoteFromSpotTickers$ = defer(() => getSpotTickers({})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'SPOT', ticker.currency_pair),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n ask_volume: ticker.lowest_size ?? '0',\n bid_volume: ticker.highest_size ?? '0',\n // 现货没有持仓量的概念,设置为0\n open_interest: '0',\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$, usdtFutureContracts$];\n\nconst quote$ = defer(() =>\n merge(\n ...quoteSources$.map((x$) =>\n defer(() => x$).pipe(\n // 防止单个流关闭导致整体关闭\n catchError(() => EMPTY),\n ),\n ),\n ),\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 写入数据库\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n terminal.channel.publishChannel('quote', { pattern: `^GATE/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n"]}
@@ -40,7 +40,7 @@ const limitedGetSpotPrice = createConcurrencyLimiter(5, public_api_1.getSpotPric
40
40
  */
41
41
  const getEarningAccountInfo = async (credential) => {
42
42
  const balances = await (0, private_api_1.getEarnBalance)(credential, {});
43
- const positions = await Promise.all(balances.map(async (balance) => {
43
+ const positions = await Promise.all((balances !== null && balances !== void 0 ? balances : []).map(async (balance) => {
44
44
  // 过滤零余额条目
45
45
  if (+balance.amount <= 0)
46
46
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"earning.js","sourceRoot":"","sources":["../../../src/services/accounts/earning.ts"],"names":[],"mappings":";;;AAAA,uDAAwE;AACxE,yCAA2C;AAC3C,uDAAoE;AACpE,qDAAoD;AAEpD;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAC/B,WAAmB,EACnB,EAAiC,EACA,EAAE;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,OAAO,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzD,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC5B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,IAAU,EAAE,EAAE;QACvB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,EAAE,CAAC,GAAG,IAAI,CAAC;qBACR,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;qBACrB,OAAO,CAAC,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACP,CAAC,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,4BAA4B;AAC5B,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,CAAC,EAAE,yBAAY,CAAC,CAAC;AAEtE;;GAEG;AACI,MAAM,qBAAqB,GAAG,KAAK,EAAE,UAAuB,EAAwB,EAAE;IAC3F,MAAM,QAAQ,GAAG,MAAM,IAAA,4BAAc,EAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC7B,UAAU;QACV,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAE3C,8BAA8B;QAC9B,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAEzD,6CAA6C;QAC7C,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAElE,OAAO,IAAA,+BAAgB,EAAC;YACtB,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,WAAW,OAAO,CAAC,QAAQ,EAAE;YAC1C,UAAU,EAAE,IAAA,kBAAU,EAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC3D,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM;YACvB,WAAW,EAAE,UAAU;YACvB,cAAc,EAAE,aAAa;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IAEF,kBAAkB;IAClB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAoB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC,CAAC;AA5BW,QAAA,qBAAqB,yBA4BhC","sourcesContent":["import { makeSpotPosition, type IPosition } from '@yuants/data-account';\nimport { encodePath } from '@yuants/utils';\nimport { getEarnBalance, ICredential } from '../../api/private-api';\nimport { getSpotPrice } from '../../api/public-api';\n\n/**\n * 简单的并发限制器(支持带参数的函数)\n */\nconst createConcurrencyLimiter = <T, Args extends any[]>(\n concurrency: number,\n fn: (...args: Args) => Promise<T>,\n): ((...args: Args) => Promise<T>) => {\n let running = 0;\n const queue: Array<() => void> = [];\n\n const runNext = () => {\n if (running >= concurrency || queue.length === 0) return;\n running++;\n const task = queue.shift()!;\n task();\n };\n\n return (...args: Args) => {\n return new Promise<T>((resolve, reject) => {\n const task = () => {\n fn(...args)\n .then(resolve, reject)\n .finally(() => {\n running--;\n runNext();\n });\n };\n queue.push(task);\n runNext();\n });\n };\n};\n\n// 限制现货价格查询的并发数(避免触发 API 限流)\nconst limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);\n\n/**\n * 获取理财账户信息\n */\nexport const getEarningAccountInfo = async (credential: ICredential): Promise<IPosition[]> => {\n const balances = await getEarnBalance(credential, {});\n\n const positions = await Promise.all(\n balances.map(async (balance) => {\n // 过滤零余额条目\n if (+balance.amount <= 0) return undefined;\n\n // 计算可用余额:理财总数量减去已申请赎回未到账的冻结部分\n const frozen = +balance.frozen_amount || 0;\n const freeVolume = Math.max(0, +balance.amount - frozen);\n\n // 获取币种对 USDT 的现货价格(getSpotPrice 已处理特殊映射和默认值)\n const closablePrice = await limitedGetSpotPrice(balance.currency);\n\n return makeSpotPosition({\n datasource_id: 'GATE',\n position_id: `earning/${balance.currency}`,\n product_id: encodePath('GATE', 'EARNING', balance.currency),\n volume: +balance.amount,\n free_volume: freeVolume,\n closable_price: closablePrice,\n });\n }),\n );\n\n // 过滤 undefined 条目\n return positions.filter((pos): pos is IPosition => !!pos);\n};\n"]}
1
+ {"version":3,"file":"earning.js","sourceRoot":"","sources":["../../../src/services/accounts/earning.ts"],"names":[],"mappings":";;;AAAA,uDAAwE;AACxE,yCAA2C;AAC3C,uDAAoE;AACpE,qDAAoD;AAEpD;;GAEG;AACH,MAAM,wBAAwB,GAAG,CAC/B,WAAmB,EACnB,EAAiC,EACA,EAAE;IACnC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,MAAM,KAAK,GAAsB,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,OAAO,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzD,OAAO,EAAE,CAAC;QACV,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC5B,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;IAEF,OAAO,CAAC,GAAG,IAAU,EAAE,EAAE;QACvB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,GAAG,EAAE;gBAChB,EAAE,CAAC,GAAG,IAAI,CAAC;qBACR,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;qBACrB,OAAO,CAAC,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC;YACP,CAAC,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,4BAA4B;AAC5B,MAAM,mBAAmB,GAAG,wBAAwB,CAAC,CAAC,EAAE,yBAAY,CAAC,CAAC;AAEtE;;GAEG;AACI,MAAM,qBAAqB,GAAG,KAAK,EAAE,UAAuB,EAAwB,EAAE;IAC3F,MAAM,QAAQ,GAAG,MAAM,IAAA,4BAAc,EAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,CAAC,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACrC,UAAU;QACV,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAE3C,8BAA8B;QAC9B,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QAEzD,6CAA6C;QAC7C,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAElE,OAAO,IAAA,+BAAgB,EAAC;YACtB,aAAa,EAAE,MAAM;YACrB,WAAW,EAAE,WAAW,OAAO,CAAC,QAAQ,EAAE;YAC1C,UAAU,EAAE,IAAA,kBAAU,EAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC;YAC3D,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM;YACvB,WAAW,EAAE,UAAU;YACvB,cAAc,EAAE,aAAa;SAC9B,CAAC,CAAC;IACL,CAAC,CAAC,CACH,CAAC;IAEF,kBAAkB;IAClB,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAoB,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC,CAAC;AA5BW,QAAA,qBAAqB,yBA4BhC","sourcesContent":["import { makeSpotPosition, type IPosition } from '@yuants/data-account';\nimport { encodePath } from '@yuants/utils';\nimport { getEarnBalance, ICredential } from '../../api/private-api';\nimport { getSpotPrice } from '../../api/public-api';\n\n/**\n * 简单的并发限制器(支持带参数的函数)\n */\nconst createConcurrencyLimiter = <T, Args extends any[]>(\n concurrency: number,\n fn: (...args: Args) => Promise<T>,\n): ((...args: Args) => Promise<T>) => {\n let running = 0;\n const queue: Array<() => void> = [];\n\n const runNext = () => {\n if (running >= concurrency || queue.length === 0) return;\n running++;\n const task = queue.shift()!;\n task();\n };\n\n return (...args: Args) => {\n return new Promise<T>((resolve, reject) => {\n const task = () => {\n fn(...args)\n .then(resolve, reject)\n .finally(() => {\n running--;\n runNext();\n });\n };\n queue.push(task);\n runNext();\n });\n };\n};\n\n// 限制现货价格查询的并发数(避免触发 API 限流)\nconst limitedGetSpotPrice = createConcurrencyLimiter(5, getSpotPrice);\n\n/**\n * 获取理财账户信息\n */\nexport const getEarningAccountInfo = async (credential: ICredential): Promise<IPosition[]> => {\n const balances = await getEarnBalance(credential, {});\n\n const positions = await Promise.all(\n (balances ?? []).map(async (balance) => {\n // 过滤零余额条目\n if (+balance.amount <= 0) return undefined;\n\n // 计算可用余额:理财总数量减去已申请赎回未到账的冻结部分\n const frozen = +balance.frozen_amount || 0;\n const freeVolume = Math.max(0, +balance.amount - frozen);\n\n // 获取币种对 USDT 的现货价格(getSpotPrice 已处理特殊映射和默认值)\n const closablePrice = await limitedGetSpotPrice(balance.currency);\n\n return makeSpotPosition({\n datasource_id: 'GATE',\n position_id: `earning/${balance.currency}`,\n product_id: encodePath('GATE', 'EARNING', balance.currency),\n volume: +balance.amount,\n free_volume: freeVolume,\n closable_price: closablePrice,\n });\n }),\n );\n\n // 过滤 undefined 条目\n return positions.filter((pos): pos is IPosition => !!pos);\n};\n"]}
@@ -8,7 +8,13 @@ const rxjs_1 = require("rxjs");
8
8
  const public_api_1 = require("../../api/public-api");
9
9
  const terminal = protocol_1.Terminal.fromNodeEnv();
10
10
  // 获取所有USDT永续合约
11
- const usdtFutureContracts$ = (0, rxjs_1.defer)(() => (0, public_api_1.getFuturesContracts)('usdt', {})).pipe((0, rxjs_1.repeat)({ delay: 3600000 }), (0, rxjs_1.retry)({ delay: 60000 }), (0, rxjs_1.shareReplay)(1));
11
+ const usdtFutureContracts$ = (0, rxjs_1.defer)(() => (0, public_api_1.getFuturesContracts)('usdt', {})).pipe((0, rxjs_1.mergeMap)((contracts) => contracts), (0, rxjs_1.map)((contract) => ({
12
+ interest_rate_next_settled_at: contract.funding_next_apply
13
+ ? `${contract.funding_next_apply * 1000}`
14
+ : undefined,
15
+ datasource_id: 'GATE',
16
+ product_id: (0, utils_1.encodePath)('GATE', 'FUTURE', contract.name),
17
+ })), (0, rxjs_1.repeat)({ delay: 3600000 }), (0, rxjs_1.retry)({ delay: 60000 }), (0, rxjs_1.shareReplay)(1));
12
18
  // 从tickers获取价格和交易量数据
13
19
  const quoteFromTickers$ = (0, rxjs_1.defer)(() => (0, public_api_1.getFuturesTickers)('usdt', {})).pipe((0, rxjs_1.mergeMap)((tickers) => tickers), (0, rxjs_1.map)((ticker) => ({
14
20
  datasource_id: 'GATE',
@@ -41,7 +47,7 @@ const quoteFromSpotTickers$ = (0, rxjs_1.defer)(() => (0, public_api_1.getSpotTi
41
47
  open_interest: '0',
42
48
  });
43
49
  }), (0, rxjs_1.repeat)({ delay: 5000 }), (0, rxjs_1.retry)({ delay: 1000 }));
44
- const quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$];
50
+ const quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$, usdtFutureContracts$];
45
51
  const quote$ = (0, rxjs_1.defer)(() => (0, rxjs_1.merge)(...quoteSources$.map((x$) => (0, rxjs_1.defer)(() => x$).pipe(
46
52
  // 防止单个流关闭导致整体关闭
47
53
  (0, rxjs_1.catchError)(() => rxjs_1.EMPTY))))).pipe((0, rxjs_1.groupBy)((x) => (0, utils_1.encodePath)(x.datasource_id, x.product_id)), (0, rxjs_1.mergeMap)((group$) => {
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote, setMetricsQuoteState } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n defer,\n EMPTY,\n filter,\n groupBy,\n map,\n merge,\n mergeMap,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n} from 'rxjs';\nimport { getFuturesContracts, getFuturesTickers, getSpotTickers } from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\n// 获取所有USDT永续合约\nconst usdtFutureContracts$ = defer(() => getFuturesContracts('usdt', {})).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 60_000 }),\n shareReplay(1),\n);\n\n// 从tickers获取价格和交易量数据\nconst quoteFromTickers$ = defer(() => getFuturesTickers('usdt', {})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'FUTURE', ticker.contract),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n // GATE API doesn't provide bid/ask volumes in tickers endpoint\n // We'll need to use order book for that if required\n ask_volume: '0',\n bid_volume: '0',\n // total_size is the open interest\n open_interest: ticker.total_size,\n // funding_rate is the current funding rate\n interest_rate_long: ticker.funding_rate ? `${-parseFloat(ticker.funding_rate)}` : undefined,\n interest_rate_short: ticker.funding_rate,\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\n// 从现货tickers获取价格和交易量数据\nconst quoteFromSpotTickers$ = defer(() => getSpotTickers({})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'SPOT', ticker.currency_pair),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n ask_volume: ticker.lowest_size ?? '0',\n bid_volume: ticker.highest_size ?? '0',\n // 现货没有持仓量的概念,设置为0\n open_interest: '0',\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$];\n\nconst quote$ = defer(() =>\n merge(\n ...quoteSources$.map((x$) =>\n defer(() => x$).pipe(\n // 防止单个流关闭导致整体关闭\n catchError(() => EMPTY),\n ),\n ),\n ),\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 写入数据库\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n terminal.channel.publishChannel('quote', { pattern: `^GATE/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n"]}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote, setMetricsQuoteState } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n defer,\n EMPTY,\n filter,\n groupBy,\n map,\n merge,\n mergeMap,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n} from 'rxjs';\nimport { getFuturesContracts, getFuturesTickers, getSpotTickers } from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\n\n// 获取所有USDT永续合约\nconst usdtFutureContracts$ = defer(() => getFuturesContracts('usdt', {})).pipe(\n mergeMap((contracts) => contracts),\n map(\n (contract): Partial<IQuote> => ({\n interest_rate_next_settled_at: contract.funding_next_apply\n ? `${contract.funding_next_apply * 1000}`\n : undefined,\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'FUTURE', contract.name),\n }),\n ),\n repeat({ delay: 3600_000 }),\n retry({ delay: 60_000 }),\n shareReplay(1),\n);\n\n// 从tickers获取价格和交易量数据\nconst quoteFromTickers$ = defer(() => getFuturesTickers('usdt', {})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'FUTURE', ticker.contract),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n // GATE API doesn't provide bid/ask volumes in tickers endpoint\n // We'll need to use order book for that if required\n ask_volume: '0',\n bid_volume: '0',\n // total_size is the open interest\n open_interest: ticker.total_size,\n // funding_rate is the current funding rate\n interest_rate_long: ticker.funding_rate ? `${-parseFloat(ticker.funding_rate)}` : undefined,\n interest_rate_short: ticker.funding_rate,\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\n// 从现货tickers获取价格和交易量数据\nconst quoteFromSpotTickers$ = defer(() => getSpotTickers({})).pipe(\n mergeMap((tickers) => tickers),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'GATE',\n product_id: encodePath('GATE', 'SPOT', ticker.currency_pair),\n last_price: ticker.last,\n ask_price: ticker.lowest_ask,\n bid_price: ticker.highest_bid,\n ask_volume: ticker.lowest_size ?? '0',\n bid_volume: ticker.highest_size ?? '0',\n // 现货没有持仓量的概念,设置为0\n open_interest: '0',\n }),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteSources$ = [quoteFromTickers$, quoteFromSpotTickers$, usdtFutureContracts$];\n\nconst quote$ = defer(() =>\n merge(\n ...quoteSources$.map((x$) =>\n defer(() => x$).pipe(\n // 防止单个流关闭导致整体关闭\n catchError(() => EMPTY),\n ),\n ),\n ),\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 写入数据库\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n terminal.channel.publishChannel('quote', { pattern: `^GATE/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuants/vendor-gate",
3
- "version": "0.9.7",
3
+ "version": "0.9.9",
4
4
  "main": "lib/index.js",
5
5
  "files": [
6
6
  "lib",
@@ -9,21 +9,21 @@
9
9
  ],
10
10
  "dependencies": {
11
11
  "rxjs": "~7.5.6",
12
- "@yuants/cache": "0.3.14",
13
- "@yuants/transfer": "0.2.51",
14
- "@yuants/protocol": "0.54.4",
15
- "@yuants/data-account": "0.11.11",
16
- "@yuants/data-order": "0.7.12",
17
- "@yuants/data-ohlc": "0.6.7",
18
- "@yuants/data-trade": "0.1.37",
19
- "@yuants/sql": "0.9.42",
20
- "@yuants/data-product": "0.5.12",
21
- "@yuants/utils": "0.19.6",
22
- "@yuants/http-services": "0.5.1",
23
- "@yuants/data-series": "0.3.64",
24
- "@yuants/data-quote": "0.4.11",
25
- "@yuants/data-interest-rate": "0.2.12",
26
- "@yuants/exchange": "0.8.22"
12
+ "@yuants/cache": "0.3.15",
13
+ "@yuants/data-account": "0.11.12",
14
+ "@yuants/protocol": "0.55.0",
15
+ "@yuants/transfer": "0.2.52",
16
+ "@yuants/data-order": "0.7.13",
17
+ "@yuants/data-ohlc": "0.6.8",
18
+ "@yuants/data-trade": "0.1.38",
19
+ "@yuants/sql": "0.9.43",
20
+ "@yuants/http-services": "0.5.3",
21
+ "@yuants/utils": "0.19.7",
22
+ "@yuants/data-series": "0.3.65",
23
+ "@yuants/data-product": "0.5.13",
24
+ "@yuants/data-interest-rate": "0.2.13",
25
+ "@yuants/exchange": "0.8.23",
26
+ "@yuants/data-quote": "0.4.12"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@microsoft/api-extractor": "~7.55.2",
@@ -33,7 +33,7 @@
33
33
  "@types/heft-jest": "1.0.6",
34
34
  "@types/node": "24",
35
35
  "typescript": "~5.9.3",
36
- "@yuants/extension": "0.2.42",
36
+ "@yuants/extension": "0.2.43",
37
37
  "@yuants/tool-kit": "0.2.3"
38
38
  },
39
39
  "publishConfig": {