account.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { computed, toRefs, shallowReadonly } from 'vue'
  2. import { v4 } from 'uuid'
  3. import { formatDecimal } from '@/filters'
  4. import { BuyOrSell } from '@/constants/buyorsell'
  5. import { queryTaAccounts } from '@/services/api/account'
  6. import { queryErmcpTradePosition } from '@/services/api/trade'
  7. import subscribe from '@/services/subscribe'
  8. import { useLoginStore } from './login'
  9. import { useFuturesStore } from './futures'
  10. import { VueStore } from '../base'
  11. import eventBus from '@/services/bus'
  12. interface StoreState {
  13. loading: boolean;
  14. accountId: number; // 当前资金账户ID
  15. accountList: Ermcp.TaAccountsRsp[]; // 资金账户列表
  16. accountPositionList: Ermcp.TradePositionRsp[]; // 资金账户持仓列表
  17. }
  18. function useFormula(item: Ermcp.TradePositionRsp) {
  19. const { getGoodsPriceByCode } = useFuturesStore()
  20. const { goodscode, buyorsell, curpositionqty, agreeunit, positionpl, opencost, positioncost, decimalplace } = item
  21. // 计算开仓均价
  22. const calcOpenAveragePrice = () => {
  23. // 开仓成本 ÷ 期末头寸 ÷ 合约单位
  24. return opencost / curpositionqty / agreeunit
  25. }
  26. // 计算持仓均价
  27. const calcPositionAveragePrice = () => {
  28. // 持仓成本 ÷ 期末头寸 ÷ 合约单位
  29. return positioncost / curpositionqty / agreeunit
  30. }
  31. // 计算浮动盈亏 - 对应市场收益权
  32. const calcProfitLoss = () => {
  33. const last = getGoodsPriceByCode(goodscode)
  34. let result = positionpl
  35. if (last.value) {
  36. const positionAveragePrice = calcPositionAveragePrice()
  37. // 收益权 = (最新价 - 持仓均价) * 持仓数量 * 合约单位 * 方向(买1卖-1)
  38. const rightToEarnings = (last.value - positionAveragePrice) * curpositionqty * agreeunit
  39. result = rightToEarnings * (buyorsell === BuyOrSell.Buy ? 1 : -1)
  40. }
  41. return formatDecimal(result, decimalplace)
  42. }
  43. return {
  44. calcOpenAveragePrice,
  45. calcPositionAveragePrice,
  46. calcProfitLoss
  47. }
  48. }
  49. /**
  50. * 账户存储类
  51. */
  52. const store = new (class extends VueStore<StoreState>{
  53. constructor() {
  54. const state: StoreState = {
  55. loading: false,
  56. accountId: 0,
  57. accountList: [],
  58. accountPositionList: [],
  59. }
  60. super(state)
  61. // 接收资金变动通知
  62. eventBus.$on('MoneyChangedNotify', () => {
  63. this.actions.getAccountList()
  64. })
  65. }
  66. private uuid = v4()
  67. private pending = Promise.resolve() // 请求等待状态,防止重复请求
  68. /** 当前资金账户信息 */
  69. private accountInfo = computed(() => {
  70. const account = this.state.accountList.find((e) => e.accountid === this.state.accountId)
  71. // 总浮动盈亏
  72. const totalProfitLoss = this.futuresPositionList.value.reduce((res, cur) => res + cur.positionpl, 0)
  73. return {
  74. ...account,
  75. totalProfitLoss,
  76. }
  77. })
  78. /** 期货持仓列表 */
  79. private futuresPositionList = computed(() => {
  80. const positionList = this.state.accountPositionList
  81. return positionList.reduce((res, item) => {
  82. const { calcProfitLoss } = useFormula(item)
  83. res.push({
  84. ...item,
  85. positionpl: Number(calcProfitLoss()),
  86. })
  87. return res
  88. }, [] as Ermcp.TradePositionRsp[])
  89. })
  90. getters = {
  91. accountInfo: this.accountInfo,
  92. futuresPositionList: this.futuresPositionList
  93. }
  94. actions = {
  95. /** 获取资金账户列表 */
  96. getAccountList: async () => {
  97. await this.pending
  98. const { getLoginId } = useLoginStore()
  99. this.state.loading = true
  100. this.pending = queryTaAccounts({
  101. data: {
  102. loginID: getLoginId()
  103. },
  104. success: (res) => {
  105. const dataList = res.data
  106. if (dataList.length) {
  107. this.state.accountList = dataList
  108. // 查找当前选中的资金账户
  109. const account = dataList.find((e) => e.accountid === this.state.accountId)
  110. if (account) {
  111. this.state.loading = false
  112. } else {
  113. // 如果不存在,默认选中第一个账户
  114. this.state.accountId = dataList[0].accountid
  115. this.actions.getAccountPositionList()
  116. }
  117. } else {
  118. this.state.loading = false
  119. this.actions.reset()
  120. }
  121. },
  122. fail: () => {
  123. this.state.loading = false
  124. }
  125. })
  126. return this.pending
  127. },
  128. /** 获取资金账户持仓列表 */
  129. getAccountPositionList: async (marketID?: number) => {
  130. await this.pending
  131. subscribe.removeQuoteSubscribe(this.uuid)
  132. this.state.loading = true
  133. this.pending = queryErmcpTradePosition({
  134. data: {
  135. accountID: this.state.accountId,
  136. ...marketID ? { marketID } : {}
  137. },
  138. success: (res) => {
  139. const codes = res.data.map((e) => e.goodscode)
  140. this.state.accountPositionList = res.data
  141. if (codes.length) {
  142. // 行情订阅
  143. subscribe.addQuoteSubscribe(codes, this.uuid).start()
  144. }
  145. },
  146. complete: () => {
  147. this.state.loading = false
  148. }
  149. })
  150. return this.pending
  151. },
  152. /** 重置数据 */
  153. reset: () => {
  154. this.state.accountId = 0
  155. this.state.accountList = []
  156. this.state.accountPositionList = []
  157. }
  158. }
  159. })
  160. export function useAccountStore() {
  161. return shallowReadonly({
  162. ...toRefs(store.state),
  163. ...store.getters,
  164. ...store.actions,
  165. ...store.methods,
  166. })
  167. }