futures.ts 12 KB

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