FloatPLExpressions.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. using System.Collections.Generic;
  2. using System.Collections.ObjectModel;
  3. using Muchinfo.MTPClient.Data.Model;
  4. using Muchinfo.MTPClient.Infrastructure.Cache;
  5. using Muchinfo.MTPClient.Data.Enums;
  6. using Muchinfo.MTPClient.Data.Model.Account;
  7. using Muchinfo.MTPClient.Infrastructure.Utilities;
  8. using System;
  9. using System.Linq;
  10. namespace Muchinfo.MTPClient.Infrastructure.Helpers
  11. {
  12. /// <summary>
  13. /// Class FloatPLExpressions.
  14. /// </summary>
  15. public class FloatPLExpressions
  16. {
  17. /// <summary>
  18. /// 保留盈亏的小数位数
  19. /// </summary>
  20. public const int c_figures = 2;
  21. /// <summary>
  22. /// 计算持仓单的浮动盈亏(没取到交易所时计算使用持仓均价计算)
  23. /// </summary>
  24. /// <param name="openOrder">持仓单</param>
  25. /// <returns>计算的盈亏</returns>
  26. public static decimal OrderDetailFloatPL(HoldingOrder openOrder, QuoteGoods goods)
  27. {
  28. decimal result = 0m;
  29. //var exchange =
  30. // CacheManager.CacheExchanges.FirstOrDefault(
  31. // (exchangeItem) => exchangeItem.ExchangeId == UserManager.CurrentTradeAccount.ExchangeId);
  32. var orderFlag = (openOrder.Direction == Direction.Ask) ? -1 : 1;
  33. if (goods == null || openOrder.ClosePrice <= 0)
  34. {
  35. return result;
  36. }
  37. var makeGoodsGroup = CacheManager.CacheMarketsGroups.FirstOrDefault((goodsGroup) => goodsGroup.MarketID == goods.GoodsParameters.SortId);
  38. if (makeGoodsGroup != null)
  39. {
  40. #region MTP2.0 注销
  41. //var reckonMode = makeGoodsGroup.ReckonMode;
  42. //switch (reckonMode)
  43. //{
  44. // case ReckonModeType.HadDebat:
  45. // result = (openOrder.ClosePrice * openOrder.Lot * orderFlag
  46. // * goods.GoodsParameters.AgreeUnit *
  47. // (goods.GoodsParameters.ReckonRate == 0
  48. // ? 1
  49. // : goods.GoodsParameters.ReckonRate)) - (
  50. // openOrder.OpenPrice * openOrder.Lot * orderFlag
  51. // * goods.GoodsParameters.AgreeUnit *
  52. // (goods.GoodsParameters.ReckonRate == 0
  53. // ? 1
  54. // : goods.GoodsParameters.ReckonRate));
  55. // break;
  56. // case ReckonModeType.NoDebat:
  57. // result = (openOrder.ClosePrice * openOrder.Lot * orderFlag
  58. // * goods.GoodsParameters.AgreeUnit *
  59. // (goods.GoodsParameters.ReckonRate == 0
  60. // ? 1
  61. // : goods.GoodsParameters.ReckonRate))
  62. // - (openOrder.HoldingPrice * openOrder.Lot * orderFlag
  63. // * goods.GoodsParameters.AgreeUnit *
  64. // (goods.GoodsParameters.ReckonRate == 0
  65. // ? 1
  66. // : goods.GoodsParameters.ReckonRate));
  67. // break;
  68. // default:
  69. // break;
  70. //}
  71. #endregion
  72. }
  73. else
  74. {
  75. result = (openOrder.ClosePrice - openOrder.HoldingPrice) * openOrder.Lot * orderFlag
  76. * goods.GoodsParameters.AgreeUnit *
  77. (goods.GoodsParameters.ReckonRate == 0
  78. ? 1
  79. : goods.GoodsParameters.ReckonRate);
  80. }
  81. return Math.Round(result, c_figures, MidpointRounding.AwayFromZero);
  82. }
  83. /// <summary>
  84. /// 计算持仓单的盈亏汇总
  85. /// </summary>
  86. /// <param name="openOrder">持仓单</param>
  87. /// <returns></returns>
  88. public static decimal CalcOrderDetailSumPL(HoldingOrder openOrder)
  89. {
  90. decimal result = 0m;
  91. if (UserManager.CurrentTradeAccount == null || openOrder.QuoteGoods == null ||
  92. openOrder.QuoteGoods.GoodsParameters == null || openOrder.ClosePrice <= 0)
  93. {
  94. return result;
  95. }
  96. var orderFlag = (openOrder.Direction == Direction.Ask) ? -1 : 1;
  97. result = (openOrder.ClosePrice * openOrder.Lot * orderFlag
  98. * openOrder.QuoteGoods.GoodsParameters.AgreeUnit *
  99. (openOrder.QuoteGoods.GoodsParameters.ReckonRate == 0
  100. ? 1
  101. : openOrder.QuoteGoods.GoodsParameters.ReckonRate)) - (
  102. openOrder.OpenPrice * openOrder.Lot * orderFlag
  103. * openOrder.QuoteGoods.GoodsParameters.AgreeUnit *
  104. (openOrder.QuoteGoods.GoodsParameters.ReckonRate == 0
  105. ? 1
  106. : openOrder.QuoteGoods.GoodsParameters.ReckonRate));
  107. return Math.Round(result, c_figures, MidpointRounding.AwayFromZero);
  108. }
  109. /// <summary>
  110. /// 计算全额商品持仓市值
  111. /// </summary>
  112. /// <param name="openOrder">持仓单</param>
  113. /// <returns>持仓市值</returns>
  114. public static decimal CalcHoldAmount(HoldingOrder openOrder, QuoteGoods goods)
  115. {
  116. decimal result = 0m;
  117. if (goods == null || goods.GoodsParameters == null)
  118. {
  119. return result;
  120. }
  121. else if (goods.GoodsParameters.MoneyMode ==
  122. eMoneyMode.MONEYMODE_MARGIN)
  123. {
  124. return result;
  125. }
  126. else if (openOrder.ClosePrice <= 0) ////取不到昨收时,最新价时使用持仓金额
  127. {
  128. return openOrder.HoldAmount;
  129. }
  130. result = (openOrder.ClosePrice * openOrder.Lot
  131. * goods.GoodsParameters.AgreeUnit *
  132. (goods.GoodsParameters.ReckonRate == 0
  133. ? 1
  134. : goods.GoodsParameters.ReckonRate));
  135. return Math.Round(result, c_figures, MidpointRounding.AwayFromZero);
  136. }
  137. /// <summary>
  138. /// 计算平仓单的盈亏汇总
  139. /// </summary>
  140. /// <param name="openOrder">持仓单</param>
  141. /// <returns></returns>
  142. public static decimal CalcCloseOrderSumPL(CloseOrder closeOrder)
  143. {
  144. decimal result = 0m;
  145. var orderFlag = (closeOrder.Direction == Direction.Ask) ? 1 : -1;
  146. result = (closeOrder.ClosePrice * closeOrder.Lot * orderFlag
  147. * closeOrder.AgreeUnit) - (closeOrder.OpenPrice * closeOrder.Lot * orderFlag
  148. * closeOrder.AgreeUnit);
  149. return Math.Round(result, c_figures, MidpointRounding.AwayFromZero);
  150. }
  151. /// <summary>
  152. /// 可取资金
  153. /// </summary>
  154. /// <param name="ratios">阀值计算</param>
  155. /// <returns>可取资金数量</returns>
  156. public static decimal SetUseAmount(List<OutMoneyThresholdRatio> ratios)
  157. {
  158. decimal sum = 0;
  159. if (UserManager.CurrentTradeAccount != null &&
  160. UserManager.CurrentTradeAccount.FundsAccounts != null &&
  161. UserManager.CurrentTradeAccount.FundsAccounts.Any())
  162. {
  163. var ratiosThreshold = 0m;
  164. var currentNet = UserManager.CurrentTradeAccount.FundsAccounts[0].CurrentNetWorth;
  165. if (ratios != null && ratios.Any() && currentNet > 0)
  166. {
  167. for (int i = 0; i < ratios.Count; i++) //阈值比例计算的阈值=净值*阈值比例
  168. {
  169. if (i == ratios.Count - 1)
  170. {
  171. ratiosThreshold = ratios[i].ThresholdRatio * currentNet;
  172. break;
  173. }
  174. else if (ratios[i].NetWorth <= currentNet && ratios[i + 1].NetWorth > currentNet)
  175. {
  176. ratiosThreshold = ratios[i].ThresholdRatio * currentNet;
  177. break;
  178. }
  179. }
  180. }
  181. var threshold = Math.Max(UserManager.CurrentTradeAccount.FundsAccounts[0].OutThreshold,
  182. UserManager.CurrentTradeAccount.FundsAccounts[0].MincurrentBalance); ////最低保证金,仓单冲抵最低保证金
  183. threshold = Math.Max(threshold, ratiosThreshold); //5. 出金阈值=max(最低保证金+仓单冲抵最低保证金,阈值比例计算的阈值)
  184. threshold = Math.Max(threshold, UserManager.CurrentTradeAccount.FundsAccounts[0].ManualFreeze); //人工冻结
  185. sum = UserManager.CurrentTradeAccount.FundsAccounts[0].Balance; ///可用
  186. sum += UserManager.CurrentTradeAccount.FundsAccounts[0].OutAmount;
  187. sum -= UserManager.CurrentTradeAccount.FundsAccounts[0].OutAmountFreeze;
  188. var avail = UserManager.CurrentTradeAccount.FundsAccounts[0].ManualFreeze + UserManager.CurrentTradeAccount.FundsAccounts[0].AvailMargin; ///(冻结汇总减了一次ManualFreeze)
  189. if (UserManager.CurrentTradeAccount.FundsAccounts[0].IsAllowInMoney == 1) ////当天入金可出
  190. {
  191. sum = Math.Min(sum + UserManager.CurrentTradeAccount.FundsAccounts[0].InAmount, avail);
  192. }
  193. else
  194. {
  195. sum = Math.Min(sum, avail);
  196. }
  197. sum -= threshold; ///可出-出金阈值
  198. }
  199. sum = sum < 0 ? 0 : sum;
  200. return Math.Round(sum, 2, MidpointRounding.AwayFromZero);
  201. }
  202. /// <summary>
  203. /// 计算持仓汇总浮动盈亏(默认计算使用持仓均价计算)}
  204. /// </summary>
  205. /// <param name="holdingSummary">持仓汇总</param>
  206. /// <returns>计算的盈亏</returns>
  207. public static decimal PositionFloatPL(HoldingSummary holdingSummary)
  208. {
  209. decimal result = 0m;
  210. if (UserManager.CurrentTradeAccount == null || holdingSummary.QuoteGoods == null ||
  211. holdingSummary.QuoteGoods.GoodsParameters == null)
  212. {
  213. return result;
  214. }
  215. var makeGoodsGroup = CacheManager.CacheMarketsGroups.FirstOrDefault((goodsGroup) => goodsGroup.MarketID == holdingSummary.QuoteGoods.GoodsParameters.SortId);
  216. var orderFlag = (holdingSummary.Direction == Direction.Ask) ? -1 : 1;
  217. if (makeGoodsGroup != null)
  218. {
  219. #region MTP2.0 注销
  220. //var reckonMode = makeGoodsGroup.ReckonMode;
  221. //switch (reckonMode)
  222. //{
  223. // case ReckonModeType.HadDebat:
  224. // result = (holdingSummary.ClosePrice - holdingSummary.AVGPrice) * holdingSummary.Lot * orderFlag
  225. // * holdingSummary.QuoteGoods.GoodsParameters.AgreeUnit;
  226. // break;
  227. // case ReckonModeType.NoDebat:
  228. // result = (holdingSummary.ClosePrice - holdingSummary.HoldingAVGPrice) * holdingSummary.Lot * orderFlag
  229. // * holdingSummary.QuoteGoods.GoodsParameters.AgreeUnit;
  230. // break;
  231. // default:
  232. // break;
  233. //}
  234. #endregion
  235. }
  236. else
  237. {
  238. result = (holdingSummary.ClosePrice - holdingSummary.HoldingAVGPrice) * holdingSummary.Lot * orderFlag
  239. * holdingSummary.QuoteGoods.GoodsParameters.AgreeUnit;
  240. }
  241. return result;
  242. }
  243. /// 计算全额浮动盈亏或保证金浮动盈亏
  244. /// </summary>
  245. /// <param name="holders"></param>
  246. /// <param name="payPL">全额浮动盈亏</param>
  247. /// <param name="marginPL">保证金浮动盈亏</param>
  248. public static void CalcFullPayPLWithSumPL(ObservableCollection<HoldingOrder> holders, out decimal payPL, out decimal marginPL)
  249. {
  250. marginPL = 0;
  251. payPL = 0;
  252. foreach (var holdingOrder in holders)
  253. {
  254. if (holdingOrder.QuoteGoods != null && holdingOrder.QuoteGoods.GoodsParameters.MoneyMode ==
  255. eMoneyMode.MONEYMODE_PAY)
  256. {
  257. payPL += holdingOrder.PLFloat;
  258. }
  259. else
  260. {
  261. marginPL += holdingOrder.PLFloat;
  262. }
  263. }
  264. }
  265. #region MTP2.0
  266. /// <summary>
  267. /// 更新账户的总浮动盈亏
  268. /// </summary>
  269. /// <returns>System.Decimal.</returns>
  270. public static void UpdateAccountTotalFloatPL()
  271. {
  272. if (UserManager.CurrentTradeAccount.FundsAccounts != null &&
  273. UserManager.CurrentTradeAccount.FundsAccounts.Count > 0)
  274. {
  275. foreach (var accountId in UserManager.CurrentTradeAccount.FundsAccountIds)
  276. {
  277. decimal sum = 0;
  278. var fundsAccount = UserManager.CurrentTradeAccount.FundsAccounts.FirstOrDefault(z => z.AccountId == accountId);
  279. if (fundsAccount == null) continue;
  280. //查询持仓汇总缓存-通用查询回来的对象
  281. var holders = UserManager.GetHoldSummaries(accountId);
  282. if (holders == null) continue;
  283. foreach (var item in holders)
  284. {
  285. var quoteGoods = CacheManager.CacheGoodsBaseInfos.FirstOrDefault(p => p.GoodsId == item.GoodsId);
  286. CalcFloatPL(quoteGoods, item, true);
  287. sum += item.PLFloatWithRate;
  288. }
  289. fundsAccount.FloatPL = sum;
  290. }
  291. }
  292. }
  293. /// <summary>
  294. /// Calculates the float pl by quote goods.
  295. /// </summary>
  296. /// <param name="quoteGoods">The quote goods.</param>
  297. /// <param name="holdingSummaries">The holding summaries.</param>
  298. public static void CalcFloatPLByQuoteGoods(QuoteGoods quoteGoods, ObservableCollection<HoldingSummary> holdingSummaries)
  299. {
  300. if (quoteGoods == null || holdingSummaries == null || !holdingSummaries.Any()) return;
  301. foreach (var item in holdingSummaries)
  302. {
  303. CalcFloatPL(quoteGoods, item, false);
  304. }
  305. }
  306. /// <summary>
  307. /// Calculates the float pl.
  308. /// </summary>
  309. /// <param name="holdingSummaries">The holding summaries.</param>
  310. public static void CalcFloatPL(ObservableCollection<HoldingSummary> holdingSummaries)
  311. {
  312. if (holdingSummaries == null || !holdingSummaries.Any()) return;
  313. foreach (var item in holdingSummaries)
  314. {
  315. var quoteGoods = CacheManager.CacheGoodsBaseInfos.FirstOrDefault(p => p.GoodsId == item.GoodsId);
  316. CalcFloatPL(quoteGoods, item, false);
  317. }
  318. }
  319. /// <summary>
  320. /// Updates the float pl. MTP2.0 统一计算持仓盈亏
  321. /// </summary>
  322. /// <param name="quoteGoods">The quote goods.</param>
  323. /// <summary>
  324. /// Calculates the float pl.
  325. /// </summary>
  326. /// <param name="quoteGoods">The quote goods.</param>
  327. /// <param name="holdingSummary">The holding summary.</param>
  328. /// <param name="needRate">是否需要算汇率</param>
  329. private static void CalcFloatPL(QuoteGoods quoteGoods, HoldingSummary holdingSummary, Boolean needRate = false)
  330. {
  331. if (quoteGoods == null || holdingSummary == null || holdingSummary.GoodsId != quoteGoods.GoodsId) return;
  332. if (needRate)
  333. {
  334. //// 计算汇率,主要用于资金账户盈亏计算
  335. //var tradeRateConfig = UserManager.TradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == holdingSummary.AccountId && z.OriCurrencyId == quoteGoods.Currencyid);
  336. //// 如果用当前资金账户找不到,再尝试用所属母账户获取汇率配置
  337. //if (tradeRateConfig == null && UserManager.CurrentTradeAccount.FundsAccounts.Count > 0 && UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID != 0)
  338. //{
  339. // tradeRateConfig = UserManager.TradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID && z.OriCurrencyId == quoteGoods.Currencyid);
  340. //}
  341. //// 如果还是找不到,则再尝试从外部汇率模版配置表获取
  342. //tradeRateConfig = UserManager.ExtenalTradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == holdingSummary.AccountId && z.OriCurrencyId == quoteGoods.Currencyid);
  343. //if (tradeRateConfig == null && UserManager.CurrentTradeAccount.FundsAccounts.Count > 0 && UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID != 0)
  344. //{
  345. // tradeRateConfig = UserManager.ExtenalTradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID && z.OriCurrencyId == quoteGoods.Currencyid);
  346. //}
  347. // 从新的资金账户汇率查询中获取汇率
  348. TradeRateTmpConfig tradeRateConfig = null;
  349. if (UserManager.TaAccountExchangeRateConfigs != null)
  350. {
  351. tradeRateConfig = UserManager.TaAccountExchangeRateConfigs.FirstOrDefault(z => z.AccountId == holdingSummary.AccountId && z.OriCurrencyId == quoteGoods.Currencyid);
  352. }
  353. var tradeRate = tradeRateConfig == null ? 1 : tradeRateConfig.ExchangeRate;
  354. holdingSummary.QuoteGoods = quoteGoods;
  355. holdingSummary.LatestPrice = holdingSummary.QuoteGoods.CurrentPrice != 0 ? holdingSummary.QuoteGoods.CurrentPrice : holdingSummary.QuoteGoods.LastClose;
  356. if (holdingSummary.Lot > 0 && holdingSummary.LatestPrice != 0)
  357. {
  358. if (holdingSummary.Direction == Direction.Ask)
  359. holdingSummary.PLFloatWithRate = (holdingSummary.CurHolderAmount - (holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit)) * tradeRate;
  360. else if (holdingSummary.Direction == Direction.Bid)
  361. holdingSummary.PLFloatWithRate = ((holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit) - holdingSummary.CurHolderAmount) * tradeRate;
  362. }
  363. }
  364. else
  365. {
  366. // 不计算汇率,用于持仓汇总盈亏计算
  367. holdingSummary.QuoteGoods = quoteGoods;
  368. holdingSummary.LatestPrice = holdingSummary.QuoteGoods.CurrentPrice != 0 ? holdingSummary.QuoteGoods.CurrentPrice : holdingSummary.QuoteGoods.LastClose;
  369. if (holdingSummary.Lot > 0 && holdingSummary.LatestPrice != 0)
  370. {
  371. if (holdingSummary.Direction == Direction.Ask)
  372. holdingSummary.PLFloat = holdingSummary.CurHolderAmount - (holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit);
  373. else if (holdingSummary.Direction == Direction.Bid)
  374. holdingSummary.PLFloat = (holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit) - holdingSummary.CurHolderAmount;
  375. // 错误 #92030 计算开仓浮动盈亏
  376. holdingSummary.OpenProfitLoss = (holdingSummary.LatestPrice - holdingSummary.OpenAvaPrice) * holdingSummary.AvailQty * holdingSummary.AgreeUnit * (holdingSummary.Direction == Direction.Ask ? -1 : 1);
  377. }
  378. }
  379. }
  380. #endregion
  381. #region HoldingOrder20的持仓盈亏计算方式
  382. public static void CalcPl(HoldingOrder20 order20, QuoteGoods quoteGoods)
  383. {
  384. if (order20 == null || quoteGoods == null)
  385. {
  386. return;
  387. }
  388. // 不计算汇率,用于持仓汇总盈亏计算
  389. order20.QuoteGoods = quoteGoods;
  390. order20.LastPrice = order20.QuoteGoods.CurrentPrice != 0 ? order20.QuoteGoods.CurrentPrice : order20.QuoteGoods.LastClose;
  391. if (order20.HolderQty > 0 && order20.LastPrice != 0)
  392. {
  393. if (order20.Direction == Direction.Ask)
  394. order20.ProfitLoss = order20.HolderAmount - (order20.HolderQty * order20.LastPrice * order20.QuoteGoods.AgreeUnit);
  395. else if (order20.Direction == Direction.Bid)
  396. order20.ProfitLoss = (order20.HolderQty * order20.LastPrice * order20.QuoteGoods.AgreeUnit) - order20.HolderAmount;
  397. }
  398. }
  399. #endregion
  400. }
  401. }