futures.ts 13 KB

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