futures.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import { reactive, computed, toRefs } from 'vue'
  2. import { timerInterceptor } from '@/utils/timer'
  3. import { queryErmcpGoods, queryQuoteDay } from '@/services/api/goods'
  4. import { defineStore } from '../store'
  5. import { timerTask } from '@/utils/timer'
  6. import eventBus from '@/services/bus'
  7. import moment from 'moment'
  8. /**
  9. * 期货存储对象
  10. * @returns
  11. */
  12. export const useFuturesStore = defineStore(() => {
  13. const state = reactive({
  14. loading: false,
  15. quotes: <Proto.Quote[]>[], // 行情数据
  16. goodsList: <Model.GoodsRsp[]>[], // 商品列表
  17. quoteDayList: <Model.QuoteDayRsp[]>[], // 盘面列表
  18. })
  19. // 行情列表
  20. const quoteList = computed(() => {
  21. return state.goodsList.reduce((res, cur) => {
  22. const { agreeunit, goodsid, goodscode, goodsname, goodunitid, goodsgroupid, marketid, decimalplace, quoteminunit } = cur
  23. const quoteDayInfo = state.quoteDayList.find((e) => e.goodscode.toUpperCase() === goodscode.toUpperCase())
  24. const {
  25. averageprice = 0,
  26. last = 0,
  27. bid = 0,
  28. bid2 = 0,
  29. bid3 = 0,
  30. bid4 = 0,
  31. bid5 = 0,
  32. ask = 0,
  33. ask2 = 0,
  34. ask3 = 0,
  35. ask4 = 0,
  36. ask5 = 0,
  37. bidvolume = 0,
  38. bidvolume2 = 0,
  39. bidvolume3 = 0,
  40. bidvolume4 = 0,
  41. bidvolume5 = 0,
  42. askvolume = 0,
  43. askvolume2 = 0,
  44. askvolume3 = 0,
  45. askvolume4 = 0,
  46. askvolume5 = 0,
  47. totalvolume = 0,
  48. lastvolume = 0,
  49. holdvolume = 0,
  50. holdincrement = 0,
  51. presettle = 0,
  52. preclose = 0,
  53. totalturnover = 0,
  54. opened = 0,
  55. highest = 0,
  56. lowest = 0,
  57. lasttime = '',
  58. settle = 0,
  59. limitup = 0,
  60. limitdown = 0,
  61. } = quoteDayInfo ?? {}
  62. const rise = last ? last - presettle : 0 // 涨跌额/涨跌: 最新价 - 昨结价
  63. const change = presettle ? rise / presettle : 0 // 涨跌幅/幅度: (最新价 - 昨结价) / 昨结价
  64. const amplitude = last ? (highest - lowest) / last : 0 // 振幅: (最高价 - 最低价 ) / 最新价
  65. // 处理行情价格颜色
  66. const handleColor = (value: number) => {
  67. if (value === 0 || value === presettle) {
  68. return ''
  69. } else if (value > presettle) {
  70. return 'g-up-color'
  71. } else {
  72. return 'g-down-color'
  73. }
  74. }
  75. const item: Model.Futures = {
  76. agreeunit,
  77. averageprice,
  78. marketid,
  79. goodsgroupid,
  80. goodsid,
  81. goodscode,
  82. goodsname,
  83. goodunitid,
  84. decimalplace,
  85. last,
  86. lasttime,
  87. rise,
  88. change,
  89. amplitude,
  90. bid,
  91. bid2,
  92. bid3,
  93. bid4,
  94. bid5,
  95. ask,
  96. ask2,
  97. ask3,
  98. ask4,
  99. ask5,
  100. bidvolume,
  101. bidvolume2,
  102. bidvolume3,
  103. bidvolume4,
  104. bidvolume5,
  105. askvolume,
  106. askvolume2,
  107. askvolume3,
  108. askvolume4,
  109. askvolume5,
  110. totalvolume,
  111. lastvolume,
  112. holdvolume,
  113. holdincrement,
  114. presettle,
  115. preclose,
  116. totalturnover,
  117. opened,
  118. highest,
  119. lowest,
  120. settle,
  121. limitup,
  122. limitdown,
  123. quoteminunit,
  124. bidColor: handleColor(bid),
  125. askColor: handleColor(ask),
  126. lastColor: handleColor(last),
  127. openedColor: handleColor(opened),
  128. highestColor: handleColor(highest),
  129. lowestColor: handleColor(lowest),
  130. }
  131. res.push(item)
  132. return res
  133. }, [] as Model.Futures[])
  134. })
  135. // 获取商品列表
  136. const getGoodsList = async () => {
  137. state.loading = true
  138. try {
  139. const res = await queryErmcpGoods()
  140. const codes = res.data.map((e) => e.goodscode)
  141. state.goodsList = res.data
  142. // 获取商品盘面信息
  143. const getQuoteDay = async () => {
  144. try {
  145. const res_1 = await queryQuoteDay({
  146. data: {
  147. goodsCodes: codes.join(',')
  148. }
  149. })
  150. state.quoteDayList = res_1.data
  151. } finally {
  152. // 每5分钟获取一次盘面
  153. timerTask.setTimeout(() => getQuoteDay(), 5 * 60 * 1000, 'quoteDay')
  154. }
  155. }
  156. if (codes.length) {
  157. getQuoteDay()
  158. }
  159. } finally {
  160. state.loading = false
  161. }
  162. }
  163. // 通过 goodscode 获取实时行情信息
  164. const getQuoteInfo = (code?: string | number) => {
  165. return computed(() => quoteList.value.find((e) => e.goodscode === code || e.goodsid === code))
  166. }
  167. // 通过 goodscode 获取实时行情报价
  168. const getQuotePrice = (code?: string | number) => {
  169. return computed(() => {
  170. const quote = getQuoteInfo(code)
  171. return quote.value?.last ?? 0
  172. })
  173. }
  174. // 获取商品名称
  175. const getGoodsName = (code?: string | number) => {
  176. const quote = state.goodsList.find((e) => e.goodscode === code || e.goodsid === code)
  177. return quote?.goodsname
  178. }
  179. // 获取商品市场ID
  180. const getGoodsMarket = (code?: string | number) => {
  181. const quote = state.goodsList.find((e) => e.goodscode === code || e.goodsid === code)
  182. return quote?.marketid ?? 0
  183. }
  184. // 处理行情数据
  185. const handleQuote = timerInterceptor.setThrottle(() => {
  186. state.quotes.forEach((item) => {
  187. const goods = state.goodsList.find((e) => e.goodscode.toUpperCase() === item.goodscode?.toUpperCase())
  188. const quote: Model.QuoteDayRsp | undefined = state.quoteDayList.find((e) => e.goodscode.toUpperCase() === item.goodscode?.toUpperCase())
  189. const lasttime = (item.date && item.time) ? moment(item.date + item.time, 'YYYYMMDDHHmmss').format('YYYY-MM-DD HH:mm:ss') : ''
  190. // 处理报价小数位
  191. const handleDeimalplace = (value = 0) => {
  192. const decimal = goods ? Math.pow(10, goods.decimalplace) : 0
  193. if (decimal) {
  194. return value / decimal
  195. }
  196. return value
  197. }
  198. const quoteItem: Model.QuoteDayRsp = {
  199. ask: handleDeimalplace(item.ask),
  200. ask2: handleDeimalplace(item.ask2),
  201. ask3: handleDeimalplace(item.ask3),
  202. ask4: handleDeimalplace(item.ask4),
  203. ask5: handleDeimalplace(item.ask5),
  204. ask6: 0,
  205. ask7: 0,
  206. ask8: 0,
  207. ask9: 0,
  208. ask10: 0,
  209. askorderid: 0,
  210. askorderid2: 0,
  211. askorderid3: 0,
  212. askorderid4: 0,
  213. askorderid5: 0,
  214. askordervolume: 0,
  215. askordervolume2: 0,
  216. askordervolume3: 0,
  217. askordervolume4: 0,
  218. askordervolume5: 0,
  219. askordervolume6: 0,
  220. askordervolume7: 0,
  221. askordervolume8: 0,
  222. askordervolume9: 0,
  223. askordervolume10: 0,
  224. askqueueinfo: '',
  225. askvolume: item.askvolume ?? 0,
  226. askvolume2: item.askvolume2 ?? 0,
  227. askvolume3: item.askvolume3 ?? 0,
  228. askvolume4: item.askvolume4 ?? 0,
  229. askvolume5: item.askvolume5 ?? 0,
  230. askvolume6: 0,
  231. askvolume7: 0,
  232. askvolume8: 0,
  233. askvolume9: 0,
  234. askvolume10: 0,
  235. averageprice: 0,
  236. bid: handleDeimalplace(item.bid),
  237. bid2: handleDeimalplace(item.bid2),
  238. bid3: handleDeimalplace(item.bid3),
  239. bid4: handleDeimalplace(item.bid4),
  240. bid5: handleDeimalplace(item.bid5),
  241. bid6: 0,
  242. bid7: 0,
  243. bid8: 0,
  244. bid9: 0,
  245. bid10: 0,
  246. bidorderid: 0,
  247. bidorderid2: 0,
  248. bidorderid3: 0,
  249. bidorderid4: 0,
  250. bidorderid5: 0,
  251. bidordervolume: 0,
  252. bidordervolume2: 0,
  253. bidordervolume3: 0,
  254. bidordervolume4: 0,
  255. bidordervolume5: 0,
  256. bidordervolume6: 0,
  257. bidordervolume7: 0,
  258. bidordervolume8: 0,
  259. bidordervolume9: 0,
  260. bidordervolume10: 0,
  261. bidqueueinfo: "",
  262. bidvolume: item.bidvolume ?? 0,
  263. bidvolume2: item.bidvolume2 ?? 0,
  264. bidvolume3: item.bidvolume3 ?? 0,
  265. bidvolume4: item.bidvolume4 ?? 0,
  266. bidvolume5: item.bidvolume5 ?? 0,
  267. bidvolume6: 0,
  268. bidvolume7: 0,
  269. bidvolume8: 0,
  270. bidvolume9: 0,
  271. bidvolume10: 0,
  272. calloptionpremiums: item.calloptionpremiums ?? 0,
  273. calloptionpremiums2: item.calloptionpremiums2 ?? 0,
  274. calloptionpremiums3: item.calloptionpremiums3 ?? 0,
  275. calloptionpremiums4: item.calloptionpremiums4 ?? 0,
  276. calloptionpremiums5: item.calloptionpremiums5 ?? 0,
  277. cleartime: 0,
  278. exchangecode: item.exchangecode ?? 0,
  279. exchangedate: item.exchangedate ?? 0,
  280. goodscode: item.goodscode ?? '',
  281. grepmarketprice: 0,
  282. highest: handleDeimalplace(item.highest),
  283. holdincrement: 0,
  284. holdvolume: item.holdvolume ?? 0,
  285. iep: 0,
  286. iev: 0,
  287. inventory: item.inventory ?? 0,
  288. iscleared: 0,
  289. issettled: 0,
  290. last: handleDeimalplace(item.last),
  291. lastlot: 0,
  292. lasttime,
  293. Lastturnover: 0,
  294. lastvolume: item.lastvolume ?? 0,
  295. limitdown: handleDeimalplace(item.limitlow),
  296. limitup: handleDeimalplace(item.limithigh),
  297. lowest: handleDeimalplace(item.lowest),
  298. nontotalholdervolume: 0,
  299. nontotallot: 0,
  300. nontotalturnover: 0,
  301. nontotalvolume: 0,
  302. opened: handleDeimalplace(item.opened),
  303. opentime: '',
  304. orderid: 0,
  305. preclose: handleDeimalplace(item.preclose),
  306. preholdvolume: item.preholdvolume ?? 0,
  307. presettle: handleDeimalplace(item.presettle),
  308. publictradetype: '',
  309. putoptionpremiums: handleDeimalplace(item.putoptionpremiums),
  310. putoptionpremiums2: handleDeimalplace(item.putoptionpremiums2),
  311. putoptionpremiums3: handleDeimalplace(item.putoptionpremiums3),
  312. putoptionpremiums4: handleDeimalplace(item.putoptionpremiums4),
  313. putoptionpremiums5: handleDeimalplace(item.putoptionpremiums5),
  314. settle: handleDeimalplace(item.settle),
  315. strikeprice: 0,
  316. totalaskvolume: 0,
  317. totalbidvolume: 0,
  318. totallot: 0,
  319. totalturnover: handleDeimalplace(item.totalturnover),
  320. totalvolume: item.totalvolume ?? 0,
  321. utclasttime: ''
  322. }
  323. if (quote) {
  324. Object.keys(item).forEach((key) => {
  325. // 只更新存在的属性
  326. if (Reflect.has(quote, key)) {
  327. (<K extends keyof Model.QuoteDayRsp>(prop: K) => {
  328. quote[prop] = quoteItem[prop]
  329. })(key as keyof Model.QuoteDayRsp)
  330. }
  331. })
  332. // 处理最高最低价
  333. if (quote.last) {
  334. if (quote.last > quote.highest) {
  335. quote.highest = quote.last
  336. }
  337. if (quote.last < quote.lowest) {
  338. quote.lowest = quote.last
  339. }
  340. }
  341. // 处理最新时间
  342. if (lasttime) {
  343. quote.lasttime = lasttime
  344. }
  345. } else {
  346. console.warn('行情推送的商品 ' + item.goodscode + ' 缺少盘面信息')
  347. state.quoteDayList.push(quoteItem)
  348. }
  349. })
  350. }, 100)
  351. // 接收行情推送通知
  352. const quotePushNotify = eventBus.$on('QuotePushNotify', (res) => {
  353. const data = res as Proto.Quote[]
  354. data.forEach((item) => {
  355. const index = state.quotes.findIndex((e) => e.goodscode === item.goodscode)
  356. if (index > -1) {
  357. state.quotes[index] = item
  358. } else {
  359. state.quotes.push(item)
  360. }
  361. })
  362. handleQuote()
  363. })
  364. return {
  365. ...toRefs(state),
  366. quoteList,
  367. getGoodsList,
  368. getQuoteInfo,
  369. getQuotePrice,
  370. getGoodsName,
  371. getGoodsMarket,
  372. quotePushNotify,
  373. }
  374. })