futures.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. import { shallowRef, reactive, computed, toRefs, onUnmounted } from 'vue'
  2. import { v4 } from 'uuid'
  3. import { isMatch } from 'lodash'
  4. import { timerTask } from '@/utils/timer'
  5. import { handlePriceColor } from '@/filters'
  6. import { BuyOrSell } from '@/constants/order'
  7. import { queryMemberGoodsLimitConfig } from '@/services/api/common'
  8. import { queryErmcpGoods, queryQuoteDay } from '@/services/api/goods'
  9. import { wordArrayToUint8Array } from '@/services/websocket/package/crypto'
  10. import { decodeProto } from '@/services/websocket/package/package50/proto'
  11. import { defineStore } from '../store'
  12. import { useAccountStore } from './account'
  13. import CryptoJS from 'crypto-js'
  14. import eventBus from '@/services/bus'
  15. import moment from 'moment'
  16. import { i18n } from './language'
  17. /**
  18. * 期货存储对象
  19. * @returns
  20. */
  21. export const useFuturesStore = defineStore(() => {
  22. // 行情监听集合
  23. const quoteWatchMap = new Map<string, { keys: string[]; callback: (value: Partial<Model.QuoteDayRsp>) => void; }>()
  24. // 当前市场ID
  25. const marketIds = shallowRef<number[]>([])
  26. // 请求商品列表成功后的回调集合
  27. // 由于异步请求原因,页面发起行情订阅时商品数据可能还不存在
  28. // 将回调事件收集到合集中,待请求成功后再执行
  29. const dataCompletedCallback = new Set<() => void>()
  30. const state = reactive({
  31. loading: false,
  32. goodsList: <(Model.GoodsRsp & Partial<Model.MemberGoodsLimitConfigRsp>)[]>[], // 商品列表
  33. quotationList: <Model.GoodsQuote[]>[], // 行情列表
  34. selectedGoodsId: <number | undefined>undefined, // 当前选中的商品ID
  35. buyOrSell: <number>0,
  36. })
  37. // 指定市场ID的商品列表
  38. const marketGoodsList = computed(() => {
  39. const list = state.quotationList.filter((e) => marketIds.value.includes(e.marketid))
  40. return list.sort((a, b) => a.goodscode.localeCompare(b.goodscode)) // 根据 goodscode 字母升序排序
  41. })
  42. // 当前选中的商品
  43. const selectedGoods = computed(() => state.quotationList.find((e) => e.goodsid === state.selectedGoodsId))
  44. // 设置当前市场ID
  45. const setMarketId = (...values: number[]) => {
  46. marketIds.value = values
  47. const list = marketGoodsList.value
  48. if (list.length) {
  49. // 查找当前选中的商品ID
  50. if (!list.every((e) => e.goodsid === state.selectedGoodsId)) {
  51. state.selectedGoodsId = list[0].goodsid
  52. }
  53. } else {
  54. state.selectedGoodsId = undefined
  55. }
  56. }
  57. const getGoodsListByMarketId = (...marketId: number[]) => {
  58. const list = state.quotationList.filter((e) => marketId.includes(e.marketid))
  59. return list.sort((a, b) => a.goodscode.localeCompare(b.goodscode)) // 根据 goodscode 字母升序排序
  60. }
  61. const getGoodsListByTradeMode = (...tradeMode: number[]) => {
  62. const list = state.quotationList.filter((e) => tradeMode.includes(e.trademode))
  63. return list.sort((a, b) => a.goodscode.localeCompare(b.goodscode)) // 根据 goodscode 字母升序排序
  64. }
  65. // 根据商品 ID 查找对应的商品信息
  66. const getGoodsListByGoodsID = (goodsid: number[]) => {
  67. const list = state.quotationList.filter((e) => goodsid.includes(e.goodsid))
  68. return list.sort((a, b) => a.goodscode.localeCompare(b.goodscode)) // 根据 goodscode 字母升序排序
  69. }
  70. // 获取商品列表
  71. const getGoodsList = async () => {
  72. state.loading = true
  73. state.goodsList = []
  74. state.quotationList = []
  75. timerTask.clearTimeout('quoteDay')
  76. const { currentAccountId, currentAccountConfig } = useAccountStore()
  77. const { todayAccountMargins = [], todayAccountTradeRules = [], todayAccountTradefees = [] } = currentAccountConfig ?? {}
  78. // 任务 #5197
  79. const { data: limitConfig } = await queryMemberGoodsLimitConfig({
  80. data: {
  81. roletype: 7
  82. }
  83. })
  84. const { data: goodsList } = await queryErmcpGoods()
  85. for (let i = 0; i < goodsList.length; i++) {
  86. const item = goodsList[i]
  87. const limit = limitConfig.find((e) => e.goodsid === item.goodsid)
  88. // 跳过不显示的商品
  89. if (limit?.isnodisplay) {
  90. continue
  91. }
  92. // 更新商品配置
  93. const findMargin = todayAccountMargins.find((e) => e.accountid === currentAccountId && e.goodsid === item.goodsid)
  94. const margin = findMargin ?? todayAccountMargins.find((e) => e.accountid === 0 && e.goodsid === item.goodsid)
  95. if (margin) {
  96. const marginWordArray = CryptoJS.enc.Base64.parse(margin.infocontent) // 解析base64
  97. const marginUint8Array = wordArrayToUint8Array(marginWordArray)
  98. const marginData = await decodeProto<Proto.MarginInfoStruct>('MarginInfoStruct', marginUint8Array) // proto数据解析
  99. item.marketmarginalgorithm = marginData.MarginAlgorithm
  100. item.marketmarginvalue = marginData.MarketMarginValue
  101. }
  102. const findRule = todayAccountTradeRules.find((e) => e.accountid === currentAccountId && e.goodsid === item.goodsid)
  103. const rule = findRule ?? todayAccountTradeRules.find((e) => e.accountid === 0 && e.goodsid === item.goodsid)
  104. if (rule) {
  105. const ruleWordArray = CryptoJS.enc.Base64.parse(rule.infocontent) // 解析base64
  106. const ruleUint8Array = wordArrayToUint8Array(ruleWordArray)
  107. const ruleData = await decodeProto<Proto.TradeRuleInfoStruct>('TradeRuleInfoStruct', ruleUint8Array) // proto数据解析
  108. item.traderules = ruleData.TradeRules
  109. } else {
  110. item.traderules = []
  111. }
  112. // 交易费用
  113. const findfee = todayAccountTradefees.find((e) => e.accountid === currentAccountId && e.goodsid === item.goodsid)
  114. const fee = findfee ?? todayAccountTradefees.find((e) => e.accountid === 0 && e.goodsid === item.goodsid && e.tradetype === 1)
  115. if (fee) {
  116. const ruleWordArray = CryptoJS.enc.Base64.parse(fee.infocontent) // 解析base64
  117. const ruleUint8Array = wordArrayToUint8Array(ruleWordArray)
  118. const ruleData = await decodeProto<Proto.TradeFeeInfoStruct>('TradeFeeInfoStruct', ruleUint8Array) // proto数据解析
  119. item.tradefees = ruleData.TradeFees
  120. } else {
  121. item.tradefees = []
  122. }
  123. // 组合商品属性
  124. state.goodsList.push({
  125. ...item,
  126. iscannotbuy: limit?.iscannotbuy ?? 0,
  127. iscannotsell: limit?.iscannotsell ?? 0
  128. })
  129. }
  130. // 待优化
  131. getQuoteDay().then(() => {
  132. dataCompletedCallback.forEach((callback) => callback())
  133. dataCompletedCallback.clear()
  134. }).finally(() => {
  135. state.loading = false
  136. if (!state.selectedGoodsId) {
  137. state.selectedGoodsId = marketGoodsList.value[0]?.goodsid
  138. }
  139. // 每5分钟获取一次盘面
  140. timerTask.setTimeout(() => getQuoteDay(), 5 * 60 * 1000, 'quoteDay')
  141. })
  142. }
  143. // 获取商品盘面信息(待优化)
  144. const getQuoteDay = async () => {
  145. state.loading = true
  146. const values = state.goodsList.map((e) => e.goodscode.toString())
  147. const res = await queryQuoteDay({
  148. data: {
  149. goodsCodes: values.join(',')
  150. }
  151. })
  152. // 只遍历有盘面的商品
  153. // res.data.forEach((item) => {
  154. // updateQuotation(item)
  155. // })
  156. // 遍历所有商品
  157. values.forEach((goodscode) => {
  158. const item = res.data.find((e_1) => e_1.goodscode === goodscode)
  159. updateQuotation(item ?? { goodscode })
  160. })
  161. state.loading = false
  162. }
  163. // 盘面数据加载完成后触发
  164. const onDataCompleted = (callback: () => void) => {
  165. if (state.quotationList.length) {
  166. setTimeout(() => callback(), 0)
  167. } else {
  168. dataCompletedCallback.add(callback)
  169. onUnmounted(() => dataCompletedCallback.delete(callback))
  170. }
  171. }
  172. // 通过 goodscode 获取实时盘面
  173. const getGoodsQuote = (code: string | number) => {
  174. return computed(() => state.quotationList.find((e) => e.goodscode === code || e.goodsid === code))
  175. }
  176. const getQuoteItem = (query: Partial<Model.GoodsQuote>) => {
  177. return state.quotationList.find(item => isMatch(item, query))
  178. }
  179. // 获取费用值
  180. const getFeeValue = (goodsItem?: Model.GoodsQuote, feeId = 0) => {
  181. const { tradefees = [] } = goodsItem ?? {}
  182. const { FeeAlgorithm = 0, ExchangeValue = 0, MemberDefaultValue = 0 } = tradefees.find((e) => e.FeeID === feeId) ?? {}
  183. return {
  184. FeeAlgorithm,
  185. feeValue: ExchangeValue + MemberDefaultValue
  186. }
  187. }
  188. // 通过 goodscode 获取实时行情报价
  189. const getQuotePrice = (goodscode: string) => {
  190. const { last = 0, presettle = 0, preclose = 0 } = getQuoteItem({ goodscode }) ?? {}
  191. if (last != 0.0) {
  192. return last
  193. } else if (presettle != 0.0) {
  194. return presettle
  195. } else if (preclose != 0.0) {
  196. return preclose
  197. } else {
  198. return 0.0
  199. }
  200. }
  201. // 获取商品名称
  202. const getGoodsName = (code: string | number) => {
  203. const goods = getGoods(code)
  204. return goods?.goodsname ?? ''
  205. }
  206. // 获取对应的商品
  207. const getGoods = (code: string | number) => {
  208. const goods = state.goodsList.find((e) => e.goodscode === code || e.goodsid === code)
  209. return goods
  210. }
  211. // 获取商品国际化名称
  212. const getI18nGoodsName = (code: string | number) => {
  213. const item = getGoods(code)
  214. switch (i18n.global.locale) {
  215. case 'zh-CN':
  216. return item?.goodsname ?? ''
  217. case 'en-US':
  218. return item?.goodsnameen ?? ''
  219. case 'zh-TW':
  220. return item?.goodsnametw ?? ''
  221. case 'vi':
  222. return item?.goodsnamevi ?? ''
  223. default:
  224. return item?.goodsnameth ?? ''
  225. }
  226. }
  227. // 获取商品市场ID
  228. const getGoodsMarket = (code: string | number) => {
  229. const quote = state.goodsList.find((e) => e.goodscode === code || e.goodsid === code)
  230. return quote?.marketid ?? 0
  231. }
  232. // 监听行情推送
  233. const quoteWatch = (goodsCodes: string | string[], callback: (value: Partial<Model.QuoteDayRsp>) => void) => {
  234. const uuid = v4()
  235. quoteWatchMap.set(uuid, {
  236. keys: Array.isArray(goodsCodes) ? goodsCodes : [goodsCodes],
  237. callback
  238. })
  239. const append = (...goodsCodes: string[]) => {
  240. const value = quoteWatchMap.get(uuid)
  241. value?.keys.push(...goodsCodes)
  242. }
  243. const stop = () => {
  244. quoteWatchMap.delete(uuid)
  245. }
  246. // 页面离开时停止监听事件,防止事件重复触发
  247. onUnmounted(() => stop())
  248. return {
  249. uuid,
  250. append,
  251. stop
  252. }
  253. }
  254. // 更新行情数据
  255. const updateQuotation = (quote: Partial<Model.QuoteDayRsp>) => {
  256. // 查找对应的商品行情
  257. const item: Model.GoodsQuote = state.quotationList.find((e) => e.goodscode === quote.goodscode) ?? {
  258. goodsid: 0,
  259. goodscode: quote.goodscode ?? '',
  260. goodsname: '',
  261. goodsgroupid: 0,
  262. goodsgroupname: '',
  263. goodunitid: 0,
  264. marketid: 0,
  265. trademode: 0,
  266. agreeunit: 0,
  267. decimalplace: 0,
  268. decimalvalue: 0,
  269. quoteminunit: 0,
  270. quotegear: 0,
  271. last: quote.last ?? 0,
  272. lasttime: quote.lasttime ?? '',
  273. bid: quote.bid ?? 0,
  274. bid2: quote.bid2 ?? 0,
  275. bid3: quote.bid3 ?? 0,
  276. bid4: quote.bid4 ?? 0,
  277. bid5: quote.bid5 ?? 0,
  278. bidvolume: quote.bidvolume ?? 0,
  279. bidvolume2: quote.bidvolume2 ?? 0,
  280. bidvolume3: quote.bidvolume3 ?? 0,
  281. bidvolume4: quote.bidvolume4 ?? 0,
  282. bidvolume5: quote.bidvolume5 ?? 0,
  283. ask: quote.ask ?? 0,
  284. ask2: quote.ask2 ?? 0,
  285. ask3: quote.ask3 ?? 0,
  286. ask4: quote.ask4 ?? 0,
  287. ask5: quote.ask5 ?? 0,
  288. askvolume: quote.askvolume ?? 0,
  289. askvolume2: quote.askvolume2 ?? 0,
  290. askvolume3: quote.askvolume3 ?? 0,
  291. askvolume4: quote.askvolume4 ?? 0,
  292. askvolume5: quote.askvolume5 ?? 0,
  293. lastvolume: quote.lastvolume ?? 0,
  294. lastturnover: quote.Lastturnover ?? 0,
  295. presettle: quote.presettle ?? 0,
  296. preclose: quote.preclose ?? 0,
  297. opened: quote.opened ?? 0,
  298. highest: quote.highest ?? 0,
  299. lowest: quote.lowest ?? 0,
  300. limitup: quote.limitup ?? 0,
  301. limitdown: quote.limitdown ?? 0,
  302. averageprice: quote.averageprice ?? 0,
  303. totalvolume: quote.totalvolume ?? 0,
  304. totalturnover: quote.totalturnover ?? 0,
  305. holdvolume: quote.holdvolume ?? 0,
  306. provideraccountid: 0,
  307. goodscurrencyid: 0,
  308. currencyid: 0,
  309. provideruserid: 0,
  310. marketmarginalgorithm: 0,
  311. marketmarginvalue: 0,
  312. goodstradetype: 0,
  313. maxspread: 0,
  314. minspread: 0,
  315. goodsorder: '',
  316. tradeproperty: 0,
  317. rise: 0,
  318. change: 0,
  319. amplitude: 0,
  320. iscannotbuy: 0,
  321. iscannotsell: 0,
  322. bidColor: '',
  323. bid2Color: '',
  324. bid3Color: '',
  325. bid4Color: '',
  326. bid5Color: '',
  327. askColor: '',
  328. ask2Color: '',
  329. ask3Color: '',
  330. ask4Color: '',
  331. ask5Color: '',
  332. lastColor: '',
  333. averagepriceColor: '',
  334. openedColor: '',
  335. highestColor: '',
  336. lowestColor: '',
  337. pictureurl: '',
  338. thumurls: '',
  339. tpslflag: 0,
  340. tpslforceflag: 0,
  341. slratiodefault: 0,
  342. slratiodown: 0,
  343. slratioup: 1,
  344. tpratiodefault: 0,
  345. tpratiodown: 0,
  346. tpratioup: 1,
  347. basecurrencycode: '',
  348. quotecurrencycode: '',
  349. buyslpoint: 0,
  350. buytppoint: 0,
  351. sellslpoint: 0,
  352. selltppoint: 0,
  353. riskcontrolmode: 1,
  354. basedecimalplace: 0,
  355. quotedecimalplace: 0,
  356. traderules: [],
  357. tradefees: []
  358. }
  359. if (item.goodsid) {
  360. // 更新对象属性
  361. Object.entries(quote).forEach(([key, value]) => {
  362. if (value !== undefined) {
  363. type TKey = keyof Model.GoodsQuote
  364. (<K extends TKey>(prop: K, value: Model.GoodsQuote[K]) => {
  365. item[prop] = value
  366. })(key as TKey, value)
  367. }
  368. })
  369. } else {
  370. const goods = state.goodsList.find((e) => e.goodscode === quote.goodscode)
  371. if (goods) {
  372. ({
  373. goodsid: item.goodsid,
  374. goodsname: item.goodsname,
  375. goodsgroupid: item.goodsgroupid,
  376. goodunitid: item.goodunitid,
  377. goodsgroupname: item.goodsgroupname,
  378. marketid: item.marketid,
  379. trademode: item.trademode,
  380. agreeunit: item.agreeunit,
  381. decimalplace: item.decimalplace,
  382. quoteminunit: item.quoteminunit,
  383. quotegear: item.quotegear,
  384. marketmarginalgorithm: item.marketmarginalgorithm,
  385. marketmarginvalue: item.marketmarginvalue,
  386. maxspread: item.maxspread,
  387. minspread: item.minspread,
  388. goodsorder: item.goodsorder,
  389. tradeproperty: item.tradeproperty,
  390. provideraccountid: item.provideraccountid,
  391. provideruserid: item.provideruserid,
  392. goodstradetype: item.goodstradetype,
  393. goodscurrencyid: item.goodscurrencyid,
  394. currencyid: item.currencyid,
  395. pictureurl: item.pictureurl,
  396. traderules: item.traderules,
  397. tradefees: item.tradefees,
  398. thumurls: item.thumurls,
  399. tpslflag: item.tpslflag,
  400. tpslforceflag: item.tpslforceflag,
  401. slratiodefault: item.slratiodefault,
  402. slratiodown: item.slratiodown,
  403. slratioup: item.slratioup,
  404. tpratiodefault: item.tpratiodefault,
  405. tpratiodown: item.tpratiodown,
  406. tpratioup: item.tpratioup,
  407. basecurrencycode: item.basecurrencycode,
  408. quotecurrencycode: item.quotecurrencycode,
  409. buyslpoint: item.buyslpoint,
  410. buytppoint: item.buytppoint,
  411. sellslpoint: item.sellslpoint,
  412. selltppoint: item.selltppoint,
  413. riskcontrolmode: item.riskcontrolmode,
  414. basedecimalplace: item.basedecimalplace,
  415. quotedecimalplace: item.quotedecimalplace
  416. } = goods)
  417. item.iscannotbuy = goods.iscannotbuy ?? 0
  418. item.iscannotsell = goods.iscannotsell ?? 0
  419. // 向列表添加新数据
  420. state.quotationList.push(item)
  421. }
  422. }
  423. // 任务 #5677 任务 #6268
  424. const hprice = (item.trademode === 52 || item.trademode === 10) ? item.ask : item.last
  425. const lprice = (item.trademode === 52 || item.trademode === 10) ? item.bid : item.last
  426. item.opened = item.opened || item.last // 没有开盘价默认取最新价
  427. item.highest = item.highest || hprice // 没有最高价默认取最新价
  428. item.lowest = item.lowest || lprice // 没有最低价价默认取最新价
  429. // 处理最高最低价
  430. if (item.last) {
  431. if (hprice > item.highest) {
  432. item.highest = hprice
  433. }
  434. if (lprice < item.lowest) {
  435. item.lowest = lprice
  436. }
  437. }
  438. item.averageprice = item.totalvolume ? item.totalturnover / (item.totalvolume * item.agreeunit) : 0 // 计算均价
  439. item.rise = item.last ? item.last - item.presettle : 0 // 涨跌额/涨跌: 最新价 - 昨结价
  440. item.change = item.presettle ? item.rise / item.presettle : 0 // 涨跌幅/幅度: (最新价 - 昨结价) / 昨结价
  441. item.amplitude = item.presettle ? (item.highest - item.lowest) / item.presettle : 0 // 振幅: (最高价 - 最低价 ) / 最新价
  442. // 计算小数单位值
  443. item.decimalvalue = item.quoteminunit * Math.pow(10, item.decimalplace * -1)
  444. // 处理行情价格颜色
  445. const handleColor = (value: number | string) => handlePriceColor(Number(value), item.presettle)
  446. item.bidColor = handleColor(item.bid)
  447. item.bid2Color = handleColor(item.bid2)
  448. item.bid3Color = handleColor(item.bid3)
  449. item.bid4Color = handleColor(item.bid4)
  450. item.bid5Color = handleColor(item.bid5)
  451. item.askColor = handleColor(item.ask)
  452. item.ask2Color = handleColor(item.ask2)
  453. item.ask3Color = handleColor(item.ask3)
  454. item.ask4Color = handleColor(item.ask4)
  455. item.ask5Color = handleColor(item.ask5)
  456. item.lastColor = handleColor(item.last)
  457. item.averagepriceColor = handleColor(item.averageprice.toFixed(item.decimalplace))
  458. item.openedColor = handleColor(item.opened)
  459. item.highestColor = handleColor(item.highest)
  460. item.lowestColor = handleColor(item.lowest)
  461. if (item?.trademode === 80 || item?.trademode === 81) {
  462. // 处理数量小数位
  463. const handleDecimalPlace = (value: number) => {
  464. if (value) {
  465. const decimal = Math.pow(10, item.basedecimalplace)
  466. return value / decimal
  467. }
  468. return value
  469. }
  470. item.askvolume = handleDecimalPlace(item.askvolume)
  471. item.askvolume2 = handleDecimalPlace(item.askvolume2)
  472. item.askvolume3 = handleDecimalPlace(item.askvolume3)
  473. item.askvolume4 = handleDecimalPlace(item.askvolume4)
  474. item.askvolume5 = handleDecimalPlace(item.askvolume5)
  475. item.bidvolume = handleDecimalPlace(item.bidvolume)
  476. item.bidvolume2 = handleDecimalPlace(item.bidvolume2)
  477. item.bidvolume3 = handleDecimalPlace(item.bidvolume3)
  478. item.bidvolume4 = handleDecimalPlace(item.bidvolume4)
  479. item.bidvolume5 = handleDecimalPlace(item.bidvolume5)
  480. }
  481. }
  482. // 处理行情数据
  483. const handleQuotation = (quote: Proto.Quote): Partial<Model.QuoteDayRsp> => {
  484. const goods = state.goodsList.find((e) => e.goodscode.toUpperCase() === quote.goodscode?.toUpperCase())
  485. // 处理报价小数位
  486. const handleDecimalPlace = (value?: number) => {
  487. if (goods && value) {
  488. const decimal = Math.pow(10, goods.decimalplace)
  489. return value / decimal
  490. }
  491. return value
  492. }
  493. return {
  494. goodscode: quote.goodscode,
  495. last: handleDecimalPlace(quote.last),
  496. lasttime: (quote.date && quote.time) ? moment(quote.date + quote.time, 'YYYYMMDDHHmmss').toISOString(true) : undefined,
  497. ask: handleDecimalPlace(quote.ask),
  498. ask2: handleDecimalPlace(quote.ask2),
  499. ask3: handleDecimalPlace(quote.ask3),
  500. ask4: handleDecimalPlace(quote.ask4),
  501. ask5: handleDecimalPlace(quote.ask5),
  502. askvolume: quote.askvolume,
  503. askvolume2: quote.askvolume2,
  504. askvolume3: quote.askvolume3,
  505. askvolume4: quote.askvolume4,
  506. askvolume5: quote.askvolume5,
  507. bid: handleDecimalPlace(quote.bid),
  508. bid2: handleDecimalPlace(quote.bid2),
  509. bid3: handleDecimalPlace(quote.bid3),
  510. bid4: handleDecimalPlace(quote.bid4),
  511. bid5: handleDecimalPlace(quote.bid5),
  512. bidvolume: quote.bidvolume,
  513. bidvolume2: quote.bidvolume2,
  514. bidvolume3: quote.bidvolume3,
  515. bidvolume4: quote.bidvolume4,
  516. bidvolume5: quote.bidvolume5,
  517. calloptionpremiums: quote.calloptionpremiums,
  518. calloptionpremiums2: quote.calloptionpremiums2,
  519. calloptionpremiums3: quote.calloptionpremiums3,
  520. calloptionpremiums4: quote.calloptionpremiums4,
  521. calloptionpremiums5: quote.calloptionpremiums5,
  522. exchangecode: quote.exchangecode,
  523. exchangedate: quote.exchangedate,
  524. highest: handleDecimalPlace(quote.highest),
  525. holdvolume: quote.holdvolume,
  526. inventory: quote.inventory,
  527. lastvolume: quote.lastvolume,
  528. Lastturnover: handleDecimalPlace(quote.lastturnover),
  529. limitdown: handleDecimalPlace(quote.limitlow),
  530. limitup: handleDecimalPlace(quote.limithigh),
  531. lowest: handleDecimalPlace(quote.lowest),
  532. opened: handleDecimalPlace(quote.opened),
  533. preclose: handleDecimalPlace(quote.preclose),
  534. preholdvolume: quote.preholdvolume,
  535. presettle: handleDecimalPlace(quote.presettle),
  536. putoptionpremiums: handleDecimalPlace(quote.putoptionpremiums),
  537. putoptionpremiums2: handleDecimalPlace(quote.putoptionpremiums2),
  538. putoptionpremiums3: handleDecimalPlace(quote.putoptionpremiums3),
  539. putoptionpremiums4: handleDecimalPlace(quote.putoptionpremiums4),
  540. putoptionpremiums5: handleDecimalPlace(quote.putoptionpremiums5),
  541. settle: handleDecimalPlace(quote.settle),
  542. totalturnover: handleDecimalPlace(quote.totalturnover),
  543. totalvolume: quote.totalvolume,
  544. }
  545. }
  546. // 接收行情推送通知
  547. const quotePushNotify = eventBus.$on('QuotePushNotify', (res) => {
  548. const data = res as Proto.Quote[]
  549. data.forEach((item) => {
  550. const quote = handleQuotation(item)
  551. if (!state.loading) {
  552. updateQuotation(quote)
  553. }
  554. // 触发行情监听事件
  555. for (const e of quoteWatchMap.values()) {
  556. if (e.keys.includes(quote.goodscode ?? '')) {
  557. e.callback(quote)
  558. }
  559. }
  560. })
  561. })
  562. // 商品显示
  563. const goodsdisplay = (code: string | number) => {
  564. const item = getGoods(code)
  565. if (item) {
  566. return item.trademode != 10 ? item.goodscode + '/' + item.goodsname : item.goodscode
  567. }
  568. return '--'
  569. }
  570. // 计算浮动盈亏
  571. const calcFloatingPL = (quote: Model.GoodsQuote | undefined, buyorsell: number, quantity: number, amount: number, exchangerate = 1) => {
  572. const { trademode = 0, last = 0, presettle = 0, ask = 0, bid = 0, agreeunit = 1 } = quote ?? {}
  573. const isSupportedMode = [10, 52, 80].includes(trademode)
  574. const lastPrice = (isSupportedMode && (buyorsell === BuyOrSell.Buy ? bid : ask)) || last || presettle
  575. const result = {
  576. lastPrice, // 最新价
  577. marketValue: 0, // 市值
  578. profitLoss: 0, // 浮动盈亏
  579. lastPriceClass: '', // 最新价样式
  580. profitLossClass: '' // 浮动盈亏样式
  581. }
  582. if (lastPrice) {
  583. // 计算市值 = 现价 * 数量 * 合约单位 * 汇率
  584. result.marketValue = lastPrice * quantity * agreeunit * exchangerate
  585. // 计算浮动盈亏 任务 #5600 #6013
  586. result.profitLoss = (result.marketValue - amount) * (buyorsell === BuyOrSell.Buy ? 1 : -1)
  587. result.lastPriceClass = handlePriceColor(lastPrice, presettle)
  588. result.profitLossClass = handlePriceColor(result.profitLoss)
  589. }
  590. return result
  591. }
  592. return {
  593. ...toRefs(state),
  594. marketGoodsList,
  595. selectedGoods,
  596. onDataCompleted,
  597. setMarketId,
  598. getQuoteDay,
  599. getQuotePrice,
  600. getGoodsList,
  601. getGoodsQuote,
  602. getGoodsName,
  603. getGoodsMarket,
  604. getGoodsListByMarketId,
  605. getGoodsListByTradeMode,
  606. getGoodsListByGoodsID,
  607. updateQuotation,
  608. quoteWatch,
  609. quotePushNotify,
  610. getGoods,
  611. goodsdisplay,
  612. getQuoteItem,
  613. getFeeValue,
  614. calcFloatingPL,
  615. getI18nGoodsName
  616. }
  617. })