2399 lines
140 KiB
C#
2399 lines
140 KiB
C#
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.Model.HengTaiModel;
|
|
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.PosiNetPrice * ConsGlobal.bondShowPriceMultiple
|
|
: s.PosiNetPrice;
|
|
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);
|
|
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.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 获取成交收益率
|
|
{
|
|
BondCalcApi(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 ?? 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;
|
|
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;
|
|
clientBanlances = db.ClientBalanceDaily.Where(t =>
|
|
t.BalanceDate == lastSettletDate && clientIds.Contains(t.ClientId)).ToList();
|
|
}
|
|
|
|
//获取实时持仓
|
|
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 lastSettletDateAddOne = lastSettletDate.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" && t.HappenDate >= lastSettletDateAddOne && 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" && t.HappenDate >= lastSettletDateAddOne && 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;
|
|
//资金净流入 + 权利金收支 + 期权费收支
|
|
var clientCashSwap = ClientCashSwapList.Where(a => a.ClientId == client.id).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).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)
|
|
{
|
|
var margin = trade.InitialMargin ?? 0;
|
|
var endMoney = margin + tradePrice;
|
|
AvailableAmount = Math.Max(clientBalance.AvailableAmount + clientBalance.TotalCredit, 0);
|
|
if (AvailableAmount < margin + tradePrice)
|
|
{
|
|
errorMsg = $"当前交易应付预付金:{trade.InitialMargin ?? 0:#,##0.000},应付成交金额:{tradePrice:#,##0.000},总应付资金:{endMoney:#,##0.000}。当前剩余资金:{clientBalance.AmountFund:F3},冻结资金:{clientBalance.AllFreezeBalance():F3},抵押品价值:{clientBalance.GuaranteesTotalAmount:F3},授信额度:{clientBalance.TotalCredit:F3},可用总额度:{AvailableAmount:F3}。不足以支付上述金额,交易费用:{tradePrice ?? 0: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;
|
|
}
|
|
}
|
|
}
|