Files
zszq-trs/YLErpDAL/BLL/EodSettlement/RealtimePnlCalc.cs
T

2462 lines
146 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using CsvHelper;
using DocumentFormat.OpenXml.Bibliography;
using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Drawing.Charts;
using DocumentFormat.OpenXml.Spreadsheet;
using iTextSharp.text.pdf.security;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Math.EC.Multiplier;
using Qdp.Pricing.Base.Implementations;
using System.IO;
using System.Security.Cryptography.Xml;
using YLErp.Abstract;
using YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation;
using YLErp.BLL.MarginCalculation;
using YLErp.Cache;
using YLErp.Commons;
using YLErp.DataBase;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Models;
using YLErp.Modules;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.EodModule.QueryModule;
using YLErp.Modules.SwapModule;
using YLErp.Modules.TradeDalModule;
using YLErp.QdpModule;
namespace YLErp.BLL.Eod
{
public class RealtimePnlCalc
{
public static bool IsListOld = true;
protected static object _RefrshLock = new object();
private static IYLCache _yLCache;
private static IKafkaProduce kafkaProduceHelper;
///系统参数
protected static valuedate _SystemDate = null;
///当前计算日期
protected static DateTime _ValueDate = DateTime.MinValue;
///最后交易日
protected static DateTime _LastSettleDate = DateTime.MinValue;
///最后结算日日终持仓信息
protected static List<eod_trade_position> _LastTradePositionList = new List<eod_trade_position>();
//
protected static List<trade> _TradeAllList;
protected static List<trade> _TradeSwapList;
protected static DateTime _TradeLastUpdateTime;
static IYcLogger _logger = LogFactory.GetLogger("RealtimePnlCalc");
/// 设置为false时 节假日当天
protected static bool IsFirstHoliday = true;
public static void InitCache(IYLCache yLCache)
{
_yLCache = yLCache;
}
public static void InitKafka(IKafkaProduce kafkaProduce)
{
kafkaProduceHelper = kafkaProduce;
}
/// <summary>
/// 刷新当日静态数据
/// </summary>
public static void RestInitData()
{
//刷新系统日期
valuedateBLL.ResetValueDate();
var isReload = false;
if (_ValueDate == DateTime.MinValue || _ValueDate != valuedateBLL.ValueDate)
{
isReload = true;
_SystemDate = valuedateBLL.SystemDate;
_ValueDate = _SystemDate.ValueDate;
}
InitLastTradePosition(isReload);
//增量初始化交易信息
InitTradeList();
}
/// <summary>
/// 初始化最后结算日结算信息
/// </summary>
private static void InitLastTradePosition(bool isReload)
{
if (isReload)
{
//获取昨日持仓
_LastSettleDate = EodOperationBase.GetLastSettlementDate(_ValueDate, true);
//获取昨日持仓
_LastTradePositionList = new EodSettlementService(OptUserInfo.SystemUser).GetEodTradePosition<eod_trade_position>(_LastSettleDate).ToList();
}
}
/// <summary>
/// 初始化交易信息
/// </summary>
private static void InitTradeList()
{
using (var db = new YLContext())
{
//获取停牌信息
var suspensionUnderlyingIdList = DataCacheProvider.GetUnderlyingDataSource().AsQueryable()
.Where(t => t.UnderlyingStatus == underlying_manager.Status_Suspension).Select(t => t.id).ToList();
//昨日持仓交易
var tradeIds = _LastTradePositionList.Where(t => 0 != t.TradeId).Select(t => t.TradeId).ToList();
//初始化待交易交易列表
_TradeAllList = db.trade.AsNoTracking().Where(t => t.ValidState != "InValid" && t.TradeDate <= _ValueDate && ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus)).ToList(); //有效的交易
//if (PS.Config.IsGuoJun)
//{
// _TradeAllList = _TradeAllList.Where(x=>x.TradeType!="收益互换").ToList();
//}
if (_TradeAllList != null && _TradeAllList.Any())
{
var allTradeIds = _TradeAllList.Select(d => d.id).Distinct().ToList();
var metaList = db.TradeMeta.AsNoTracking().Where(d => allTradeIds.Contains(d.TradeId)).ToList();
_TradeAllList.ForEach(p =>
{
p.MetaDic = metaList.Where(x => x.TradeId == p.id).ToDictionary(g => g.MetaKey, g => g.MetaValue);
});
}
_TradeSwapList = _TradeAllList.Where(x => x.TradeType == "收益互换").ToList();
_TradeAllList = _TradeAllList.Where(x => x.TradeType != "收益互换").ToList();
}
}
/// <summary>
/// 计算实时持仓并返回结果
/// </summary>
public static List<intraday_trade_position> RealtimePosition(OptUserInfo optUser)
{
if (optUser is null)
{
throw new ArgumentNullException(nameof(optUser));
}
lock (_RefrshLock)
{
using (var db = new YLContext())
{
RestInitData();
#region 获取昨日持仓 标的当前价格 当前持仓以及当日行权交易 并计算Risk信息
//标的
var underlyingPrice = DataCacheProvider.GetUnderlyingDataSource();
//获取待计算交列表
var tradeList = _TradeAllList;
var tradeIdList = tradeList.Select(t => t.id).ToList();
//设置option info
tradeBLL.SetFieldsByTradeType(tradeList);
//OTC 交易
var otcTradeList = tradeList.Where(t => !ConsTrade.TradeTypesForHedge.Contains(t.TradeType)).ToList();
new TradeDalService(optUser).SetSubTradeList(otcTradeList);
//OTC持仓交易
//过滤结构化交易,不参与计算预付金逻辑
var positionTradeList = otcTradeList.Where(t => ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus) && t.TradeType != "结构化交易").ToList();
//还未了结的交易
var liveTradeList = otcTradeList.Where(t => ConsTrade.LiveTradeStatusList.Contains(t.TradeStatus)).ToList();
//预付金计算
var req = new RunMarginCalculationReq(optUser)
{
tradeList = positionTradeList,
settleDate = _ValueDate,
PriceProvider = underlyingPrice,
hasOptionInfo = true
};
var tradeSpans = MarginDefault.RunMarginCalculation(req.Clone(forOtherSide: false));
//试算出交易买卖方向反向的tradeSpan(目前只有国君有这个需求,做过处理)
List<trade_span> tradeSpansOtherSide = new List<trade_span>();
if (MarginDefault.IsMarginCalcNeedSpecial(_ValueDate))
{
//试算出交易买卖方向反向的tradeSpan(目前只有国君有这个需求,做过处理)
tradeSpansOtherSide = MarginDefault.RunMarginCalculation(req.Clone(forOtherSide: true));
}
//根据交易预付金合计客户预付金计入client_span
var calcClientMarginReq = new CalcClientMarginReq(optUser)
{
settleDate = _ValueDate,
tradeSpans = tradeSpans,
tradeSpansOtherSide = tradeSpansOtherSide,
SpanType = ClientSpan.SpanType_RealTime
};
tradeSpans = MarginDefault.CalcClientMargin(calcClientMarginReq);
if (PS.Config.Company == Configuration.CompanyEnum.渤海)
{
//预付金计算
tradeSpans = MarginDefault.RunMarginCalculation(req.Clone(forOtherSide: false));
}
//实时计算rsik
var volTypes = new List<string> { "持仓" };
if (PS.Config.Is国投)
{
volTypes.Add("开仓");
}
var riskList = RealTimeRiskCalc(_ValueDate, liveTradeList, underlyingPrice, volTypes);
#endregion
#region 平仓费行权费 交易预付金计算 昨日持仓对冲交易过滤
var unWindTradeCashAction = new List<string> { ClientCashInCashOut.系统操作_平仓费, ClientCashInCashOut.系统操作_行权费, ClientCashInCashOut.系统操作_期权费 };
//获取平仓到期交易的trade_cash;
var tradeCashList = db.trade_cash.Where(t => tradeIdList.Contains(t.TradeId) && unWindTradeCashAction.Contains(t.Action) && t.ValidState != "InValid" && !t.IsDeleted).ToList();
//实时结果对象类
var realtimePositionList = new List<intraday_trade_position>();
//旧持仓对冲交易
var lastHedgingPositionList = _LastTradePositionList.Where(t => ConsTrade.TradeTypesForHedge.Contains(t.TradeType)).ToList();
#endregion
#region 处理昨日持仓今日为未交易股票 商品期货
//2021-02-02 实时持仓服务不再计算对冲交易,统一从 trade_Position 获取;
////对冲 交易
//var hedgeTradeList = db.ExchangeTrade.Where(t => t.IsValid && t.TradeDate <= _ValueDate && t.TradeDate > _LastSettleDate).ToList();
//var hedgePnlCalc = GetRealTimeHedgePnlCalc(_ValueDate, volType: "交易", optUser: optUser);
//var hedgePnl = hedgePnlCalc.Calculate(hedgeTradeList, lastHedgingPositionList);
//if (hedgePnl != null && hedgePnl.Any())
//{
// foreach (var t in hedgePnl)
// {
// var realtimePosition = new intraday_trade_position
// {
// ValueDate = _ValueDate,
// BookId = t.BookId,
// TradeType = t.TradeType,
// //ClientId = t.ClientId,
// PositionType = t.PositionType,
// UnderlyingCode = t.UnderlyingCode,
// BuySell = t.BuySell,
// Cost = Convert.ToDouble(t.Cost),
// Notional = Convert.ToDouble(t.Notional),
// Amount = Convert.ToDouble(t.Notional),
// LastPv = t.LastPv,
// Pv = t.Pv,
// DailyPnL = t.DailyPnL,
// TotalPnL = Convert.ToDouble(t.TotalPnl),
// PositionPnL = Convert.ToDouble(t.TotalPnl),
// ActualPv = Convert.ToDouble(t.Pv),
// ActualLastPv = Convert.ToDouble(t.LastPv),
// ActualDailyPnL = Convert.ToDouble(t.DailyPnL),
// ActualTotalPnL = Convert.ToDouble(t.TotalPnl),
// RealizedPnL = Convert.ToDouble(t.RealizedPnL),
// TradeStatus = "确认成交",
// OptDate = DateTime.Now,
// OptId = optUser.UserId,
// OptName = optUser.UserName,
// ParentTradeId = 0
// };
// realtimePositionList.Add(realtimePosition);
// }
//}
#endregion
#region 当日持仓期权交易 已行权已到期期权交易 以及当日对冲交易计算持仓
var tradeIds = otcTradeList.Select(x => x.id).ToArray();
var positionRealizedTradeCashs = db.trade_cash.Where(x => (x.Action == "系统操作-票息" || x.Action == "系统操作-互换") && x.ValidState != "InValid" && x.ValueDate <= _ValueDate && tradeIds.Contains(x.id) && !x.IsLastAction).ToList();
//循环交易 OTC交易
otcTradeList.ForEach(t =>
{
var Pv = 0.0; //当日pv
var RoundedPv = 0.0;
var LastPv = 0.0; //昨日pv
var Quantity = 0.0; //数量
var RealizedPnL = 0.0;//已归因盈亏
var DailyPnl = 0.0; //当日盈亏
var UnRealizedPnL = 0.0;//未归因盈亏
var TotalPnL = 0.0; //总盈亏
var PositionPnL = 0.0; //持仓盈亏
var RoundedPositionPnL = 0.0;
var NotionalPrincipal = 0.0;
var ActualPv = 0.0; //当日实际pv
var ActualLastPv = 0.0; //昨日实际pv
var ActualDailyPnl = 0.0; //当日实际浮动盈亏
var ActualTotalPnL = 0.0; //实际总盈亏
//部分平仓计算
var tempTradeCashList = tradeCashList.Where(a => a.TradeId == t.id);
//已实现
var RealizeProfitAndloss = tempTradeCashList.Any() ? tempTradeCashList.Sum(a => a.Amount) : 0.0;
//预付金
var tradeSpan = tradeSpans.FirstOrDefault(a => a.TradeId == t.id);
if (ConsTrade.LiveTradeStatusList.Contains(t.TradeStatus))
{
if (riskList.Any(a => a.TradeId == t.id))
{
var valueResult = riskList.FirstOrDefault(a => a.TradeId == t.id);
//最新pv
Pv = valueResult.Pv ?? 0;
RoundedPv = valueResult.RoundedPv ?? 0;
PositionPnL = valueResult.PositionPnl ?? 0.0;
RoundedPositionPnL = valueResult.RoundedPositionPnl ?? 0.0;
}
else
{
Pv = 0.0;
RoundedPv = 0.0;
}
//var valueResult = holdRisResult.ContainsKey(t.id) ? holdRisResult[t.id] : null;
//if (valueResult == null) continue;
var lastEodPosition = _LastTradePositionList.FirstOrDefault(e => e.TradeId == t.id);
//if (lastEodPosition != null) _LastTradePositionList.Remove(lastEodPosition);
//昨日pv
LastPv = lastEodPosition == null ? 0 : Convert.ToDouble(lastEodPosition.Pv);
//ActualLastPv = lastEodPosition == null ? 0 : Convert.ToDouble(lastEodPosition.ActualPv ?? 0.0);
//总持仓量
Quantity = lastEodPosition == null ? t.Notional : lastEodPosition.Amount;
//归因盈亏
RealizedPnL = lastEodPosition != null ? 0 : ((t.TradeSinglePrice ?? 0) * t.Notional * TradeCalcHelper.GetSign(t.BuySell) * (-1));
//未归因盈亏
UnRealizedPnL = Pv - LastPv;
//估值盈亏
DailyPnl = RealizedPnL + UnRealizedPnL;
//名义本金 如果是股票则取名义本金字段 如果为商品期货则去 spotprice * 份额
NotionalPrincipal = t.OriginalStockEqvNotional > 0
? ((t.OriginalStockEqvNotional * t.Notional / t.OriginalNotional) ?? 0) : TradeHelper.GetStockEqvNotional((t.SpotPrice ?? 0.0) * t.Notional, t.ParticipationRate, t.AnnualizeFactor);
//根据看涨看跌以及当前价格与行权价涨跌判断是否有实际pv
var callPutFlag = "Call".Equals(t.CallPut);
var unSpotPrice = underlyingPrice.GetPrice(t.UnderlyingCode);
var StrikeFlag = unSpotPrice > (t.IsMoneynessOptionData ? (t.Strike * t.SpotPrice) : (t.Strike ?? 0.0));
//实值pv计算
ActualPv = 0.0;
if (callPutFlag == StrikeFlag)
{
var Strike = t.IsMoneynessOptionData ? ((t.Strike ?? 0.0) * t.SpotPrice ?? 0.0) : t.Strike ?? 0.0;
var SpotPrice = (t.SpotPrice ?? 0);
if (SpotPrice > 0)
{
ActualPv = t.StockEqvNotional * Math.Max((unSpotPrice - Strike) * (callPutFlag ? 1 : -1), 0) / SpotPrice * TradeCalcHelper.GetSign(t.BuySell);
}
}
//实值浮动盈亏
//ActualDailyPnl = ActualPv - ActualLastPv;
//实值总盈亏
//ActualTotalPnL = (lastEodPosition == null ? 0 : lastEodPosition.ActualTotalPnL ?? 0.0) + ActualDailyPnl;
//总盈亏
TotalPnL = (lastEodPosition == null ? 0 : lastEodPosition.TotalPnL) + DailyPnl;
//PositionPnL = Pv + RealizeProfitAndloss;
}
else if (ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus))
{//已执行 已平仓 已到期的交易计算持仓信息
var tradeCash = tradeCashList.FirstOrDefault(a => a.TradeId == t.id && a.Action != ClientCashInCashOut.系统操作_期权费);
Pv = ActualPv = tradeCash == null ? 0.0 : tradeCash.Amount;
RoundedPv = Pv;
var lastEodPosition = _LastTradePositionList.FirstOrDefault(e => e.TradeId == t.id);
//if (lastEodPosition != null) _LastTradePositionList.Remove(lastEodPosition);
//昨日pv
LastPv = ActualLastPv = lastEodPosition == null ? 0 : Convert.ToDouble(lastEodPosition.Pv);
//总持仓量
Quantity = lastEodPosition == null ? t.Notional : lastEodPosition.Amount;
//归因盈亏
RealizedPnL = Pv;
//未归因盈亏
UnRealizedPnL = -LastPv;
//估值盈亏
DailyPnl = RealizedPnL + UnRealizedPnL;
//名义本金 如果是股票则取名义本金字段 如果为商品期货则去 spotprice * 份额
NotionalPrincipal = t.OriginalStockEqvNotional > 0
? ((t.OriginalStockEqvNotional * t.Notional / t.OriginalNotional) ?? 0) : TradeHelper.GetStockEqvNotional((t.SpotPrice ?? 0.0) * t.Notional, t.ParticipationRate, t.AnnualizeFactor);
//根据看涨看跌以及当前价格与行权价涨跌判断是否有实际pv
//实值pv计算
ActualDailyPnl = ActualPv - ActualLastPv;
//总盈亏
ActualTotalPnL = TotalPnL = Pv - ((t.TradeSinglePrice ?? 0.0) * t.Notional * TradeCalcHelper.GetSign(t.BuySell));
PositionPnL = RealizeProfitAndloss;
RoundedPositionPnL = RealizeProfitAndloss;
}
if (double.IsNaN(NotionalPrincipal))
{
NotionalPrincipal = 0;
}
var Margin = tradeSpan == null ? 0.0 : double.IsNaN(tradeSpan.WorstCastClientPayable ?? double.NaN) ? 0 : tradeSpan.WorstCastClientPayable.Value;
realtimePositionList.Add(
new intraday_trade_position
{
TradeId = t.id,
TradeNumber = t.TradeNumber,
ValueDate = _ValueDate,
BookId = t.AssetId,
TradeType = t.TradeType,
ClientId = t.ClientId,
UnderlyingCode = t.UnderlyingCode,
BuySell = t.BuySell,
Cost = (t.OriginalNotional > 0 ? t.TradePrice * t.Notional / t.OriginalNotional : t.TradePrice) ?? 0,
Notional = NotionalPrincipal,
Amount = Quantity,
LastPv = (decimal)LastPv,
Pv = (decimal)Pv,
RoundedPv = (decimal)RoundedPv,
DailyPnL = (decimal)DailyPnl,
RealizedPnL = RealizedPnL,
TotalPnL = TotalPnL,
PositionPnL = PositionPnL,
RoundedPositionPnl = RoundedPositionPnL,
ActualPv = ActualPv,
ActualLastPv = ActualLastPv,
ActualDailyPnL = ActualDailyPnl,
ActualTotalPnL = ActualTotalPnL,
Margin = Margin,
PositionRelizedAmount = positionRealizedTradeCashs.Where(x => x.TradeId == t.id).Sum(x => x.Amount),
TradeStatus = t.TradeStatus,
OptDate = DateTime.Now,
OptId = optUser.UserId,
OptName = optUser.UserName,
ParentTradeId = t.ParentTradeId,
//收益互换 应缴预付金 初始预付金 盈亏
SwapInitMargin = 0,
SwapWinLoss = 0,
SwapUnMargin = 0
});
});
#endregion
//删除过期和无效的交易
var intradayTradeIds = db.intraday_trade_position.Where(t => t.TradeId > 0).Select(t => t.TradeId).ToList();
var inValidTradeIds = db.trade.Where(t => intradayTradeIds.Contains(t.id) && t.ValidState == "InValid").Select(t => t.id).ToList();
var removePosition = db.intraday_trade_position.Where(t => t.ValueDate != _ValueDate || inValidTradeIds.Contains(t.TradeId));
if (removePosition.Any())
{
db.intraday_trade_position.RemoveRange(removePosition);
db.SaveChanges();
}
//插入数据库还未有过的数据(根据TradeId,ValueDate筛选)
var intradayTradeIdsExist = db.intraday_trade_position.Where(x => x.ValueDate == _ValueDate).Select(x => x.TradeId).ToList();
var realtimePositionListInsert = realtimePositionList.Where(x => !intradayTradeIdsExist.Contains(x.TradeId));
MySqlBulkExtensions.BulkInsert(db, realtimePositionListInsert);
//根据TradeId,ValueDate更新已经存在的数据
var tradeids = realtimePositionList.Select(t => t.TradeId).ToList();
var intradayTradePositions = db.intraday_trade_position.Where(x => tradeids.Contains(x.TradeId) && x.ValueDate == _ValueDate).ToList();
for (var i = 0; i < intradayTradePositions.Count; i++)
{
var intradayTradePosition = realtimePositionList.FirstOrDefault(x => x.TradeId == intradayTradePositions[i].TradeId)?.Clone();
intradayTradePosition.id = intradayTradePositions[i].id;
db.Entry(intradayTradePositions[i]).CurrentValues.SetValues(intradayTradePosition);
}
//删除不该存在的场外交易,例如到期日被修改到了今天之前的某一天
var intradayTradePositionDelete = db.intraday_trade_position.Where(x => !tradeids.Contains(x.TradeId) && x.TradeId > 0).ToList();
if (intradayTradePositionDelete.Any())
{
db.intraday_trade_position.RemoveRange(intradayTradePositionDelete);
}
db.SaveChanges();
return realtimePositionList;
}
}
}
/// <summary>
/// 计算实时持仓并返回结果
/// </summary>
public static List<ClientPosition> RealtimeSwapPosition(OptUserInfo optUser)
{
string bondCalcTopic = Environment.GetEnvironmentVariable("KafkaConfig_ReqCalcBondTopic");
bool.TryParse(Environment.GetEnvironmentVariable("KafkaConfig_EnableCalcBongd"), out bool enableCalcBongd);
if (optUser is null)
{
throw new ArgumentNullException(nameof(optUser));
}
RestInitData();
lock (_RefrshLock)
{
using (var db = new YLContext())
{
#region 获取当前持仓 标的当前价格 当前未簿记流水 并计算Risk信息
//标的
var underlyingPrice = DataCacheProvider.GetUnderlyingDataSource();
//获取待计算交列表
var tradeList = db.trade.AsNoTracking().Where(t => t.ValidState != "InValid" && t.TradeDate <= _ValueDate//有效的交易
&& (ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus))
&& t.TradeType == "收益互换").ToList();
//OTC持仓交易
var tradeIdList = tradeList.Select(t => t.id).ToList();
var positions = db.swap_position.Where(x => tradeIdList.Contains(x.SwapTradeId) && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid).ToList();
var swapFlows = db.swap_flow.Where(x => x.DataState == (int)SwapFlowDateStateEnum.等待完成).ToList();
using var bondDb = new BondOmsDBContext();
var clientPositions = bondDb.client_position.AsEnumerable();
var eodSwaps = db.eod_swap_position.Where(x => tradeIdList.Contains(x.SwapTradeId) && x.PosiDirection > 0).AsEnumerable().GroupBy(p => p.SwapTradeId)
.Select(g => g.OrderByDescending(p => p.ValueDate).First()).ToList();
#endregion
#region 实时持仓计算
//实时结果对象类
var realtimePositionList = new List<ClientPosition>();
#endregion
#region 当日持仓期权交易 以及当前交易流水计算持仓
var query = from td in tradeList
join p in positions on td.id equals p.SwapTradeId
select new
{
td,
p
};
var positionGroup = query.AsEnumerable().GroupBy(x => new { x.p.UnderlyingCode, x.td.ClientId, x.p.PosiDirection });
var dealSwapFlowIds = new List<long>();
SwapTradeAutoService swapTradeAutoService = new SwapTradeAutoService(optUser);
var datenow = DateTime.Now;
foreach (var pair in positionGroup)
{
var client = DataCacheProvider.GetClientDataSource().GetData(pair.Key.ClientId);
if (client == null || string.IsNullOrEmpty(pair.Key.UnderlyingCode))
{
continue;
}
var clientPosition = clientPositions.FirstOrDefault(x => x.client_id == pair.Key.ClientId && x.security_id == pair.Key.UnderlyingCode && x.direction == pair.Key.PosiDirection);
var trades = pair.Select(s => s.td).ToList();
var tradeIds = trades.Select(x => x.id);
var positionGroupItems = positions.Where(x => tradeIds.Contains(x.SwapTradeId) && x.UnderlyingCode == pair.Key.UnderlyingCode && x.PosiDirection == pair.Key.PosiDirection);
var lastEodSwaps = eodSwaps.Where(x => tradeIds.Contains(x.SwapTradeId)).ToList();
var lastPv = lastEodSwaps.Sum(s => s.UnderlyingMarketValue);
if (!positionGroupItems.Any())
{
continue;
}
var comminsions = positionGroupItems.Sum(s => s.PosiTradingFeePending);
var lastPosi = positionGroupItems.OrderByDescending(x => x.id).First();
var newSwapFlows = swapFlows.Where(x => x.ClientId == pair.Key.ClientId && x.UnderlyingCode == pair.Key.UnderlyingCode).ToList();
var cunrentPositions = positionGroupItems.Sum(x => x.PosiQuantity * (x.PositionType == (int)PositionTypeFlag.Long ? 1 : -1));
var positionType = cunrentPositions > 0 ? PositionTypeFlag.Long : PositionTypeFlag.Short;
var posiQty = Math.Abs(cunrentPositions);
var multiplier = ConsGlobal.InstrumentType.IsBond(lastPosi.UnderlyingInstrumentType) ? ConsGlobal.bondShowPriceMultiple : 1;
// 计算加权平均价格(区分债券和非债券)
var weightedPrice = posiQty == 0 ? 0 : positionGroupItems
.Sum(s =>
{
decimal price = ConsGlobal.InstrumentType.IsBond(lastPosi.UnderlyingInstrumentType)
? s.PosiGrossPrice * ConsGlobal.bondShowPriceMultiple
: s.PosiGrossPrice;
return s.PosiQuantity * price;
}) / posiQty;
// 使用标的期初价格不含费计算
var weightedNetPrice = posiQty == 0 ? 0 : positionGroupItems
.Sum(s =>
{
decimal price = ConsGlobal.InstrumentType.IsBond(lastPosi.UnderlyingInstrumentType)
? (s.PosiNetNoFeePrice ?? 0) * ConsGlobal.bondShowPriceMultiple
: s.PosiNetNoFeePrice ?? 0;
return s.PosiQuantity * price;
}) / posiQty;
// 替换原代码中的 price 和 netPrice
var price = weightedPrice;
var netPrice = weightedNetPrice;
var totalNotional = positionGroupItems.Sum(s => s.PosiNotionalValue);
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(pair.Key.UnderlyingCode);
clientPosition = CreateClientPosition(clientPosition, pair.Key.ClientId, pair.Key.UnderlyingCode, netPrice, price, posiQty / 10000, comminsions, positionType == PositionTypeFlag.Long ? 0 : 1, lastPosi.ContractSize, pair.Key.PosiDirection);
clientPosition.position_notional_principal = totalNotional;
if (pair.Key.PosiDirection == (int)SwapDirectionEnum.支付)
{
var flowMerges = MergeSwapFlow(newSwapFlows, multiplier);
dealSwapFlowIds.AddRange(newSwapFlows.Select(s => s.id));
if (flowMerges.Any())
{
MergeSwapPosition(flowMerges, positionType, clientPosition, posiQty);
}
}
clientPosition.update_user = 0;
SetClientPositionPrice(clientPosition);
// full_price_now 为债券报价(面值百分比,×100形式);×100 还原市值数量级
clientPosition.swap_market_value = clientPosition.full_price_now * clientPosition.position_qty * (clientPosition.side == 0 ? 1 : -1) * ConsGlobal.bondShowPriceMultiple;
// 盈亏 = (现价−成本) × 价差系数(÷100,bondPriceMultiple) × 数量(万手×10000) × 方向。此处 0.01m 与 10000 分属价格/数量两个维度,不宜合并为 BondPriceConverter
clientPosition.position_profit_loss = (clientPosition.full_price_now - clientPosition.deal_full_price_avg) * ConsGlobal.bondPriceMultiple * (clientPosition.position_qty * 10000) * (clientPosition.side == 0 ? 1 : -1) - clientPosition.commission;
clientPosition.position_profit_loss = Math.Round(clientPosition.position_profit_loss ?? 0, 2, MidpointRounding.AwayFromZero);
clientPosition.today_profit_loss = clientPosition.swap_market_value - lastPv;
if (clientPosition.deal_full_price_avg > 0 && enableCalcBongd)//发kafka 获取成交收益率
{
CalcBandPrice(clientPosition);
}
if (clientPosition.id == 0 && clientPosition.position_qty > 0)
{
clientPosition.update_time = DateTime.Now;
bondDb.client_position.Add(clientPosition);
}
else if (clientPosition.id > 0 && (clientPosition.position_qty > 0 || clientPosition.today_profit_loss != 0))
{
clientPosition.update_time = DateTime.Now;
}
bondDb.SaveChanges();
}
var sql = $"{nameof(ClientPosition.create_time)}<'{datenow.AddSeconds(-1):yyyy-MM-dd HH:mm:ss}' or {nameof(ClientPosition.position_qty)}=0";
bondDb.BulkDelete<ClientPosition>(sql);
bondDb.SaveChanges();
var nodealSwapFlows = swapFlows.Where(x => !dealSwapFlowIds.Contains(x.id));
if (nodealSwapFlows.Any())
{
var nodealSwapFlowGroup = nodealSwapFlows.AsEnumerable().GroupBy(g => new { g.ClientId, g.UnderlyingCode });
foreach (var swapFlowGroup in nodealSwapFlowGroup)
{
var client = DataCacheProvider.GetClientDataSource().GetData(swapFlowGroup.Key.ClientId ?? 0);
if (client == null)
{
continue;
}
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(swapFlowGroup.Key.UnderlyingCode);
var multiplier = um != null && um.IsBond() ? ConsGlobal.bondShowPriceMultiple : 1;
var flowMerges = MergeSwapFlow(swapFlowGroup.ToList(), multiplier);
var flowMergeMax = flowMerges.OrderByDescending(s => s.TradingQty).First();
var flowMergeMin = flowMerges.FirstOrDefault(x => x.BsType != flowMergeMax.BsType);
var comminsions = flowMerges.Sum(s => s.TradingFee);
var qty = flowMergeMax.TradingQty;
if (flowMergeMin != null)
{
qty -= flowMergeMin.TradingQty;
}
var clientPosition = clientPositions.FirstOrDefault(x => x.client_id == swapFlowGroup.Key.ClientId && x.security_id == swapFlowGroup.Key.UnderlyingCode);
clientPosition = CreateClientPosition(clientPosition, swapFlowGroup.Key.ClientId ?? 0, swapFlowGroup.Key.UnderlyingCode, flowMergeMax.TradingAmountNetAvg ?? 0, flowMergeMax.TradingAmountAvg, qty / 10000, comminsions, flowMergeMax.BsType == (int)PositionTypeFlag.Long ? 0 : 1, flowMergeMax.ContractSize, (int)SwapDirectionEnum.支付);
clientPosition.position_qty = qty / 10000;
clientPosition.update_user = 0;
if (clientPosition.id == 0)
{
clientPosition.update_time = DateTime.Now;
bondDb.client_position.Add(clientPosition);
}
SetClientPositionPrice(clientPosition);
clientPosition.swap_market_value = clientPosition.full_price_now * clientPosition.position_qty * (clientPosition.side == 0 ? 1 : -1);
clientPosition.position_profit_loss = (clientPosition.full_price_now - clientPosition.deal_full_price_avg) * clientPosition.position_qty * (clientPosition.side == 0 ? 1 : -1);
clientPosition.position_profit_loss = Math.Round(clientPosition.position_profit_loss ?? 0, 2, MidpointRounding.AwayFromZero);
clientPosition.today_profit_loss = clientPosition.swap_market_value;
//发kafka 获取成交收益率
if (clientPosition.deal_full_price_avg > 0 && enableCalcBongd)
{
CalcBandPrice(clientPosition);
}
else if (clientPosition.id > 0 && (clientPosition.position_qty > 0 || clientPosition.today_profit_loss != 0))
{
clientPosition.update_time = DateTime.Now;
}
bondDb.SaveChanges();
}
}
sql = $"{nameof(ClientPosition.create_time)}<'{datenow.AddSeconds(-1):yyyy-MM-dd HH:mm:ss}' or {nameof(ClientPosition.position_qty)}=0";
bondDb.BulkDelete<ClientPosition>(sql);
bondDb.SaveChanges();
#endregion
return realtimePositionList;
}
}
}
/// <summary>
/// 判断是否有新流水
/// </summary>
/// <param name="calcTime"></param>
/// <returns></returns>
public static bool HasNewFlow(DateTime calcTime)
{
using (var db = new YLContext())
{
return db.swap_flow.Any(x => x.OptTime >= calcTime);
}
}
/// <summary>
/// 计算RealtimeRisk
/// </summary>
public static void RiskCalc()
{
lock (_RefrshLock)
{
using (var db = new YLContext())
{
RestInitData();
#region 获取当前持仓 标的当前价格 当前未簿记流水 并计算Risk信息
//标的
var underlyingPrice = DataCacheProvider.GetUnderlyingDataSource();
//获取待计算交列表
var tradeList = _TradeSwapList;
//OTC持仓交易
//实时计算rsik
var volTypes = new List<string> { "持仓" };
var riskList = RealTimeRiskCalc(_ValueDate, tradeList, underlyingPrice, volTypes);
#endregion
}
}
}
/// <summary>
/// DMA预付金计算
/// </summary>
public static void CalcDMAMargin()
{
var baseUrl = Environment.GetEnvironmentVariable("BondOmsInterface_BaseUrl");
var calculateMarginUrl = "/marginAlgorithm/realTimeMarginCalc";
var balanceDate = valuedateBLL.ValueDate;
var preSettleDate = BLL.valuedateBLL.GetNonHolidayDefore(balanceDate.AddDays(-1));
CalculateMarginRequest request = new CalculateMarginRequest()
{
currentDate = balanceDate.ToString("yyyy-MM-dd"),
preSettleDate = preSettleDate.ToString("yyyy-MM-dd")
};
if (!string.IsNullOrEmpty(baseUrl))
{
var httpHelper = new HttpHelper(baseUrl, null);
// http 请求 Web项目接口
var result = httpHelper.PostRequestNoAuth<CalculateMarginRequest, CalculateMarginResponse>(calculateMarginUrl, request).Result;
if (result != null && !result.success)
{
LogFactory.GetLogger("DMA预付金计算").Info("互换DMA预付金计算失败:" + result.message);
}
}
}
#region 新互换实时持仓私有方法
private static void BondCalcApi(ClientPosition clientPosition)
{
var resp = BondCalcHepler.BondCalc(clientPosition.security_id, clientPosition.deal_full_price_avg * ConsGlobal.bondShowPriceMultiple ?? 0, "DP");
if (resp != null)
{
clientPosition.deal_yield_avg = resp.ytm * ConsGlobal.bondPriceMultiple;
_yLCache.StringSetWithNoPrefix<CalBondResult>("TRS-BondFullPrice:" + clientPosition.security_id, resp, TimeSpan.FromHours(1));
}
}
/// <summary>
/// 创建持仓
/// </summary>
/// <param name="clientId"></param>
/// <param name="underlyingCode"></param>
/// <param name="underlyingName"></param>
/// <param name="price"></param>
/// <param name="fullPrice"></param>
/// <param name="qty"></param>
/// <param name="comminsion"></param>
/// <param name="side"></param>
/// <returns></returns>
private static ClientPosition CreateClientPosition(ClientPosition clientPosition, int clientId, string underlyingCode, decimal price, decimal fullPrice, decimal qty, decimal comminsion, int side, decimal contractsize, int direction)
{
var underlyingName = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode)?.UnderlyingName;
var client = DataCacheProvider.GetClientDataSource().GetData(clientId);
if (clientPosition == null)
{
clientPosition = new ClientPosition()
{
create_time = DateTime.Now,
};
}
clientPosition.client_id = clientId;
clientPosition.client_name = client?.Name;
clientPosition.security_id = underlyingCode;
clientPosition.symbol = underlyingName;
clientPosition.deal_price_avg = price;
clientPosition.deal_full_price_avg = fullPrice;
clientPosition.position_qty = qty;
clientPosition.commission = comminsion;
clientPosition.side = side;
clientPosition.create_time = DateTime.Now;
clientPosition.client_user_id = 0;
clientPosition.position_notional_principal = fullPrice * qty * 10000 * contractsize * ConsGlobal.bondPriceMultiple;
clientPosition.direction = direction;
return clientPosition;
}
/// <summary>
/// 合并互换流水
/// </summary>
/// <param name="swapFlows"></param>
/// <returns></returns>
private static List<swap_flow_merge> MergeSwapFlow(List<swap_flow> swapFlows, decimal multiplier)
{
List<swap_flow_merge> list = new List<swap_flow_merge>();
var newSwapFlowsGroups = swapFlows.GroupBy(g => g.BsType);
foreach (var gourpItem in newSwapFlowsGroups)
{
var swapflow = gourpItem.First();
swap_flow_merge swap_flow_summary = new swap_flow_merge()
{
OccurTime = swapflow.OccurTime.Value,
FundAccount = swapflow.FundAccount,
SwapTradeId = swapflow.SwapTradeId,
SwapTradeNo = swapflow.SwapTradeNo,
UnderlyingCode = swapflow.UnderlyingCode,
BsType = swapflow.BsType,
TradingQty = gourpItem.Sum(s => s.TradingQty),
TradingFeePending = gourpItem.Sum(s => s.TradingFee),
DataState = (int)SwapFlowDateStateEnum.等待完成,
ContractSize = swapflow.ContractSize,
ClientId = swapflow.ClientId,
TradingAmount = gourpItem.Sum(s => s.TradingAmount)
};
int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1;
swap_flow_summary.FirstFlowTime = swapflow.OptTime;
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountFeeAvg = Math.Round(swap_flow_summary.TradingAmountFeeAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetAvg = Math.Round(swap_flow_summary.TradingAmountNetAvg ?? 0, ConsGlobal.PriceRound);
swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetFeeAvg = Math.Round(swap_flow_summary.TradingAmountNetFeeAvg ?? 0, ConsGlobal.PriceRound);
swap_flow_summary.TradingAmountAvg *= multiplier;
swap_flow_summary.TradingAmountFeeAvg *= multiplier;
swap_flow_summary.TradingAmountNetAvg *= multiplier;
swap_flow_summary.TradingAmountNetFeeAvg *= multiplier;
list.Add(swap_flow_summary);
}
return list.OrderByDescending(o => o.TradingQty).ToList();
}
/// <summary>
/// 流水合并持仓
/// </summary>
/// <param name="flowMerges"></param>
/// <param name="positionType"></param>
/// <param name="clientPosition"></param>
/// <param name="posiQty"></param>
private static void MergeSwapPosition(List<swap_flow_merge> flowMerges, PositionTypeFlag positionType, ClientPosition clientPosition, decimal posiQty)
{
var flowMergeSame = flowMerges.FirstOrDefault(x => x.BsType == (int)positionType);
var flowMergeNagetive = flowMerges.FirstOrDefault(x => x.BsType != (int)positionType);
var sameQty = posiQty;
if (flowMergeSame != null)
{
sameQty += flowMergeSame.TradingQty;
}
var allPosiQty = sameQty;
if (flowMergeNagetive != null)
{
allPosiQty -= flowMergeNagetive.TradingQty;
if (allPosiQty >= 0)
{
var priceResult = CalcWeightedAverage(clientPosition, flowMergeSame);
clientPosition.deal_full_price_avg = priceResult.Item1;
}
else
{
clientPosition.deal_full_price_avg = flowMergeNagetive.TradingAmountAvg;
}
}
else
{
var priceResult = CalcWeightedAverage(clientPosition, flowMergeSame);
clientPosition.deal_full_price_avg = priceResult.Item1;
}
clientPosition.commission = flowMerges.Sum(s => s.TradingFee);
clientPosition.position_qty = Math.Abs(allPosiQty / 10000);
clientPosition.position_notional_principal = Math.Abs(allPosiQty) * clientPosition.deal_full_price_avg * ConsGlobal.bondPriceMultiple;
if (allPosiQty < 0)
{
clientPosition.side = clientPosition.side == 0 ? 1 : 0;
}
}
private static (decimal, decimal) CalcWeightedAverage(ClientPosition clientPosition, swap_flow_merge flowMergeSame)
{
var originalAmount = (clientPosition.deal_full_price_avg ?? 0) * (clientPosition.position_qty ?? 0);
var originalNetAmount = (clientPosition.deal_price_avg ?? 0) * (clientPosition.position_qty ?? 0);
if (flowMergeSame == null)
{
return (clientPosition.deal_full_price_avg ?? 0, clientPosition.deal_price_avg ?? 0);
}
var sameAmount = flowMergeSame.TradingAmountAvg * flowMergeSame.TradingQty;
var sameNetAmount = (flowMergeSame.TradingAmountNetAvg ?? 0) * flowMergeSame.TradingQty;
var totalQty = (clientPosition.position_qty ?? 0) + flowMergeSame.TradingQty;
if (totalQty == 0)
{
return (0, 0);
}
var price = (originalAmount + sameAmount) / totalQty;
var priceFee = (originalNetAmount + sameNetAmount) / totalQty;
price = Math.Round(price, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
priceFee = Math.Round(priceFee, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
return (price, priceFee);
}
/// <summary>
/// 从缓存中拿取债券价格
/// </summary>
/// <param name="clientPosition"></param>
private static void SetClientPositionPrice(ClientPosition clientPosition)
{
if (clientPosition.position_qty <= 0)
{
return;
}
try
{
//TRS-BondDepthMarket:160010.IB-0
var bondPrice = EodPriceQueryService.GetChinaBondPrice(valuedateBLL.ValueDate, clientPosition.security_id);
if (bondPrice != null)
{
clientPosition.full_price_now = bondPrice.dirty_price_close;
clientPosition.swap_market_value = clientPosition.full_price_now * clientPosition.position_qty * 10000 * (clientPosition.side == 0 ? 1 : -1) * ConsGlobal.bondPriceMultiple;
clientPosition.position_profit_loss = (clientPosition.full_price_now - clientPosition.deal_full_price_avg) * clientPosition.position_qty * 10000 * ConsGlobal.bondPriceMultiple * (clientPosition.side == 0 ? 1 : -1);
clientPosition.price_now = bondPrice.net_price;
clientPosition.yield_now = bondPrice.yield * ConsGlobal.bondPriceMultiple;
}
else
{
clientPosition.full_price_now = 100;
clientPosition.swap_market_value = clientPosition.full_price_now * clientPosition.position_qty * 10000 * (clientPosition.side == 0 ? 1 : -1) * ConsGlobal.bondPriceMultiple;
clientPosition.position_profit_loss = (clientPosition.full_price_now - clientPosition.deal_full_price_avg) * clientPosition.position_qty * 10000 * ConsGlobal.bondPriceMultiple * (clientPosition.side == 0 ? 1 : -1);
clientPosition.price_now = 100;
clientPosition.yield_now = 0.01m;
}
}
catch (Exception ex)
{
}
}
/// <summary>
/// 计算债券价格
/// </summary>
/// <param name="clientPosition"></param>
private static void CalcBandPrice(ClientPosition clientPosition)
{
if (clientPosition.position_qty <= 0 || _yLCache == null)
{
return;
}
try
{
//TRS-BondDepthMarket:160010.IB-0
CalBondResult bondPrice = _yLCache.StringGetWithNoPrefix<CalBondResult>("TRS-BondFullPrice:" + clientPosition.security_id);
if (bondPrice?.dirtyPrice != clientPosition.deal_full_price_avg)
{
BondCalcApi(clientPosition);
}
}
catch (Exception ex)
{
}
}
#endregion
/// <summary>
/// 实时risk计算
/// </summary>
public static List<realtime_trade_risk> RealTimeRiskCalc(DateTime valueDate, List<trade> tradeList, IPriceProvider priceProvider, List<string> volTypes = null, bool useSave = true, PricingRequest pricingRequest = QdpPricingRequest.BASIC_GREEKS)
{
// LogFactory.GetLogger<RealtimePnlCalc>().Info("CalcTradeValue Run Time:" + DateTime.Now.ToString());
//获取所有已确认交易信息
using (var db = new YLContext())
{
var risks = new List<realtime_trade_risk>();
if (tradeList == null || !tradeList.Any())
{
return risks;
}
//计算Risk
if (volTypes == null || !volTypes.Any())
{
volTypes = new List<string> { ConsGlobal.VolType.PositionVol };
}
var tradeIds = tradeList.Select(t => t.id).ToList();
var tradeCashList = db.trade_cash.AsNoTracking().Where(t => t.ValidState != "InValid" && !t.IsDeleted && tradeIds.Contains(t.TradeId) && (t.Action == ClientCashInCashOut.系统操作_平仓费 || t.Action == ClientCashInCashOut.系统操作_行权费)).ToList();
foreach (var volType in volTypes)
{
var resultRisks = new List<realtime_trade_risk>();
var isUseTradeVol = (volType == "持仓" || volType == "对冲") && PS.Config.IsTradeVol;
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(new CalculateRisksForTradesReq
{
valueDate = valueDate,
tradeList = tradeList,
priceProvider = priceProvider,
pricingRequest = pricingRequest,
volType = volType,
isUseTradeVol = isUseTradeVol,
addVolRateDic = null,
isAddVolPercent = true,
isMarginCalc = false,
overrideVolsForTrade = null,
PreciseTimeMode = true,
calcScenario = Enums.CalcScenarioEnum.RealtimePosition,
canUseManual = true
});
if (tradeRiskResult != null)
{
var eodForwardMargins = db.eod_forward_margin.Where(x => x.ValueDate == valueDate && tradeIds.Contains(x.TradeId)).ToList();
var forwards = db.trade_forward.Where(n => tradeIds.Contains(n.TradeId)).ToList();
foreach (var result in tradeRiskResult.Results)
{
try
{
var tempTradeCashList = tradeCashList.Where(t => t.TradeId == result.Trade.id).ToList();
var pnl = 0.0;// 当日盈亏
var tradePosition = _LastTradePositionList.FirstOrDefault(t => t.TradeId == result.Trade.id);
if (tradePosition != null)
{
pnl = (double.IsNaN(result.ValueResult.Pv) ? 0.0 : (result.ValueResult.Pv)) - Convert.ToDouble(tradePosition.Pv);
}
else
{
//var futureCommission = tradeCommissionDict.ContainsKey(result.Trade.id) ? tradeCommissionDict[result.Trade.id] : 0;
var isUnOption = ConsTrade.TradeTypesForHedge.Contains(result.Trade.TradeType);
var cost = (isUnOption ? (result.Trade.TradePrice ?? 0.0) : (result.Trade.TradeSinglePrice ?? 0)) * result.Trade.Notional;
if (result.Trade.TradeType == "远期")
{
pnl = NumberHelper.Normalize(result.ValueResult.Pv) + cost;// - futureCommission;
}
else
{
pnl = NumberHelper.Normalize(result.ValueResult.Pv) + cost * TradeCalcHelper.GetSign(result.Trade.BuySell) * -1;// - futureCommission;
}
}
for (var i = 0; i < result.Underlyings.Length; ++i)
{
resultRisks.Add(createRisk(result.Trade, valueDate, result.Underlyings[i], null, volType, result.ValueResult, pnl, i, priceProvider.GetPrice(result.Trade.UnderlyingCode), tempTradeCashList, eodForwardMargins, forwards));
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
if (useSave)
{
//删除非交易日当天的实时持仓数据
var sql = $"{nameof(realtime_trade_risk.ValueDate)}!='{valueDate.ToSqlDate()}'";
db.BulkDelete<realtime_trade_risk>(sql);
//插入数据库还未有过的实时持仓服务计算的数据(根据TradeId,VolType,ValueDate筛选)
var tradeIdsExist = db.realtime_trade_risk.Where(x => x.VolType == volType && x.ValueDate == valueDate).Select(x => x.TradeId).ToList();
var resultRisksInsert = resultRisks.Where(x => !tradeIdsExist.Contains(x.TradeId));
MySqlBulkExtensions.BulkInsert(db, resultRisksInsert);
//根据TradeId,VolType,ValueDate更新已经存在的数据
var tradeids = resultRisks.Select(t => t.TradeId).ToList();
var tradeRisks = db.realtime_trade_risk.Where(x => tradeids.Contains(x.TradeId) && x.VolType == volType && x.ValueDate == valueDate).ToList();
var delTradeRisks = db.realtime_trade_risk.Where(x => !tradeids.Contains(x.TradeId) && x.VolType == volType && x.ValueDate == valueDate).ToList();
db.realtime_trade_risk.RemoveRange(delTradeRisks);
for (var i = 0; i < tradeRisks.Count; i++)
{
var tradeRisk = resultRisks.FirstOrDefault(x => x.TradeId == tradeRisks[i].TradeId)?.Clone();
tradeRisk.id = tradeRisks[i].id;
db.Entry(tradeRisks[i]).CurrentValues.SetValues(tradeRisk);
}
db.SaveChanges();
}
if (volType == "持仓")
{
risks = resultRisks;
}
}
return risks;
}
}
/// <summary>
/// 刷新RealTimeRisk表对应交易记录和实时持仓信息(适用于OTC场内期权交易)
/// </summary>
public static void RefreshRiskAndIntradayPositionForOtc(List<trade> tradeList, OptUserInfo user)
{
if (tradeList is null)
{
throw new ArgumentNullException(nameof(tradeList));
}
if (user is null)
{
throw new ArgumentNullException(nameof(user));
}
if (tradeList != null && tradeList.Any())
{
new TradeDalService(user).SetSubTradeList(tradeList);
using (var db = new YLContext())
{
RestInitData();
//获取停牌信息
var suspensionUnderlyingIdList = underlying_managerBLL.GetQuery().Where(t => t.UnderlyingStatus == underlying_manager.Status_Suspension).Select(t => t.id).ToList();
#region 获取昨日持仓 标的当前价格 当前持仓以及当日行权交易 并计算Risk信息
var clientTrades = db.trade.AsNoTracking().Where(t => t.ValidState != "InValid" && t.TradeDate <= valuedateBLL.ValueDate//有效的交易
&& ((ConsTrade.LiveTradeStatusList.Contains(t.TradeStatus) && (t.ExerciseDate >= _ValueDate || (suspensionUnderlyingIdList.Contains(t.UnderlyingId) && !ConsTrade.TradeTypesForHedge.Contains(t.TradeType))) && !ConsTrade.TradeTypesForHedge.Contains(t.TradeType)) //未过期的(包括股票停牌)期权持仓交易
|| (t.TradeDate > _LastSettleDate && ConsTrade.TradeTypesForHedge.Contains(t.TradeType)) //最后结算日到当日介于区间的对冲交易
|| (t.UnWindDate > _LastSettleDate && t.UnWindDate <= _ValueDate && ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)) //最后结算日到当日区间内平仓到期执行的交易
)
&& ConsTrade.TradeTypesForOtcOptions.Contains(t.TradeType)).ToList();
//昨日持仓交易
var tradeIdList = tradeList.Select(t => t.id).ToList();
var underlyingIds = tradeList.Select(t => t.UnderlyingId).Distinct().ToList();
//标的
var underlyingPrice = DataCacheProvider.GetUnderlyingDataSource();
//设置option info
tradeBLL.SetFieldsByTradeType(tradeList);
//OTC 交易
var otcTradeList = tradeList.Where(t => t.TradeType != "结构化交易" && t.ClientId > 0).ToList();
//预付金计算
//远期不参与计算预付金逻辑
var req = new RunMarginCalculationReq(user)
{
tradeList = otcTradeList,
settleDate = _ValueDate,
PriceProvider = underlyingPrice,
hasOptionInfo = true
};
var tradeSpans = MarginDefault.RunMarginCalculation(req.Clone(forOtherSide: false));
//试算出交易买卖方向反向的tradeSpan(目前只有国君有这个需求,做过处理)
List<trade_span> tradeSpansOtherSide = new List<trade_span>();
if (MarginDefault.IsMarginCalcNeedSpecial(_ValueDate))
{
//试算出交易买卖方向反向的tradeSpan(目前只有国君有这个需求,做过处理)
tradeSpansOtherSide = MarginDefault.RunMarginCalculation(req.Clone(forOtherSide: true));
}
//根据交易预付金合计客户预付金计入client_span
var req2 = new CalcClientMarginReq(user)
{
settleDate = _ValueDate,
tradeSpans = tradeSpans,
tradeSpansOtherSide = tradeSpansOtherSide,
SpanType = ClientSpan.SpanType_RealTime
};
MarginDefault.CalcClientMargin(req2);
//实时计算rsik
var volTypes = new List<string> { "持仓" };
if (PS.Config.Is国投)
{
volTypes.Add("开仓");
}
var riskList = RealTimeRiskCalc(_ValueDate, otcTradeList, underlyingPrice, volTypes);
#endregion
#region 平仓费行权费 交易预付金计算 昨日持仓对冲交易过滤
var unWindTradeCashAction = new List<string> { ClientCashInCashOut.系统操作_平仓费, ClientCashInCashOut.系统操作_行权费, ClientCashInCashOut.系统操作_期权费 };
//获取平仓到期交易的trade_cash;
var tradeCashList = db.trade_cash.Where(t => tradeIdList.Contains(t.TradeId) && unWindTradeCashAction.Contains(t.Action) && !t.IsDeleted).ToList();
//实时结果对象类
var realtimePositionList = new List<intraday_trade_position>();
#endregion
#region 当日持仓期权交易 已行权已到期期权交易 以及当日对冲交易计算持仓
//循环交易 OTC交易
otcTradeList.ForEach(t =>
{
var Pv = 0.0; //当日pv
var RoundedPv = 0.0;
var LastPv = 0.0; //昨日pv
var Quantity = 0.0; //数量
var RealizedPnL = 0.0;//已归因盈亏
var DailyPnl = 0.0; //当日盈亏
var UnRealizedPnL = 0.0;//未归因盈亏
var TotalPnL = 0.0; //总盈亏
var PositionPnL = 0.0; //持仓盈亏
var RoundedPositionPnL = 0.0;
var NotionalPrincipal = 0.0;
var ActualPv = 0.0; //当日实际pv
var ActualLastPv = 0.0; //昨日实际pv
var ActualDailyPnl = 0.0; //当日实际浮动盈亏
var ActualTotalPnL = 0.0; //实际总盈亏
//部分平仓计算
var tempTradeCashList = tradeCashList.Where(a => a.TradeId == t.id);
//已实现
var RealizeProfitAndloss = (tempTradeCashList.Any() ? tempTradeCashList.Sum(a => a.Amount) : 0.0);
//预付金
var tradeSpan = tradeSpans.FirstOrDefault(a => a.TradeId == t.id);
//已实现盈亏
var positionRealizedTradeCashs = db.trade_cash.Where(x => (x.Action == "系统操作-票息" || x.Action == "系统操作-互换") && t.ValidState != "InValid" && x.ValueDate <= _ValueDate && (x.TradeId == t.id || x.ParentTradeId == t.id)).ToList();
if (ConsTrade.LiveTradeStatusList.Contains(t.TradeStatus))
{
if (riskList.Any(a => a.TradeId == t.id))
{
var openValueResult = riskList.FirstOrDefault(a => a.TradeId == t.id && a.VolType == "开仓");
if (PS.Config.Is国投 && openValueResult != null)
{
Pv = openValueResult.Pv ?? 0;
RoundedPv = openValueResult.RoundedPv ?? 0;
PositionPnL = openValueResult.PositionPnl ?? 0;
RoundedPositionPnL = openValueResult.RoundedPositionPnl ?? 0;
}
else
{
var valueResult = riskList.FirstOrDefault(a => a.TradeId == t.id);
Pv = valueResult.Pv ?? 0;
RoundedPv = valueResult.RoundedPv ?? 0;
PositionPnL = valueResult.PositionPnl ?? 0;
RoundedPositionPnL = valueResult.RoundedPositionPnl ?? 0;
}
}
else
{
Pv = 0.0;
RoundedPv = 0.0;
}
//var valueResult = holdRisResult.ContainsKey(t.id) ? holdRisResult[t.id] : null;
//if (valueResult == null) continue;
var lastEodPosition = _LastTradePositionList.FirstOrDefault(e => e.TradeId == t.id);
//if (lastEodPosition != null) _LastTradePositionList.Remove(lastEodPosition);
//昨日pv
LastPv = lastEodPosition == null ? 0 : Convert.ToDouble(lastEodPosition.Pv);
//ActualLastPv = lastEodPosition == null ? 0 : Convert.ToDouble(lastEodPosition.ActualPv ?? 0.0);
//总持仓量
Quantity = lastEodPosition == null ? t.Notional : lastEodPosition.Amount;
//归因盈亏
RealizedPnL = lastEodPosition != null ? 0 : ((t.TradeSinglePrice ?? 0) * t.Notional * TradeCalcHelper.GetSign(t.BuySell) * (-1));
//未归因盈亏
UnRealizedPnL = Pv - LastPv;
//估值盈亏
DailyPnl = RealizedPnL + UnRealizedPnL;
//名义本金 如果是股票则取名义本金字段 如果为商品期货则去 spotprice * 份额
NotionalPrincipal = "Stock".Equals(t.UnderlyingInstrumentType) ? t.StockEqvNotional : (t.Notional * (t.SpotPrice ?? 0.0));
//根据看涨看跌以及当前价格与行权价涨跌判断是否有实际pv
var callPutFlag = "Call".Equals(t.CallPut);
var unSpotPrice = underlyingPrice.GetPrice(t.UnderlyingCode);
var StrikeFlag = unSpotPrice > (t.IsMoneynessOptionData ? (t.Strike * t.SpotPrice) : t.Strike ?? 0.0);
//实值pv计算
ActualPv = 0.0;
if (callPutFlag == StrikeFlag)
{
var Strike = t.IsMoneynessOptionData ? ((t.Strike ?? 0.0) * t.SpotPrice ?? 0.0) : t.Strike ?? 0.0;
var SpotPrice = t.SpotPrice ?? 0;
if (SpotPrice > 0)
{
ActualPv = t.StockEqvNotional * Math.Max((unSpotPrice - Strike) * (callPutFlag ? 1 : -1), 0) / SpotPrice * TradeCalcHelper.GetSign(t.BuySell);
}
}
//实值浮动盈亏
//ActualDailyPnl = ActualPv - ActualLastPv;
//实值总盈亏
//ActualTotalPnL = (lastEodPosition == null ? 0 : lastEodPosition.ActualTotalPnL ?? 0.0) + ActualDailyPnl;
//总盈亏
TotalPnL = (lastEodPosition == null ? 0 : lastEodPosition.TotalPnL) + DailyPnl;
//PositionPnL = Pv + RealizeProfitAndloss;
}
else if (ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus))
{//已执行 已平仓 已到期的交易计算持仓信息
var tradeCash = tradeCashList.FirstOrDefault(a => a.TradeId == t.id && a.Action != ClientCashInCashOut.系统操作_期权费);
Pv = ActualPv = tradeCash == null ? 0.0 : tradeCash.Amount;
RoundedPv = Pv;
var lastEodPosition = _LastTradePositionList.FirstOrDefault(e => e.TradeId == t.id);
//if (lastEodPosition != null) _LastTradePositionList.Remove(lastEodPosition);
//昨日pv
LastPv = ActualLastPv = lastEodPosition == null ? 0 : Convert.ToDouble(lastEodPosition.Pv);
//总持仓量
Quantity = lastEodPosition == null ? t.Notional : lastEodPosition.Amount;
//归因盈亏
RealizedPnL = Pv;
//未归因盈亏
UnRealizedPnL = -LastPv;
//估值盈亏
DailyPnl = RealizedPnL + UnRealizedPnL;
//名义本金 如果是股票则取名义本金字段 如果为商品期货则去 spotprice * 份额
NotionalPrincipal = "Stock".Equals(t.UnderlyingInstrumentType) ? t.StockEqvNotional : (t.Notional * (t.SpotPrice ?? 0.0));
//根据看涨看跌以及当前价格与行权价涨跌判断是否有实际pv
//实值pv计算
ActualDailyPnl = ActualPv - ActualLastPv;
//总盈亏
ActualTotalPnL = TotalPnL = Pv - ((t.TradeSinglePrice ?? 0.0) * t.Notional * TradeCalcHelper.GetSign(t.BuySell));
TotalPnL = PositionPnL = RoundedPositionPnL = Pv + RealizeProfitAndloss;
}
var Margin = tradeSpan == null ? 0.0 : double.IsNaN(tradeSpan.WorstCastClientPayable ?? double.NaN) ? 0 : tradeSpan.WorstCastClientPayable.Value;
//收益互换 应缴预付金 初始预付金 盈亏
var SwapMargin = t.TradeType == "收益互换" ? (tradeSpan == null ? 0.0 : double.IsNaN(tradeSpan.WorstCastClientPayable ?? double.NaN) ? 0 : tradeSpan.WorstCastClientPayable.Value) : 0.0;
var SwapInitMargin = t.TradeType == "收益互换" ? (tradeSpan == null ? 0.0 : double.IsNaN(tradeSpan.SwapInitMargin ?? double.NaN) ? 0 : tradeSpan.SwapInitMargin.Value) : 0.0;
var SwapWinLoss = t.TradeType == "收益互换" ? (tradeSpan == null ? 0.0 : double.IsNaN(tradeSpan.SwapWinLoss ?? double.NaN) ? 0 : tradeSpan.SwapWinLoss.Value) : 0.0;
var SwapUnMargin = 0d;
//收益互换子交易 根据父交易预付金 按名义本金比例计算子交易预付金
if (t.TradeType == "收益互换")
{
//trade_span 追保==0 时 取客户浮亏作为互换可容忍金额
SwapUnMargin = tradeSpan == null || double.IsNaN(tradeSpan.SwapWinLoss ?? double.NaN) ? 0.0 : Math.Max(tradeSpan.Margin == 0 ? tradeSpan.SwapWinLoss.Value : 0.0, 0);
//收益互换名义本金
NotionalPrincipal = t.StockEqvNotional;
if (t.IsGroup == 2 && tradeSpans.Any(span => span.TradeId == t.ParentTradeId) && otcTradeList.Any(o => o.id == t.ParentTradeId))
{
var parentTrade = otcTradeList.Find(o => o.id == t.ParentTradeId);
var parentSpan = tradeSpans.Find(span => span.TradeId == t.ParentTradeId);
if (parentTrade.StockEqvNotional != 0)
{
SwapMargin = Margin = (parentSpan.WorstCastClientPayable ?? 0.0) * t.StockEqvNotional / parentTrade.StockEqvNotional;
SwapInitMargin = (parentSpan.SwapInitMargin ?? 0.0) * t.StockEqvNotional / parentTrade.StockEqvNotional;
SwapWinLoss = (parentSpan.SwapWinLoss ?? 0.0) * t.StockEqvNotional / parentTrade.StockEqvNotional;
}
}
}
realtimePositionList.Add(
new intraday_trade_position
{
TradeId = t.id,
TradeNumber = t.TradeNumber,
ValueDate = _ValueDate,
BookId = t.AssetId,
TradeType = t.TradeType,
ClientId = t.ClientId,
UnderlyingCode = t.UnderlyingCode,
BuySell = t.BuySell,
Cost = (t.OriginalNotional > 0 ? t.TradePrice * t.Notional / t.OriginalNotional : t.TradePrice) ?? 0,
Notional = NotionalPrincipal,
Amount = Quantity,
LastPv = (decimal)LastPv,
Pv = (decimal)Pv,
RoundedPv = (decimal)RoundedPv,
DailyPnL = (decimal)DailyPnl,
RealizedPnL = RealizedPnL,
TotalPnL = TotalPnL,
PositionPnL = PositionPnL,
RoundedPositionPnl = RoundedPositionPnL,
ActualPv = ActualPv,
ActualLastPv = ActualLastPv,
ActualDailyPnL = ActualDailyPnl,
ActualTotalPnL = ActualTotalPnL,
Margin = Margin,
PositionRelizedAmount = positionRealizedTradeCashs.Sum(x => x.Amount),
TradeStatus = t.TradeStatus,
OptDate = DateTime.Now,
OptId = user.UserId,
OptName = user.UserName,
ParentTradeId = t.ParentTradeId,
//收益互换 应缴预付金 初始预付金 盈亏
SwapInitMargin = SwapInitMargin,
SwapWinLoss = SwapWinLoss,
SwapUnMargin = SwapUnMargin
});
});
#endregion
//对冲唯一标识
//var hedgeUniqueCodeList = realtimePositionList.Where(t => trade.TradeTypesForHedge.Contains(t.TradeType)).Select(t => t.HedgeUniqueCode).ToList();
var removePosition = db.intraday_trade_position.Where(t => t.ValueDate == _ValueDate && (tradeIdList.Contains(t.TradeId)));
if (removePosition.Any())
{
db.intraday_trade_position.RemoveRange(removePosition);
}
db.intraday_trade_position.AddRange(realtimePositionList);
//if (removePosition != null) MySqlBulkExtensions.BulkDelete(db, removePosition);
//MySqlBulkExtensions.BulkInsert(db, realtimePositionList);
db.SaveChanges();
}
}
}
/// <summary>
/// 实时风险计算模型转换
/// </summary>
private static realtime_trade_risk createRisk(trade tradeObj, DateTime valueDate, underlying_manager udm, Variety variety, string volType, TradeValueResult optionValueResult, double pnl, int i, double underlyingPrice, List<trade_cash> tradeCashList, List<eod_forward_margin> eodForwardMargins, List<trade_forward> forwards)
{
//todo:感觉这个表应该加上计算时使用的期末价格
if (udm == null)
{
udm = new underlying_manager();
}
var result = new realtime_trade_risk
{
ValueDate = valueDate,
TradeId = tradeObj.id,
Exposure = udm.UnderlyingCode,
VolType = volType,
BookId = tradeObj.AssetId + "",
ClientId = tradeObj.ClientId,
Pv = NumberExtensions.Norm(optionValueResult.Pv),
RoundedPv = NumberExtensions.Norm(optionValueResult.RoundedPv),
Theta = NumberExtensions.Norm(optionValueResult.Theta),
Rho = NumberExtensions.Norm(optionValueResult.Rho),
DdeltaDt = NumberExtensions.Norm(optionValueResult.DDeltaDt),
DdeltaDvol = NumberExtensions.Norm(optionValueResult.DDeltaDVol),
DvegaDt = NumberExtensions.Norm(optionValueResult.DVegaDt),
DvegaDvol = NumberExtensions.Norm(optionValueResult.DVegaDVol),
Vol = NumberExtensions.Norm(optionValueResult.Vol),
Pnl = pnl,
OptId = 1,
OptName = "管理员",
OptDate = DateTime.Now,
Delta = NumberExtensions.Norm(optionValueResult.GetDelta(i)),
Gamma = NumberExtensions.Norm(optionValueResult.GetGamma(i)),
Vega = NumberExtensions.Norm(optionValueResult.GetVega(i)),
DeltaCash = NumberExtensions.Norm(optionValueResult.GetDeltaCash(i)),
GammaCash = NumberExtensions.Norm(optionValueResult.GetGammaCash(i)),
VegaCash = NumberExtensions.Norm(optionValueResult.VegaCash),
UnderlyingPrice = optionValueResult.SpotPrice,
};
result.DeltaInLots = TradeLotsCalc.CalcDeltaInLots(result.Delta ?? 0, variety, udm);
var pvShift = tradeObj.TradeType == "雪球期权" && tradeObj.trade_snowball.PrepaymentUsed
? -tradeObj.Notional * (tradeObj.SpotPrice ?? 0) * (tradeObj.trade_snowball.PrepaymentRatio ?? 0) * (tradeObj.BuySell == "卖出" ? -1 : 1)
: 0;
if (tradeObj.TradeType == "收益互换")
{
result.PositionPnl = optionValueResult.Pv;
result.RoundedPositionPnl = optionValueResult.RoundedPv;
}
else
{
result.PositionPnl = EodOperationBase.GetPositionPnl((result.Pv ?? 0.0) + pvShift, tradeObj.TradePrice ?? 0.0, tradeObj.Notional, tradeObj.OriginalNotional ?? 0, tradeObj.BuySell);
result.RoundedPositionPnl = EodOperationBase.GetPositionPnl((result.RoundedPv ?? 0.0) + pvShift, tradeObj.TradePrice ?? 0.0, tradeObj.Notional, tradeObj.OriginalNotional ?? 0, tradeObj.BuySell);
}
result.RealizedPnl = optionValueResult.ExtendInfo.RealPnl;
if (tradeObj.TradeType == "远期")
{
using (var db = new YLContext())
{
var marginCost = eodForwardMargins.Where(x => x.TradeId == tradeObj.id)
.Select(n => (double?)n.MarginCost).FirstOrDefault() ?? 0;
var forward = forwards.FirstOrDefault(n => n.TradeId == tradeObj.id);
var unwindTradeCashList = tradeCashList.Where(t => t.TradeId == tradeObj.id && t.ValidState != "InValid" && !t.IsDeleted && t.Action != ClientCashInCashOut.系统操作_期权费 && t.ValueDate == valueDate).ToList();
//开仓费用 + 平仓费用
var positionCost = forward.OpenCommission * (tradeObj.Lots ?? 0) + unwindTradeCashList.Sum(a => (a.UnwindPrice ?? 0) * a.UnwindNotional.Value / tradeObj.OriginalNotional.Value * (tradeObj.Lots ?? 0));
//平仓比例
double unwindRatio = unwindTradeCashList.Any() ? unwindTradeCashList.Sum(a => a.UnwindNotional.Value) / tradeObj.OriginalNotional.Value : 0;
//持仓比例
double positionRatio = 1 - unwindRatio;
//实现盈亏 = (交易已平仓) 开仓总费用 * 平仓比例 + 平仓总费用
//实现盈亏 = (交易未平仓) 0
double totalFee = unwindTradeCashList.Any() ? (tradeObj.TradePrice ?? 0) * unwindRatio + unwindTradeCashList.Sum(a => a.Amount) : 0;
//持仓市值 = 交易员视角的合约总价值(远期合约价值+开仓费用+平仓费用)-交易员视角的已实现盈亏
result.Pv = result.Pv.Value;
result.RoundedPv = result.RoundedPv.Value;
//持仓盈亏 = 持仓市值+开仓总费用 * 持仓比例
result.PositionPnl = result.Pv.Value + (tradeObj.TradePrice ?? 0) * positionRatio + marginCost;
result.RoundedPositionPnl = result.RoundedPv + (tradeObj.TradePrice ?? 0) * positionRatio + marginCost;
result.RealizedPnl = totalFee;
result.Pnl += marginCost;
}
}
double? nowPrice = 0;
if (!string.IsNullOrWhiteSpace(tradeObj.UnderlyingCode))
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tradeObj.UnderlyingCode);
if (underlying == null)
{
throw new Exception($"未找到标的:{tradeObj.UnderlyingCode}");
}
else
{
nowPrice = underlying.Price;
}
}
result.CreditExposure = EodPositionRisksQueryService.ExposureCalc(tradeObj, nowPrice ?? 0, BLL.valuedateBLL.ValueDate);
if (result.UnderlyingPrice == null)
{
result.UnderlyingPrice = underlyingPrice;
}
if (tradeCashList != null && tradeCashList.Any() && tradeObj.TradeType != "远期")
{
tradeCashList.ForEach(t =>
{
result.RealizedPnl += t.Amount - ((tradeObj.TradePrice ?? 0.0) * (t.UnwindPercentRate ?? 0.0) * TradeCalcHelper.GetSign(tradeObj.BuySell));
});
}
return result;
}
/// <summary>
/// 计算客户实时资金
/// </summary>
public static List<ClientSettleBalance> GetRealTimeClientBanlance(List<int> clientIdList, DateTime? startDate = null)
{
if (null == clientIdList || !clientIdList.Any())
{
return null;
}
if (!startDate.HasValue)
{
startDate = DateTime.MinValue;
}
var cashSwapActions = new List<string>() { ClientCashInCashOut.系统操作_应付预付金, ClientCashInCashOut.系统操作_预付金返息, ClientCashInCashOut.系统操作_期权费 };
using (var db = DbContextFactory.GetYLDbContext())
using (var db2 = DbContextFactory.GetClientDbContext(null))
{
//获取系统参数
var systemDate = valuedateBLL.SystemDate;
//系统交易日
var valueDate = systemDate.ValueDate;
//客户信息
var clientList = db2.client.Where(t => clientIdList.Contains(t.id)).ToList();
var clientIds = clientList.Select(t => t.id).ToList();
var clientparentId = clientList.Select(t => t.ParentId).ToList();
//获取客户预付金(实时计算更新)
var clientSpans = db.client_span.Where(t =>
t.ValueDate == valueDate && t.SpanType == ClientSpan.SpanType_RealTime);
//获取客户昨日资金结算信息
var clientBanlances = new List<ClientBalanceDaily>();
var lastSettletDate = DateTime.MinValue;
var query = db.ClientBalanceDaily.Where(
t => t.BalanceDate < valueDate && t.BalanceDate.HasValue);
if (query.Any())
{
lastSettletDate = query.Max(t => t.BalanceDate).Value;
//基数行按客户各自最近一次结算日取(找不到全局最新结算日的行时向上回溯)——
//无持仓/无交易的客户不参与每日EOD日结(如休眠客户、仅入金客户),其日结行会落后于全局结算日,
//严格取全局日期的行会把结存算成0(2026-08-27 客户84实证:日结只到8-24、全局已到8-26,100万入金被漏)。
//lastSettletDate 仍保留全局口径供下方交易窗口等既有逻辑使用。
clientBanlances = db.ClientBalanceDaily.Where(t =>
t.BalanceDate < valueDate && clientIds.Contains(t.ClientId)).ToList()
.GroupBy(t => t.ClientId)
.Select(g => g.OrderByDescending(x => x.BalanceDate).First())
.ToList();
}
//各客户自身基数日(+1为其流水窗口下界);无任何结算行的客户须全量流水
var baseDateByClient = clientBanlances
.Where(t => t.BalanceDate.HasValue)
.GroupBy(t => t.ClientId)
.ToDictionary(g => g.Key, g => g.Max(t => t.BalanceDate.Value));
var anyClientWithoutBaseRow = clientIds.Any(id => !baseDateByClient.ContainsKey(id));
DateTime? flowWindowStart = null;
if (!anyClientWithoutBaseRow && baseDateByClient.Count > 0)
{
//窗口下界放宽到最早基数+1(≤原全局结算日+1,超集拉取),消费点按各客户自身基数过滤防双算
flowWindowStart = baseDateByClient.Values.Min().AddDays(1);
}
//获取实时持仓
var positionList = db.intraday_trade_position
.Where(t => t.ValueDate == valueDate && clientIds.Contains(t.ClientId)).ToList();
var tradeIds = positionList.Where(t => t.TradeId != 0).Select(t => t.TradeId).ToList();
var positionTrades = db.trade.AsNoTracking().Where(t => tradeIds.Contains(t.id)).ToList();
var swapFlows = db.swap_flow.Where(x => x.DataState == (int)SwapFlowDateStateEnum.等待完成 && x.OccurTime == valueDate).ToList(); ;
//获取当日所有出入金记录
var newValuedate = valueDate.AddDays(1);
//var ClientCashList = from cash in db.ClientCashInCashOut.Where(t => t.ValidState != "InValid" && t.HappenDate >= lastSettletDateAddOne && t.HappenDate < newValuedate && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认 || (t.Direction == "出金" && ClientCashInCashOut.outCashCals.Contains(t.State))) && clientIds.Contains(t.ClientId ?? 0))
var ClientCashList = from cash in db.ClientCashInCashOut.Where(t => t.ValidState != "InValid"
&& (flowWindowStart == null || t.HappenDate >= flowWindowStart)
&& t.HappenDate < newValuedate && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认) && clientIds.Contains(t.ClientId ?? 0))
join trade in db.trade on cash.TradeId equals trade.id into trade
from td in trade.DefaultIfEmpty()
where td.TradeType != "收益互换"
select cash;
var ClientCashSwapList = from cash in db.ClientCashInCashOut.Where(t => t.ValidState != "InValid"
&& (flowWindowStart == null || t.HappenDate >= flowWindowStart)
&& t.HappenDate < newValuedate && (t.State == ClientCashInCashOut.已结算 || t.State == ClientCashInCashOut.已确认) && clientIds.Contains(t.ClientId ?? 0))
join trade in db.trade.Where(x => x.TradeType == "收益互换") on cash.TradeId equals trade.id
select cash;
//获取客户所有现存(抵押状态)抵押品信息
var clientProductList = db.clientcashincashout_product.Where(t =>
(t.Status == Clientcashincashout_productStatusEnum.抵押.ToString() && t.OptStatus == ClientCashInCashOut.已确认
|| t.Status == Clientcashincashout_productStatusEnum.赎回.ToString() && t.OptStatus != ClientCashInCashOut.已确认
|| t.Status == Clientcashincashout_productStatusEnum.赎回.ToString() && t.OptStatus == ClientCashInCashOut.已确认 && t.BackDate >= newValuedate)
&& clientIds.Contains(t.ClientId) && t.HappenDate < newValuedate).ToList();
var productUnderlyingIds = clientProductList.Select(t => t.UnderlyingId ?? 0).ToHashSet();
var umList = DataCacheProvider.GetUnderlyingDataSource();
var eodPriceProvider = EodPriceProviderFactory.Get(valueDate).GetPriceProvider(TradeCalcHelper.GetSettlementType());
var allTradeList = db.trade.Where(t => t.ValidState != "InValid" &&
t.TradeDate <= valueDate &&
clientIdList.Contains(t.ClientId) &&
ConsTrade.TradeStatusAfterConfirmed
.Contains(t.TradeStatus) &&
t.ParentTradeId == 0
).ToList();
var finishedTradeCash = from tc in db.trade_cash
join td in db.trade on tc.TradeId equals td.id
where (tc.ValueDate >= startDate && tc.ValueDate <= valueDate && tc.HappenedDate == null || tc.HappenedDate >= startDate && tc.HappenedDate <= valueDate)
&& tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted
&& (tc.Action == ClientCashInCashOut.系统操作_平仓费 || tc.Action == ClientCashInCashOut.系统操作_行权费 || tc.Action == ClientCashInCashOut.系统操作_票息 || tc.Action == ClientCashInCashOut.系统操作_互换 || tc.Action == ClientCashInCashOut.人工操作_其他)
&& td.IsGroup != 1
select tc;
var finishedTradeNotionalList = finishedTradeCash.ToList();
var finishedTradeNotionalDic = finishedTradeNotionalList.GroupBy(tc => tc.TradeId).ToDictionary(g => g.Key,
g => g.Sum(tc =>
tc.Action == ClientCashInCashOut.系统操作_平仓费 ? (tc.UnwindNotional ?? 0) : tc.Notional));
//所有当日待结算的trade_cash记录(当日为应付日的冻结资金)
//var payDateTradeIds = db.trade.Where(t => t.TradeDate != valuedate && t.PremiumPayDate == valuedate).Select(t => t.id).ToList();
//var tradeCashList = db.trade_cash.Where(t => payDateTradeIds.Contains(t.TradeId ?? 0) && t.Status == TradeCashStatusEnum.冻结).ToList();
//获取当日所有执行的交易 交易日为当前交易日或者行权日为当前交易日
var positionTradeList = db.trade.Where(t => clientIds.Contains(t.ClientId))
.Where(t => t.TradeType != "场内期权")
.Where(t => t.ValidState != "InValid") //有效的交易
.Where(t => (t.TradeDate > lastSettletDate && t.TradeDate <= valueDate) &&
ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus)) //最后结算日到当日成交的交易
.Where(t => GlobalDicionary.SupportTradeTypes.Contains(t.TradeType));
//当日成交
var todayTradeList = positionTradeList.Where(t => t.TradeDate == valueDate).ToList();
var todayTradesAfterConfirmed = db.trade.Where(t => clientIds.Contains(t.ClientId))
.Where(t => t.TradeType != "场内期权")
.Where(t => t.ValidState != "InValid")
.Where(t => (t.TradeDate == valueDate) &&
ConsTrade.TradeStatusAfterConfirmed.Contains(t.TradeStatus))
.Where(t => (GlobalDicionary.SupportTradeTypes.Contains(t.TradeType) || t.TradeType == "结构化交易") && t.ParentTradeId == 0);
//positionTradeList.Where(t => t.TradeDate == valuedate || (unWindTradeStatus.Contains(t.TradeStatus) && t.UnWindDate==valuedate)).ToList();
var creditList = new List<CreditTable>();
//获取所有客户授信
creditList = db.credit.Where(t =>
clientIds.Contains(t.ClientId ?? 0) && t.ProcessStatus == "已审批" &&
(!t.CreditDeadLine.HasValue || t.CreditDeadLine >= valueDate) && (!t.CreditStartDate.HasValue || t.CreditStartDate <= valueDate)).ToList();
var clients = DataCacheProvider.GetClientDataSource().AsQueryable().ToList();
var parentexit = clients.Where(c => clientparentId.Contains(c.id)).ToList();
if (creditList.Count() == 0 && parentexit != null)
{
creditList = db.credit.Where(t =>
clientparentId.Contains(t.ClientId ?? 0) && t.ProcessStatus == "已审批" &&
(!t.CreditDeadLine.HasValue || t.CreditDeadLine >= valueDate) && (!t.CreditStartDate.HasValue || t.CreditStartDate <= valueDate)).ToList();
}
//获取所有资信等级
var creditRatingList = db2.credit_rating.Where(t => t.ValidState != "InValid").ToList();
//获取冻结资金信息
var clientFrozenFunds = new ClientFrozenFundsService(OptUserInfo.SystemUser).GetDatas(valueDate, clientIdList);
//预付金比率
var marginRation = systemDate.MarginRatio ?? 0.15;
//最大可提取预付金上限比率
var marginMaxRation = marginRation + 0.02;
DateTime? UpdateDate = null;
var ClientBanlanceList = new List<ClientSettleBalance>();
//客户盘中结算
clientList.ForEach(client =>
{
//当日应付冻结权利金
var FreezePremium = 0.0;
//冻结出金
var FrozenOutFund = 0.0;
//冻结抵押品赎回
var FrozenRedeemFunds = 0.0;
var OptionPremium = 0.0;
var OptionPremiumSwap = 0.0;
//平仓行权收益
var SettlementBalance = 0.0;
//平仓收益
var UnwindBalance = 0.0;
//行权收益
var ExerciseBalance = 0.0;
//互换收益
var SwapBalance = 0.0;
//当日应收冻结权利金
var ReceivablesPremium = 0.0;
//当日资金净出入
var NetFund = 0.0;
//其他收支
var OtherFund = 0.0;
//票息
var Coupon = 0.0;
//当日入金
var InFund = 0.0;
//当日出金
var OutFund = 0.0;
//其他收入
var InFundOther = 0.0;
//其他支出
var OutFundOther = 0.0;
//账户资金
var AmountFund = 0.0;
//前日账户资金
var LastDayRemainFund = 0.0;
//今日可用资金
var AvailableAmount = 0.0;
//授信额度
var lineOfCredit = 0.0;
//交易总数
var totalTradeCount = 0;
//交易笔数(当日成交)
var todayTradeCount = 0;
//交易总额
//var TradeTotalFund = 0.0;
//卖权交易数
var sellCount = 0;
//名义本金(卖出)
var SellNotionalPrincipal = 0.0;
//买权交易数
var buyCount = 0;
//名义本金(买入)
var BuyNotionalPrincipal = 0.0;
//名义本金总额
var totalNotionalPrincipal = 0.0;
//当日交易名义本金
var todayNotionalPrincipal = 0.0;
//持仓笔数
var PositionCount = 0;
//持仓市值
var PositionPv = 0.0;
var RoundedPositionPv = 0.0;
//空头持仓市值
var SellPv = 0.0;
//客户持仓交易预付金总和,客户买入为正,客户卖出为负
double PrepaymentAmount = 0.0;
//当日盈亏
var DaliyPnl = 0.0;
//持仓盈亏
var PositionPnl = 0.0;
var RoundedPositionPnl = 0.0;
//期权空头浮动盈利=∑max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的
var ClientSellPositionPnl = 0.0;
//抵押品
//应付预付金(要么为负要么为零)
//最新概念:负数代表客户应缴预付金,正数代表客户应收预付金
var PayableMargin = 0.0;
//互换占用预付金
var SwapPayableMargin = 0.0;
//互换容忍金额
var SwapUnMargin = 0.0;
//互换追保金额
var SwapMargin = 0d;
//双向预付金
var TwoSideMargin = 0.0;
//当日新增应付预付金
var TodayNewMargin = 0.0;
//潜在行权盈余
var PotentialSurpluses = 0.0;
//预付金余额
var MarginBalance = 0.0;
//追保金额
var Margin = 0.0;
//可取预付金
//可取预付金为预付金余额 - 应缴预付金(上浮2%)
var AdvisableMargin = 0.0;
//授信是否可用于期权费
var IsTradeCredit = true;
//实现盈亏
var WinLoss = 0.0;
//持仓期权费净额(客户角度卖出为负,买入为正)
var PositionPremiumNetCash = 0.0;
//权利金冻结:当日该客户所有持仓的卖出期权权利金
var SellTradePrice = 0.0;
// 期初持仓交易净额
var LastDayPositionPremiumNetCash = 0.0;
//可用名义本金规模
double? AvailableStockEqvNotional = null;
//当前互换流水费用
decimal curSwapFee = 0;
//获取客户授信
var credits = creditList?.Where(a => a.ClientId == client.id || a.ClientId == client.ParentId);
if (credits.Any())
{
lineOfCredit = credits.Sum(t => t.Credit ?? 0);
AvailableStockEqvNotional = creditList.Sum(O => O.StockEqvNotional) ?? 0;
}
else
{
lineOfCredit = 0.0;
AvailableStockEqvNotional = null;
}
var creditRatingId = 0;
var clientRatingQuery = from cr in db2.Client_Rating.Where(x => !x.IsDeleted && x.ProcessStatus == "已审批")
where cr.RatingStartDate <= valueDate && cr.RatingDeadLine >= valueDate && cr.ClientId == client.id
orderby cr.RatingDeadLine descending
orderby cr.ProcessOptDate descending
select cr.CreditRatingId;
if (clientRatingQuery.Any())
{
creditRatingId = clientRatingQuery.FirstOrDefault();
}
//资信等级授信额度
var creditRating = creditRatingList?.FirstOrDefault(a => a.id == creditRatingId);
var clientBanlance = clientBanlances.FirstOrDefault(a => a.ClientId == client.id);
AmountFund = clientBanlance == null ? 0.0 : clientBanlance.ToDayRemainFund ?? 0.0;
//上日资金余额
LastDayRemainFund = AmountFund;
//昨日抵押品总价值
var lastGuaranteesTotalAmount = clientBanlance == null
? 0.0
: clientBanlance.TodayRemianFundProduct ?? 0.0;
//资金净流入 + 权利金收支 + 期权费收支
//流水按该客户自身基数日过滤(窗口下界放宽到最早基数+1 拉取,此处防已结算进基数行的流水双算)
var clientFlowStart = baseDateByClient.TryGetValue(client.id, out var clientBaseDate) ? clientBaseDate.AddDays(1) : DateTime.MinValue;
var clientCashSwap = ClientCashSwapList.Where(a => a.ClientId == client.id && a.HappenDate >= clientFlowStart).ToList();
if (clientCashSwap.Any())
{
if (clientCashSwap.Any(a => a.Direction == "应收" && cashSwapActions.Contains(a.Action)))
{
OptionPremiumSwap = clientCashSwap.Where(a => a.Direction == "应收" && cashSwapActions.Contains(a.Action)).Sum(a => a.Money ?? 0.0);
}
if (clientCashSwap.Any(a => a.Direction == "应收" && (a.Action == ClientCashInCashOut.系统操作_平仓费 || a.Action == ClientCashInCashOut.系统操作_互换)))
{
SwapBalance = clientCashSwap.Where(a => a.Direction == "应收" && (a.Action == ClientCashInCashOut.系统操作_平仓费 || a.Action == ClientCashInCashOut.系统操作_互换)).Sum(a => a.Money ?? 0.0);
}
}
curSwapFee = swapFlows.Where(x => x.ClientId == client.id).Sum(s => s.TradingFee);
OptionPremiumSwap += Convert.ToDouble(curSwapFee);
var clientCash = ClientCashList.Where(a => a.ClientId == client.id && a.HappenDate >= clientFlowStart).ToList();
if (clientCash.Any())
{
if (clientCash.Any(a =>
a.Direction == "应收" && a.Action == ClientCashInCashOut.系统操作_期权费))
{
OptionPremium = clientCash
.Where(a => a.Direction == "应收" && a.Action == ClientCashInCashOut.系统操作_期权费)
.Sum(a => a.Money ?? 0.0);
}
if (clientCash.Any(a =>
a.Direction == "应收" &&
(a.Action == ClientCashInCashOut.系统操作_行权费 ||
a.Action == ClientCashInCashOut.系统操作_平仓费)))
{
var settlementCashList = clientCash.Where(a =>
a.Direction == "应收" &&
(a.Action == ClientCashInCashOut.系统操作_行权费 ||
a.Action == ClientCashInCashOut.系统操作_平仓费));
SettlementBalance = settlementCashList.Sum(a => a.Money ?? 0.0);
UnwindBalance = settlementCashList.Where(x => x.Action == ClientCashInCashOut.系统操作_平仓费).Sum(a => a.Money ?? 0.0);
ExerciseBalance = settlementCashList.Where(x => x.Action == ClientCashInCashOut.系统操作_行权费).Sum(a => a.Money ?? 0.0);
}
if (clientCash.Any(a => a.Action == ClientCashInCashOut.系统操作_票息))
{
Coupon = clientCash
.Where(a => a.Action == ClientCashInCashOut.系统操作_票息)
.Sum(a => a.Money ?? 0.0);
}
#region 计算实现盈亏
var todayFinishedTrades = allTradeList.Where(t => finishedTradeNotionalDic.Select(d => d.Key).Contains(t.id) && t.ClientId == client.id).ToList();
var todayFinishedTradeCash = from tc in finishedTradeCash
join td in db.trade on tc.TradeId equals td.id
where td.IsGroup != 1
group tc by td.ClientId into g
select new
{
ClientId = g.Key,
amountSum = g.Sum(n => n.Amount)
};
WinLoss = todayFinishedTrades.Sum(t => t.TradeType != "远期" ? (TradeCalcHelper.GetSign(t.BuySell) * (t.TradeSinglePrice ?? 0) * finishedTradeNotionalDic[t.id])
: (-(t.TradePrice ?? 0) * (finishedTradeNotionalDic[t.id] / (t.OriginalNotional ?? 0)))) //远期开仓总费用所占比重
- (todayFinishedTradeCash.FirstOrDefault(x => x.ClientId == client.id)?.amountSum ?? 0);
#endregion
if (clientCash.Any(a => a.Direction == "入金" || a.Direction == "出金"))
{
var inFunds = clientCash.Where(a => a.Direction == "入金").AsEnumerable().GroupBy(x => x.CurrencyCode).Select(x => new { x.Key, Money = x.Sum(y => y.Money ?? 0) }).ToList();
inFunds.ForEach(x =>
{
new EodCurrencyProvider(valueDate, false).TryGetCurrencyRate(x.Key, client.SettlementCurrency, out var rateObj);
if (rateObj == null)
{
throw new Exception($"{valueDate.ToString("yyyy-MM-dd")}未找到汇率:{x.Key}{client.SettlementCurrency}");
}
InFund += x.Money * rateObj.Rate;
});
var outFunds = clientCash.Where(a => a.Direction == "出金").AsEnumerable().GroupBy(x => x.CurrencyCode).Select(x => new { x.Key, Money = x.Sum(y => y.Money ?? 0) }).ToList();
outFunds.ForEach(x =>
{
new EodCurrencyProvider(valueDate, false).TryGetCurrencyRate(x.Key, client.SettlementCurrency, out var rateObj);
if (rateObj == null)
{
throw new Exception($"{valueDate.ToString("yyyy-MM-dd")}未找到汇率:{x.Key}{client.SettlementCurrency}");
}
OutFund += x.Money * rateObj.Rate;
});
NetFund = InFund - OutFund;
}
if (clientCash.Any(a => a.Direction == "其他收入" || a.Direction == "其他支出"))
{
var inFundOthers = clientCash.Where(a => a.Direction == "其他收入").AsEnumerable().GroupBy(x => x.CurrencyCode).Select(x => new { x.Key, Money = x.Sum(y => y.Money ?? 0) }).ToList();
inFundOthers.ForEach(x =>
{
new EodCurrencyProvider(valueDate, false).TryGetCurrencyRate(x.Key, client.SettlementCurrency, out var rateObj);
if (rateObj == null)
{
throw new Exception($"{valueDate.ToString("yyyy-MM-dd")}未找到汇率:{x.Key}{client.SettlementCurrency}");
}
InFundOther += x.Money * rateObj.Rate;
});
var outFundOthers = clientCash.Where(a => a.Direction == "其他支出").AsEnumerable().GroupBy(x => x.CurrencyCode).Select(x => new { x.Key, Money = x.Sum(y => y.Money ?? 0) }).ToList();
outFundOthers.ForEach(x =>
{
new EodCurrencyProvider(valueDate, false).TryGetCurrencyRate(x.Key, client.SettlementCurrency, out var rateObj);
if (rateObj == null)
{
throw new Exception($"{valueDate.ToString("yyyy-MM-dd")}未找到汇率:{x.Key}{client.SettlementCurrency}");
}
OutFundOther += x.Money * rateObj.Rate;
});
OtherFund = InFundOther + OutFundOther;
}
}
//抵押品资金价值
var tempClientProductList = clientProductList.Where(a => a.ClientId == client.id).ToList();
var GuaranteesTotalAmount = 0.0;
if (tempClientProductList.Any())
{
foreach (var t in tempClientProductList)
{
var um = umList.GetData(t.UnderlyingId ?? 0);
if (um != null)
{
if (!eodPriceProvider.TryGetPrice(um.UnderlyingCode, out var price_cp))
{
price_cp = umList.GetPrice(um.UnderlyingCode);
}
GuaranteesTotalAmount += (t.ProductAmount ?? 0) * (t.Rate ?? 0.0) * price_cp;
}
}
}
//持仓
//潜在行权盈余
PotentialSurpluses = 0.0;
var clientPositionList = positionList.Where(a =>
a.ClientId == client.id && ConsTrade.NeedMarginTradeStatusList.Contains(a.TradeStatus))
.ToList();
//持仓笔数
var clientPositionTrades = positionTrades.Where(t => t.ClientId == client.id &&
ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus));
PositionCount = clientPositionTrades.Count(t => t.ParentTradeId == 0) +
clientPositionTrades.Where(t => t.ParentTradeId > 0)
.AsEnumerable()
.GroupBy(t => t.ParentTradeId).Count();
//应付预付金
SellNotionalPrincipal = 0.0;
BuyNotionalPrincipal = 0.0;
totalNotionalPrincipal = 0.0;
UpdateDate = null;
if (PositionCount > 0)
{
UpdateDate = clientPositionList.Max(a => a.OptDate);
if (valuedate.PotentialSurplusCalcMode_ActualPv.Equals(systemDate
.PotentialSurplusCalcMode))
{
PotentialSurpluses =
clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a => a.ActualPv) * (-1); //潜在行权收益等于实值额
}
else
{
PotentialSurpluses =
Convert.ToDouble(clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a => a.Pv) * (-1)); //潜在行权收益等于实值额
}
PositionPv = Convert.ToDouble(clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a => a.Pv));
RoundedPositionPv = Convert.ToDouble(clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a => a.RoundedPv));
SellPv = Convert.ToDouble(clientPositionList.Where(x => x.TradeType != "结构化交易").Where(x => x.BuySell == "买入").Sum(a => a.Pv));
//客户持仓交易预付金总和,客户买入为正,客户卖出为负
PrepaymentAmount = positionTrades.Sum(x => x.StockEqvNotional * (x.trade_snowball?.PrepaymentRatio ?? 0) * (x.BuySell == "卖出" ? 1 : -1));
DaliyPnl = Convert.ToDouble(clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a =>
a.DailyPnL));
PositionPnl = clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a => a.PositionPnL);
RoundedPositionPnl = clientPositionList.Where(x => x.TradeType != "结构化交易").Sum(a => a.RoundedPositionPnl);
#region 期权空头浮动盈利=max(期权空头持仓*(期权合约成本价-期权合约现价), 0) 从客户角度看的, 结构化交易需要将两条腿的空头Pnl合计
var parentIds = clientPositionList.Where(x => x.TradeType == "结构化交易").Select(x => x.TradeId).ToList();
var parentPositionPnlList = from t in clientPositionList.Where(x => parentIds.Contains(x.ParentTradeId))
group t by new { t.ParentTradeId } into g
select new
{
parentTradeId = g.Key.ParentTradeId,
ClientSellPositionPnl = Math.Max(g.Sum(n => n.BuySell == "买入" ? -n.PositionPnL : 0), 0)
};
var parentPositionPnlTotal = parentPositionPnlList.Sum(x => x.ClientSellPositionPnl);
var singlePositionPnlTotal = clientPositionList.Where(x => x.TradeType != "结构化交易" && x.ParentTradeId == 0).Sum(n => n.BuySell == "买入" ? Math.Max(-n.PositionPnL, 0) : 0);
ClientSellPositionPnl = parentPositionPnlTotal + singlePositionPnlTotal;
#endregion
}
//获取tradespan 追保金额 = (当日资金余额 - 维持预付金) + 授信额度
var clientSpan = clientSpans.FirstOrDefault(t => t.ClientId == client.id);
PayableMargin = clientSpan == null || clientSpan.WorstCastClientPayable == null ? 0.0 : clientSpan.WorstCastClientPayable.Value;
TwoSideMargin = clientSpan == null || clientSpan.WorstCastClientPayable == null ? 0.0 : clientSpan.WorstCastClientPayable.Value;
SwapPayableMargin = clientSpan == null || clientSpan.SwapWorstCastClientPayable == null ? 0.0 : clientSpan.SwapWorstCastClientPayable.Value;
SwapUnMargin = clientSpan == null || clientSpan.SwapUnMargin == null ? 0.0 : clientSpan.SwapUnMargin.Value;
//存量交易持仓名义本金规模
AvailableStockEqvNotional -= allTradeList.Where(O => O.ClientId == client.id && ConsTrade.PositionTradeStatusList.Contains(O.TradeStatus) && O.ParentTradeId == 0).Sum(O => (double?)O.StockEqvNotional) ?? 0;
//交易数量
var clientAllTradeList = allTradeList.Where(t => t.ClientId == client.id).ToList();
if (clientAllTradeList.Count > 0)
{
//名义本金取 客户买入(交易员为卖出)的客户
SellNotionalPrincipal = clientAllTradeList.Sum(a =>
(a.OriginalStockEqvNotional > 0
? (a.OriginalStockEqvNotional ?? 0.0)
: ((a.SpotPrice ?? 0.0) * (a.OriginalNotional ?? 0))) *
("买入".Equals(a.BuySell) ? 1 : 0));
BuyNotionalPrincipal = clientAllTradeList.Sum(a =>
(a.OriginalStockEqvNotional > 0
? (a.OriginalStockEqvNotional ?? 0.0)
: ((a.SpotPrice ?? 0.0) * (a.OriginalNotional ?? 0))) *
("卖出".Equals(a.BuySell) ? 1 : 0));
totalNotionalPrincipal = SellNotionalPrincipal + BuyNotionalPrincipal;
buyCount = clientAllTradeList.Where(p => p.BuySell == "卖出").Count();
sellCount = clientAllTradeList.Where(p => p.BuySell == "买入").Count();
totalTradeCount = buyCount + sellCount;
}
//当日成交 新增交易
var todayClientTradeList = todayTradeList.Where(a => a.ClientId == client.id).ToList();
var addNewTradeIds = todayClientTradeList.Select(a => a.id).ToList();
if (addNewTradeIds.Count > 0 &&
clientPositionList.Where(x => x.ParentTradeId == 0).Any(a => addNewTradeIds.Contains(a.TradeId)))
{
//当日新增预付金
TodayNewMargin =
clientPositionList.Where(x => x.ParentTradeId == 0).Sum(a => (a.Margin) * (a.BuySell == "买入" ? -1 : 0));
}
var todayClientTradesAfterConfirmed = todayTradesAfterConfirmed.Where(a => a.ClientId == client.id).ToList();
//交易笔数
todayTradeCount = todayClientTradesAfterConfirmed.Count;
todayNotionalPrincipal = todayClientTradesAfterConfirmed.Sum(a =>
(a.OriginalStockEqvNotional > 0
? (a.OriginalStockEqvNotional ?? 0.0)
: ((a.SpotPrice ?? 0.0) * (a.OriginalNotional ?? 0))));
//应付冻结权利金
var clientFrozenFund = clientFrozenFunds[client.id];
FreezePremium = clientFrozenFund.FrozenPayableOptionMoney;
ReceivablesPremium = clientFrozenFund.FrozenReceivableOptionMoney;
FrozenOutFund = clientFrozenFund.OutFunds;
FrozenRedeemFunds = clientFrozenFund.RedeemFunds;
//冻结预付金(绝对值)
var FrozenMarginMoney = clientFrozenFund.FrozenMarginMoney;
//var tempTrades = positionTrade.Where(a => a.ClientId == client.id && trade.NeedMarginTradeStatusList.Contains(a.TradeStatus) && a.PremiumPayDate > valueDate).ToList();
//if (tempTrades.Any())
//{
// //应付冻结权利金
// if (tempTrades.Where(a => a.BuySell == "卖出").Any())
// {
// FreezePremium = tempTrades.Where(a => a.BuySell == "卖出").Sum(a => a.TradePrice ?? 0.0);
// }
// //应收冻结权利金
// if (tempTrades.Where(a => a.BuySell == "买入").Any())
// {
// ReceivablesPremium = tempTrades.Where(a => a.BuySell == "买入").Sum(a => a.TradePrice ?? 0.0);
// }
//}
//当前账号资金
AmountFund = AmountFund + NetFund + OtherFund + Coupon + SwapBalance + OptionPremium + OptionPremiumSwap + SettlementBalance;
//当日可用资金 账户总资金 - 应付冻结权利金 - 冻结出金 - 冻结抵押品赎回 + 应收冻结权利金 - 应付预付金 - 冻结预付金
AvailableAmount = AmountFund - FreezePremium - FrozenOutFund - FrozenRedeemFunds +
ReceivablesPremium + PayableMargin - FrozenMarginMoney;
//预付金余额
MarginBalance = AmountFund - FreezePremium - FrozenOutFund - FrozenRedeemFunds +
ReceivablesPremium + GuaranteesTotalAmount;
//判断潜在行权盈余是否计入预付金余额
if ((systemDate.IsPotentialSurplusUseMargin ?? 0) == 1)
{
MarginBalance += PotentialSurpluses;
AvailableAmount += PotentialSurpluses;
}
////追保金额
//Margin = Math.Max(PayableMargin - MarginBalance, 0);
// 除收益互换预付金资金占用后剩余资金
var marginBalanceSurplus = 0d;
//计算追保金额
//是否期权费授信
if ((client.IsTradeCredit ?? 0) == 1)
{
IsTradeCredit = true;
//授信是否可用于互换
if (client.creditCanApplySwap)
{
///期权费授信可以支付权利金 则预付金余额未0时 资金从 互换剔除互换容忍金额
Margin = Math.Min(MarginBalance + lineOfCredit + (PayableMargin - SwapUnMargin), 0);
}
else
{
//互换最大可用资金
marginBalanceSurplus = Math.Max(Math.Min(Math.Max(MarginBalance - OptionPremiumSwap + lineOfCredit + (PayableMargin - SwapPayableMargin), 0), Math.Max(MarginBalance - OptionPremiumSwap, 0)), 0);
//收益互换追保金额 剩余可支付互换资金 - (互换占用预付金 - 互换容忍预付金额度)
SwapMargin = Math.Min(marginBalanceSurplus + OptionPremiumSwap + (SwapPayableMargin - SwapUnMargin), 0);
//计算期权追保
Margin = Math.Min(MarginBalance - OptionPremiumSwap + lineOfCredit + (PayableMargin - SwapPayableMargin), 0) + SwapMargin;
}
}
else
{
IsTradeCredit = false;
//授信是否可用于互换
if (client.creditCanApplySwap)
{
//期权费授信不可以支付权力金时
Margin = Math.Min(Math.Max(MarginBalance, 0) + lineOfCredit + (PayableMargin - SwapUnMargin), 0) + Math.Min(MarginBalance, 0);
}
else
{
//互换最大可用资金
marginBalanceSurplus = Math.Max(Math.Min(Math.Max(Math.Max(MarginBalance - OptionPremiumSwap, 0) + lineOfCredit + (PayableMargin - SwapUnMargin), 0), Math.Max(MarginBalance - OptionPremiumSwap, 0)), 0);
//收益互换追保金额 剩余可支付互换资金 - (互换占用预付金 - 互换容忍预付金额度)
SwapMargin = Math.Min(marginBalanceSurplus + OptionPremiumSwap + (SwapPayableMargin - SwapUnMargin), 0) + Math.Min(marginBalanceSurplus + OptionPremiumSwap, 0);
//计算期权追保
Margin = Math.Min(Math.Max(MarginBalance - OptionPremiumSwap, 0) + lineOfCredit + (PayableMargin - SwapUnMargin), 0) + Math.Min(MarginBalance - OptionPremiumSwap, 0) + SwapMargin;
}
}
//可取预付金为预付金余额 - 应缴预付金(上浮 + 0.02)
AdvisableMargin = Math.Max(MarginBalance + PayableMargin * marginMaxRation / marginRation, 0);
LastDayPositionPremiumNetCash = clientBanlance == null ? 0.0 : clientBanlance.PositionPremiumNetCash ?? 0.0;
PositionPremiumNetCash = clientPositionList.Sum(t =>
t.Cost * (-TradeCalcHelper.GetSign(t.BuySell)));
SellTradePrice = clientPositionList.Where(x => x.BuySell == "买入").Sum(t => (t.Cost));
#region 新交易权利金, 新交易预付金,新交易初保,额外追保,总追保金额
var todayNewClientPostionList = clientPositionList
.Where(a => addNewTradeIds.Contains(a.TradeId)).ToList();
#endregion
ClientBanlanceList.Add(new ClientSettleBalance
{
ValueDate = valueDate,
ClientId = client.id,
FreezePremium = FreezePremium,
FrozenOutFund = FrozenOutFund,
FrozenRedeemFunds = FrozenRedeemFunds,
ReceivablesPremium = ReceivablesPremium,
NetFund = NetFund,
OtherFund = OtherFund,
AmountFund = AmountFund,
LastDayRemainFund = LastDayRemainFund,
LastDayRemainFundWithProduct = LastDayRemainFund + lastGuaranteesTotalAmount,
AvailableAmount = AvailableAmount,
CreditRating = creditRating == null ? "" : creditRating.CreditName,
TotalCredit = lineOfCredit,
//CreditUsed = lineOfCredit > 0 ? Math.Max(-AvailableAmount / lineOfCredit, 0.0) : 0.0,
TradeCount = todayTradeCount,
SellNotionalPrincipal = SellNotionalPrincipal,
BuyNotionalPrincipal = BuyNotionalPrincipal,
PositionCount = PositionCount,
PayableMargin = PayableMargin,
TwoSideMargin = TwoSideMargin,
PotentialSurpluses = PotentialSurpluses,
MarginBalance = MarginBalance,
Margin = Margin,
AdvisableMargin = AdvisableMargin,
UpdateDate = UpdateDate,
IsTradeCredit = IsTradeCredit,
OptionPremium = OptionPremium,
OptionPremiumSwap = OptionPremiumSwap,
SwapPayableMargin = SwapPayableMargin,
SwapUnMargin = SwapUnMargin,
SettlementBalance = SettlementBalance,
UnwindBalance = UnwindBalance,
ExerciseBalance = ExerciseBalance,
SwapBalance = SwapBalance,
Coupon = Coupon,
TodayNewMargin = TodayNewMargin,
GuaranteesTotalAmount = GuaranteesTotalAmount,
PositionPv = PositionPv * (-1), //客户方向取反
RoundedPositionPv = RoundedPositionPv * (-1),
SellPv = SellPv * (-1),
PrepaymentAmount = PrepaymentAmount,
DaliyPnl = DaliyPnl * (-1),
PositionPnl = PositionPnl * (-1),
RoundedPositionPnl = RoundedPositionPnl * (-1),
ClientSellPositionPnl = ClientSellPositionPnl,
LastGuaranteesTotalAmount = lastGuaranteesTotalAmount,
CashInCashOutProductChange = GuaranteesTotalAmount - lastGuaranteesTotalAmount,
TotalTradeCount = totalTradeCount,
SellCount = sellCount,
BuyCount = buyCount,
TotalNotionalPrincipal = totalNotionalPrincipal,
TodayNotionalPrincipal = todayNotionalPrincipal,
PositionPremiumNetCash = PositionPremiumNetCash,
SellTradePrice = SellTradePrice,
LastDayPositionPremiumNetCash = LastDayPositionPremiumNetCash,
WinLoss = WinLoss,
AvailableStockEqvNotional = AvailableStockEqvNotional ?? 0,
});
});
return ClientBanlanceList;
}
}
public static bool TradeCanBeConfirm(int clientId, trade trade, out string errorMsg)
{
errorMsg = "";
if (trade.TradeType == "现金流交易" || (trade.TradeType == "结构化交易" && trade.UnderlyingCode == null))
{
return true;
}
var clientBalances = GetRealTimeClientBanlance(new List<int> { clientId });
if (clientBalances == null || clientBalances.Count <= 0)
{
errorMsg = "客户实时资金错误!";
return false;
}
if (trade.Warning)
{
new TradeDalService(OptUserInfo.SystemUser).RollbackToBeforeSettle(trade, valuedateBLL.ValueDate);
}
if (trade.IsOption() || trade.TradeType == "结构化交易" || trade.TradeType == "自定义交易")
{
//交易为买入 即:客户为卖出
if (EnumTradeBuySell.Buy.Equals(trade.BuySell))
{
var clientBalance = clientBalances[0];
using (var db = new YLContext())
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingId);
if (underlying == null)
{
errorMsg = "未找到标的信息";
return false;
}
if (trade.ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
{
var tradePrice = trade.TradePrice * (-TradeCalcHelper.GetSign(trade.BuySell));
var margin = trade.InitialMargin ?? 0;
var endMoney = margin + tradePrice;
var AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0);
if (AvailableAmount < endMoney)
{
errorMsg = $"当前交易应付预付金:{margin:#,##0.000},应付成交金额:{tradePrice:#,##0.000},总应付资金:{endMoney:#,##0.000}。当前剩余资金:{clientBalance.AmountFund:F3},冻结资金:{clientBalance.AllFreezeBalance():F3},抵押品价值:{clientBalance.GuaranteesTotalAmount:F3},授信额度:{clientBalance.TotalCredit:F3},可用总额度:{AvailableAmount:F3}。不足以支付上述金额。";
return false;
}
}
return true;
}
}
else//交易为卖出 即:客户为买入
{
var clientBalance = clientBalances[0];
var tradePrice = trade.TradePrice;
var AvailableAmount = clientBalance.AvailablePremium();
var endMoney = tradePrice;
if (AvailableAmount >= tradePrice)
{
return true;
}
else
{
errorMsg = $"当前交易应付预付金:{0:#,##0.000},应付成交金额:{tradePrice:#,##0.000},总应付资金:{endMoney:#,##0.000}。当前剩余资金:{clientBalance.AmountFund:F3},冻结资金:{clientBalance.AllFreezeBalance():F3},抵押品价值:{clientBalance.GuaranteesTotalAmount:F3},授信额度:{clientBalance.TotalCredit:F3},可用总额度:{AvailableAmount:F3}。不足以支付上述金额。";
return false;
}
}
}
else if (trade.TradeType == "收益互换")
{
var clientBalance = clientBalances[0];
using (var db = new YLContext())
{
var tradePrice = trade.TradePrice * (-TradeCalcHelper.GetSign(trade.BuySell));
var AvailableAmount = clientBalance.AvailablePremium();
if (AvailableAmount < tradePrice && tradePrice > 0)
{
var endMoney = tradePrice;
errorMsg = $"当前交易应付预付金:{0:#,##0.000},应付成交金额:{tradePrice:#,##0.000},总应付资金:{endMoney:#,##0.000}。当前剩余资金:{clientBalance.AmountFund:F3},冻结资金:{clientBalance.AllFreezeBalance():F3},抵押品价值:{clientBalance.GuaranteesTotalAmount:F3},授信额度:{clientBalance.TotalCredit:F3},可用总额度:{AvailableAmount:F3}。不足以支付上述金额。";
return false;
}
else
{
if (trade.StructureType != "多空组合")
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingId);
if (underlying == null)
{
errorMsg = "未找到标的信息";
return false;
}
}
if (trade.ExerciseDate.Value.Date >= valuedateBLL.ValueDate.Date)
{
// R4 簿记资金校验口径(2026-08-21 业务强调"走了资金的就不能占用授信"):
// 按腿的资金走向分流——走现金的部分(选现金/未选且交易级资金来源非授信 + 成交金额)只认现金结存;
// 按授信的腿(腿选授信,或腿未选回退交易级 margin_fund_source=授信,ConsFundTag.PreferCredit
// 认 剩余可用授信(有效授信−已使用授信,授信出入表 Σ(amount)),
// 授信不够覆盖的部分回落现金,同样只认现金结存。杜绝"现金腿拿授信垫付校验→现金透支"。
var marginModes = new[] { (int)InterestModeEnum.追加预付金, (int)InterestModeEnum.初始预付金 };
var legs = trade.swap_positions?.Where(x => marginModes.Contains(x.InterestMode)).ToList();
if (legs == null || legs.Count == 0)
{
legs = db.swap_position.Where(x => x.SwapTradeId == trade.id && x.IsInitial && !x.Invalid
&& marginModes.Contains(x.InterestMode)).ToList();
}
//客户应付为正:收取方向(dir=1)腿 fix 为正应付额;支付方向为客户收钱不参与
double creditPayable = 0, cashPayable = 0;
foreach (var leg in legs)
{
var payable = Convert.ToDouble(leg.InterestPrincipalFix) * (leg.InterestDirection == 1 ? 1 : -1);
if (payable <= 0)
{
continue;
}
if (YLErp.DBModels.ConsFundTag.PreferCredit(leg.FundTag, trade.MarginFundSource))
{
creditPayable += payable;
}
else
{
cashPayable += payable;
}
}
var usedCredit = Modules.SwapModule.ClientCreditInoutService.GetUsedCredit(clientId, db);
var creditCap = Math.Max(clientBalance.TotalCredit - usedCredit, 0);
var creditCovered = Math.Min(creditPayable, creditCap);
//授信覆盖不足的回落现金部分 + 走现金部分 + 成交金额,合计必须 ≤ 现金结存
var cashNeed = tradePrice + cashPayable + (creditPayable - creditCovered);
if (cashNeed > clientBalance.AmountFund)
{
var totalPayable = tradePrice + cashPayable + creditPayable;
errorMsg = $"当前交易应付总额:{totalPayable:#,##0.000}(走现金:{cashPayable + tradePrice:#,##0.000},选授信:{creditPayable:#,##0.000})。当前现金结存:{clientBalance.AmountFund:F3}(走现金部分只认现金结存),授信额度:{clientBalance.TotalCredit:F3},已使用授信:{usedCredit:F3},剩余授信:{creditCap:F3}(授信仅覆盖选授信部分,不足回落现金)。现金不足以覆盖应付的现金部分。";
return false;
}
}
return true;
}
}
}
else if (trade.TradeType == "远期")
{
using (var db = new YLContext())
{
var forwardTrade = db.trade_forward.FirstOrDefault(x => x.TradeId == trade.id);
if (forwardTrade != null)
{
//远期验资,验证客户余额>= 开仓总费用
var clientBalance = clientBalances[0];
var totalOpenCommission = trade.TradePrice ?? 0;
var AvailableAmount = clientBalance.AvailablePremium();
if (AvailableAmount >= totalOpenCommission || totalOpenCommission < 0)
{
return true;
}
else
{
errorMsg = $"当前剩余资金:{clientBalance.AmountFund:F3},冻结资金:{clientBalance.AllFreezeBalance():F3},应付预付金:{clientBalance.PayableMargin:F3},抵押品价值:{clientBalance.GuaranteesTotalAmount:F3},授信额度:{clientBalance.TotalCredit:F3},可用总额度:{AvailableAmount:F3}。不足以支付开仓总金额:{totalOpenCommission:F3}";
return false;
}
}
else
{
errorMsg = "客户远期交易数据未找到!";
return false;
}
}
}
else
{
errorMsg = "非期权交易,无需检查!";
return false;
}
}
/// <summary>
/// 实时计算收益互换实时持仓
/// </summary>
/// <param name="td"></param>
/// <returns></returns>
public static intraday_trade_position RealTimeSwapCalc(trade td, YLContext db, List<swap_flow> swapFlows)
{
intraday_trade_position posi = new intraday_trade_position();
var position = db.swap_position.FirstOrDefault(x => x.SwapTradeId == td.id && x.PosiDirection > 0 && x.IsInitial);
if (position == null)
{
return null;
}
var swapFlowItems = swapFlows.Where(x => x.ClientId == td.ClientId && x.UnderlyingCode == position.UnderlyingCode);
return posi;
}
public static ClientBalanceForTrsResponse GetClientBalance(int clientId)
{
if (_yLCache != null)
{
return _yLCache.StringGet<ClientBalanceForTrsResponse>("ClientBalance:" + clientId);
}
return null;
}
}
}