From 66151d78a0d7a9dd2a22fdc267eb2e3fa0a2561a Mon Sep 17 00:00:00 2001 From: clz Date: Fri, 24 Jul 2026 13:58:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor(hq):=20=E6=8A=BD=E7=A6=BB=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E8=A1=8C=E6=83=85=E8=AE=A2=E9=98=85=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/market-ranking-interfaces.md | 11 +- .../main/ets/ai-pick/AiPickDataFetcher.ets | 130 +---------- .../ets/ai-pick/AiPickHqRequestClient.ets | 158 +++++++++++++ entry/src/main/ets/ai-pick/AiPickModels.ets | 4 +- .../main/ets/ai-pick/AiPickNodeComponent.ets | 25 ++- entry/src/main/ets/common/HqFieldId.ets | 21 ++ .../market-ranking/MarketRankingConstant.ets | 23 +- .../MarketRankingDataFetcher.ets | 173 +-------------- .../MarketRankingHqRequestClient.ets | 210 ++++++++++++++++++ .../market-ranking/MarketRankingModels.ets | 56 +++-- .../MarketRankingNodeComponent.ets | 23 +- 11 files changed, 469 insertions(+), 365 deletions(-) create mode 100644 entry/src/main/ets/ai-pick/AiPickHqRequestClient.ets create mode 100644 entry/src/main/ets/common/HqFieldId.ets create mode 100644 entry/src/main/ets/market-ranking/MarketRankingHqRequestClient.ets diff --git a/doc/market-ranking-interfaces.md b/doc/market-ranking-interfaces.md index 5736a12..31757d7 100644 --- a/doc/market-ranking-interfaces.md +++ b/doc/market-ranking-interfaces.md @@ -1,7 +1,7 @@ # 市场排名接口文档 -本文记录 `MarketRankingNodeComponent` 卡片使用的合约榜单 HTTP 接口及 4106 行情订阅协议。当前实现位于 -`entry/src/main/ets/market-ranking/MarketRankingDataFetcher.ets`。 +本文记录 `MarketRankingNodeComponent` 卡片使用的合约榜单 HTTP 接口及 4106 行情订阅协议。HTTP +实现位于 `MarketRankingDataFetcher.ets`,行情实现位于 `MarketRankingHqRequestClient.ets`。 ## 合约榜单 HTTP 接口 @@ -93,12 +93,13 @@ GET {API_HOST}futgwapi/api/market/homepage/v1/recommend_futures | `chg_speed_10min` | 34875 | 10 分钟涨跌速 | | `chg_speed_15min` | 34876 | 15 分钟涨跌速 | -原始推送为“字段 ID → 数组”的分列结构,相同数组下标表示同一合约。Fetcher 将其合并为 -`RankItem[]`,再通过完整替换 `CardData` 刷新 UI。 +鸿蒙原始推送为 `TableData` 行式结构。`tableRows` 中每个 Row 代表一份合约行情,通过 +`row.get(字段 ID)` 读取对应值。`MarketRankingHqRequestClient` 将其转换为 `RankItem[]`,再通过 +完整替换 `CardData` 刷新 UI。 ## 当前 Mock 行为 - HTTP:使用 `@ohos.net.http` 构造 GET URL 和请求选项,但不调用 `request()`。 -- 行情:构造并输出完整 4106 参数,不连接真实行情桥。 +- 行情:构造并输出完整 4106 参数,不连接真实行情桥;Mock 使用 `tableRows + row.get(id)` 模拟回调。 - Mock 行情每 `2500ms` 推送一次,与 `pushtime=2.5` 保持一致。 - 每次请求及订阅参数均使用 `[MarketRanking]` 前缀输出到控制台。 diff --git a/entry/src/main/ets/ai-pick/AiPickDataFetcher.ets b/entry/src/main/ets/ai-pick/AiPickDataFetcher.ets index 0fb4297..9798dd2 100644 --- a/entry/src/main/ets/ai-pick/AiPickDataFetcher.ets +++ b/entry/src/main/ets/ai-pick/AiPickDataFetcher.ets @@ -1,10 +1,7 @@ -// AI选期数据层:负责网络请求、行情订阅及原始数据到视图数据的转换。 +// AI 选期数据层:负责网络请求及原始数据到视图数据的转换。 import http from '@ohos.net.http'; import allCardsMock from '../common/mock/AllCardsMock.json'; import { - AiPickQuoteData, - AiPickQuoteRequestParams, - CardData, PickContractItem, PickStrategy, PickTabData, @@ -24,7 +21,6 @@ import { MAX_ITEMS, MAX_TABS, MAX_TAGS } from './AiPickConstant'; // 模拟网络延迟(毫秒),验证 loading 状态和异步流程用;接入真实接口后删除 const MOCK_DELAY_MS = 300; -const MOCK_QUOTE_INTERVAL_MS = 2500; const EMPTY_RAW_STRATEGY_ITEM: RawStrategyItem = {}; const EMPTY_RAW_STRATEGY_DETAIL: RawStrategyDetail = { id: '', @@ -32,8 +28,6 @@ const EMPTY_RAW_STRATEGY_DETAIL: RawStrategyDetail = { strategy_type: '', }; -export type AiPickCardDataListener = (cardData: CardData) => void; - // 对应 Vue 端 index.vue 中 formatTabData 的单项处理(新版 isCustomVersion=true) function parseRawTab(item: RawPickTabItem): PickTabData { const isSystemPeriod = item.type === 'system_period'; @@ -143,128 +137,6 @@ export class AiPickDataFetcher { }); } - // 模拟 Vue 的共享 4106 行情订阅:立即推送一次,之后每 2.5 秒整体更新 CardData。 - subscribeQuotes(cardData: CardData, listener: AiPickCardDataListener): () => void { - const requestParams = this.buildQuoteRequestParams(cardData); - console.info(`[AiPick] subscribeQuotes params: ${JSON.stringify(requestParams)}`); - const contracts = this.getUniqueContracts(cardData); - let tick = 0; - const emit = (): void => { - tick += 1; - const quoteData = this.createMockQuoteData(contracts, tick); - listener(this.mergeQuoteData(cardData, quoteData)); - }; - emit(); - const timer = setInterval(emit, MOCK_QUOTE_INTERVAL_MS); - return (): void => { - clearInterval(timer); - }; - } - - // 对齐 Vue buildHqParams,生成 AI 选期共享行情连接的完整请求参数。 - private buildQuoteRequestParams(cardData: CardData): AiPickQuoteRequestParams { - const dataItems: string[] = ['55', '10', '4', '36103', '34818', '6', '7', '66', '34877']; - const requestDic = `sortid=-1\r\n` + - `sortorder=0\r\n` + - `push=1\r\n` + - `dataitem=${dataItems.map((item: string): string => `${item},`).join('')}\r\n` + - `codelist=${this.formatCodeList(cardData)}\r\n` + - `scenario=qht_qihuo_sort\r\n` + - `precision=1\r\n` + - `pushtime=2.5\r\n`; - return { - protocolId: '4106', - pageId: '2201', - onlineId: 'contractHQData', - columnOrder: dataItems.join('|'), - requestDic: requestDic, - }; - } - - private getUniqueContracts(cardData: CardData): PickContractItem[] { - const contracts: PickContractItem[] = []; - const contractKeys: string[] = []; - cardData.tabs.forEach((tab: PickTabData): void => { - tab.items.forEach((item: PickContractItem): void => { - const contractKey = `${item.contract}_${item.market}`; - if (!contractKeys.includes(contractKey)) { - contractKeys.push(contractKey); - contracts.push(item); - } - }); - }); - return contracts; - } - - private formatCodeList(cardData: CardData): string { - const contracts = this.getUniqueContracts(cardData); - const markets: string[] = []; - const codeGroups: string[][] = []; - contracts.forEach((contract: PickContractItem): void => { - const marketIndex = markets.indexOf(contract.market); - if (marketIndex >= 0) { - codeGroups[marketIndex].push(contract.contract); - } else { - markets.push(contract.market); - codeGroups.push([contract.contract]); - } - }); - return markets.map((market: string, index: number): string => - `${market}(${codeGroups[index].map((code: string): string => `${code},`).join('')});` - ).join(''); - } - - private createMockQuoteData(contracts: PickContractItem[], tick: number): AiPickQuoteData { - const data: AiPickQuoteData = { - code: [], - market: [], - price: [], - priceChg: [], - }; - contracts.forEach((contract: PickContractItem, index: number): void => { - data.code.push(contract.contract); - data.market.push(contract.market); - data.price.push(100 + index * 10 + tick * 0.1); - data.priceChg.push(((tick + index) % 5 - 2) * 0.35); - }); - return data; - } - - private mergeQuoteData(cardData: CardData, quoteData: AiPickQuoteData): CardData { - const tabs = cardData.tabs.map((tab: PickTabData): PickTabData => { - const items = tab.items.map((item: PickContractItem): PickContractItem => { - let quoteIndex = -1; - for (let index = 0; index < quoteData.code.length; index += 1) { - if (quoteData.code[index] === item.contract && quoteData.market[index] === item.market) { - quoteIndex = index; - break; - } - } - const nextItem: PickContractItem = { - contract: item.contract, - market: item.market, - name: item.name, - price: quoteIndex >= 0 ? quoteData.price[quoteIndex] : undefined, - priceChg: quoteIndex >= 0 ? quoteData.priceChg[quoteIndex] : undefined, - isMain: item.isMain, - extraFields: item.extraFields, - }; - return nextItem; - }); - const nextTab: PickTabData = { - strategy: tab.strategy, - items: items, - keysList: tab.keysList, - fieldList: tab.fieldList, - }; - return nextTab; - }); - const nextCardData: CardData = { - tabs: tabs, - }; - return nextCardData; - } - private buildRequestUrl(configuredUrl: string, timestamp: number): string { const separator = configuredUrl.includes('?') ? '&' : '?'; return `${configuredUrl}${separator}_=${timestamp}`; diff --git a/entry/src/main/ets/ai-pick/AiPickHqRequestClient.ets b/entry/src/main/ets/ai-pick/AiPickHqRequestClient.ets new file mode 100644 index 0000000..dca2127 --- /dev/null +++ b/entry/src/main/ets/ai-pick/AiPickHqRequestClient.ets @@ -0,0 +1,158 @@ +import { + AiPickHqData, + AiPickHqRequestParams, + CardData, + PickContractItem, + PickTabData, +} from './AiPickModels'; +import { HqFieldId } from '../common/HqFieldId'; + +const MOCK_QUOTE_INTERVAL_MS = 2500; + +export type AiPickCardDataListener = (cardData: CardData) => void; + +// AI 选期行情请求客户端:负责合约去重、4106 参数、订阅和行情数据合并。 +export class AiPickHqRequestClient { + private static readonly instance: AiPickHqRequestClient = new AiPickHqRequestClient(); + + private constructor() { + } + + static getInstance(): AiPickHqRequestClient { + return AiPickHqRequestClient.instance; + } + + // 模拟共享 4106 行情订阅:立即推送一次,之后每 2.5 秒更新完整卡片数据。 + subscribeHq(cardData: CardData, listener: AiPickCardDataListener): () => void { + const requestParams = this.buildHqRequestParams(cardData); + console.info(`[AiPick] subscribeHq params: ${JSON.stringify(requestParams)}`); + // todo: 接入真实的行情订阅。 + const contracts = this.getUniqueContracts(cardData); + let tick = 0; + const emit = (): void => { + tick += 1; + const hqData = this.createMockHqData(contracts, tick); + listener(this.mergeHqData(cardData, hqData)); + }; + + emit(); + const timer = setInterval(emit, MOCK_QUOTE_INTERVAL_MS); + return (): void => { + clearInterval(timer); + }; + } + + // 对齐 Vue buildHqParams,生成 AI 选期共享行情连接的完整请求参数。 + buildHqRequestParams(cardData: CardData): AiPickHqRequestParams { + const dataItems: number[] = [ + HqFieldId.DATA_ID_NAME, + HqFieldId.DATA_ID_PRICE, + HqFieldId.DATA_ID_CODE, + HqFieldId.DATA_ID_MARKET, + HqFieldId.DATA_ID_PRICE_CHG, + HqFieldId.DATA_ID_PRE_PRICE, + HqFieldId.DATA_ID_OPEN_PRICE, + HqFieldId.DATA_ID_PRE_SETTLE_PRICE, + HqFieldId.DATA_ID_PRICE_CHG_YTD, + ]; + const requestDic = `sortid=-1\r\n` + + `sortorder=0\r\n` + + `push=1\r\n` + + `dataitem=${dataItems.map((item: number): string => `${item},`).join('')}\r\n` + + `codelist=${this.formatCodeList(cardData)}\r\n` + + `scenario=qht_qihuo_sort\r\n` + + `precision=1\r\n` + + `pushtime=2.5\r\n`; + return { + protocolId: '4106', + pageId: '2201', + onlineId: 'contractHQData', + columnOrder: dataItems.join('|'), + requestDic: requestDic, + }; + } + + private getUniqueContracts(cardData: CardData): PickContractItem[] { + const contracts: PickContractItem[] = []; + const contractKeys: string[] = []; + cardData.tabs.forEach((tab: PickTabData): void => { + tab.items.forEach((item: PickContractItem): void => { + const contractKey = `${item.contract}_${item.market}`; + if (!contractKeys.includes(contractKey)) { + contractKeys.push(contractKey); + contracts.push(item); + } + }); + }); + return contracts; + } + + private formatCodeList(cardData: CardData): string { + const contracts = this.getUniqueContracts(cardData); + const markets: string[] = []; + const codeGroups: string[][] = []; + contracts.forEach((contract: PickContractItem): void => { + const marketIndex = markets.indexOf(contract.market); + if (marketIndex >= 0) { + codeGroups[marketIndex].push(contract.contract); + } else { + markets.push(contract.market); + codeGroups.push([contract.contract]); + } + }); + return markets.map((market: string, index: number): string => + `${market}(${codeGroups[index].map((code: string): string => `${code},`).join('')});` + ).join(''); + } + + private createMockHqData(contracts: PickContractItem[], tick: number): AiPickHqData { + const data: AiPickHqData = { + code: [], + market: [], + price: [], + priceChg: [], + }; + contracts.forEach((contract: PickContractItem, index: number): void => { + data.code.push(contract.contract); + data.market.push(contract.market); + data.price.push(100 + index * 10 + tick * 0.1); + data.priceChg.push(((tick + index) % 5 - 2) * 0.35); + }); + return data; + } + + private mergeHqData(cardData: CardData, hqData: AiPickHqData): CardData { + const tabs = cardData.tabs.map((tab: PickTabData): PickTabData => { + const items = tab.items.map((item: PickContractItem): PickContractItem => { + let hqIndex = -1; + for (let index = 0; index < hqData.code.length; index += 1) { + if (hqData.code[index] === item.contract && hqData.market[index] === item.market) { + hqIndex = index; + break; + } + } + const nextItem: PickContractItem = { + contract: item.contract, + market: item.market, + name: item.name, + price: hqIndex >= 0 ? hqData.price[hqIndex] : undefined, + priceChg: hqIndex >= 0 ? hqData.priceChg[hqIndex] : undefined, + isMain: item.isMain, + extraFields: item.extraFields, + }; + return nextItem; + }); + const nextTab: PickTabData = { + strategy: tab.strategy, + items: items, + keysList: tab.keysList, + fieldList: tab.fieldList, + }; + return nextTab; + }); + const nextCardData: CardData = { + tabs: tabs, + }; + return nextCardData; + } +} diff --git a/entry/src/main/ets/ai-pick/AiPickModels.ets b/entry/src/main/ets/ai-pick/AiPickModels.ets index ca1186d..270a54c 100644 --- a/entry/src/main/ets/ai-pick/AiPickModels.ets +++ b/entry/src/main/ets/ai-pick/AiPickModels.ets @@ -39,14 +39,14 @@ export interface CardData { tabs: PickTabData[]; } -export interface AiPickQuoteData { +export interface AiPickHqData { code: string[]; market: string[]; price: number[]; priceChg: number[]; } -export interface AiPickQuoteRequestParams { +export interface AiPickHqRequestParams { protocolId: string; pageId: string; onlineId: string; diff --git a/entry/src/main/ets/ai-pick/AiPickNodeComponent.ets b/entry/src/main/ets/ai-pick/AiPickNodeComponent.ets index 80d0ad0..852748a 100644 --- a/entry/src/main/ets/ai-pick/AiPickNodeComponent.ets +++ b/entry/src/main/ets/ai-pick/AiPickNodeComponent.ets @@ -7,6 +7,7 @@ import { } from './AiPickModels'; import { AllCardsCard } from '../common/AllCardsModels'; import { AiPickDataFetcher } from './AiPickDataFetcher'; +import { AiPickHqRequestClient } from './AiPickHqRequestClient'; import { cellColor, cellValue, formatColName } from './AiPickUtils'; import { EMPTY_IMAGE_DARK, @@ -34,7 +35,7 @@ export struct AiPickNodeComponent { @State tabContentWidth: number = 0; @State tabScrollOffset: number = 0; @State showExplainSheet: boolean = false; - private quoteUnsubscribe: (() => void) | undefined; + private hqUnsubscribe: (() => void) | undefined; private showTabGradient(): boolean { const maxOffset = this.tabContentWidth - this.tabViewportWidth; @@ -49,7 +50,7 @@ export struct AiPickNodeComponent { } aboutToDisappear(): void { - this.stopSubscribeQuotes(); + this.stopSubscribeHq(); } // 只负责从公共 Mock 获取并更新卡片配置。 @@ -59,25 +60,25 @@ export struct AiPickNodeComponent { // 只负责请求并更新卡片展示数据。 private async updateCardData(): Promise { - this.stopSubscribeQuotes(); + this.stopSubscribeHq(); this.cardData = { tabs: [] }; const fetcher = AiPickDataFetcher.getInstance(); try { const tabs = await fetcher.fetchCardData(); this.cardData = { tabs }; if (tabs.length > 0) { - this.subscribeQuotes(); + this.subscribeHq(); } } catch { - this.stopSubscribeQuotes(); + this.stopSubscribeHq(); this.cardData = { tabs: [] }; } } // 使用当前 CardData 的全部 Tab 合约订阅行情,推送后整体更新 CardData。 - private subscribeQuotes(): void { - this.stopSubscribeQuotes(); - this.quoteUnsubscribe = AiPickDataFetcher.getInstance().subscribeQuotes( + private subscribeHq(): void { + this.stopSubscribeHq(); + this.hqUnsubscribe = AiPickHqRequestClient.getInstance().subscribeHq( this.cardData, (cardData: CardData): void => { this.cardData = cardData; @@ -85,10 +86,10 @@ export struct AiPickNodeComponent { ); } - private stopSubscribeQuotes(): void { - if (this.quoteUnsubscribe !== undefined) { - this.quoteUnsubscribe(); - this.quoteUnsubscribe = undefined; + private stopSubscribeHq(): void { + if (this.hqUnsubscribe !== undefined) { + this.hqUnsubscribe(); + this.hqUnsubscribe = undefined; } } diff --git a/entry/src/main/ets/common/HqFieldId.ets b/entry/src/main/ets/common/HqFieldId.ets new file mode 100644 index 0000000..979c168 --- /dev/null +++ b/entry/src/main/ets/common/HqFieldId.ets @@ -0,0 +1,21 @@ +// 4106 行情协议通用字段 ID。 +export class HqFieldId { + static readonly DATA_ID_CODE: number = 4; // 合约代码 + static readonly DATA_ID_PRE_PRICE: number = 6; // 昨收价 + static readonly DATA_ID_OPEN_PRICE: number = 7; // 今日开盘价 + static readonly DATA_ID_PRICE: number = 10; // 最新价 + static readonly DATA_ID_TURNOVER: number = 19; // 成交额 + static readonly DATA_ID_NAME: number = 55; // 合约名称 + static readonly DATA_ID_PRE_SETTLE_PRICE: number = 66; // 昨日结算价 + static readonly DATA_ID_CHG_SPEED_5MIN: number = 34325; // 5 分钟涨跌速 + static readonly DATA_ID_INCRE_POSIT: number = 34355; // 日增仓 + static readonly DATA_ID_PRICE_CHG: number = 34818; // 涨跌幅 + static readonly DATA_ID_CHG_SPEED_1MIN: number = 34874; // 1 分钟涨跌速 + static readonly DATA_ID_CHG_SPEED_10MIN: number = 34875; // 10 分钟涨跌速 + static readonly DATA_ID_CHG_SPEED_15MIN: number = 34876; // 15 分钟涨跌速 + static readonly DATA_ID_PRICE_CHG_YTD: number = 34877; // 昨收涨跌幅 + static readonly DATA_ID_MARKET: number = 36103; // 市场 ID + + private constructor() { + } +} diff --git a/entry/src/main/ets/market-ranking/MarketRankingConstant.ets b/entry/src/main/ets/market-ranking/MarketRankingConstant.ets index 2c902af..07ef82d 100644 --- a/entry/src/main/ets/market-ranking/MarketRankingConstant.ets +++ b/entry/src/main/ets/market-ranking/MarketRankingConstant.ets @@ -1,4 +1,5 @@ import { PeriodRange, TabMetric } from './MarketRankingModels'; +import { HqFieldId } from '../common/HqFieldId'; export const TITLE_ARROW_LIGHT = 'https://s.thsi.cn/staticS3/mobileweb-upload-static-server.img/offline/pkg/e6b471c4-b9a6-4c73-a2e4-7c3176308daf.png'; @@ -9,20 +10,20 @@ export const INFO_IMAGE_LIGHT = export const INFO_IMAGE_DARK = 'https://u.thsi.cn/imgsrc/bbs/19f7f20bcf800ec2b8e9fc0fa0d73369.png'; -// 一级 Tab 配置:涨幅/跌幅/涨速/跌速/成交额/日增仓,hqId/hqName 用于后续接入行情推送 +// 一级 Tab 配置:涨幅/跌幅/涨速/跌速/成交额/日增仓。 export const TAB_METRICS: TabMetric[] = [ - { id: 'rise', label: $r('app.string.market_ranking_tab_rise'), stat: 'rise', api: 'rise_percent', hqId: '34818', hqName: 'price_chg', index: 0, hasChildren: false }, - { id: 'fall', label: $r('app.string.market_ranking_tab_fall'), stat: 'fall', api: 'fall_percent', hqId: '34818', hqName: 'price_chg', index: 1, hasChildren: false }, - { id: 'riseSpeed', label: $r('app.string.market_ranking_tab_rise_speed'), stat: 'riseSpeed', api: 'rise_percent', hqId: '34821', hqName: 'chg_speed', index: 2, hasChildren: true }, - { id: 'fallSpeed', label: $r('app.string.market_ranking_tab_fall_speed'), stat: 'fallSpeed', api: 'fall_percent', hqId: '34821', hqName: 'chg_speed', index: 3, hasChildren: true }, - { id: 'turnOver', label: $r('app.string.market_ranking_tab_turnover'), stat: 'turnOver', api: 'turnover', hqId: '19', hqName: 'turnover', index: 4, hasChildren: false }, - { id: 'increPosit', label: $r('app.string.market_ranking_tab_increase_position'), stat: 'increPosit', api: 'increase_position', hqId: '34355', hqName: 'incre_posit', index: 5, hasChildren: false }, + { id: 'rise', label: $r('app.string.market_ranking_tab_rise'), api: 'rise_percent', hqId: HqFieldId.DATA_ID_PRICE_CHG, hasChildren: false }, + { id: 'fall', label: $r('app.string.market_ranking_tab_fall'), api: 'fall_percent', hqId: HqFieldId.DATA_ID_PRICE_CHG, hasChildren: false }, + { id: 'riseSpeed', label: $r('app.string.market_ranking_tab_rise_speed'), api: 'rise_percent', hqId: HqFieldId.DATA_ID_CHG_SPEED_1MIN, hasChildren: true }, + { id: 'fallSpeed', label: $r('app.string.market_ranking_tab_fall_speed'), api: 'fall_percent', hqId: HqFieldId.DATA_ID_CHG_SPEED_1MIN, hasChildren: true }, + { id: 'turnOver', label: $r('app.string.market_ranking_tab_turnover'), api: 'turnover', hqId: HqFieldId.DATA_ID_TURNOVER, hasChildren: false }, + { id: 'increPosit', label: $r('app.string.market_ranking_tab_increase_position'), api: 'increase_position', hqId: HqFieldId.DATA_ID_INCRE_POSIT, hasChildren: false }, ]; // 二级 Tab 配置:仅涨速/跌速下出现,按周期筛选 export const PERIOD_RANGES: PeriodRange[] = [ - { id: '1min', label: $r('app.string.market_ranking_period_1min'), stat: 'min1', api: 'one_minute', hqId: '34874', index: 0 }, - { id: '5min', label: $r('app.string.market_ranking_period_5min'), stat: 'min5', api: 'five_minute', hqId: '34325', index: 1 }, - { id: '10min', label: $r('app.string.market_ranking_period_10min'), stat: 'min10', api: 'ten_minute', hqId: '34875', index: 2 }, - { id: '15min', label: $r('app.string.market_ranking_period_15min'), stat: 'min15', api: 'fifteen_minute', hqId: '34876', index: 3 }, + { id: '1min', label: $r('app.string.market_ranking_period_1min'), api: 'one_minute', hqId: HqFieldId.DATA_ID_CHG_SPEED_1MIN }, + { id: '5min', label: $r('app.string.market_ranking_period_5min'), api: 'five_minute', hqId: HqFieldId.DATA_ID_CHG_SPEED_5MIN }, + { id: '10min', label: $r('app.string.market_ranking_period_10min'), api: 'ten_minute', hqId: HqFieldId.DATA_ID_CHG_SPEED_10MIN }, + { id: '15min', label: $r('app.string.market_ranking_period_15min'), api: 'fifteen_minute', hqId: HqFieldId.DATA_ID_CHG_SPEED_15MIN }, ]; diff --git a/entry/src/main/ets/market-ranking/MarketRankingDataFetcher.ets b/entry/src/main/ets/market-ranking/MarketRankingDataFetcher.ets index 9c3a6c3..adc34b4 100644 --- a/entry/src/main/ets/market-ranking/MarketRankingDataFetcher.ets +++ b/entry/src/main/ets/market-ranking/MarketRankingDataFetcher.ets @@ -7,9 +7,6 @@ import { AllCardsResponse, } from '../common/AllCardsModels'; import { - CardData, - MarketRankingQuoteData, - MarketRankingQuoteRequestParams, RankItem, RecommendFuturesItem, } from './MarketRankingModels'; @@ -17,14 +14,11 @@ import { MOCK_RANK_LISTS, MOCK_SPEED_LISTS } from './MarketRankingMock'; import { PERIOD_RANGES, TAB_METRICS } from './MarketRankingConstant'; const MOCK_REQUEST_DELAY_MS = 300; -const MOCK_QUOTE_INTERVAL_MS = 2500; const TEST_API_HOST = 'https://futures-test.10jqka.com.cn/'; const RELEASE_API_HOST = 'https://ftapi.10jqka.com.cn/'; const RECOMMEND_FUTURES_API_PATH = 'futgwapi/api/market/homepage/v1/recommend_futures'; const RECOMMEND_FUTURES_NUMBER = 50; -export type MarketRankingCardDataListener = (cardData: CardData) => void; - export class MarketRankingDataFetcher { private static readonly instance: MarketRankingDataFetcher = new MarketRankingDataFetcher(); @@ -47,14 +41,8 @@ export class MarketRankingDataFetcher { return undefined; } - private findMarketRankingCard(floor: AllCardsFloor): AllCardsCard | undefined { - return floor.card_list.find((card: AllCardsCard): boolean => - card.render_key === 'market-ranking' - ); - } - /** - * 市场排名合约列表接口(当前暂用 Mock,后续只替换本方法内部实现): + * 市场排名合约列表接口(todo: 当前暂用 Mock,后续只替换本方法内部实现): * GET {API_HOST}futgwapi/api/market/homepage/v1/recommend_futures * dev/test API_HOST: https://futures-test.10jqka.com.cn/ * release API_HOST: https://ftapi.10jqka.com.cn/ @@ -97,37 +85,18 @@ export class MarketRankingDataFetcher { }); } + private findMarketRankingCard(floor: AllCardsFloor): AllCardsCard | undefined { + return floor.card_list.find((card: AllCardsCard): boolean => + card.render_key === 'market-ranking' + ); + } + private buildRecommendFuturesUrl(quoteType: string): string { const apiHost = BuildProfile.DEBUG ? TEST_API_HOST : RELEASE_API_HOST; return `${apiHost}${RECOMMEND_FUTURES_API_PATH}` + `?quote_type=${encodeURIComponent(quoteType)}&number=${RECOMMEND_FUTURES_NUMBER}`; } - // 模拟行情订阅:先生成原始推送数据并完成合并,再返回完整 CardData。 - subscribeQuotes( - contracts: RecommendFuturesItem[], - primaryIdx: number, - secondaryIdx: number, - listener: MarketRankingCardDataListener, - ): () => void { - const requestParams = this.buildQuoteRequestParams(contracts, primaryIdx, secondaryIdx); - console.info(`[MarketRanking] subscribeQuotes params: ${JSON.stringify(requestParams)}`); - // todo: 接入真实的行情订阅 - const quoteType = this.getQuoteType(primaryIdx, secondaryIdx); - let tick = 0; - const emit = (): void => { - tick += 1; - const quoteData = this.createMockQuoteData(contracts, quoteType, tick); - listener(this.mergeQuoteData(quoteData)); - }; - - emit(); - const timer = setInterval(emit, MOCK_QUOTE_INTERVAL_MS); - return (): void => { - clearInterval(timer); - }; - } - private getQuoteType(primaryIdx: number, secondaryIdx: number): string { const metric = TAB_METRICS[primaryIdx]; if (metric.hasChildren) { @@ -136,134 +105,6 @@ export class MarketRankingDataFetcher { return metric.api; } - // 对齐 Vue generateHqParam,生成 4106 行情订阅所需的完整请求参数。 - buildQuoteRequestParams( - contracts: RecommendFuturesItem[], - primaryIdx: number, - secondaryIdx: number, - ): MarketRankingQuoteRequestParams { - const metric = TAB_METRICS[primaryIdx]; - const sortId = metric.hasChildren ? PERIOD_RANGES[secondaryIdx].hqId : metric.hqId; - const sortOrder = metric.id === 'fall' || metric.id === 'fallSpeed' ? '1' : '0'; - const dataItems: string[] = ['55', '10', '4', '36103', '34818', '6', '7', '66']; - if (!dataItems.includes(sortId)) { - dataItems.push(sortId); - } - const columnOrder = dataItems.join('|'); - const requestDic = `sortid=${sortId}\r\n` + - `sortorder=${sortOrder}\r\n` + - `push=1\r\n` + - `dataitem=${dataItems.map((item: string): string => `${item},`).join('')}\r\n` + - `codelist=${this.formatCodeList(contracts)}\r\n` + - `scenario=qht_qihuo_sort\r\n` + - `precision=1\r\n` + - `pushtime=2.5\r\n`; - return { - protocolId: '4106', - // HarmonyOS 当前沿用 Android 行情页面标识。 - pageId: '2201', - onlineId: 'marketRankingData', - columnOrder, - requestDic, - }; - } - - // 按市场拼成 4106 所需格式,例如 70(CU2501,RB2505,);。 - private formatCodeList(contracts: RecommendFuturesItem[]): string { - const markets: string[] = []; - const codeGroups: string[][] = []; - contracts.forEach((contract: RecommendFuturesItem): void => { - const marketIndex = markets.indexOf(contract.market); - if (marketIndex >= 0) { - codeGroups[marketIndex].push(contract.contract_code); - } else { - markets.push(contract.market); - codeGroups.push([contract.contract_code]); - } - }); - return markets.map((market: string, index: number): string => - `${market}(${codeGroups[index].map((code: string): string => `${code},`).join('')});` - ).join(''); - } - - // 模拟 4106 行情接口原始分列数据;非当前 Tab 请求的扩展字段返回空数组。 - private createMockQuoteData( - contracts: RecommendFuturesItem[], - quoteType: string, - tick: number, - ): MarketRankingQuoteData { - const data: MarketRankingQuoteData = { - code: [], - market: [], - name: [], - price: [], - price_chg: [], - pre_price: [], - open_price: [], - pre_settle_price: [], - turnover: [], - incre_posit: [], - chg_speed_1min: [], - chg_speed_5min: [], - chg_speed_10min: [], - chg_speed_15min: [], - }; - contracts.forEach((contract: RecommendFuturesItem, index: number): void => { - const price = 100 + index * 10 + tick * 0.1; - const priceChg = ((tick + index) % 5 - 2) * 0.35; - data.code.push(contract.contract_code); - data.market.push(contract.market); - data.name.push(contract.contract_name); - data.price.push(price); - data.price_chg.push(priceChg); - data.pre_price.push(price - 0.5); - data.open_price.push(price - 0.2); - data.pre_settle_price.push(price - 0.4); - if (quoteType === 'turnover') { - data.turnover.push(100000 + index * 5000 + tick * 100); - } - if (quoteType === 'increase_position') { - data.incre_posit.push(1000 + index * 100 + tick * 10); - } - const speed = priceChg / 2; - if (quoteType === 'one_minute_rise_percent' || quoteType === 'one_minute_fall_percent') { - data.chg_speed_1min.push(speed); - } else if (quoteType === 'five_minute_rise_percent' || quoteType === 'five_minute_fall_percent') { - data.chg_speed_5min.push(speed); - } else if (quoteType === 'ten_minute_rise_percent' || quoteType === 'ten_minute_fall_percent') { - data.chg_speed_10min.push(speed); - } else if (quoteType === 'fifteen_minute_rise_percent' || quoteType === 'fifteen_minute_fall_percent') { - data.chg_speed_15min.push(speed); - } - }); - return data; - } - - // 对齐 Vue handleSpeedHqRes/handleNormalHqRes:按数组下标合并为 View 使用的行数据。 - private mergeQuoteData(data: MarketRankingQuoteData): CardData { - let speedList: number[] = data.chg_speed_1min; - if (speedList.length === 0) { - speedList = data.chg_speed_5min; - } - if (speedList.length === 0) { - speedList = data.chg_speed_10min; - } - if (speedList.length === 0) { - speedList = data.chg_speed_15min; - } - const tableList = data.code.map((code: string, index: number): RankItem => ({ - code, - market: data.market[index], - name: data.name[index], - price: data.price[index], - price_chg: data.price_chg[index], - chg_speed: speedList[index], - turnover: data.turnover[index], - incre_posit: data.incre_posit[index], - })); - return { tableList }; - } - private mockRankList(quoteType: string): RankItem[] { switch (quoteType) { case 'rise_percent': diff --git a/entry/src/main/ets/market-ranking/MarketRankingHqRequestClient.ets b/entry/src/main/ets/market-ranking/MarketRankingHqRequestClient.ets new file mode 100644 index 0000000..2f3bd60 --- /dev/null +++ b/entry/src/main/ets/market-ranking/MarketRankingHqRequestClient.ets @@ -0,0 +1,210 @@ +import { + CardData, + MarketRankingHqValue, + MarketRankingHqRequestParams, + MarketRankingTableData, + MarketRankingTableRow, + RankItem, + RecommendFuturesItem, +} from './MarketRankingModels'; +import { + PERIOD_RANGES, + TAB_METRICS, +} from './MarketRankingConstant'; +import { HqFieldId } from '../common/HqFieldId'; + +const MOCK_QUOTE_INTERVAL_MS = 2500; + +export type MarketRankingCardDataListener = (cardData: CardData) => void; + +// 市场排名行情请求客户端:负责构造 4106 参数、管理订阅及转换行情数据。 +export class MarketRankingHqRequestClient { + private static readonly instance: MarketRankingHqRequestClient = + new MarketRankingHqRequestClient(); + + private constructor() { + } + + static getInstance(): MarketRankingHqRequestClient { + return MarketRankingHqRequestClient.instance; + } + + // 模拟行情订阅:先推送一次,之后每 2.5 秒更新完整卡片数据。 + subscribeHq( + contracts: RecommendFuturesItem[], + primaryIdx: number, + secondaryIdx: number, + listener: MarketRankingCardDataListener, + ): () => void { + const requestParams = this.buildHqRequestParams(contracts, primaryIdx, secondaryIdx); + console.info(`[MarketRanking] subscribeHq params: ${JSON.stringify(requestParams)}`); + // todo: 接入真实的行情订阅。 + const quoteType = this.getQuoteType(primaryIdx, secondaryIdx); + let tick = 0; + const emit = (): void => { + tick += 1; + const tableData = this.createMockTableData(contracts, quoteType, tick); + listener(this.mergeTableData(tableData, this.getSortId(primaryIdx, secondaryIdx))); + }; + + emit(); + const timer = setInterval(emit, MOCK_QUOTE_INTERVAL_MS); + return (): void => { + clearInterval(timer); + }; + } + + // 对齐 Vue generateHqParam,生成 4106 行情订阅所需的完整请求参数。 + buildHqRequestParams( + contracts: RecommendFuturesItem[], + primaryIdx: number, + secondaryIdx: number, + ): MarketRankingHqRequestParams { + const sortId = this.getSortId(primaryIdx, secondaryIdx); + const metric = TAB_METRICS[primaryIdx]; + const sortOrder = metric.id === 'fall' || metric.id === 'fallSpeed' ? '1' : '0'; + const dataItems: number[] = [ + HqFieldId.DATA_ID_NAME, + HqFieldId.DATA_ID_PRICE, + HqFieldId.DATA_ID_CODE, + HqFieldId.DATA_ID_MARKET, + HqFieldId.DATA_ID_PRICE_CHG, + HqFieldId.DATA_ID_PRE_PRICE, + HqFieldId.DATA_ID_OPEN_PRICE, + HqFieldId.DATA_ID_PRE_SETTLE_PRICE, + ]; + if (!dataItems.includes(sortId)) { + dataItems.push(sortId); + } + const columnOrder = dataItems.join('|'); + const requestDic = `sortid=${sortId}\r\n` + + `sortorder=${sortOrder}\r\n` + + `push=1\r\n` + + `dataitem=${dataItems.map((item: number): string => `${item},`).join('')}\r\n` + + `codelist=${this.formatCodeList(contracts)}\r\n` + + `scenario=qht_qihuo_sort\r\n` + + `precision=1\r\n` + + `pushtime=2.5\r\n`; + return { + protocolId: '4106', + // HarmonyOS 当前沿用 Android 行情页面标识。 + pageId: '2201', + onlineId: 'marketRankingData', + columnOrder: columnOrder, + requestDic: requestDic, + }; + } + + private getQuoteType(primaryIdx: number, secondaryIdx: number): string { + const metric = TAB_METRICS[primaryIdx]; + if (metric.hasChildren) { + return `${PERIOD_RANGES[secondaryIdx].api}_${metric.api}`; + } + return metric.api; + } + + private getSortId(primaryIdx: number, secondaryIdx: number): number { + const metric = TAB_METRICS[primaryIdx]; + return metric.hasChildren ? PERIOD_RANGES[secondaryIdx].hqId : metric.hqId; + } + + // 按市场拼成 4106 所需格式,例如 70(CU2501,RB2505,);。 + private formatCodeList(contracts: RecommendFuturesItem[]): string { + const markets: string[] = []; + const codeGroups: string[][] = []; + contracts.forEach((contract: RecommendFuturesItem): void => { + const marketIndex = markets.indexOf(contract.market); + if (marketIndex >= 0) { + codeGroups[marketIndex].push(contract.contract_code); + } else { + markets.push(contract.market); + codeGroups.push([contract.contract_code]); + } + }); + return markets.map((market: string, index: number): string => + `${market}(${codeGroups[index].map((code: string): string => `${code},`).join('')});` + ).join(''); + } + + // 模拟真实鸿蒙行情 TableData,每个 Row 通过字段 ID 读取数据。 + private createMockTableData( + contracts: RecommendFuturesItem[], + quoteType: string, + tick: number, + ): MarketRankingTableData { + const tableData = new MarketRankingTableData(); + contracts.forEach((contract: RecommendFuturesItem, index: number): void => { + const price = 100 + index * 10 + tick * 0.1; + const priceChg = ((tick + index) % 5 - 2) * 0.35; + const row = new MarketRankingTableRow(); + row.set(HqFieldId.DATA_ID_CODE, contract.contract_code); + row.set(HqFieldId.DATA_ID_MARKET, contract.market); + row.set(HqFieldId.DATA_ID_NAME, contract.contract_name); + row.set(HqFieldId.DATA_ID_PRICE, price); + row.set(HqFieldId.DATA_ID_PRICE_CHG, priceChg); + row.set(HqFieldId.DATA_ID_PRE_PRICE, price - 0.5); + row.set(HqFieldId.DATA_ID_OPEN_PRICE, price - 0.2); + row.set(HqFieldId.DATA_ID_PRE_SETTLE_PRICE, price - 0.4); + if (quoteType === 'turnover') { + row.set(HqFieldId.DATA_ID_TURNOVER, 100000 + index * 5000 + tick * 100); + } + if (quoteType === 'increase_position') { + row.set(HqFieldId.DATA_ID_INCRE_POSIT, 1000 + index * 100 + tick * 10); + } + const speed = priceChg / 2; + if (quoteType === 'one_minute_rise_percent' || quoteType === 'one_minute_fall_percent') { + row.set(HqFieldId.DATA_ID_CHG_SPEED_1MIN, speed); + } else if (quoteType === 'five_minute_rise_percent' || + quoteType === 'five_minute_fall_percent') { + row.set(HqFieldId.DATA_ID_CHG_SPEED_5MIN, speed); + } else if (quoteType === 'ten_minute_rise_percent' || + quoteType === 'ten_minute_fall_percent') { + row.set(HqFieldId.DATA_ID_CHG_SPEED_10MIN, speed); + } else if (quoteType === 'fifteen_minute_rise_percent' || + quoteType === 'fifteen_minute_fall_percent') { + row.set(HqFieldId.DATA_ID_CHG_SPEED_15MIN, speed); + } + tableData.addRow(row); + }); + return tableData; + } + + // 遍历 tableRows,通过字段 ID 转换为 View 使用的行数据。 + private mergeTableData(data: MarketRankingTableData, sortFieldId: number): CardData { + const tableList = data.tableRows.map((row: MarketRankingTableRow): RankItem => { + const item: RankItem = { + code: this.getString(row, HqFieldId.DATA_ID_CODE), + market: this.getString(row, HqFieldId.DATA_ID_MARKET), + name: this.getString(row, HqFieldId.DATA_ID_NAME), + }; + item.price = this.getNumber(row, HqFieldId.DATA_ID_PRICE); + item.price_chg = this.getNumber(row, HqFieldId.DATA_ID_PRICE_CHG); + if (sortFieldId === HqFieldId.DATA_ID_TURNOVER) { + item.turnover = this.getNumber(row, HqFieldId.DATA_ID_TURNOVER); + } else if (sortFieldId === HqFieldId.DATA_ID_INCRE_POSIT) { + item.incre_posit = this.getNumber(row, HqFieldId.DATA_ID_INCRE_POSIT); + } else if (sortFieldId !== HqFieldId.DATA_ID_PRICE_CHG) { + item.chg_speed = this.getNumber(row, sortFieldId); + } + return item; + }); + return { tableList: tableList }; + } + + private getString(row: MarketRankingTableRow, fieldId: number): string { + const value: MarketRankingHqValue | undefined = row.get(fieldId); + return value === undefined ? '' : `${value}`; + } + + private getNumber(row: MarketRankingTableRow, fieldId: number): number | undefined { + const value: MarketRankingHqValue | undefined = row.get(fieldId); + if (typeof value === 'number') { + return value; + } + if (typeof value === 'string' && value !== '') { + const result = Number(value); + return Number.isNaN(result) ? undefined : result; + } + return undefined; + } +} diff --git a/entry/src/main/ets/market-ranking/MarketRankingModels.ets b/entry/src/main/ets/market-ranking/MarketRankingModels.ets index 41e74d2..b0f4aa3 100644 --- a/entry/src/main/ets/market-ranking/MarketRankingModels.ets +++ b/entry/src/main/ets/market-ranking/MarketRankingModels.ets @@ -21,29 +21,32 @@ export interface CardData { tableList: RankItem[]; } -// 4106 行情推送的原始分列数据。Vue 中实际为“字段 ID -> 数组”的字典, -// ArkTS 使用显式属性描述同一结构,数组中相同下标代表同一份合约行情。 -// 字段 ID:code=4、market=36103、name=55、price=10、price_chg=34818、 -// pre_price=6、open_price=7、pre_settle_price=66、turnover=19、incre_posit=34355、 -// chg_speed_1min=34874、chg_speed_5min=34325、chg_speed_10min=34875、chg_speed_15min=34876。 -export interface MarketRankingQuoteData { - code: string[]; - market: string[]; - name: string[]; - price: number[]; - price_chg: number[]; - pre_price: number[]; - open_price: number[]; - pre_settle_price: number[]; - turnover: number[]; - incre_posit: number[]; - chg_speed_1min: number[]; - chg_speed_5min: number[]; - chg_speed_10min: number[]; - chg_speed_15min: number[]; +export type MarketRankingHqValue = string | number; + +// 对齐真实鸿蒙行情返回行:通过行情字段 ID 获取当前合约的值。 +export class MarketRankingTableRow { + private values: Map = + new Map(); + + set(fieldId: number, value: MarketRankingHqValue): void { + this.values.set(fieldId, value); + } + + get(fieldId: number): MarketRankingHqValue | undefined { + return this.values.get(fieldId); + } } -export interface MarketRankingQuoteRequestParams { +// 对齐真实鸿蒙行情 TableData:每个 tableRows 元素代表一份合约行情。 +export class MarketRankingTableData { + tableRows: MarketRankingTableRow[] = []; + + addRow(row: MarketRankingTableRow): void { + this.tableRows.push(row); + } +} + +export interface MarketRankingHqRequestParams { protocolId: string; pageId: string; onlineId: string; @@ -54,21 +57,16 @@ export interface MarketRankingQuoteRequestParams { export interface TabMetric { id: string; label: ResourceStr; - stat: string; // 埋点用 - api: string; // HTTP 接口 quote_type 参数用 - hqId: string; // 行情订阅 sortid 用 - hqName: string; // 推送字段映射用 - index: number; + api: string; // HTTP 接口 quote_type 参数用 + hqId: number; // 行情订阅 sortid 用 hasChildren: boolean; // Vue 用 children 数组本身判断,这里简化为布尔标记 } export interface PeriodRange { id: string; label: ResourceStr; - stat: string; api: string; - hqId: string; - index: number; + hqId: number; } // ============ 后端接口原始响应类型(recommend_futures) ============ diff --git a/entry/src/main/ets/market-ranking/MarketRankingNodeComponent.ets b/entry/src/main/ets/market-ranking/MarketRankingNodeComponent.ets index 6cefc41..12497e1 100644 --- a/entry/src/main/ets/market-ranking/MarketRankingNodeComponent.ets +++ b/entry/src/main/ets/market-ranking/MarketRankingNodeComponent.ets @@ -5,6 +5,7 @@ import { RankItem, RecommendFuturesItem, TabMetric, } from './MarketRankingModels'; import { MarketRankingDataFetcher } from './MarketRankingDataFetcher'; +import { MarketRankingHqRequestClient } from './MarketRankingHqRequestClient'; import { formatGreatNumber, formatPercent, riseFallColor } from '../common/NumberFormat'; import { AllCardsCard } from '../common/AllCardsModels'; import { @@ -32,7 +33,7 @@ export struct MarketRankingNodeComponent { @State tabScrollOffset: number = 0; @State showExplainSheet: boolean = false; @StorageLink('enableDarkMode') isDarkMode: boolean = false; - private quoteUnsubscribe: (() => void) | undefined; + private hqUnsubscribe: (() => void) | undefined; aboutToAppear(): void { this.updateCardConfig(); @@ -43,7 +44,7 @@ export struct MarketRankingNodeComponent { } aboutToDisappear(): void { - this.stopSubscribeQuotes(); + this.stopSubscribeHq(); } // 只负责从公共 Mock 获取并更新卡片配置。 @@ -347,7 +348,7 @@ export struct MarketRankingNodeComponent { // 先请求合约榜单填充列表,再根据当前 CardData 订阅行情。 private async updateCardData(primaryIdx: number, secondaryIdx: number): Promise { // 切换 Tab 后立即清理旧列表,避免新请求返回前闪现上一 Tab 的数据。 - this.stopSubscribeQuotes(); + this.stopSubscribeHq(); this.cardData = { tableList: [] }; try { const contracts = await MarketRankingDataFetcher @@ -360,31 +361,31 @@ export struct MarketRankingNodeComponent { name: contract.contract_name, })), }; - this.subscribeQuotes(primaryIdx, secondaryIdx); + this.subscribeHq(primaryIdx, secondaryIdx); } catch { this.cardData = { tableList: [] }; } } // 使用当前 CardData 中的合约列表订阅行情,推送后整体更新 CardData。 - private subscribeQuotes(primaryIdx: number, secondaryIdx: number): void { + private subscribeHq(primaryIdx: number, secondaryIdx: number): void { const contracts = this.cardData.tableList.map((item: RankItem): RecommendFuturesItem => ({ contract_code: item.code, contract_name: item.name, market: item.market, })); - this.quoteUnsubscribe = MarketRankingDataFetcher + this.hqUnsubscribe = MarketRankingHqRequestClient .getInstance() - .subscribeQuotes(contracts, primaryIdx, secondaryIdx, (cardData: CardData): void => { + .subscribeHq(contracts, primaryIdx, secondaryIdx, (cardData: CardData): void => { this.cardData = cardData; }); } - private stopSubscribeQuotes(): void { - if (this.quoteUnsubscribe !== undefined) { - this.quoteUnsubscribe(); - this.quoteUnsubscribe = undefined; + private stopSubscribeHq(): void { + if (this.hqUnsubscribe !== undefined) { + this.hqUnsubscribe(); + this.hqUnsubscribe = undefined; } }