futures.ts 13 KB

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