@yuants/vendor-aster 0.9.2 → 0.9.4

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.
@@ -6,6 +6,7 @@ import { decodePath, encodePath } from '@yuants/utils';
6
6
  import { catchError, combineLatest, concatMap, defer, exhaustMap, filter, from, groupBy, map, merge, mergeMap, of, repeat, retry, scan, shareReplay, startWith, timer, } from 'rxjs';
7
7
  import { getFApiV1ExchangeInfo, getFApiV1OpenInterest, getFApiV1PremiumIndex, getFApiV1TickerPrice, } from '../../api/public-api';
8
8
  const terminal = Terminal.fromNodeEnv();
9
+ const DISABLE_OPEN_INTEREST = process.env.DISABLE_OPEN_INTEREST === 'true';
9
10
  const DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;
10
11
  const OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120000;
11
12
  const toIntervalMs = (interval, intervalNum) => {
@@ -96,7 +97,7 @@ const quoteFromFundingRate$ = fundingRate$.pipe(mergeMap((premiumDataArray) => p
96
97
  interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,
97
98
  interest_rate_short: premiumData.lastFundingRate,
98
99
  })));
99
- const quote$ = merge(quoteFromTicker$, quoteFromOpenInterest$, quoteFromFundingRate$).pipe(groupBy((quote) => quote.product_id), mergeMap((group$) => group$.pipe(scan((acc, cur) => Object.assign(acc, cur, {
100
+ const quote$ = merge(quoteFromTicker$, quoteFromFundingRate$, DISABLE_OPEN_INTEREST ? of() : quoteFromOpenInterest$).pipe(groupBy((quote) => quote.product_id), mergeMap((group$) => group$.pipe(scan((acc, cur) => Object.assign(acc, cur, {
100
101
  datasource_id: 'ASTER',
101
102
  product_id: group$.key,
102
103
  }), {}))), shareReplay({ bufferSize: 1, refCount: true }));
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { setMetricsQuoteState, type IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n combineLatest,\n concatMap,\n defer,\n exhaustMap,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n of,\n repeat,\n retry,\n scan,\n shareReplay,\n startWith,\n timer,\n} from 'rxjs';\nimport {\n getFApiV1ExchangeInfo,\n getFApiV1OpenInterest,\n getFApiV1PremiumIndex,\n getFApiV1TickerPrice,\n IAsterRateLimit,\n} from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\nconst DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;\nconst OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120_000;\n\nconst toIntervalMs = (interval?: string, intervalNum?: number) => {\n switch (interval) {\n case 'SECOND':\n return (intervalNum ?? 1) * 1_000;\n case 'MINUTE':\n return (intervalNum ?? 1) * 60_000;\n case 'HOUR':\n return (intervalNum ?? 1) * 3_600_000;\n case 'DAY':\n return (intervalNum ?? 1) * 86_400_000;\n default:\n return undefined;\n }\n};\n\nconst getRequestIntervalMs = (rateLimits: IAsterRateLimit[] | undefined, fallbackMs: number) => {\n const intervals: number[] = [];\n for (const item of rateLimits ?? []) {\n if (item.rateLimitType !== 'REQUEST_WEIGHT' && item.rateLimitType !== 'RAW_REQUESTS') continue;\n const duration = toIntervalMs(item.interval, item.intervalNum);\n const limit = item.limit;\n if (duration == null || limit == null || limit <= 0) continue;\n intervals.push(Math.ceil(duration / limit));\n }\n if (!intervals.length) return fallbackMs;\n return Math.max(fallbackMs, Math.max(...intervals));\n};\n\nconst openInterestCache = createCache<string>(\n async (symbol: string) => {\n try {\n const data = await getFApiV1OpenInterest({ symbol });\n return data.openInterest;\n } catch (error) {\n console.warn('getFApiV1OpenInterest failed', symbol, error);\n return undefined;\n }\n },\n { expire: OPEN_INTEREST_TTL },\n);\n\nconst requestInterval$ = defer(() => getFApiV1ExchangeInfo({})).pipe(\n map((info) => getRequestIntervalMs(info.rateLimits, DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS)),\n catchError((err) => {\n console.warn('getFApiV1ExchangeInfo failed when calculating request interval', err);\n return of(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS);\n }),\n startWith(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS),\n retry({ delay: 60_000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst ticker$ = defer(() => getFApiV1TickerPrice({})).pipe(\n map((tickers) => (Array.isArray(tickers) ? tickers : [])),\n repeat({ delay: 1000 }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst quoteFromTicker$ = ticker$.pipe(\n mergeMap((tickers) => tickers || []),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', ticker.symbol),\n last_price: `${ticker.price}`,\n bid_price: `${ticker.price}`,\n ask_price: `${ticker.price}`,\n }),\n ),\n);\n\n// Extract unique symbols from ticker stream\nconst symbolList$ = ticker$.pipe(\n map((tickers) => tickers.map((t) => t.symbol)),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Create a controlled rotation stream for fetching open interest\n// This cycles through all symbols with proper delays, independent of ticker updates\nconst OPEN_INTEREST_CYCLE_DELAY = process.env.OPEN_INTEREST_CYCLE_DELAY\n ? Number(process.env.OPEN_INTEREST_CYCLE_DELAY)\n : 60_000; // 60 seconds between full cycles\n\nconst openInterestRotation$ = combineLatest([symbolList$, requestInterval$]).pipe(\n exhaustMap(([symbols, requestInterval]) =>\n defer(() => {\n console.info(\n `Starting open interest rotation for ${symbols.length} symbols with ${requestInterval}ms interval`,\n );\n return from(symbols).pipe(\n concatMap((symbol, index) =>\n (index > 0 ? timer(requestInterval) : of(0)).pipe(\n mergeMap(() => from(openInterestCache.query(symbol))),\n map((openInterest) => ({\n symbol,\n openInterest: openInterest ?? '0',\n timestamp: Date.now(),\n })),\n catchError((err) => {\n console.warn(`Failed to fetch open interest for ${symbol}:`, err);\n return of(undefined);\n }),\n ),\n ),\n );\n }),\n ),\n filter((x) => !!x),\n shareReplay({ bufferSize: 1000, refCount: true }), // Cache open interest for all symbols\n);\n\n// Convert open interest rotation data to quote format\nconst quoteFromOpenInterest$ = openInterestRotation$.pipe(\n map(\n (data): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', data?.symbol),\n open_interest: data?.openInterest,\n }),\n ),\n);\n\n// Funding rate stream - fetches all symbols at once (more efficient than per-symbol)\nconst fundingRate$ = defer(() => getFApiV1PremiumIndex({})).pipe(\n map((data) => {\n // Handle both single object and array responses\n const premiumDataArray = Array.isArray(data) ? data : [data];\n return premiumDataArray;\n }),\n repeat({ delay: OPEN_INTEREST_CYCLE_DELAY }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Convert funding rate data to quote format\nconst quoteFromFundingRate$ = fundingRate$.pipe(\n mergeMap((premiumDataArray) => premiumDataArray),\n map(\n (premiumData): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', premiumData.symbol),\n interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,\n interest_rate_short: premiumData.lastFundingRate,\n }),\n ),\n);\n\nconst quote$ = merge(quoteFromTicker$, quoteFromOpenInterest$, quoteFromFundingRate$).pipe(\n groupBy((quote) => quote.product_id),\n mergeMap((group$) =>\n group$.pipe(\n scan(\n (acc, cur) =>\n Object.assign(acc, cur, {\n datasource_id: 'ASTER',\n product_id: group$.key,\n }),\n {} as Partial<IQuote>,\n ),\n ),\n ),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n tableName: 'quote',\n writeInterval: 1000,\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n\n terminal.channel.publishChannel('quote', { pattern: '^ASTER/' }, (channel_id) => {\n const [datasourceId, productId] = decodePath(channel_id);\n if (!datasourceId || !productId) {\n throw new Error(`Invalid channel_id: ${channel_id}`);\n }\n return quote$.pipe(filter((quote) => quote.product_id === productId));\n });\n}\n"]}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { setMetricsQuoteState, type IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n combineLatest,\n concatMap,\n defer,\n exhaustMap,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n of,\n repeat,\n retry,\n scan,\n shareReplay,\n startWith,\n timer,\n} from 'rxjs';\nimport {\n getFApiV1ExchangeInfo,\n getFApiV1OpenInterest,\n getFApiV1PremiumIndex,\n getFApiV1TickerPrice,\n IAsterRateLimit,\n} from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\nconst DISABLE_OPEN_INTEREST = process.env.DISABLE_OPEN_INTEREST === 'true';\nconst DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;\nconst OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120_000;\n\nconst toIntervalMs = (interval?: string, intervalNum?: number) => {\n switch (interval) {\n case 'SECOND':\n return (intervalNum ?? 1) * 1_000;\n case 'MINUTE':\n return (intervalNum ?? 1) * 60_000;\n case 'HOUR':\n return (intervalNum ?? 1) * 3_600_000;\n case 'DAY':\n return (intervalNum ?? 1) * 86_400_000;\n default:\n return undefined;\n }\n};\n\nconst getRequestIntervalMs = (rateLimits: IAsterRateLimit[] | undefined, fallbackMs: number) => {\n const intervals: number[] = [];\n for (const item of rateLimits ?? []) {\n if (item.rateLimitType !== 'REQUEST_WEIGHT' && item.rateLimitType !== 'RAW_REQUESTS') continue;\n const duration = toIntervalMs(item.interval, item.intervalNum);\n const limit = item.limit;\n if (duration == null || limit == null || limit <= 0) continue;\n intervals.push(Math.ceil(duration / limit));\n }\n if (!intervals.length) return fallbackMs;\n return Math.max(fallbackMs, Math.max(...intervals));\n};\n\nconst openInterestCache = createCache<string>(\n async (symbol: string) => {\n try {\n const data = await getFApiV1OpenInterest({ symbol });\n return data.openInterest;\n } catch (error) {\n console.warn('getFApiV1OpenInterest failed', symbol, error);\n return undefined;\n }\n },\n { expire: OPEN_INTEREST_TTL },\n);\n\nconst requestInterval$ = defer(() => getFApiV1ExchangeInfo({})).pipe(\n map((info) => getRequestIntervalMs(info.rateLimits, DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS)),\n catchError((err) => {\n console.warn('getFApiV1ExchangeInfo failed when calculating request interval', err);\n return of(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS);\n }),\n startWith(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS),\n retry({ delay: 60_000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst ticker$ = defer(() => getFApiV1TickerPrice({})).pipe(\n map((tickers) => (Array.isArray(tickers) ? tickers : [])),\n repeat({ delay: 1000 }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst quoteFromTicker$ = ticker$.pipe(\n mergeMap((tickers) => tickers || []),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', ticker.symbol),\n last_price: `${ticker.price}`,\n bid_price: `${ticker.price}`,\n ask_price: `${ticker.price}`,\n }),\n ),\n);\n\n// Extract unique symbols from ticker stream\nconst symbolList$ = ticker$.pipe(\n map((tickers) => tickers.map((t) => t.symbol)),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Create a controlled rotation stream for fetching open interest\n// This cycles through all symbols with proper delays, independent of ticker updates\nconst OPEN_INTEREST_CYCLE_DELAY = process.env.OPEN_INTEREST_CYCLE_DELAY\n ? Number(process.env.OPEN_INTEREST_CYCLE_DELAY)\n : 60_000; // 60 seconds between full cycles\n\nconst openInterestRotation$ = combineLatest([symbolList$, requestInterval$]).pipe(\n exhaustMap(([symbols, requestInterval]) =>\n defer(() => {\n console.info(\n `Starting open interest rotation for ${symbols.length} symbols with ${requestInterval}ms interval`,\n );\n return from(symbols).pipe(\n concatMap((symbol, index) =>\n (index > 0 ? timer(requestInterval) : of(0)).pipe(\n mergeMap(() => from(openInterestCache.query(symbol))),\n map((openInterest) => ({\n symbol,\n openInterest: openInterest ?? '0',\n timestamp: Date.now(),\n })),\n catchError((err) => {\n console.warn(`Failed to fetch open interest for ${symbol}:`, err);\n return of(undefined);\n }),\n ),\n ),\n );\n }),\n ),\n filter((x) => !!x),\n shareReplay({ bufferSize: 1000, refCount: true }), // Cache open interest for all symbols\n);\n\n// Convert open interest rotation data to quote format\nconst quoteFromOpenInterest$ = openInterestRotation$.pipe(\n map(\n (data): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', data?.symbol),\n open_interest: data?.openInterest,\n }),\n ),\n);\n\n// Funding rate stream - fetches all symbols at once (more efficient than per-symbol)\nconst fundingRate$ = defer(() => getFApiV1PremiumIndex({})).pipe(\n map((data) => {\n // Handle both single object and array responses\n const premiumDataArray = Array.isArray(data) ? data : [data];\n return premiumDataArray;\n }),\n repeat({ delay: OPEN_INTEREST_CYCLE_DELAY }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Convert funding rate data to quote format\nconst quoteFromFundingRate$ = fundingRate$.pipe(\n mergeMap((premiumDataArray) => premiumDataArray),\n map(\n (premiumData): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', premiumData.symbol),\n interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,\n interest_rate_short: premiumData.lastFundingRate,\n }),\n ),\n);\n\nconst quote$ = merge(\n quoteFromTicker$,\n quoteFromFundingRate$,\n DISABLE_OPEN_INTEREST ? of() : quoteFromOpenInterest$,\n).pipe(\n groupBy((quote) => quote.product_id),\n mergeMap((group$) =>\n group$.pipe(\n scan(\n (acc, cur) =>\n Object.assign(acc, cur, {\n datasource_id: 'ASTER',\n product_id: group$.key,\n }),\n {} as Partial<IQuote>,\n ),\n ),\n ),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n tableName: 'quote',\n writeInterval: 1000,\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n\n terminal.channel.publishChannel('quote', { pattern: '^ASTER/' }, (channel_id) => {\n const [datasourceId, productId] = decodePath(channel_id);\n if (!datasourceId || !productId) {\n throw new Error(`Invalid channel_id: ${channel_id}`);\n }\n return quote$.pipe(filter((quote) => quote.product_id === productId));\n });\n}\n"]}
@@ -8,6 +8,7 @@ const utils_1 = require("@yuants/utils");
8
8
  const rxjs_1 = require("rxjs");
9
9
  const public_api_1 = require("../../api/public-api");
10
10
  const terminal = protocol_1.Terminal.fromNodeEnv();
11
+ const DISABLE_OPEN_INTEREST = process.env.DISABLE_OPEN_INTEREST === 'true';
11
12
  const DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;
12
13
  const OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120000;
13
14
  const toIntervalMs = (interval, intervalNum) => {
@@ -98,7 +99,7 @@ const quoteFromFundingRate$ = fundingRate$.pipe((0, rxjs_1.mergeMap)((premiumDat
98
99
  interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,
99
100
  interest_rate_short: premiumData.lastFundingRate,
100
101
  })));
101
- const quote$ = (0, rxjs_1.merge)(quoteFromTicker$, quoteFromOpenInterest$, quoteFromFundingRate$).pipe((0, rxjs_1.groupBy)((quote) => quote.product_id), (0, rxjs_1.mergeMap)((group$) => group$.pipe((0, rxjs_1.scan)((acc, cur) => Object.assign(acc, cur, {
102
+ const quote$ = (0, rxjs_1.merge)(quoteFromTicker$, quoteFromFundingRate$, DISABLE_OPEN_INTEREST ? (0, rxjs_1.of)() : quoteFromOpenInterest$).pipe((0, rxjs_1.groupBy)((quote) => quote.product_id), (0, rxjs_1.mergeMap)((group$) => group$.pipe((0, rxjs_1.scan)((acc, cur) => Object.assign(acc, cur, {
102
103
  datasource_id: 'ASTER',
103
104
  product_id: group$.key,
104
105
  }), {}))), (0, rxjs_1.shareReplay)({ bufferSize: 1, refCount: true }));
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { setMetricsQuoteState, type IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n combineLatest,\n concatMap,\n defer,\n exhaustMap,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n of,\n repeat,\n retry,\n scan,\n shareReplay,\n startWith,\n timer,\n} from 'rxjs';\nimport {\n getFApiV1ExchangeInfo,\n getFApiV1OpenInterest,\n getFApiV1PremiumIndex,\n getFApiV1TickerPrice,\n IAsterRateLimit,\n} from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\nconst DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;\nconst OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120_000;\n\nconst toIntervalMs = (interval?: string, intervalNum?: number) => {\n switch (interval) {\n case 'SECOND':\n return (intervalNum ?? 1) * 1_000;\n case 'MINUTE':\n return (intervalNum ?? 1) * 60_000;\n case 'HOUR':\n return (intervalNum ?? 1) * 3_600_000;\n case 'DAY':\n return (intervalNum ?? 1) * 86_400_000;\n default:\n return undefined;\n }\n};\n\nconst getRequestIntervalMs = (rateLimits: IAsterRateLimit[] | undefined, fallbackMs: number) => {\n const intervals: number[] = [];\n for (const item of rateLimits ?? []) {\n if (item.rateLimitType !== 'REQUEST_WEIGHT' && item.rateLimitType !== 'RAW_REQUESTS') continue;\n const duration = toIntervalMs(item.interval, item.intervalNum);\n const limit = item.limit;\n if (duration == null || limit == null || limit <= 0) continue;\n intervals.push(Math.ceil(duration / limit));\n }\n if (!intervals.length) return fallbackMs;\n return Math.max(fallbackMs, Math.max(...intervals));\n};\n\nconst openInterestCache = createCache<string>(\n async (symbol: string) => {\n try {\n const data = await getFApiV1OpenInterest({ symbol });\n return data.openInterest;\n } catch (error) {\n console.warn('getFApiV1OpenInterest failed', symbol, error);\n return undefined;\n }\n },\n { expire: OPEN_INTEREST_TTL },\n);\n\nconst requestInterval$ = defer(() => getFApiV1ExchangeInfo({})).pipe(\n map((info) => getRequestIntervalMs(info.rateLimits, DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS)),\n catchError((err) => {\n console.warn('getFApiV1ExchangeInfo failed when calculating request interval', err);\n return of(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS);\n }),\n startWith(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS),\n retry({ delay: 60_000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst ticker$ = defer(() => getFApiV1TickerPrice({})).pipe(\n map((tickers) => (Array.isArray(tickers) ? tickers : [])),\n repeat({ delay: 1000 }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst quoteFromTicker$ = ticker$.pipe(\n mergeMap((tickers) => tickers || []),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', ticker.symbol),\n last_price: `${ticker.price}`,\n bid_price: `${ticker.price}`,\n ask_price: `${ticker.price}`,\n }),\n ),\n);\n\n// Extract unique symbols from ticker stream\nconst symbolList$ = ticker$.pipe(\n map((tickers) => tickers.map((t) => t.symbol)),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Create a controlled rotation stream for fetching open interest\n// This cycles through all symbols with proper delays, independent of ticker updates\nconst OPEN_INTEREST_CYCLE_DELAY = process.env.OPEN_INTEREST_CYCLE_DELAY\n ? Number(process.env.OPEN_INTEREST_CYCLE_DELAY)\n : 60_000; // 60 seconds between full cycles\n\nconst openInterestRotation$ = combineLatest([symbolList$, requestInterval$]).pipe(\n exhaustMap(([symbols, requestInterval]) =>\n defer(() => {\n console.info(\n `Starting open interest rotation for ${symbols.length} symbols with ${requestInterval}ms interval`,\n );\n return from(symbols).pipe(\n concatMap((symbol, index) =>\n (index > 0 ? timer(requestInterval) : of(0)).pipe(\n mergeMap(() => from(openInterestCache.query(symbol))),\n map((openInterest) => ({\n symbol,\n openInterest: openInterest ?? '0',\n timestamp: Date.now(),\n })),\n catchError((err) => {\n console.warn(`Failed to fetch open interest for ${symbol}:`, err);\n return of(undefined);\n }),\n ),\n ),\n );\n }),\n ),\n filter((x) => !!x),\n shareReplay({ bufferSize: 1000, refCount: true }), // Cache open interest for all symbols\n);\n\n// Convert open interest rotation data to quote format\nconst quoteFromOpenInterest$ = openInterestRotation$.pipe(\n map(\n (data): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', data?.symbol),\n open_interest: data?.openInterest,\n }),\n ),\n);\n\n// Funding rate stream - fetches all symbols at once (more efficient than per-symbol)\nconst fundingRate$ = defer(() => getFApiV1PremiumIndex({})).pipe(\n map((data) => {\n // Handle both single object and array responses\n const premiumDataArray = Array.isArray(data) ? data : [data];\n return premiumDataArray;\n }),\n repeat({ delay: OPEN_INTEREST_CYCLE_DELAY }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Convert funding rate data to quote format\nconst quoteFromFundingRate$ = fundingRate$.pipe(\n mergeMap((premiumDataArray) => premiumDataArray),\n map(\n (premiumData): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', premiumData.symbol),\n interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,\n interest_rate_short: premiumData.lastFundingRate,\n }),\n ),\n);\n\nconst quote$ = merge(quoteFromTicker$, quoteFromOpenInterest$, quoteFromFundingRate$).pipe(\n groupBy((quote) => quote.product_id),\n mergeMap((group$) =>\n group$.pipe(\n scan(\n (acc, cur) =>\n Object.assign(acc, cur, {\n datasource_id: 'ASTER',\n product_id: group$.key,\n }),\n {} as Partial<IQuote>,\n ),\n ),\n ),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n tableName: 'quote',\n writeInterval: 1000,\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n\n terminal.channel.publishChannel('quote', { pattern: '^ASTER/' }, (channel_id) => {\n const [datasourceId, productId] = decodePath(channel_id);\n if (!datasourceId || !productId) {\n throw new Error(`Invalid channel_id: ${channel_id}`);\n }\n return quote$.pipe(filter((quote) => quote.product_id === productId));\n });\n}\n"]}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../src/services/markets/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { setMetricsQuoteState, type IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath } from '@yuants/utils';\nimport {\n catchError,\n combineLatest,\n concatMap,\n defer,\n exhaustMap,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n of,\n repeat,\n retry,\n scan,\n shareReplay,\n startWith,\n timer,\n} from 'rxjs';\nimport {\n getFApiV1ExchangeInfo,\n getFApiV1OpenInterest,\n getFApiV1PremiumIndex,\n getFApiV1TickerPrice,\n IAsterRateLimit,\n} from '../../api/public-api';\n\nconst terminal = Terminal.fromNodeEnv();\nconst DISABLE_OPEN_INTEREST = process.env.DISABLE_OPEN_INTEREST === 'true';\nconst DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS = 500;\nconst OPEN_INTEREST_TTL = process.env.OPEN_INTEREST_TTL ? Number(process.env.OPEN_INTEREST_TTL) : 120_000;\n\nconst toIntervalMs = (interval?: string, intervalNum?: number) => {\n switch (interval) {\n case 'SECOND':\n return (intervalNum ?? 1) * 1_000;\n case 'MINUTE':\n return (intervalNum ?? 1) * 60_000;\n case 'HOUR':\n return (intervalNum ?? 1) * 3_600_000;\n case 'DAY':\n return (intervalNum ?? 1) * 86_400_000;\n default:\n return undefined;\n }\n};\n\nconst getRequestIntervalMs = (rateLimits: IAsterRateLimit[] | undefined, fallbackMs: number) => {\n const intervals: number[] = [];\n for (const item of rateLimits ?? []) {\n if (item.rateLimitType !== 'REQUEST_WEIGHT' && item.rateLimitType !== 'RAW_REQUESTS') continue;\n const duration = toIntervalMs(item.interval, item.intervalNum);\n const limit = item.limit;\n if (duration == null || limit == null || limit <= 0) continue;\n intervals.push(Math.ceil(duration / limit));\n }\n if (!intervals.length) return fallbackMs;\n return Math.max(fallbackMs, Math.max(...intervals));\n};\n\nconst openInterestCache = createCache<string>(\n async (symbol: string) => {\n try {\n const data = await getFApiV1OpenInterest({ symbol });\n return data.openInterest;\n } catch (error) {\n console.warn('getFApiV1OpenInterest failed', symbol, error);\n return undefined;\n }\n },\n { expire: OPEN_INTEREST_TTL },\n);\n\nconst requestInterval$ = defer(() => getFApiV1ExchangeInfo({})).pipe(\n map((info) => getRequestIntervalMs(info.rateLimits, DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS)),\n catchError((err) => {\n console.warn('getFApiV1ExchangeInfo failed when calculating request interval', err);\n return of(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS);\n }),\n startWith(DEFAULT_OPEN_INTEREST_REQUEST_INTERVAL_MS),\n retry({ delay: 60_000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst ticker$ = defer(() => getFApiV1TickerPrice({})).pipe(\n map((tickers) => (Array.isArray(tickers) ? tickers : [])),\n repeat({ delay: 1000 }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nconst quoteFromTicker$ = ticker$.pipe(\n mergeMap((tickers) => tickers || []),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', ticker.symbol),\n last_price: `${ticker.price}`,\n bid_price: `${ticker.price}`,\n ask_price: `${ticker.price}`,\n }),\n ),\n);\n\n// Extract unique symbols from ticker stream\nconst symbolList$ = ticker$.pipe(\n map((tickers) => tickers.map((t) => t.symbol)),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Create a controlled rotation stream for fetching open interest\n// This cycles through all symbols with proper delays, independent of ticker updates\nconst OPEN_INTEREST_CYCLE_DELAY = process.env.OPEN_INTEREST_CYCLE_DELAY\n ? Number(process.env.OPEN_INTEREST_CYCLE_DELAY)\n : 60_000; // 60 seconds between full cycles\n\nconst openInterestRotation$ = combineLatest([symbolList$, requestInterval$]).pipe(\n exhaustMap(([symbols, requestInterval]) =>\n defer(() => {\n console.info(\n `Starting open interest rotation for ${symbols.length} symbols with ${requestInterval}ms interval`,\n );\n return from(symbols).pipe(\n concatMap((symbol, index) =>\n (index > 0 ? timer(requestInterval) : of(0)).pipe(\n mergeMap(() => from(openInterestCache.query(symbol))),\n map((openInterest) => ({\n symbol,\n openInterest: openInterest ?? '0',\n timestamp: Date.now(),\n })),\n catchError((err) => {\n console.warn(`Failed to fetch open interest for ${symbol}:`, err);\n return of(undefined);\n }),\n ),\n ),\n );\n }),\n ),\n filter((x) => !!x),\n shareReplay({ bufferSize: 1000, refCount: true }), // Cache open interest for all symbols\n);\n\n// Convert open interest rotation data to quote format\nconst quoteFromOpenInterest$ = openInterestRotation$.pipe(\n map(\n (data): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', data?.symbol),\n open_interest: data?.openInterest,\n }),\n ),\n);\n\n// Funding rate stream - fetches all symbols at once (more efficient than per-symbol)\nconst fundingRate$ = defer(() => getFApiV1PremiumIndex({})).pipe(\n map((data) => {\n // Handle both single object and array responses\n const premiumDataArray = Array.isArray(data) ? data : [data];\n return premiumDataArray;\n }),\n repeat({ delay: OPEN_INTEREST_CYCLE_DELAY }),\n retry({ delay: 5000 }),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\n// Convert funding rate data to quote format\nconst quoteFromFundingRate$ = fundingRate$.pipe(\n mergeMap((premiumDataArray) => premiumDataArray),\n map(\n (premiumData): Partial<IQuote> => ({\n datasource_id: 'ASTER',\n product_id: encodePath('ASTER', 'PERP', premiumData.symbol),\n interest_rate_long: premiumData.lastFundingRate ? `${-+premiumData.lastFundingRate}` : undefined,\n interest_rate_short: premiumData.lastFundingRate,\n }),\n ),\n);\n\nconst quote$ = merge(\n quoteFromTicker$,\n quoteFromFundingRate$,\n DISABLE_OPEN_INTEREST ? of() : quoteFromOpenInterest$,\n).pipe(\n groupBy((quote) => quote.product_id),\n mergeMap((group$) =>\n group$.pipe(\n scan(\n (acc, cur) =>\n Object.assign(acc, cur, {\n datasource_id: 'ASTER',\n product_id: group$.key,\n }),\n {} as Partial<IQuote>,\n ),\n ),\n ),\n shareReplay({ bufferSize: 1, refCount: true }),\n);\n\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n quote$\n .pipe(\n setMetricsQuoteState(terminal.terminal_id),\n writeToSQL({\n terminal,\n tableName: 'quote',\n writeInterval: 1000,\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n\n terminal.channel.publishChannel('quote', { pattern: '^ASTER/' }, (channel_id) => {\n const [datasourceId, productId] = decodePath(channel_id);\n if (!datasourceId || !productId) {\n throw new Error(`Invalid channel_id: ${channel_id}`);\n }\n return quote$.pipe(filter((quote) => quote.product_id === productId));\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuants/vendor-aster",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "main": "lib/index.js",
5
5
  "files": [
6
6
  "dist",
@@ -8,19 +8,19 @@
8
8
  "temp"
9
9
  ],
10
10
  "dependencies": {
11
- "@yuants/protocol": "0.53.5",
12
- "@yuants/cache": "0.3.6",
13
- "@yuants/data-account": "0.11.2",
14
- "@yuants/utils": "0.17.0",
15
- "@yuants/data-series": "0.3.55",
16
- "@yuants/sql": "0.9.33",
17
- "@yuants/data-product": "0.5.3",
18
- "@yuants/data-ohlc": "0.5.1",
19
- "@yuants/data-order": "0.7.3",
20
- "@yuants/data-interest-rate": "0.2.1",
21
- "@yuants/transfer": "0.2.42",
22
- "@yuants/data-quote": "0.4.2",
23
- "@yuants/exchange": "0.8.3",
11
+ "@yuants/protocol": "0.53.6",
12
+ "@yuants/cache": "0.3.7",
13
+ "@yuants/data-account": "0.11.3",
14
+ "@yuants/utils": "0.18.0",
15
+ "@yuants/data-series": "0.3.56",
16
+ "@yuants/sql": "0.9.34",
17
+ "@yuants/data-product": "0.5.4",
18
+ "@yuants/data-ohlc": "0.5.2",
19
+ "@yuants/data-order": "0.7.4",
20
+ "@yuants/data-interest-rate": "0.2.2",
21
+ "@yuants/transfer": "0.2.43",
22
+ "@yuants/data-quote": "0.4.3",
23
+ "@yuants/exchange": "0.8.4",
24
24
  "rxjs": "~7.5.6"
25
25
  },
26
26
  "devDependencies": {
@@ -30,7 +30,7 @@
30
30
  "@rushstack/heft-node-rig": "~1.10.7",
31
31
  "@types/heft-jest": "1.0.3",
32
32
  "@types/node": "22",
33
- "@yuants/extension": "0.2.34",
33
+ "@yuants/extension": "0.2.35",
34
34
  "@yuants/tool-kit": "0.2.1",
35
35
  "typescript": "~4.7.4",
36
36
  "ts-node": "~10.9.2"
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "apps/vendor-aster/AGENTS.md": "dcd006d19a5c3b73081525d7c0afea68cd6d058b",
3
- "apps/vendor-aster/CHANGELOG.json": "0e961d8a4cbaebc52f8afea4649c9266c5d904bd",
4
- "apps/vendor-aster/CHANGELOG.md": "58598d3c989ee91d7dc5bbb9cffaca242d2e48c6",
3
+ "apps/vendor-aster/CHANGELOG.json": "60a312aa8a8239ed29071af8ef987ab43e4ca440",
4
+ "apps/vendor-aster/CHANGELOG.md": "d4a6a11d7639f83b1111cd05fba8893342764756",
5
5
  "apps/vendor-aster/SESSION_NOTES.md": "d91f7a251d148fab1715b6da62395455edcb3b93",
6
6
  "apps/vendor-aster/config/jest.config.json": "4bb17bde3ee911163a3edb36a6eb71491d80b1bd",
7
7
  "apps/vendor-aster/config/rig.json": "f6c7b5537dc77a3170ba9f008bae3b6c3ee11956",
8
8
  "apps/vendor-aster/config/typescript.json": "854907e8a821f2050f6533368db160c649c25348",
9
- "apps/vendor-aster/package.json": "040ab35e46c9f68f907a7e6f675b636ec4902ae3",
9
+ "apps/vendor-aster/package.json": "8703601d2669ba91643d85fef19a78fa21209c0d",
10
10
  "apps/vendor-aster/src/api/private-api.ts": "b3288706373f8917be480210f8f02074890d5b34",
11
11
  "apps/vendor-aster/src/api/public-api.ts": "a20ac4c4465ab3e75e2dc024adc12ecc19422375",
12
12
  "apps/vendor-aster/src/index.ts": "1decd1aabd3e77bc7a678427a42a9fa5fa84cf29",
@@ -16,7 +16,7 @@
16
16
  "apps/vendor-aster/src/services/interest-rate-service.ts": "1b6061601c8c3712c0c18174d997a5589adef02c",
17
17
  "apps/vendor-aster/src/services/markets/interest_rate.ts": "8512bb506f3d66b16aceb9d153d1b3a4671de5ef",
18
18
  "apps/vendor-aster/src/services/markets/product.ts": "cae86f69c6f019747ebad07569ed2bc862a78ac6",
19
- "apps/vendor-aster/src/services/markets/quote.ts": "01274f7a820656ad480cb80a1cf526ac3805982b",
19
+ "apps/vendor-aster/src/services/markets/quote.ts": "919314f9e146c11678205467fc2a6ad4cdb180ac",
20
20
  "apps/vendor-aster/src/services/ohlc-service.ts": "6856f141561d53ec709c15b66e36744ae99323dc",
21
21
  "apps/vendor-aster/src/services/orders/cancelOrder.ts": "09a6d3b10a218ad226346dd68d0ff0207083a7f1",
22
22
  "apps/vendor-aster/src/services/orders/listOrders.ts": "579e83c45f6c4435ad3f5a2d3fe163a036bb9fd1",
@@ -24,19 +24,19 @@
24
24
  "apps/vendor-aster/src/services/quotes.ts": "9e93bc9a89a26018fd50f9ca845897334f2429ce",
25
25
  "apps/vendor-aster/tsconfig.json": "81da8f78196974b5d15da0edb6b2d9f48641063c",
26
26
  "apps/vendor-aster/.rush/temp/shrinkwrap-deps.json": "5ded440ffb547a8aa495353d5b13c1fd1b164b4a",
27
- "libraries/protocol/temp/package-deps.json": "359ac43a7382bafa1f91541e084256baec86d40b",
28
- "libraries/cache/temp/package-deps.json": "6f7f290f9a9c2e42e937d8ce3b55a6b541d2e835",
29
- "libraries/data-account/temp/package-deps.json": "a94782e226c47a0f5198a5cca619ae7815055f4a",
30
- "libraries/utils/temp/package-deps.json": "acd039ba8f5f93e664764ad2c8474878bd105c3b",
31
- "libraries/data-series/temp/package-deps.json": "ba6b4e5b9beec9dbe54fcdc12820d8af70356d50",
32
- "libraries/sql/temp/package-deps.json": "43f7034c83d7ac4b6c4224b8e916ac39fd78057d",
33
- "libraries/data-product/temp/package-deps.json": "00c23f21d7977f38a942ed06f4812e5ddc2b6248",
34
- "libraries/data-ohlc/temp/package-deps.json": "919692801e80ce34e9af7470712ac4d1c0086af7",
35
- "libraries/data-order/temp/package-deps.json": "e89dd8f11cdbe84eb8bdf474105abccdf51abfaa",
36
- "libraries/data-interest-rate/temp/package-deps.json": "5f955d9b9c04936964ba20a37d5e00816fb33f67",
37
- "libraries/transfer/temp/package-deps.json": "266da813de0fc7b8de0acb4f1a561bd5cab7f294",
38
- "libraries/data-quote/temp/package-deps.json": "6ee7c2a4c28dd7947039fd9561ff045400848bde",
39
- "libraries/exchange/temp/package-deps.json": "a254260f066adc3e196d020f7eae7354db045404",
40
- "libraries/extension/temp/package-deps.json": "89c372a6eab7dfe766edc0b102492364aa8684fe",
27
+ "libraries/protocol/temp/package-deps.json": "ad2b09da06aa0b6d98e9ca145c3da7c8418f30d1",
28
+ "libraries/cache/temp/package-deps.json": "8faf993aa54072a8cc7773ca9502618ec1660be1",
29
+ "libraries/data-account/temp/package-deps.json": "ec07276e124d707820e348948c8689cd4b4d5a84",
30
+ "libraries/utils/temp/package-deps.json": "6afd4e6b9f8348c0c24e05f7289101fc6b8fa5a9",
31
+ "libraries/data-series/temp/package-deps.json": "6744fc1995bb77846daccced427210187b5e774f",
32
+ "libraries/sql/temp/package-deps.json": "1fb8bcfa1bd3912150e7567770b065794a3b2138",
33
+ "libraries/data-product/temp/package-deps.json": "77b545e27fbd147bf5fded94beb5baaa33687563",
34
+ "libraries/data-ohlc/temp/package-deps.json": "f6a0a2248e237fe960daf27c93071ec1c3235fa3",
35
+ "libraries/data-order/temp/package-deps.json": "d2826719d6e1f86ad64cfe6556a0f61352e02897",
36
+ "libraries/data-interest-rate/temp/package-deps.json": "d1a26e2ed8d3ecd147e0b3a897927720d7760597",
37
+ "libraries/transfer/temp/package-deps.json": "52cd9bb451ece058b39c7a7b08dd3fef1a57b934",
38
+ "libraries/data-quote/temp/package-deps.json": "5aa2671e9a9e7b94827a8891049c4af5eefe757e",
39
+ "libraries/exchange/temp/package-deps.json": "8315951811f0278801081fe8e48ba07213af6816",
40
+ "libraries/extension/temp/package-deps.json": "d0a314f466619661a85b5fa9cc12ffe0b427864b",
41
41
  "tools/toolkit/temp/package-deps.json": "23e053490eb8feade23e4d45de4e54883e322711"
42
42
  }