index.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import { reactive, ref, computed } from 'vue';
  2. import { queryErmcpTradePosition } from '@/services/go/ermcp/futures';
  3. import { QueryErmcpTradePositionRsp } from '@/services/go/ermcp/futures/interface';
  4. import { getQuoteDayInfoByCode } from "@/services/bus/goods";
  5. import { BuyOrSell } from '@/common/constants/enumCommon';
  6. import { getTaAccounts } from '@/services/go/TaAccount';
  7. import { Taaccount } from "@/services/go/TaAccount/interface";
  8. import { geLoginID_number } from '@/services/bus/login';
  9. import { Systemparam } from '@/services/go/useInfo/interface';
  10. import { getSelectedAccountId, setSelectedAccount } from '@/services/bus/account'; // 为了兼容以前代码,待优化后期废除
  11. import { subcriteGoodsQuote } from '@/common/setup/table/tableQuote';
  12. import { TradeAccount, TradePosition } from './interface'
  13. import APP from '@/services';
  14. let stopSubcribe: () => void;
  15. // 加载状态
  16. const loading = ref<boolean>(false);
  17. // 资金账户列表
  18. const tradeAccountList: TradeAccount[] = reactive([]);
  19. // 当前账户期货持仓列表
  20. const tradePositionList: TradePosition[] = reactive([]);
  21. // 当前资金账户信息
  22. const tradeAccount = ref<TradeAccount>();
  23. /**
  24. * 资金账户
  25. */
  26. export function useTradeAccount() {
  27. // 获取资金账户列表
  28. const getTradeAccountList = () => {
  29. if (!loading.value) {
  30. tradeAccountList.length = 0;
  31. loading.value = true;
  32. // 登录ID
  33. const loginID = Number(geLoginID_number());
  34. getTaAccounts({ loginID }).then(async (res) => {
  35. for (let i = 0; i < res.length; i++) {
  36. const account = res[i];
  37. const positionList: TradePosition[] = [];
  38. // 获取账户下的期货持仓列表
  39. await queryErmcpTradePosition({ accountID: account.accountid }).then((res) => {
  40. res.forEach((item) => {
  41. positionList.push({
  42. ...item,
  43. ...calcPositionValue(item),
  44. })
  45. })
  46. })
  47. tradeAccountList.push({
  48. positionList,
  49. ...account,
  50. ...calcCapitalValue(account, positionList),
  51. })
  52. }
  53. tradeAccountChange(getSelectedAccountId());
  54. loading.value = false;
  55. })
  56. }
  57. }
  58. // 切换资金账户
  59. const tradeAccountChange = (accountId: number) => {
  60. tradePositionList.length = 0;
  61. // 停止上次订阅
  62. stopSubcribe && stopSubcribe();
  63. const account = tradeAccountList.find((account) => account.accountid === accountId);
  64. if (account) {
  65. tradeAccount.value = account;
  66. tradePositionList.push(...account.positionList);
  67. setSelectedAccount(account); // 为了兼容以前代码,待优化后期废除
  68. // 开始行情订阅
  69. stopSubcribe = subcriteGoodsQuote(account.positionList.map((e) => e.goodscode));
  70. } else {
  71. tradeAccount.value = undefined;
  72. }
  73. }
  74. // 期货账户列表
  75. const futuresAccountList = computed(() => tradeAccountList.filter((account) => account.taaccounttype === 1));
  76. return {
  77. loading,
  78. tradeAccountList,
  79. tradePositionList,
  80. tradeAccount,
  81. futuresAccountList,
  82. getTradeAccountList,
  83. tradeAccountChange,
  84. }
  85. }
  86. /**
  87. * 计算持仓数据
  88. * @param position
  89. * @returns
  90. */
  91. function calcPositionValue(position: QueryErmcpTradePositionRsp) {
  92. // 获取对应的商品行情
  93. const quote = getQuoteDayInfoByCode(position.goodscode);
  94. // 计算开仓均价
  95. const openAveragePrice = computed(() => {
  96. const { opencost, curpositionqty, agreeunit } = position
  97. // 开仓成本 ÷ 期末头寸 ÷ 合约单位
  98. return opencost / curpositionqty / agreeunit;
  99. })
  100. // 计算持仓均价
  101. const positionAveragePrice = computed(() => {
  102. const { positioncost, curpositionqty, agreeunit } = position
  103. // 持仓成本 ÷ 期末头寸 ÷ 合约单位
  104. return positioncost / curpositionqty / agreeunit;
  105. })
  106. // 计算浮动盈亏 - 对应市场收益权
  107. const positionProfitAndLoss = computed(() => {
  108. const { curpositionqty, agreeunit } = position
  109. if (quote?.last) {
  110. if (position.buyorsell === BuyOrSell.buy) {
  111. // 买方向 = (最新价 - 持仓均价) * 买期末头寸 * 合约单位
  112. return (quote.last - openAveragePrice.value) * curpositionqty * agreeunit
  113. } else {
  114. // 卖方向 = (持仓均价 - 最新价) * 卖期末头寸 * 合约单位
  115. return (positionAveragePrice.value - quote.last) * curpositionqty * agreeunit
  116. }
  117. }
  118. return position.positionpl
  119. })
  120. // 计算市值 - 对应市场所有权
  121. const capitalization = computed(() => {
  122. if (quote?.last) {
  123. // 市值 = 最新价 * 持仓数量 * 合约单位
  124. return quote.last * position.curpositionqty * position.agreeunit
  125. }
  126. return 0
  127. })
  128. // 计算盈亏比例
  129. const positionProfitAndLossRate = computed(() => {
  130. const { opencost } = position
  131. // 持仓盈亏 ÷ 开仓成本
  132. const result = positionProfitAndLoss.value / opencost * 100
  133. if (isNaN(result)) {
  134. return 0
  135. }
  136. return result
  137. })
  138. return {
  139. openAveragePrice,
  140. capitalization,
  141. positionAveragePrice,
  142. positionProfitAndLoss,
  143. positionProfitAndLossRate,
  144. }
  145. }
  146. /**
  147. * 计算资金数据
  148. * @param account
  149. * @param positionList
  150. * @returns
  151. */
  152. function calcCapitalValue(account: Taaccount, positionList: TradePosition[]) {
  153. // 系统参数
  154. const systemParams = <Systemparam[]>APP.get('systemParams');
  155. // 计算总浮动盈亏
  156. const positionProfitAndLoss = computed(() => positionList.reduce((res, item) => res + item.positionProfitAndLoss.value, 0));
  157. // 计算总市值
  158. const marketCap = computed(() => {
  159. // 根据系统参数“087 风险净值是否加上市值 - 0:不加 1:加“
  160. const flag = systemParams.find((e) => e.paramcode === '087')?.paramvalue === '1';
  161. if (flag) {
  162. return positionList.reduce((res, item) => res + item.capitalization.value, 0);
  163. }
  164. return 0;
  165. });
  166. // 计算可用资金
  167. const availableBalance = computed(() => {
  168. // *系统参数"113"(当日浮动盈利是否可用) 0:不可用 1:可用
  169. const flag = systemParams.find((e) => e.paramcode === '113')?.paramvalue === '1';
  170. const { currentbalance, usedmargin, freezemargin, freezecharge, otherfreezemargin, outamountfreeze } = account;
  171. const freeze = currentbalance - usedmargin - freezemargin - otherfreezemargin - freezecharge - outamountfreeze;
  172. if (positionProfitAndLoss.value < 0 || (positionProfitAndLoss.value >= 0 && flag)) {
  173. // 账户(总浮动盈亏为负) 或(总浮动盈亏为正 且 113 = 1)
  174. // 可用资金 = 总浮动盈亏 + 期末余额 - 占用 - 冻结 - 其它冻结 - 手续费冻结 - 出金冻结
  175. return positionProfitAndLoss.value + freeze;
  176. } else {
  177. // 可用资金 = 期末余额 - 占用 - 冻结 - 其它冻结 - 手续费冻结 - 出金冻结
  178. return freeze;
  179. }
  180. });
  181. // 计算权益/净值
  182. const equity = computed(() => {
  183. // 根据系统参数“307 账户净值是否减冻结资金 - 0:不减 1:减“
  184. const flag = systemParams.find((e) => e.paramcode === '307')?.paramvalue === '1';
  185. const result = account.currentbalance + marketCap.value + positionProfitAndLoss.value;
  186. if (flag) {
  187. const { otherfreezemargin, outamountfreeze } = account;
  188. // 净值 = 期末余额 + 市值(所有权) + 浮动盈亏(收益权) - 其它冻结 - 出金冻结
  189. return result - otherfreezemargin - outamountfreeze;
  190. } else {
  191. // 净值 = 期末余额 + 市值(所有权) + 浮动盈亏(收益权)
  192. return result;
  193. }
  194. })
  195. // 计算冻结资金
  196. const freezeAmount = computed(() => {
  197. const { freezemargin, otherfreezemargin, outamountfreeze, freezecharge } = account;
  198. // 冻结资金 = 冻结保证金 + 手续费冻结 + 出金冻结 + 其他冻结保证金
  199. return freezemargin + freezecharge + outamountfreeze + otherfreezemargin;
  200. })
  201. // 计算风险净值
  202. const valueAtRisk = computed(() => {
  203. // 根据系统参数“307 账户净值是否减冻结资金 - 0:不减 1:减“
  204. const flag = systemParams.find((e) => e.paramcode === '087')?.paramvalue === '1';
  205. const { otherfreezemargin, outamountfreeze } = account;
  206. const result = account.currentbalance + positionProfitAndLoss.value - otherfreezemargin - outamountfreeze;
  207. if (flag) {
  208. // 风险净值 = 期末余额 + 市值(所有权) + 浮动盈亏(收益权) - 其他冻结-出金冻结
  209. return result + marketCap.value;
  210. } else {
  211. // 风险净值 = 期末余额 + 浮动盈亏(收益权) - 其他冻结 - 出金冻结
  212. return result;
  213. }
  214. })
  215. // 计算风险率
  216. const hazardRatio = computed(() => {
  217. // 根据系统参数“132 风险率计算公式”:
  218. const flag = systemParams.find((e) => e.paramcode === '132')?.paramvalue === '1';
  219. const { usedmargin, mortgagecredit } = account;
  220. let result = 0
  221. if (flag) {
  222. // 风险率 = 占用 / 风险净值
  223. result = usedmargin / valueAtRisk.value;
  224. } else {
  225. // 风险率 = (占用 - 授信金额) / (风险净值 - 授信金额)
  226. result = (usedmargin - mortgagecredit) / (valueAtRisk.value - mortgagecredit);
  227. }
  228. return isNaN(result) ? 0 : result;
  229. })
  230. return {
  231. equity,
  232. positionProfitAndLoss,
  233. availableBalance,
  234. freezeAmount,
  235. hazardRatio,
  236. }
  237. }