using System.Collections.Generic; using System.Collections.ObjectModel; using Muchinfo.MTPClient.Data.Model; using Muchinfo.MTPClient.Infrastructure.Cache; using Muchinfo.MTPClient.Data.Enums; using Muchinfo.MTPClient.Data.Model.Account; using Muchinfo.MTPClient.Infrastructure.Utilities; using System; using System.Linq; namespace Muchinfo.MTPClient.Infrastructure.Helpers { /// /// Class FloatPLExpressions. /// public class FloatPLExpressions { /// /// 保留盈亏的小数位数 /// public const int c_figures = 2; /// /// 计算持仓单的浮动盈亏(没取到交易所时计算使用持仓均价计算) /// /// 持仓单 /// 计算的盈亏 public static decimal OrderDetailFloatPL(HoldingOrder openOrder, QuoteGoods goods) { decimal result = 0m; //var exchange = // CacheManager.CacheExchanges.FirstOrDefault( // (exchangeItem) => exchangeItem.ExchangeId == UserManager.CurrentTradeAccount.ExchangeId); var orderFlag = (openOrder.Direction == Direction.Ask) ? -1 : 1; if (goods == null || openOrder.ClosePrice <= 0) { return result; } var makeGoodsGroup = CacheManager.CacheMarketsGroups.FirstOrDefault((goodsGroup) => goodsGroup.MarketID == goods.GoodsParameters.SortId); if (makeGoodsGroup != null) { #region MTP2.0 注销 //var reckonMode = makeGoodsGroup.ReckonMode; //switch (reckonMode) //{ // case ReckonModeType.HadDebat: // result = (openOrder.ClosePrice * openOrder.Lot * orderFlag // * goods.GoodsParameters.AgreeUnit * // (goods.GoodsParameters.ReckonRate == 0 // ? 1 // : goods.GoodsParameters.ReckonRate)) - ( // openOrder.OpenPrice * openOrder.Lot * orderFlag // * goods.GoodsParameters.AgreeUnit * // (goods.GoodsParameters.ReckonRate == 0 // ? 1 // : goods.GoodsParameters.ReckonRate)); // break; // case ReckonModeType.NoDebat: // result = (openOrder.ClosePrice * openOrder.Lot * orderFlag // * goods.GoodsParameters.AgreeUnit * // (goods.GoodsParameters.ReckonRate == 0 // ? 1 // : goods.GoodsParameters.ReckonRate)) // - (openOrder.HoldingPrice * openOrder.Lot * orderFlag // * goods.GoodsParameters.AgreeUnit * // (goods.GoodsParameters.ReckonRate == 0 // ? 1 // : goods.GoodsParameters.ReckonRate)); // break; // default: // break; //} #endregion } else { result = (openOrder.ClosePrice - openOrder.HoldingPrice) * openOrder.Lot * orderFlag * goods.GoodsParameters.AgreeUnit * (goods.GoodsParameters.ReckonRate == 0 ? 1 : goods.GoodsParameters.ReckonRate); } return Math.Round(result, c_figures, MidpointRounding.AwayFromZero); } /// /// 计算持仓单的盈亏汇总 /// /// 持仓单 /// public static decimal CalcOrderDetailSumPL(HoldingOrder openOrder) { decimal result = 0m; if (UserManager.CurrentTradeAccount == null || openOrder.QuoteGoods == null || openOrder.QuoteGoods.GoodsParameters == null || openOrder.ClosePrice <= 0) { return result; } var orderFlag = (openOrder.Direction == Direction.Ask) ? -1 : 1; result = (openOrder.ClosePrice * openOrder.Lot * orderFlag * openOrder.QuoteGoods.GoodsParameters.AgreeUnit * (openOrder.QuoteGoods.GoodsParameters.ReckonRate == 0 ? 1 : openOrder.QuoteGoods.GoodsParameters.ReckonRate)) - ( openOrder.OpenPrice * openOrder.Lot * orderFlag * openOrder.QuoteGoods.GoodsParameters.AgreeUnit * (openOrder.QuoteGoods.GoodsParameters.ReckonRate == 0 ? 1 : openOrder.QuoteGoods.GoodsParameters.ReckonRate)); return Math.Round(result, c_figures, MidpointRounding.AwayFromZero); } /// /// 计算全额商品持仓市值 /// /// 持仓单 /// 持仓市值 public static decimal CalcHoldAmount(HoldingOrder openOrder, QuoteGoods goods) { decimal result = 0m; if (goods == null || goods.GoodsParameters == null) { return result; } else if (goods.GoodsParameters.MoneyMode == eMoneyMode.MONEYMODE_MARGIN) { return result; } else if (openOrder.ClosePrice <= 0) ////取不到昨收时,最新价时使用持仓金额 { return openOrder.HoldAmount; } result = (openOrder.ClosePrice * openOrder.Lot * goods.GoodsParameters.AgreeUnit * (goods.GoodsParameters.ReckonRate == 0 ? 1 : goods.GoodsParameters.ReckonRate)); return Math.Round(result, c_figures, MidpointRounding.AwayFromZero); } /// /// 计算平仓单的盈亏汇总 /// /// 持仓单 /// public static decimal CalcCloseOrderSumPL(CloseOrder closeOrder) { decimal result = 0m; var orderFlag = (closeOrder.Direction == Direction.Ask) ? 1 : -1; result = (closeOrder.ClosePrice * closeOrder.Lot * orderFlag * closeOrder.AgreeUnit) - (closeOrder.OpenPrice * closeOrder.Lot * orderFlag * closeOrder.AgreeUnit); return Math.Round(result, c_figures, MidpointRounding.AwayFromZero); } /// /// 可取资金 /// /// 阀值计算 /// 可取资金数量 public static decimal SetUseAmount(List ratios) { decimal sum = 0; if (UserManager.CurrentTradeAccount != null && UserManager.CurrentTradeAccount.FundsAccounts != null && UserManager.CurrentTradeAccount.FundsAccounts.Any()) { var ratiosThreshold = 0m; var currentNet = UserManager.CurrentTradeAccount.FundsAccounts[0].CurrentNetWorth; if (ratios != null && ratios.Any() && currentNet > 0) { for (int i = 0; i < ratios.Count; i++) //阈值比例计算的阈值=净值*阈值比例 { if (i == ratios.Count - 1) { ratiosThreshold = ratios[i].ThresholdRatio * currentNet; break; } else if (ratios[i].NetWorth <= currentNet && ratios[i + 1].NetWorth > currentNet) { ratiosThreshold = ratios[i].ThresholdRatio * currentNet; break; } } } var threshold = Math.Max(UserManager.CurrentTradeAccount.FundsAccounts[0].OutThreshold, UserManager.CurrentTradeAccount.FundsAccounts[0].MincurrentBalance); ////最低保证金,仓单冲抵最低保证金 threshold = Math.Max(threshold, ratiosThreshold); //5. 出金阈值=max(最低保证金+仓单冲抵最低保证金,阈值比例计算的阈值) threshold = Math.Max(threshold, UserManager.CurrentTradeAccount.FundsAccounts[0].ManualFreeze); //人工冻结 sum = UserManager.CurrentTradeAccount.FundsAccounts[0].Balance; ///可用 sum += UserManager.CurrentTradeAccount.FundsAccounts[0].OutAmount; sum -= UserManager.CurrentTradeAccount.FundsAccounts[0].OutAmountFreeze; var avail = UserManager.CurrentTradeAccount.FundsAccounts[0].ManualFreeze + UserManager.CurrentTradeAccount.FundsAccounts[0].AvailMargin; ///(冻结汇总减了一次ManualFreeze) if (UserManager.CurrentTradeAccount.FundsAccounts[0].IsAllowInMoney == 1) ////当天入金可出 { sum = Math.Min(sum + UserManager.CurrentTradeAccount.FundsAccounts[0].InAmount, avail); } else { sum = Math.Min(sum, avail); } sum -= threshold; ///可出-出金阈值 } sum = sum < 0 ? 0 : sum; return Math.Round(sum, 2, MidpointRounding.AwayFromZero); } /// /// 计算持仓汇总浮动盈亏(默认计算使用持仓均价计算)} /// /// 持仓汇总 /// 计算的盈亏 public static decimal PositionFloatPL(HoldingSummary holdingSummary) { decimal result = 0m; if (UserManager.CurrentTradeAccount == null || holdingSummary.QuoteGoods == null || holdingSummary.QuoteGoods.GoodsParameters == null) { return result; } var makeGoodsGroup = CacheManager.CacheMarketsGroups.FirstOrDefault((goodsGroup) => goodsGroup.MarketID == holdingSummary.QuoteGoods.GoodsParameters.SortId); var orderFlag = (holdingSummary.Direction == Direction.Ask) ? -1 : 1; if (makeGoodsGroup != null) { #region MTP2.0 注销 //var reckonMode = makeGoodsGroup.ReckonMode; //switch (reckonMode) //{ // case ReckonModeType.HadDebat: // result = (holdingSummary.ClosePrice - holdingSummary.AVGPrice) * holdingSummary.Lot * orderFlag // * holdingSummary.QuoteGoods.GoodsParameters.AgreeUnit; // break; // case ReckonModeType.NoDebat: // result = (holdingSummary.ClosePrice - holdingSummary.HoldingAVGPrice) * holdingSummary.Lot * orderFlag // * holdingSummary.QuoteGoods.GoodsParameters.AgreeUnit; // break; // default: // break; //} #endregion } else { result = (holdingSummary.ClosePrice - holdingSummary.HoldingAVGPrice) * holdingSummary.Lot * orderFlag * holdingSummary.QuoteGoods.GoodsParameters.AgreeUnit; } return result; } /// 计算全额浮动盈亏或保证金浮动盈亏 /// /// /// 全额浮动盈亏 /// 保证金浮动盈亏 public static void CalcFullPayPLWithSumPL(ObservableCollection holders, out decimal payPL, out decimal marginPL) { marginPL = 0; payPL = 0; foreach (var holdingOrder in holders) { if (holdingOrder.QuoteGoods != null && holdingOrder.QuoteGoods.GoodsParameters.MoneyMode == eMoneyMode.MONEYMODE_PAY) { payPL += holdingOrder.PLFloat; } else { marginPL += holdingOrder.PLFloat; } } } #region MTP2.0 /// /// 更新账户的总浮动盈亏 /// /// System.Decimal. public static void UpdateAccountTotalFloatPL() { if (UserManager.CurrentTradeAccount.FundsAccounts != null && UserManager.CurrentTradeAccount.FundsAccounts.Count > 0) { foreach (var accountId in UserManager.CurrentTradeAccount.FundsAccountIds) { decimal sum = 0; var fundsAccount = UserManager.CurrentTradeAccount.FundsAccounts.FirstOrDefault(z => z.AccountId == accountId); if (fundsAccount == null) continue; //查询持仓汇总缓存-通用查询回来的对象 var holders = UserManager.GetHoldSummaries(accountId); if (holders == null) continue; foreach (var item in holders) { var quoteGoods = CacheManager.CacheGoodsBaseInfos.FirstOrDefault(p => p.GoodsId == item.GoodsId); CalcFloatPL(quoteGoods, item, true); sum += item.PLFloatWithRate; } fundsAccount.FloatPL = sum; } } } /// /// Calculates the float pl by quote goods. /// /// The quote goods. /// The holding summaries. public static void CalcFloatPLByQuoteGoods(QuoteGoods quoteGoods, ObservableCollection holdingSummaries) { if (quoteGoods == null || holdingSummaries == null || !holdingSummaries.Any()) return; foreach (var item in holdingSummaries) { CalcFloatPL(quoteGoods, item, false); } } /// /// Calculates the float pl. /// /// The holding summaries. public static void CalcFloatPL(ObservableCollection holdingSummaries) { if (holdingSummaries == null || !holdingSummaries.Any()) return; foreach (var item in holdingSummaries) { var quoteGoods = CacheManager.CacheGoodsBaseInfos.FirstOrDefault(p => p.GoodsId == item.GoodsId); CalcFloatPL(quoteGoods, item, false); } } /// /// Updates the float pl. MTP2.0 统一计算持仓盈亏 /// /// The quote goods. /// /// Calculates the float pl. /// /// The quote goods. /// The holding summary. /// 是否需要算汇率 private static void CalcFloatPL(QuoteGoods quoteGoods, HoldingSummary holdingSummary, Boolean needRate = false) { if (quoteGoods == null || holdingSummary == null || holdingSummary.GoodsId != quoteGoods.GoodsId) return; if (needRate) { //// 计算汇率,主要用于资金账户盈亏计算 //var tradeRateConfig = UserManager.TradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == holdingSummary.AccountId && z.OriCurrencyId == quoteGoods.Currencyid); //// 如果用当前资金账户找不到,再尝试用所属母账户获取汇率配置 //if (tradeRateConfig == null && UserManager.CurrentTradeAccount.FundsAccounts.Count > 0 && UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID != 0) //{ // tradeRateConfig = UserManager.TradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID && z.OriCurrencyId == quoteGoods.Currencyid); //} //// 如果还是找不到,则再尝试从外部汇率模版配置表获取 //tradeRateConfig = UserManager.ExtenalTradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == holdingSummary.AccountId && z.OriCurrencyId == quoteGoods.Currencyid); //if (tradeRateConfig == null && UserManager.CurrentTradeAccount.FundsAccounts.Count > 0 && UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID != 0) //{ // tradeRateConfig = UserManager.ExtenalTradeRateTmpConfigs.FirstOrDefault(z => z.AccountId == UserManager.CurrentTradeAccount.FundsAccounts[0].ParentAccountID && z.OriCurrencyId == quoteGoods.Currencyid); //} // 从新的资金账户汇率查询中获取汇率 TradeRateTmpConfig tradeRateConfig = null; if (UserManager.TaAccountExchangeRateConfigs != null) { tradeRateConfig = UserManager.TaAccountExchangeRateConfigs.FirstOrDefault(z => z.AccountId == holdingSummary.AccountId && z.OriCurrencyId == quoteGoods.Currencyid); } var tradeRate = tradeRateConfig == null ? 1 : tradeRateConfig.ExchangeRate; holdingSummary.QuoteGoods = quoteGoods; holdingSummary.LatestPrice = holdingSummary.QuoteGoods.CurrentPrice != 0 ? holdingSummary.QuoteGoods.CurrentPrice : holdingSummary.QuoteGoods.LastClose; if (holdingSummary.Lot > 0 && holdingSummary.LatestPrice != 0) { if (holdingSummary.Direction == Direction.Ask) holdingSummary.PLFloatWithRate = (holdingSummary.CurHolderAmount - (holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit)) * tradeRate; else if (holdingSummary.Direction == Direction.Bid) holdingSummary.PLFloatWithRate = ((holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit) - holdingSummary.CurHolderAmount) * tradeRate; } } else { // 不计算汇率,用于持仓汇总盈亏计算 holdingSummary.QuoteGoods = quoteGoods; holdingSummary.LatestPrice = holdingSummary.QuoteGoods.CurrentPrice != 0 ? holdingSummary.QuoteGoods.CurrentPrice : holdingSummary.QuoteGoods.LastClose; if (holdingSummary.Lot > 0 && holdingSummary.LatestPrice != 0) { if (holdingSummary.Direction == Direction.Ask) holdingSummary.PLFloat = holdingSummary.CurHolderAmount - (holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit); else if (holdingSummary.Direction == Direction.Bid) holdingSummary.PLFloat = (holdingSummary.Lot * holdingSummary.LatestPrice * holdingSummary.AgreeUnit) - holdingSummary.CurHolderAmount; // 错误 #92030 计算开仓浮动盈亏 holdingSummary.OpenProfitLoss = (holdingSummary.LatestPrice - holdingSummary.OpenAvaPrice) * holdingSummary.AvailQty * holdingSummary.AgreeUnit * (holdingSummary.Direction == Direction.Ask ? -1 : 1); } } } #endregion #region HoldingOrder20的持仓盈亏计算方式 public static void CalcPl(HoldingOrder20 order20, QuoteGoods quoteGoods) { if (order20 == null || quoteGoods == null) { return; } // 不计算汇率,用于持仓汇总盈亏计算 order20.QuoteGoods = quoteGoods; order20.LastPrice = order20.QuoteGoods.CurrentPrice != 0 ? order20.QuoteGoods.CurrentPrice : order20.QuoteGoods.LastClose; if (order20.HolderQty > 0 && order20.LastPrice != 0) { if (order20.Direction == Direction.Ask) order20.ProfitLoss = order20.HolderAmount - (order20.HolderQty * order20.LastPrice * order20.QuoteGoods.AgreeUnit); else if (order20.Direction == Direction.Bid) order20.ProfitLoss = (order20.HolderQty * order20.LastPrice * order20.QuoteGoods.AgreeUnit) - order20.HolderAmount; } } #endregion } }