Files
zszq-trs/YLErpDAL/Modules/SwapModule/SwapEodPositionService.cs
T
2025-06-23 13:27:48 +08:00

2094 lines
119 KiB
C#

using BaseOUDAL;
using ClosedXML.Report.Utils;
using CsvHelper;
using DocumentFormat.OpenXml.Spreadsheet;
using MathNet.Numerics.LinearAlgebra.Factorization;
using MoreLinq;
using Newtonsoft.Json;
using NPOI.POIFS.NIO;
using NPOI.SS.Formula.Functions;
using NPOI.SS.UserModel;
using Org.BouncyCastle.Asn1.Cmp;
using Org.BouncyCastle.Math.EC.Multiplier;
using Org.BouncyCastle.Ocsp;
using Qdp.Pricing.Library.Common.Products.Abs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Cryptography.Xml;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Models;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.ReportModule;
using YLErp.Modules.TradeModule;
using YLErp.QdpModule;
using static alglib;
using static YLErp.ConsGlobal;
using static YLErp.DBModels.ConsTrade;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// 互换流水日终归档服务
/// </summary>
public class SwapEodPositionService : SwapTradeBaseService
{
private List<int> marginTypes = new List<int>() { (int)InterestModeEnum.追加预付金, (int)InterestModeEnum.初始预付金 };
public SwapEodPositionService(OptUserInfo optUser) : base(optUser)
{
}
public SwapEodPositionService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 多空组合 互换流水合成持仓
/// </summary>
/// <param name="tradeDate">清算日期</param>
public void SwapFlowEventCompose(DateTime tradeDate)
{
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 41, "互换流水合成持仓进行中");
var eventQueryGroup = DbContext.swap_flow_event.Where(n => n.DataState == (int)SwapFlowDateStateEnum.等待完成 && n.EventDate == tradeDate).AsEnumerable().GroupBy(g => g.SwapTradeId);
foreach (var eventQueryGroupItem in eventQueryGroup)
{
ComposePage(eventQueryGroupItem.Key, eventQueryGroupItem.ToList(), tradeDate);
}
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 42, "互换流水合成持仓完成");
}
/// <summary>
/// 多空组合 互换流水合成持仓
/// </summary>
/// <param name="tradeDate">清算日期</param>
public void SwapFlowEventCompose(List<long> flowEventIds, DateTime tradeDate)
{
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 41, "互换流水合成持仓进行中");
var eventQueryGroup = DbContext.swap_flow_event.Where(n => flowEventIds.Contains(n.id)).ToList().GroupBy(g => g.SwapTradeId);
foreach (var eventQueryGroupItem in eventQueryGroup)
{
ComposePage(eventQueryGroupItem.Key, eventQueryGroupItem.ToList(), tradeDate, false);
}
new SysJobService(UserInfo).UpdateJob("互换流水合成持仓", 42, "互换流水合成持仓完成");
}
/// <summary>
/// 收盘生成归档信息
/// </summary>
/// <param name="settleDate">结算日期</param>
public void SwapPositionCompose(DateTime settleDate, DateTime preSettleDate, IEnumerable<int> ClientIds)
{
var dateStr = settleDate.ToString("yyyy-MM-dd");
var tradePredicate = PredicateBuilder.Create<trade>(n => n.ValidState != ConsGlobal.InValid
&& n.TradeType == "收益互换"
&& n.TradeDate <= settleDate
&& n.ExerciseDate >= settleDate
&& (n.TradeStatus == ConsTrade.确认成交 || n.UnWindDate >= settleDate)
);
if (ClientIds != null && ClientIds.Any())
{
tradePredicate = tradePredicate.And(x => ClientIds.Contains(x.ClientId));
}
var tradeQueryList = DbContext.trade.Where(tradePredicate).ToList();
var tradeIds = tradeQueryList.Select(s => s.id).ToList();
var allTradePositionList = DbContext.swap_position.Where(t => tradeIds.Contains(t.SwapTradeId) && !t.Invalid).ToList();
var tradePositionList = allTradePositionList.Where(t => t.IsInitial).ToList();
var tradeRealPositionList = allTradePositionList.Where(t => !t.IsInitial).ToList();
var tradeExtendList = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList();
var eodSwapList = DbContext.eod_swap.Where(x => x.ValueDate == preSettleDate).ToList();
List<int> eventTyps = new List<int>() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
foreach (var td in tradeQueryList)
{
var trans = DbContext.Database.BeginTransaction();
try
{
List<int> removeEventTyps = new List<int>() { (int)SwapEventTypeEnum.自动互换 };
bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString();
ClearSwapPositions(td, settleDate, removeEventTyps, true);
var positions = tradePositionList.Where(x => x.SwapTradeId == td.id);
var realPositions = tradeRealPositionList.Where(s => s.SwapTradeId == td.id);
var posiList = positions.Where(x => x.PosiQuantity > 0).ToList();
var realPosiList = realPositions.ToList();
var interestList = positions.Where(x => x.InterestDirection > 0).ToList();
DateTime posiDate = td.TradeDate.Value;//交易日期
var lastEodSwap = eodSwapList.FirstOrDefault(x => x.SwapTradeId == td.id);
//上一交易日无日终归档,且不是交易日期,且当前收盘日期不是交易日期,报错
if (lastEodSwap == null && settleDate > posiDate)
{
throw new Exception($"交易{td.TradeNumber}在上一交易日【{preSettleDate:yyyy-MM-dd}】未收盘");
}
var allEodPositions = DbContext.eod_swap_position.Where(x => x.ValueDate >= preSettleDate && x.SwapTradeId == td.id && !x.Invalid);
var eodPositions = allEodPositions.Where(x => x.ValueDate == preSettleDate).ToList();//上一日终持仓信息
var tradeExtend = tradeExtendList.FirstOrDefault(x => x.TradeId == td.id);
td.trade_extend = tradeExtend;
var todyEodPositions = allEodPositions.Where(x => x.ValueDate == settleDate).ToList();
var allPositionQty = realPositions.Sum(x => x.PosiQuantity);//总剩余持仓数量
var orginPv = eodPositions.Sum(s => s.PosiNotionalValue);
if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty != 0)//多空组合判断是否已到到期日且无持仓信息
{
throw new Exception($"交易【{td.TradeNumber}】到期扔有持仓信息");
}
var flowEvents = new List<swap_flow_event>();
Expression<Func<swap_flow_event, bool>> eventExpression = x => x.SwapTradeId == td.id && x.DataState == (int)SwapFlowDateStateEnum.完成;
eventExpression = eventExpression.And(x => x.EventDate == settleDate );
//if (settleDate == td.TradeDate)
//{
// eventExpression = eventExpression.And(x => x.EventDate == settleDate);
//}
//else
//{
// eventExpression = eventExpression.And(x => x.UnwindDate == settleDate);
//}
flowEvents = DbContext.swap_flow_event.Where(eventExpression).ToList();
var preDealDate = GetPreDealDate(td.id, settleDate, eventTyps);//上一次平仓/互换/自动互换处理日期
List<swap_flow_event> autoInterests = new List<swap_flow_event>();//自动互换利息腿信息
//处理浮动腿
var curEodPosis = DealFloatPositions(posiList, realPosiList, eodPositions, todyEodPositions, settleDate, td, preSettleDate, flowEvents);
var posiLongNotional = curEodPosis.Where(s => s.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
var posiShortNotional = curEodPosis.Where(s => s.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);
var closePosiNotional = curEodPosis.Where(s => s.TdCloseQty > 0).Sum(s => s.TdCloseQty * s.ContractSize * s.PosiNetPrice);
var grossPrice = curEodPosis.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice ?? 0;
//处理利息腿
DealInterests(interestList, eodPositions, todyEodPositions, settleDate, td, flowEvents, autoInterests, lastEodSwap, posiLongNotional, posiShortNotional, closePosiNotional, grossPrice, orginPv);
DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional);
//多空组合判断是否已到到期日且无持仓信息
if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty == 0)
{
td.TradeStatus = "已到期";
td.UnWindDate = settleDate;
}
DbContext.SaveChanges();
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw new Exception(ex.Message, ex);
}
finally
{
trans.Dispose();
}
}
}
/// <summary>
/// 框架合约汇总
/// </summary>
/// <param name="settleDate"></param>
/// <param name="preSettleDate"></param>
/// <param name="ClientIds"></param>
public void SwapEodCompose(DateTime settleDate, DateTime preSettleDate, IEnumerable<int> ClientIds)
{
var tradePredicate = PredicateBuilder.Create<trade>(n => n.ValidState != ConsGlobal.InValid
&& n.TradeType == "收益互换"
&& n.TradeDate <= settleDate
&& n.ExerciseDate >= settleDate
&& (n.TradeStatus == ConsTrade.确认成交 || n.UnWindDate >= settleDate)
);
if (ClientIds != null && ClientIds.Any())
{
tradePredicate = tradePredicate.And(x => ClientIds.Contains(x.ClientId));
}
var tradeQueryList = DbContext.trade.Where(tradePredicate).ToList();
foreach (var td in tradeQueryList)
{
SaveEodSwap(td, settleDate, preSettleDate);
}
DbContext.SaveChanges();
}
/// <summary>
/// 处理利息腿归档
/// </summary>
/// <param name="interestList">利息腿持仓信息</param>
/// <param name="eodPositions">上一日终归档持仓信息</param>
/// <param name="todyEodPositions">当日归档持仓信息</param>
/// <param name="settleDate">当前结算日期</param>
/// <param name="td">互换交易主信息</param>
/// <param name="swapDeals">当日平仓/互换信息</param>
/// <param name="autoInterests">自动互换集合</param>
/// <param name="longshortCloseInterests">多空组合平仓利息腿信息</param>
/// <param name="lastEodSwap">上一日终框架合约</param>
private void DealInterests(List<swap_position> interestList,
List<eod_swap_position> eodPositions,
List<eod_swap_position> todyEodPositions,
DateTime settleDate,
trade td,
List<swap_flow_event> flowEvents,
List<swap_flow_event> autoInterests,
eod_swap lastEodSwap,
decimal posiLongNational,
decimal posiShortNational,
decimal closeNational,
decimal grossPrice,
decimal orginPv)
{
var hasClose = flowEvents.Any(x => x.EventType == (int)SwapEventTypeEnum.平仓);
var hasSwap = flowEvents.Any(x => x.EventType == (int)SwapEventTypeEnum.互换);
foreach (var interest in interestList)
{
if (interest.InterestMode == (int)InterestModeEnum.追加预付金 && interest.HappenDate < settleDate)
{
continue;
}
var eodPosition = eodPositions.FirstOrDefault(x => x.PositionId == interest.id);//上一日日终利息信息 可能不存在
var tdEodPosition = todyEodPositions.FirstOrDefault(x => x.PositionId == interest.id);//当前结算日日终利息信息
var insterval = interest.SwapIntervalList.FirstOrDefault(x => x.Date == settleDate && x.Settlement == 1);//自动互换观察日信息
List<swap_flow_event> dealInterests = new List<swap_flow_event>();
dealInterests.AddRange(flowEvents);
var dealInterest = dealInterests.FirstOrDefault(n => n.PositionId == interest.id);//当日是否做过互换或平仓
var swapEvents = flowEvents.Where(x => (x.EventType == (int)SwapEventTypeEnum.互换 || x.EventType == (int)SwapEventTypeEnum.平仓) && x.PositionId == interest.id).ToList();
//如果当日有互换/当日有平仓 不再重新生成或更新
if (insterval != null && !hasSwap)
{
if (!hasClose)//当日无平仓
{
var _autoInterests = SaveAutoEodInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, insterval, lastEodSwap, posiLongNational, posiShortNational, grossPrice, orginPv);
if (_autoInterests.Count > 0)
{
autoInterests.AddRange(_autoInterests);
}
}
else
{
var _autoInterests = SaveAutoEodWithCloseInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, insterval, posiLongNational, posiShortNational, swapEvents, closeNational, true, grossPrice, orginPv);
if (_autoInterests.Count > 0)
{
autoInterests.AddRange(_autoInterests);
}
}
}
else if (hasSwap)//当日有互换,根据互换事件重新生成
{
SaveEodInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, swapEvents);
}
else if (hasClose)
{
SaveAutoEodWithCloseInterestPosition(eodPosition, tdEodPosition, interest, td, settleDate, insterval, posiLongNational, posiShortNational, swapEvents, closeNational, false, grossPrice, orginPv);
}
else//无自动互换、互换/平仓,复制上一日终信息,并计算当日新增利息
{
SaveEodInterestPositionCopy(eodPosition, tdEodPosition, settleDate, td, interest, lastEodSwap, true, posiLongNational, posiShortNational, grossPrice, orginPv);
}
}
}
/// <summary>
/// 处理浮动腿归档
/// </summary>
/// <param name="posiList"></param>
/// <param name="eodPositions"></param>
/// <param name="todyEodPositions"></param>
/// <param name="settleDate"></param>
/// <param name="td"></param>
/// <param name="longShort"></param>
/// <param name="flowEvents"></param>
/// <param name="unwindEvent"></param>
private List<eod_swap_position> DealFloatPositions(List<swap_position> posiList,
List<swap_position> realPosiList,
List<eod_swap_position> eodPositions,
List<eod_swap_position> todyEodPositions,
DateTime settleDate,
trade td,
DateTime preSettleDate,
List<swap_flow_event> flowEvents)
{
List<eod_swap_position> list = new List<eod_swap_position>();
foreach (var posi in posiList)
{
var eodPosition = eodPositions.Where(x => x.PositionId == posi.id).FirstOrDefault();//上一日日终持仓信息
var tdEodPosition = todyEodPositions.FirstOrDefault(x => x.PositionId == posi.id);//当前结算日日终持仓信息
var unwindEvents = flowEvents.Where(x => x.PositionId == posi.id).ToList();//当前日平仓信息
var realPosition = realPosiList.FirstOrDefault(s => s.PositionId == posi.id);
eod_swap_position eodPosi = new eod_swap_position();
if (eodPosition == null)
{
eodPosi = SaveCurrentEodInitalPosi(posi, td, settleDate, preSettleDate, unwindEvents);
}
else if (unwindEvents.Count() == 0)
{
eodPosi = CopyEodPosition(eodPosition, tdEodPosition, td, settleDate, preSettleDate);
}
else
{
eodPosi = UpdateEodPosition(posi, eodPosition, tdEodPosition, td, settleDate, preSettleDate, unwindEvents);
}
list.Add(eodPosi);
}
return list;
}
/// <summary>
/// 处理自动互换数据
/// </summary>
/// <param name="autoInterests"></param>
/// <param name="td"></param>
/// <param name="settleDate"></param>
/// <param name="swapDeals"></param>
private void DealAutoInterests(List<swap_flow_event> autoInterests, trade td, DateTime settleDate, DateTime? preDealDate, decimal StockEqvNotional)
{
if (autoInterests.Count == 0)
{
return;
}
//生成自动互换事件
UnwindData unwindData = new UnwindData();
unwindData.SwapTradeId = td.id;
unwindData.ValueDate = settleDate;
if (preDealDate.HasValue)
{
unwindData.StartDate = preDealDate.Value;
}
else
{
unwindData.StartDate = td.StartDate.Value;
}
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.PosiNotionalValue = StockEqvNotional;
autoInterests.ForEach(x =>
{
var ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
unwindData.SwapCloseAmount = unwindData.SwapCloseAmount + x.InterestClosePnL;
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
});
SaveAutoSwapDeal(td, autoInterests, unwindData);
}
/// <summary>
/// 保存自动互换数据信息
/// </summary>
/// <param name="td"></param>
/// <param name="swap_Deal"></param>
private long SaveAutoSwapDeal(trade td, List<swap_flow_event> flowEvents, UnwindData unwindData)
{
//td.UnWindDate = unwindData.ValueDate;
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
string data = JsonConvert.SerializeObject(unwindData);
var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, (int)SwapEventTypeEnum.自动互换, data, clientCashId, true, "系统操作-自动互换");//将互换总额存入事件
flowEvents.ForEach(x =>
{
x.EventId = swapEvent.id;
DbContext.swap_flow_event.Add(x);
});
UpdateInitalPostion(flowEvents, td.id);
return swapEvent.id;
}
/// <summary>
/// 互换更新实时持仓信息
/// </summary>
/// <param name="flowEvents"></param>
private void UpdateInitalPostion(List<swap_flow_event> flowEvents, int swapTradeId)
{
var positions = DbContext.swap_position.Where(x => !x.IsInitial && x.SwapTradeId == swapTradeId && !x.Invalid);
foreach (var position in positions)
{
var interest = flowEvents.FirstOrDefault(x => x.PositionId == position.id);
if (interest != null)
{
position.InterestAmount += interest.InterestAmount;
UpdateDbOption(position);
}
}
}
/// <summary>
/// 分页合成互换流水开平仓事件,暂时只按加权平均处理
/// </summary>
/// <param name="swapTradeId">互换交易id</param>
/// <param name="tradeDate">清算日期</param>
private void ComposePage(int swapTradeId, List<swap_flow_event> flowEvents, DateTime tradeDate, bool needTrans = true)
{
// 同一标的 事件编码一致
var eventQuery = flowEvents.OrderBy(o => o.PositionId).ToList();
if (eventQuery.Count == 0)
{
return;
}
SwapTradeService swapTradeService = new SwapTradeService(this);
var trans = needTrans ? DbContext.Database.BeginTransaction() : null;
try
{
UnwindData unwindData = new UnwindData();
unwindData.SwapTradeId = swapTradeId;
var swapEvent = new SwapEventService(this).AddSwapEventDate(tradeDate, swapTradeId, (int)SwapEventTypeEnum.合成持仓, string.Empty, 0, true, "系统操作-自动合成持仓");//不用回退,回退时过滤该事件类型
var td = DbContext.trade.Find(swapTradeId);
var preSettleDate = GetPreValueDate(tradeDate);//上一交易日期
List<int> eventTyps = new List<int>() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
List<int> removeEventTyps = new List<int>() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
ClearSwapPositions(td, tradeDate, removeEventTyps, false);
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == swapTradeId);
var allEodPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.Invalid && x.ValueDate >= preSettleDate);
//var curentInvalidEodPostions = allEodPositions.Where(x => x.SwapTradeId == swapTradeId && !x.Invalid && x.ValueDate >= tradeDate);//废弃当前清算日期之后的归档数据
//curentInvalidEodPostions.ForEach(x =>
//{
// x.Invalid = true;
//});
var eodPositions = allEodPositions.Where(x => x.ValueDate == preSettleDate).ToList();//上一日终持仓信息
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == swapTradeId && !x.Invalid).ToList();
var oriPositions = positions.Where(x => x.IsInitial).ToList();
var realPositions = positions.Where(x => !x.IsInitial).ToList();
var fpositions = positions.Where(x => x.PosiDirection > 0).ToList();
decimal tdCloseQty = 0;
decimal totalPosiNotionalValue = 0;//总剩余名义本金
decimal tdCloseNotionalValue = 0;//当日平仓名义本金
var preDealDate = GetPreDealDate(swapTradeId, tradeDate, eventTyps);//上一次平仓/互换/自动互换事件日期
var lastEodSwap = DbContext.eod_swap.FirstOrDefault(x => x.SwapTradeId == swapTradeId && x.ValueDate == preSettleDate);
decimal stockEqvNotional = lastEodSwap == null ? Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0) : lastEodSwap.NotionalValue;//上一日名义本金
unwindData.NotionalValue = stockEqvNotional;
List<swap_flow_event> longshortCloseInterests = new List<swap_flow_event>();
decimal tradePrice = 0;//开仓费
decimal allPosiNotionalValue = 0;
decimal longNotionalValue = realPositions.Where(s => s.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);//剩余多头名义本金规模
decimal shortNotionalValue = realPositions.Where(s => s.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);//剩余空头名义本金规模
foreach (var eventGroup in eventQuery.GroupBy(g => g.PositionId))//持仓标的腿合成持仓
{
var eventList = eventGroup.ToList();
var eventFlow = eventGroup.Last();
var fposition = fpositions.FirstOrDefault(n => n.PositionId == eventGroup.Key);
var position = fpositions.FirstOrDefault(n => n.id == eventGroup.Key);
var eodPayPosition = allEodPositions.Where(x => x.PositionId == eventFlow.PositionId).OrderByDescending(o => o.ValueDate).FirstOrDefault();//浮动腿 日终持仓信息
decimal netPrice = fposition == null ? 0 : fposition.PosiNetPrice;//持仓均价
decimal grossPrice = fposition == null ? 0 : fposition.PosiGrossPrice;//持仓均价-不含费
decimal netFeePrice = fposition == null ? 0 : fposition.PosiNetFeePrice ?? 0;//持仓净价-含费
decimal netNoFeePrice = fposition == null ? 0 : fposition.PosiNetNoFeePrice ?? 0;//持仓净价-不含费
decimal tradingFee = fposition == null ? 0 : fposition.PosiTradingFeePending;//持仓交易费用
decimal payQty = fposition == null ? 0 : fposition.PosiQuantity;//持仓数量
decimal posiNotionalValue = fposition == null ? 0 : fposition.PosiNotionalValue;//剩余名义本金
decimal dividendIn = 0;//当日浮动端分红
decimal tdDividendIn = 0;//当日浮动端平仓盈亏分红
decimal openQty = fposition == null ? 0 : fposition.PosiQuantity;//开仓数量
decimal openAmount = fposition == null ? 0 : openQty * grossPrice;//开仓累计成交金额不含费
decimal openAmountFee = fposition == null ? 0 : openQty * netPrice;//开仓累计成交金额含费
decimal openAmountNetFee = fposition == null ? 0 : openQty * netFeePrice;//开仓累计成交净价金额含费
decimal openAmountNet = fposition == null ? 0 : openQty * netNoFeePrice;//开仓累计成交净价金额不含费
decimal closeQty = 0;//当日平仓数量
decimal closeFee = 0;//当日平仓费用
decimal closeMtmPnl = 0;//当日平仓盈亏
var posiType = fposition == null ? 0 : fposition.PositionType;
var swapInsertEnum = SwapPushDataEnum.收益互换交易新增;
var swapPosiEnum = SwapPushDataEnum.互换资产交易新增;
if (fposition != null)
{
swapInsertEnum = SwapPushDataEnum.收益互换交易修改;
swapPosiEnum = SwapPushDataEnum.互换资产交易修改;
}
eventList.ForEach(x =>
{
x.EventId = swapEvent.id;
decimal ratio = x.EventType == 1 ? 1 : -1;//开仓为加法,平仓为减法
tradingFee = tradingFee + x.TradingFeePending;//开仓累计
if (x.EventType == 1)
{
openAmountFee = openAmountFee + x.TradingAmountFeeAvg * x.Quantity;
openAmount = openAmount + x.TradingAmountAvg * x.Quantity;
openAmountNetFee = openAmountNetFee + (x.TradingAmountNetFeeAvg * x.Quantity) ?? 0;
openAmountNet = openAmountNet + (x.TradingAmountNetAvg * x.Quantity) ?? 0;
openQty = openQty + x.Quantity;
if (posiType != x.PositionType)
{
payQty = x.Quantity;
posiType = x.PositionType;
}
else
{
payQty = payQty + x.Quantity;
}
}
else
{
decimal amount = x.MarkClosePnl + x.CloseFee + x.DividendIn;
//记录资金记录
if (amount != 0)
{
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(x.MarkClosePnl), ClientCashInCashOut.系统操作_平仓费, x.UnwindDate.Value);
x.ClientCashId = clientCashId;
}
payQty = payQty - x.Quantity;
closeQty = closeQty + x.Quantity;
closeFee = closeFee + x.CloseFee;
tdDividendIn = tdDividendIn + x.DividendIn;
tdCloseNotionalValue = tdCloseNotionalValue + x.TradingAmount;
}
closeMtmPnl = closeMtmPnl + x.MarkClosePnl;
x.PositionQty = payQty;
dividendIn = dividendIn + x.DividendIn;
x.DataState = (int)SwapFlowDateStateEnum.完成;
});
tdCloseQty += closeQty;
if (eventFlow.EventType == 1)//最后一条是开仓
{
payQty = eventFlow.Quantity;
}
bool newOpen = openQty != 0 && closeQty == 0;
if (openQty != 0 && closeQty == 0)//只有开仓,价格加权平均
{
netPrice = openAmountFee / openQty;//持仓均价=((上一日持仓含费均价*上一日持仓数量)+(开仓成交均价*开仓数量))/所有开仓数量
grossPrice = openAmount / openQty;//持仓均价-不含费=((上一日持仓不含费均价*上一日持仓数量)+(开仓成交均价-不含费*开仓数量))/所有开仓数量
netFeePrice = openAmountNetFee / openQty;
netNoFeePrice = openAmountNet / openQty;
}
else if (posiType != fposition?.PositionType)//平仓完新开仓
{
netPrice = eventFlow.TradingAmountFeeAvg;
grossPrice = eventFlow.TradingAmountAvg;
netFeePrice = eventFlow.TradingAmountNetFeeAvg ?? 0;
netNoFeePrice = eventFlow.TradingAmountNetAvg ?? 0;
}
if (eodPayPosition == null)//无日终持仓
{
eodPayPosition = new eod_swap_position();
eodPayPosition.PosiStartDate = eventFlow.PayDate.Value;
eodPayPosition.ClientId = td.ClientId;
eodPayPosition.SwapTradeId = td.id;
eodPayPosition.ContractSize = eventFlow.ContractSize;
}
posiNotionalValue = eventFlow.ContractSize * netPrice * Math.Abs(payQty);
totalPosiNotionalValue = totalPosiNotionalValue + posiNotionalValue;
allPosiNotionalValue += posiNotionalValue;
tradePrice += SaveEodPosition(eodPayPosition, td, eventFlow, netPrice, grossPrice, netFeePrice, netNoFeePrice, payQty, tradingFee, posiNotionalValue, dividendIn, tdDividendIn, closeQty, closeFee, closeMtmPnl, posiType, fposition == null);
}
if (tdCloseQty != 0)
{
td.HasPartialUnWind = 1;
}
// td.StockEqvNotional += Convert.ToDouble(totalPosiNotionalValue);
td.TradePrice += Convert.ToDouble(tradePrice);
unwindData.PosiNotionalValue = allPosiNotionalValue;
unwindData.CloseNotionalValue = tdCloseNotionalValue;
swapEvent.EventData = JsonHelper.Serialize(unwindData);
DbContext.SaveChanges();
SaveEodSwap(td, tradeDate,preSettleDate);
DbContext.SaveChanges();
trans?.Commit();
}
catch (Exception ex)
{
trans?.Rollback();
throw new Exception(ex.Message, ex);
}
finally
{
trans?.Dispose();
}
}
/// <summary>
/// 产生互换用
/// </summary>
/// <param name="eodPayPosition">上一日日终归档</param>
/// <param name="newEodPayPosition">当日归档</param>
/// <param name="position">持仓腿</param>
/// <param name="td">主体交易</param>
/// <param name="startDate">计息开始日</param>
/// <param name="valueDate">计息结束日</param>
/// <param name="closeAmount">平仓金额</param>
private void SaveEodInterestPosition(eod_swap_position eodPayPosition,
eod_swap_position newEodPayPosition,
swap_position position,
trade td,
DateTime valueDate,
List<swap_flow_event> flowEvents)
{
if (eodPayPosition == null)
{
eodPayPosition = new eod_swap_position();
eodPayPosition.ClientId = td.ClientId;
eodPayPosition.SwapTradeId = td.id;
eodPayPosition.PosiStartDate = position.PosiStartDate;
eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
}
var tradeExtend = td.trade_extend.ExtendObj;
decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(position.InterestMode))
{
ratio = -ratio;
}
if (newEodPayPosition == null)
{
newEodPayPosition = new eod_swap_position();
newEodPayPosition.ClientId = td.ClientId;
newEodPayPosition.SwapTradeId = td.id;
newEodPayPosition.PosiStartDate = position.PosiStartDate;
eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
}
newEodPayPosition.ValueDate = valueDate;
newEodPayPosition.PositionId = position.id;
newEodPayPosition.ClientId = td.ClientId;
newEodPayPosition.SwapTradeId = td.id;
UpdateDbOption(newEodPayPosition);
newEodPayPosition.PosiStatus = 0;
newEodPayPosition.Invalid = false;
//持仓内容-利息腿
newEodPayPosition.InterestDirection = position.InterestDirection;
newEodPayPosition.InterestMode = position.InterestMode;
newEodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix;
newEodPayPosition.InterestRateDefault = position.InterestRateDefault;
newEodPayPosition.InterestSwapInterval = position.InterestSwapInterval;
newEodPayPosition.IsAnnualized = position.IsAnnualized;
newEodPayPosition.HappenDate = position.HappenDate;
newEodPayPosition.Currency = position.Currency;
newEodPayPosition.InterestType = position.InterestType;
newEodPayPosition.interest_rest_days = position.interest_rest_days;
newEodPayPosition.interest_rule = position.interest_rule;
newEodPayPosition.FloatRate = flowEvents.FirstOrDefault()?.FloatRate ?? 0;
newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode;
newEodPayPosition.InterestFeePending = 0;
//利息端估值用信息
newEodPayPosition.TdInterestPrincipal = flowEvents.FirstOrDefault()?.InterestPrincipal ?? 0;
newEodPayPosition.TdInterestRate = flowEvents.FirstOrDefault()?.InterestRate ?? 0;
//当日已实现
newEodPayPosition.TdInterestFee = flowEvents.Sum(x => x.InterestFee);
newEodPayPosition.TdCloseInterest = flowEvents.Sum(x => x.InterestAmount);
newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee;
//持仓内容-利息腿-损益统计(本方视角)
newEodPayPosition.TdInterestIncome = newEodPayPosition.TdCloseInterest;
newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + newEodPayPosition.TdInterestIncome - newEodPayPosition.TdCloseInterest;
newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum;
//持仓价值
newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum;
//累计已实现
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate
, seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate);
if (newEodPayPosition.id == 0)
{
DbContext.eod_swap_position.Add(newEodPayPosition);
}
}
/// <summary>
/// 自动互换用,当日无互换,当日无平仓
/// </summary>
/// <param name="eodPayPosition">上一日日终持仓</param>
/// <param name="newEodPayPosition">当前收盘日日终持仓</param>
/// <param name="position">利息腿信息</param>
/// <param name="td">框架合约</param>
/// <param name="valueDate">当前收盘日</param>
/// <param name="interval">当前观察日</param>
/// <param name="preDealDate">上一平仓/互换日期</param>
/// <param name="closeAmount">当日平仓金额</param>
/// <param name="lastEodSwap">上一日终框架合约估值</param>
private List<swap_flow_event> SaveAutoEodInterestPosition(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, swap_position position, trade td, DateTime valueDate, IntervalModel interval, eod_swap lastEodSwap, decimal posiLongNotional, decimal posiShortNational, decimal grossPrice, decimal orginPv)
{
var tradeExtend = td.trade_extend.ExtendObj;
decimal oriPosiNotionalValue = posiLongNotional + posiShortNational;
decimal posiNotionalValue = oriPosiNotionalValue;
bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString();
if (lastEodSwap != null)
{
posiNotionalValue = lastEodSwap.NotionalValue;
}
decimal closePercent = 1;
decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(position.InterestMode))
{
ratio = -ratio;
}
if (eodPayPosition == null)
{
eodPayPosition = new eod_swap_position();
eodPayPosition.ClientId = td.ClientId;
eodPayPosition.SwapTradeId = td.id;
eodPayPosition.PosiStartDate = td.StartDate.Value;
eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
}
if (newEodPayPosition == null)
{
newEodPayPosition = eodPayPosition.Clone();
newEodPayPosition.id = 0;
}
List<swap_position> positions = new List<swap_position>();
positions.Add(position);
List<eod_swap_position> preEodPositions = new List<eod_swap_position>();
preEodPositions.Add(eodPayPosition);
if (position.InterestMode == (int)InterestModeEnum.固定值)
{
orginPv = eodPayPosition.InterestPrincipalFix;
}
var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNotional, posiShortNational, posiNotionalValue, closePercent, (int)SwapEventTypeEnum.自动互换, false, true, grossPrice, orginPv, true);
decimal InterestAmount = interests.Sum(x => x.InterestAmount);
decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount);
newEodPayPosition.ValueDate = valueDate;
newEodPayPosition.PositionId = position.id;
UpdateDbOption(newEodPayPosition);
newEodPayPosition.Invalid = false;
//持仓内容-利息腿
newEodPayPosition.InterestDirection = position.InterestDirection;
newEodPayPosition.InterestMode = position.InterestMode;
newEodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix;
newEodPayPosition.InterestRateDefault = position.InterestRateDefault;
newEodPayPosition.InterestSwapInterval = position.InterestSwapInterval;
newEodPayPosition.IsAnnualized = position.IsAnnualized;
newEodPayPosition.HappenDate = position.HappenDate;
newEodPayPosition.Currency = position.Currency;
newEodPayPosition.InterestType = position.InterestType;
newEodPayPosition.interest_rest_days = position.interest_rest_days;
newEodPayPosition.interest_rule = position.interest_rule;
newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0;
newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode;
newEodPayPosition.InterestFeePending = 0;
//利息端估值用信息
newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0;
newEodPayPosition.TdInterestRate = interval.Rate;
//当日已实现
//newEodPayPosition.TdInterestFee = 0;
newEodPayPosition.TdCloseInterest = InterestAmount;
// newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee;
//持仓内容-利息腿-损益统计(本方视角)
newEodPayPosition.TdInterestIncome = TdInterestAmount;
newEodPayPosition.InterestIncomeSum = 0;
newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum;
//持仓价值
newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum;
//累计已实现
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate
, seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate);
if (newEodPayPosition.id == 0)
{
DbContext.eod_swap_position.Add(newEodPayPosition);
}
return interests;
}
/// <summary>
/// 自动互换用,当日无互换,当日有平仓
/// </summary>
/// <param name="eodPayPosition">上一日日终持仓</param>
/// <param name="newEodPayPosition">当前收盘日日终持仓 不可能为空</param>
/// <param name="position">利息腿信息</param>
/// <param name="td">框架合约</param>
/// <param name="valueDate">当前收盘日</param>
/// <param name="interval">当前观察日</param>
/// <param name="preDealDate">上一平仓/互换日期</param>
/// <param name="closeAmount">当日平仓金额</param>
/// <param name="lastEodSwap">上一日终框架合约估值</param>
/// <param name="unwintotal">平仓主信息</param>
private List<swap_flow_event> SaveAutoEodWithCloseInterestPosition(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, swap_position position, trade td, DateTime valueDate, IntervalModel interval, decimal posiLongNotional, decimal posiShortNational, List<swap_flow_event> flowEvents, decimal closeNational, bool autoSwap, decimal grossPrice, decimal orginPv)
{
var tradeExtend = td.trade_extend.ExtendObj;
decimal oriPosiNotionalValue = posiLongNotional + posiShortNational + closeNational;
decimal posiNotionalValue = oriPosiNotionalValue;
decimal ratio = position.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(position.InterestMode))
{
ratio = -ratio;
}
if (eodPayPosition == null)
{
eodPayPosition = new eod_swap_position();
eodPayPosition.ClientId = td.ClientId;
eodPayPosition.SwapTradeId = td.id;
eodPayPosition.PosiStartDate = td.StartDate.Value;
eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
}
if (newEodPayPosition == null)
{
newEodPayPosition = eodPayPosition.Clone();
newEodPayPosition.id = 0;
}
if (position.InterestMode == (int)InterestModeEnum.固定值)
{
orginPv = eodPayPosition.InterestPrincipalFix;
}
decimal closePercent = oriPosiNotionalValue == 0 ? 0 : closeNational / oriPosiNotionalValue;
var eventType = autoSwap ? (int)SwapEventTypeEnum.自动互换 : (int)SwapEventTypeEnum.平仓;
bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString();
if (!autoSwap)
{
closePercent = oriPosiNotionalValue == 0 ? 0 : closeNational / oriPosiNotionalValue;
if (longShort)
{
closePercent = 0;
}
}
List<swap_position> positions = new List<swap_position>();
positions.Add(position);
List<eod_swap_position> preEodPositions = new List<eod_swap_position>();
preEodPositions.Add(eodPayPosition);
var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNotional, posiShortNational, closeNational, 1, eventType, false, true, grossPrice, orginPv, true);
decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount);
newEodPayPosition.ValueDate = valueDate;
newEodPayPosition.PositionId = position.id;
UpdateDbOption(newEodPayPosition);
newEodPayPosition.Invalid = false;
//持仓内容-利息腿
newEodPayPosition.InterestDirection = position.InterestDirection;
newEodPayPosition.InterestMode = position.InterestMode;
newEodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix;
newEodPayPosition.InterestPrincipalFix *= (1 - closePercent);
newEodPayPosition.InterestRateDefault = position.InterestRateDefault;
newEodPayPosition.InterestSwapInterval = position.InterestSwapInterval;
newEodPayPosition.IsAnnualized = position.IsAnnualized;
newEodPayPosition.HappenDate = position.HappenDate;
newEodPayPosition.Currency = position.Currency;
newEodPayPosition.InterestType = position.InterestType;
newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0;
newEodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode;
newEodPayPosition.InterestFeePending = 0;
newEodPayPosition.interest_rest_days = position.interest_rest_days;
newEodPayPosition.interest_rule = position.interest_rule;
//利息端估值用信息
newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0;
newEodPayPosition.TdInterestPrincipal *= (1 - closePercent);
if (interval != null)
{
newEodPayPosition.TdInterestRate = interval.Rate;
}
else
{
newEodPayPosition.TdInterestRate = flowEvents.FirstOrDefault()?.InterestRate ?? 0;
}
//当日已实现,平仓时已处理
newEodPayPosition.TdInterestFee = flowEvents.Sum(s => s.InterestFee);
newEodPayPosition.TdCloseInterestFee = newEodPayPosition.TdInterestFee;
newEodPayPosition.TdCloseInterest = flowEvents.Sum(s => s.InterestClosePnL);
if (closePercent == 1)
{
newEodPayPosition.InterestIncomeSum = 0;
}
else
{
newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + TdInterestAmount - Math.Abs(newEodPayPosition.TdCloseInterest);
}
//持仓内容-利息腿-损益统计(本方视角)
newEodPayPosition.TdInterestIncome = TdInterestAmount * (1 - closePercent);
newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum;
//持仓价值
newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum;
//累计已实现
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest;
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate
, seekPreday: true, currencyRateType: position.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate);
if (newEodPayPosition.id == 0)
{
DbContext.eod_swap_position.Add(newEodPayPosition);
}
return interests;
}
/// <summary>
/// 利息腿复制上一日终归档信息
/// </summary>
/// <param name="eodPayPosition">上一日终利息腿持仓信息</param>
/// <param name="position">利息腿信息</param>
/// <param name="preSettleDate">上一交易日</param>
/// <param name="valueDate">当前结算日</param>
/// <param name="td">互换交易主干</param>
private void SaveEodInterestPositionCopy(eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, DateTime valueDate, trade td, swap_position position, eod_swap lastEodSwap, bool needPrice, decimal posiLongNational, decimal posiShortNational, decimal grossPrice, decimal orginPv)
{
List<IntervalModel> intervals = position.SwapIntervalList;
var tradeExtend = td.trade_extend.ExtendObj;
if (eodPayPosition == null)
{
//if (position.PosiStartDate > valueDate)
//{
// return;
//}
eodPayPosition = new eod_swap_position();
eodPayPosition.InterestDirection = position.InterestDirection;
eodPayPosition.ClientId = td.ClientId;
eodPayPosition.SwapTradeId = td.id;
//eodPayPosition.PositionId = position.id; 为了算利息时找不到给日期重新赋值
eodPayPosition.InterestMode = position.InterestMode;
eodPayPosition.InterestPrincipalFix = position.InterestPrincipalFix;
eodPayPosition.InterestRateDefault = position.InterestRateDefault;
eodPayPosition.InterestSwapInterval = position.InterestSwapInterval;
eodPayPosition.TdInterestPrincipal = position.InterestMode == 1 ? eodPayPosition.InterestPrincipalFix : posiLongNational + posiShortNational;
eodPayPosition.PosiStartDate = td.StartDate.Value;
eodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
eodPayPosition.IsAnnualized = position.IsAnnualized;
eodPayPosition.HappenDate = position.HappenDate;
eodPayPosition.Currency = position.Currency;
eodPayPosition.InterestType = position.InterestType;
eodPayPosition.FloatRate = position.FloatRate;
eodPayPosition.FloatRateUnderlyingCode = position.FloatRateUnderlyingCode;
eodPayPosition.interest_rest_days = position.interest_rest_days;
eodPayPosition.interest_rule = position.interest_rule;
}
if (newEodPayPosition == null)
{
newEodPayPosition = eodPayPosition.Clone();
newEodPayPosition.id = 0;
newEodPayPosition.PositionId = position.id;
}
if (position.InterestMode == (int)InterestModeEnum.固定值)
{
orginPv = eodPayPosition.InterestPrincipalFix;
}
bool longShort = td.StructureType == ClientMarginTypeEnum.多空组合.ToString();
decimal oriPosiNotionalValue = posiLongNational + posiShortNational;
decimal posiNotionalValue = oriPosiNotionalValue;
if (lastEodSwap == null)
{
lastEodSwap = new eod_swap();
}
decimal closePercent = oriPosiNotionalValue == 0 ? 0 : posiNotionalValue / oriPosiNotionalValue;
if (longShort)
{
closePercent = 0;
}
if (td.ExerciseDate == valueDate)
{
closePercent = 1;
}
decimal ratio = eodPayPosition.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(position.InterestMode))
{
ratio = -ratio;
}
List<swap_position> positions = new List<swap_position>
{
position
};
List<eod_swap_position> preEodPositions = new List<eod_swap_position>();
if (eodPayPosition.id != 0)
{
preEodPositions.Add(eodPayPosition);
}
var interests = new SwapDealService(this).GetInterests(td, td.trade_extend, valueDate, valueDate, preEodPositions, positions, posiNotionalValue, posiLongNational, posiShortNational, posiNotionalValue, closePercent, 0, false, needPrice, grossPrice, orginPv);
UpdateDbOption(newEodPayPosition);
newEodPayPosition.PosiStatus = 0;
newEodPayPosition.Invalid = false;
newEodPayPosition.ValueDate = valueDate;
decimal InterestAmount = interests.Sum(s => s.InterestAmount);
decimal TdInterestAmount = interests.Sum(x => x.TdInterestAmount);
//利息端估值用信息
newEodPayPosition.TdInterestPrincipal = interests.Count > 0 ? interests.First().InterestPrincipal : 0;
newEodPayPosition.TdInterestRate = interests.Count > 0 ? interests.First().InterestRate : 0;
newEodPayPosition.FloatRate = interests.Count > 0 ? interests.First().FloatRate ?? 0 : 0;
//当日已实现
newEodPayPosition.TdCloseInterest = 0;
newEodPayPosition.TdCloseInterestFee = 0;
//持仓内容-利息腿-损益统计(本方视角)
newEodPayPosition.TdInterestIncome = TdInterestAmount;
newEodPayPosition.TdInterestFee = 0;
newEodPayPosition.InterestIncomeSum = eodPayPosition.InterestIncomeSum + newEodPayPosition.TdInterestIncome - newEodPayPosition.TdCloseInterest;
newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum;
//持仓价值
newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum;
//累计已实现
newEodPayPosition.RealizedInterest = eodPayPosition.RealizedInterest + newEodPayPosition.TdCloseInterest * ratio;
newEodPayPosition.RealizedInterestFee = eodPayPosition.RealizedInterestFee + newEodPayPosition.TdCloseInterestFee;
newEodPayPosition.RealizedPnl = newEodPayPosition.RealizedInterest;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, valueDate
, seekPreday: true, currencyRateType: eodPayPosition.InterestDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate);
if (newEodPayPosition.id == 0)
{
DbContext.eod_swap_position.Add(newEodPayPosition);
}
}
/// <summary>
/// 持仓腿生成期初持仓及归档信息
/// </summary>
/// <param name="eodPayPosition">上一日终持仓信息</param>
/// <param name="td">合约框架</param>
/// <param name="eventFlow">最后一条事件流水</param>
/// <param name="netPrice">期初价格</param>
/// <param name="grossPrice">期初价格不含费</param>
/// <param name="payQty">剩余持仓数量</param>
/// <param name="tradingFee">开仓交易佣金费用</param>
/// <param name="posiNotionalValue">当前剩余名义本金</param>
/// <param name="dividendIn">当日浮动端分红</param>
/// <param name="tdDividendIn">当日浮动端平仓盈亏分红</param>
/// <param name="closeQty">当日平仓数量</param>
/// <param name="closeFee">当日平仓费用</param>
/// <param name="closeMtmPnl">当日浮动盈亏</param>
private decimal SaveEodPosition(eod_swap_position newEodPayPosition,
trade td,
swap_flow_event eventFlow,
decimal netPrice,
decimal grossPrice,
decimal netFeePrice,
decimal netNoFeePrice,
decimal payQty,
decimal tradingFee,
decimal posiNotionalValue,
decimal dividendIn,
decimal tdDividendIn,
decimal closeQty,
decimal closeFee,
decimal closeMtmPnl,
int posiType,
bool open)
{
payQty = Math.Abs(payQty);
decimal ratio = eventFlow.PayDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
decimal shortRatio = newEodPayPosition.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;//多空方向
newEodPayPosition.ValueDate = eventFlow.PayDate.Value;
newEodPayPosition.PositionId = eventFlow.PositionId;
newEodPayPosition.ClientId = td.ClientId;
newEodPayPosition.SwapTradeId = td.id;
//持仓内容-浮动收益腿
newEodPayPosition.PosiDirection = eventFlow.PayDirection;
newEodPayPosition.PositionType = posiType;
newEodPayPosition.UnderlyingCode = eventFlow.UnderlyingCode;
newEodPayPosition.UnderlyingInstrumentType = eventFlow.UnderlyingInstrumentType;
newEodPayPosition.ContractSize = eventFlow.ContractSize;
newEodPayPosition.CountRatio = eventFlow.CountRatio;
newEodPayPosition.PosiNetPrice = netPrice;
newEodPayPosition.PosiGrossPrice = grossPrice;
newEodPayPosition.PosiNetFeePrice = netFeePrice;
newEodPayPosition.PosiNetNoFeePrice = netNoFeePrice;
newEodPayPosition.PosiQuantity = payQty;
newEodPayPosition.PosiNotionalValue = posiNotionalValue;
newEodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
if (newEodPayPosition.PosiQuantity == 0)
{
newEodPayPosition.PosiMatuirityDate = eventFlow.PayDate.Value;
}
//else
//{
// newEodPayPosition.PosiMatuirityDate = td.ExerciseDate.Value;
//}
newEodPayPosition.PosiFeePending = tradingFee;
//浮动端估值用信息
newEodPayPosition.UnderlyingPrice = UnderlyingCodePrice(newEodPayPosition.UnderlyingCode, eventFlow.EventDate, out decimal vobp);
newEodPayPosition.UnderlyingMarketValue = newEodPayPosition.UnderlyingPrice * newEodPayPosition.PosiQuantity * newEodPayPosition.ContractSize * shortRatio;
//当日已实现
newEodPayPosition.TdCloseQty = closeQty;
newEodPayPosition.TdChangedQty = 0;
newEodPayPosition.TdCloseMtmPnl = closeMtmPnl * ratio;
newEodPayPosition.TdCloseDividend = tdDividendIn * ratio;
newEodPayPosition.TdCloseFee = closeFee * ratio;
//持仓内容-浮动收益腿-损益统计(本方视角
newEodPayPosition.TdPosiDividend = dividendIn * ratio;
newEodPayPosition.PosiMtmPnL = (newEodPayPosition.UnderlyingPrice - newEodPayPosition.PosiGrossPrice) * newEodPayPosition.PosiQuantity * newEodPayPosition.ContractSize * shortRatio * ratio;
newEodPayPosition.PosiDividendSum = newEodPayPosition.TdPosiDividend - newEodPayPosition.TdCloseDividend;
newEodPayPosition.PosiProfitSum = newEodPayPosition.PosiMtmPnL + newEodPayPosition.PosiDividendSum + newEodPayPosition.PosiFeePending;
//持仓价值
newEodPayPosition.SwapPositionValue = newEodPayPosition.InterestProfitSum + newEodPayPosition.PosiProfitSum;
//累计已实现
newEodPayPosition.RealizedFee = closeFee;
newEodPayPosition.RealizedMtmPnL = newEodPayPosition.TdCloseMtmPnl;
newEodPayPosition.RealizedDividend = newEodPayPosition.TdCloseDividend;
newEodPayPosition.RealizedPnl = newEodPayPosition.TdCloseMtmPnl;
newEodPayPosition.PosiStatus = payQty == 0 ? 1 : 0;
UpdateDbOption(newEodPayPosition);
newEodPayPosition.Invalid = false;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, eventFlow.EventDate
, seekPreday: true, currencyRateType: posiNotionalValue < 0 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
newEodPayPosition.TdCurrency = Convert.ToDecimal(currencyRate);
decimal posiTradingFee = 0;
if (open)//更新新开仓持仓腿信息,因为在生成开仓事件时,先生成了空的持仓腿信息
{
if (td.trade_extend.ExtendObj.NeedOpenFee && td.TradeDate == eventFlow.EventDate)//开仓
{
posiTradingFee = Math.Abs(newEodPayPosition.PosiTradingFee) * Convert.ToDecimal(currencyRate);
}
UpdateSwapPosition(newEodPayPosition, td.TradeNumber);
}
UpdateSwapPositionWithRealTime(newEodPayPosition);
if (newEodPayPosition.id == 0)
{
DbContext.eod_swap_position.Add(newEodPayPosition);
}
return posiTradingFee;
}
/// <summary>
///当日无平仓,无互换,生成持仓腿日终归档,适用于上一日终存在
/// </summary>
/// <param name="eod_Swap_Positions">上一日日终归档信息</param>
/// <param name="todayPositions">当日日终归档信息</param>
/// <param name="swap_Deals">当日平仓/互换事件信息</param>
/// <param name="td">交易信息</param>
private eod_swap_position CopyEodPosition(eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate)
{
if (curretEod == null)
{
curretEod = eod.Clone();
curretEod.id = 0;
curretEod.ValueDate = valueDate;
}
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(eod.UnderlyingCode);
if (um == null)
{
return curretEod;
}
var dealDate = curretEod.ValueDate;
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
var price = UnderlyingCodePrice(eod.UnderlyingCode, dealDate, out decimal vobp);
if (curretEod.PosiStatus == 1)
{
curretEod.PosiNotionalValue = 0;
}
curretEod.UnderlyingPrice = price;
curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio;
curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio;
curretEod.TdPosiDividend = 0;
curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend;
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum+ curretEod.PosiFeePending;
curretEod.TdCloseFee = 0;
curretEod.TdCloseQty = 0;
curretEod.TdCloseMtmPnl = 0;
curretEod.TdCloseDividend = 0;
curretEod.RealizedMtmPnL = eod.RealizedMtmPnL + curretEod.TdCloseMtmPnl;
curretEod.RealizedDividend = eod.RealizedDividend + curretEod.TdCloseDividend;
curretEod.RealizedFee = eod.RealizedFee + curretEod.TdCloseFee;
curretEod.RealizedPnl = eod.RealizedPnl + curretEod.TdCloseMtmPnl;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.StartDate.Value
, seekPreday: true, currencyRateType: curretEod.PosiDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
curretEod.TdCurrency = Convert.ToDecimal(currencyRate);
//持仓价值
curretEod.SwapPositionValue = curretEod.InterestProfitSum + curretEod.PosiProfitSum;
UpdateDbOption(curretEod);
curretEod.Invalid = false;
if (curretEod.id == 0)
{
DbContext.eod_swap_position.Add(curretEod);
}
return curretEod;
}
/// <summary>
/// 更新虚拟交易费用
/// </summary>
/// <param name="curretEod"></param>
private void UpdateVTradingFee(eod_swap_position curretEod)
{
//int ratio = curretEod.PositionType == (int)PositionTypeFlag.Long ? -1 : 1;
//int shortRatio = curretEod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
//var priceFee = curretEod.PosiQuantity == 0 ? 0 : curretEod.UnderlyingPrice + curretEod.PosiFeePending / curretEod.PosiQuantity * ratio;
//curretEod.VTradingFee = -(priceFee - curretEod.PosiNetPrice - curretEod.UnderlyingPrice + curretEod.PosiGrossPrice) * curretEod.PosiNotionalValue * shortRatio;
curretEod.VTradingFee = curretEod.PosiFeePending * 2;
}
/// <summary>
/// 当日有平仓/互换,生成持仓腿日终归档,适用于上一日终存在
/// </summary>
/// <param name="eod"></param>
/// <param name="curretEod"></param>
/// <param name="td"></param>
/// <param name="valueDate"></param>
private eod_swap_position UpdateEodPosition(swap_position swapPosition, eod_swap_position eod, eod_swap_position curretEod, trade td, DateTime valueDate, DateTime preSettleDate, List<swap_flow_event> unwindEvents)
{
if (curretEod == null)
{
curretEod = eod.Clone();
curretEod.id = 0;
curretEod.ValueDate = valueDate;
}
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(eod.UnderlyingCode);
if (um == null)
{
return curretEod;
}
var dealDate = curretEod.ValueDate;
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
var price = UnderlyingCodePrice(eod.UnderlyingCode, dealDate, out decimal vobp);
SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, swapPosition);
curretEod.UnderlyingPrice = price;
curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio;
curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio;
curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend;
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;
curretEod.RealizedMtmPnL = eod.RealizedMtmPnL + curretEod.TdCloseMtmPnl;
curretEod.RealizedDividend = eod.RealizedDividend + curretEod.TdCloseDividend;
curretEod.RealizedFee = eod.RealizedFee + curretEod.TdCloseFee;
curretEod.RealizedPnl = eod.RealizedPnl + curretEod.TdCloseMtmPnl;
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
if (curretEod.PosiStatus == 1)
{
curretEod.PosiNotionalValue = 0;
}
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.StartDate.Value
, seekPreday: true, currencyRateType: curretEod.PosiDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
curretEod.TdCurrency = Convert.ToDecimal(currencyRate);
//持仓价值
curretEod.SwapPositionValue = curretEod.InterestProfitSum + curretEod.PosiProfitSum;
UpdateDbOption(curretEod);
curretEod.Invalid = false;
if (curretEod.id == 0)
{
DbContext.eod_swap_position.Add(curretEod);
}
return curretEod;
}
/// <summary>
/// 根据开平仓事件算价格及后付费用
/// </summary>
/// <param name="eod"></param>
/// <param name="curretEod"></param>
/// <param name="unwindEvents"></param>
public void SetPriceInfoByFlowEvent(eod_swap_position eod, eod_swap_position curretEod, List<swap_flow_event> unwindEvents, swap_position position)
{
if (eod.PosiDirection == 0)
{
return;
}
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
var unwindFlowEvents = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.平仓).ToList();
var openFlowEvents = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓).ToList();
decimal unwindQty = unwindFlowEvents.Sum(s => s.Quantity);
decimal openQty = openFlowEvents.Sum(s => s.Quantity);
var qty = eod.PosiQuantity + openQty - unwindQty;
curretEod.PosiQuantity = qty < 0 ? 0 : Math.Abs(qty);
if (unwindEvents.Count == 0)
{
curretEod.PosiNetPrice = position.PosiNetPrice;
curretEod.PosiGrossPrice = position.PosiGrossPrice;
curretEod.PosiNetFeePrice = position.PosiNetFeePrice;
curretEod.PosiNetNoFeePrice = position.PosiNetNoFeePrice;
curretEod.PosiQuantity = position.PosiQuantity;
curretEod.PosiFeePending = -position.PosiTradingFeePending * directionRatio;
}
else
{
var eventTradingFee = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.开仓 || x.EventType == (int)SwapFlowEventTypeEnum.平仓).Sum(s => s.TradingFeePending * (s.EventType == (int)SwapFlowEventTypeEnum.开仓 ? 1m : -1m));
curretEod.PosiFeePending = eod.PosiFeePending + eventTradingFee;
if (openFlowEvents.Count() == 0)
{
curretEod.PosiNetPrice = eod.PosiNetPrice;
curretEod.PosiGrossPrice = eod.PosiGrossPrice;
curretEod.PosiNetFeePrice = eod.PosiNetFeePrice;
curretEod.PosiNetNoFeePrice = eod.PosiNetNoFeePrice;
}
else //平仓数量一定<持仓数量
{
var posiQty = eod.PosiQuantity - unwindQty;
if (posiQty < 0)
{
posiQty = 0;
}
curretEod.PosiGrossPrice = (eod.PosiGrossPrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountAvg)) / (eod.PosiQuantity + openQty);
curretEod.PosiGrossPrice = Math.Round(curretEod.PosiGrossPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
curretEod.PosiNetPrice = (eod.PosiNetPrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountFeeAvg)) / (eod.PosiQuantity + openQty);
curretEod.PosiNetPrice = Math.Round(curretEod.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
curretEod.PosiNetNoFeePrice = (eod.PosiNetNoFeePrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountNetAvg)) / (eod.PosiQuantity + openQty);
curretEod.PosiNetNoFeePrice = Math.Round(curretEod.PosiNetNoFeePrice ?? 0, 10, MidpointRounding.AwayFromZero);
curretEod.PosiNetFeePrice = (eod.PosiNetFeePrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountNetFeeAvg)) / (eod.PosiQuantity + openQty);
curretEod.PosiNetFeePrice = Math.Round(curretEod.PosiNetFeePrice ?? 0, 10, MidpointRounding.AwayFromZero);
}
curretEod.PosiNotionalValue = curretEod.PosiGrossPrice * curretEod.PosiQuantity * curretEod.ContractSize;
curretEod.PosiNotionalValue= Math.Round(curretEod.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
curretEod.TdPosiDividend = unwindEvents.Sum(x => x.DividendIn) * directionRatio;
curretEod.TdCloseFee = unwindEvents.Sum(x => x.TradingFee);
curretEod.TdCloseQty = unwindQty;
curretEod.TdCloseMtmPnl = unwindEvents.Sum(x => x.MarkClosePnl);
}
if (curretEod.PosiQuantity==0)
{
curretEod.PosiFeePending = 0;
}
UpdateVTradingFee(curretEod);
}
/// <summary>
/// 单标的浮动腿 首次归档
/// </summary>
/// <param name="position"></param>
/// <param name="td"></param>
/// <param name="settleDate"></param>
private eod_swap_position SaveCurrentEodInitalPosi(swap_position position, trade td, DateTime settleDate, DateTime preSettleDate, List<swap_flow_event> unwindEvents)
{
eod_swap_position curretEod = new eod_swap_position();
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(position.UnderlyingCode);
if (um == null)
{
return curretEod;
}
var dealDate = settleDate;
curretEod.ValueDate = settleDate;
curretEod.PosiStartDate = position.PosiStartDate;
curretEod.PosiMatuirityDate = td.ExerciseDate.Value;
curretEod.SwapTradeId = td.id;
curretEod.PositionId = position.id;
curretEod.ClientId = td.ClientId;
int shortRatio = position.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = position.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
curretEod.PositionType = position.PositionType;
var eod = new eod_swap_position()
{
ContractSize = position.ContractSize,
PositionType = position.PositionType,
PosiDirection = position.PosiDirection,
PosiFeePending = 0,
PosiNetPrice = position.PosiNetPrice,
PosiGrossPrice = position.PosiGrossPrice,
PosiNetFeePrice = position.PosiNetFeePrice,
PosiNetNoFeePrice = position.PosiNetNoFeePrice,
};
curretEod.PosiDirection = position.PosiDirection;
curretEod.UnderlyingCode = position.UnderlyingCode;
curretEod.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
curretEod.SwapTradeId = position.SwapTradeId;
curretEod.ContractSize = position.ContractSize;
curretEod.CountRatio = position.CountRatio;
curretEod.PosiTradingFee = position.PosiTradingFee;
curretEod.UnderlyingPrice = UnderlyingCodePrice(position.UnderlyingCode, dealDate, out decimal vobp);
SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, position);
if (settleDate == td.TradeDate)
{
curretEod.UnderlyingPrice = curretEod.PosiGrossPrice;
//curretEod.TdCloseMtmPnl = 0;
//curretEod.TdCloseFee = 0;
}
curretEod.TdCloseDividend = curretEod.TdPosiDividend;
curretEod.UnderlyingMarketValue = curretEod.UnderlyingPrice * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio;
curretEod.PosiMtmPnL = (curretEod.UnderlyingPrice - curretEod.PosiGrossPrice) * curretEod.PosiQuantity * curretEod.ContractSize * shortRatio * directionRatio;
curretEod.PosiProfitSum = curretEod.PosiMtmPnL + curretEod.PosiDividendSum + curretEod.PosiFeePending;
curretEod.RealizedMtmPnL = curretEod.TdCloseMtmPnl;
curretEod.RealizedDividend = curretEod.TdCloseDividend;
curretEod.RealizedFee = curretEod.TdCloseFee;
curretEod.RealizedPnl = curretEod.TdCloseMtmPnl;
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
if (curretEod.PosiStatus == 1)
{
curretEod.PosiNotionalValue = 0;
}
//持仓价值
curretEod.SwapPositionValue = curretEod.InterestProfitSum + curretEod.PosiProfitSum;
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(td.QuoteCurrency, td.SettlementCurrency, td.StartDate.Value
, seekPreday: true, currencyRateType: curretEod.PosiDirection == (int)SwapDirectionEnum.收取 ? CurrencyRateType.Buy : CurrencyRateType.Sell);
curretEod.TdCurrency = Convert.ToDecimal(currencyRate);
UpdateDbOption(curretEod);
curretEod.Invalid = false;
DbContext.eod_swap_position.Add(curretEod);
return curretEod;
}
/// <summary>
/// 获取标的收盘价格
/// </summary>
/// <param name="code">标的代码</param>
/// <param name="settleDate">收盘日</param>
/// <returns></returns>
public decimal UnderlyingCodePrice(string code, DateTime settleDate, out decimal vobp)
{
vobp = 0;
var data = DataCacheProvider.GetUnderlyingDataSource().GetData(code);
if (data == null)
{
return 0;
}
if (data.IsBond())
{
return BondPrice(data, settleDate, out vobp);
}
var price = data.Price ?? 0;
if (EodPriceQueryService.TryGetEodPrice(settleDate, code, out var eodPrice))
{
price = eodPrice.GetPrice(SettlementTypeEnum.ClosePrice);
}
return Convert.ToDecimal(price);
}
/// <summary>
/// 获取债券收盘价格
/// </summary>
/// <param name="code"></param>
/// <param name="settleDate"></param>
/// <returns></returns>
public decimal BondPrice(underlying_manager data, DateTime settleDate, out decimal vobp)
{
vobp = 0;
var price = data.Price ?? 0;
if (EodPriceQueryService.TryGetBondEodPrice(settleDate, data.UnderlyingCode, out var eodPrice))
{
price = eodPrice.GetPrice(SettlementTypeEnum.ClosePrice);
vobp = eodPrice.Vobp ?? 0;
}
else
{
price = price * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
}
return Convert.ToDecimal(price);
}
/// <summary>
/// 框架合约估值
/// </summary>
/// <param name="td">互换交易</param>
/// <param name="settleDate">收盘日</param>
private void SaveEodSwap(trade td, DateTime settleDate,DateTime preSettleDate)
{
var eod_Swaps = DbContext.eod_swap.Where(x => x.SwapTradeId == td.id && x.ValueDate >= preSettleDate && x.ValueDate<= settleDate).ToList();
var eod_Swap = eod_Swaps.FirstOrDefault(x => x.ValueDate == settleDate);
var preEodSwap= eod_Swaps.FirstOrDefault(x => x.ValueDate == preSettleDate);
if (eod_Swap == null)
{
eod_Swap = new eod_swap();
}
var tradeSpan = DbContext.trade_span.FirstOrDefault(x => x.TradeId == td.id && x.ValueDate == settleDate);
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList();
var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿
var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿
eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
eod_Swap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);
eod_Swap.NotionalValue = eod_Swap.NotionalValueLong + eod_Swap.NotionalValueShort;
eod_Swap.SwapTradeId = td.id;
eod_Swap.SwapTradeNo = td.TradeNumber;
eod_Swap.ClientId = td.ClientId;
eod_Swap.BookId = td.AssetId;
eod_Swap.ValueDate = settleDate;
eod_Swap.StructureType = td.StructureType;
eod_Swap.MarketValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.UnderlyingMarketValue);
eod_Swap.MarketValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.UnderlyingMarketValue);
eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum);
decimal interestPnL = 0;
interestPositions.ForEach(x =>
{
decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(x.InterestMode))
{
ratio = -ratio;
}
interestPnL += x.InterestProfitSum * ratio;
});
eod_Swap.InterestPnL = interestPnL;
eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue);
eod_Swap.RealizedPnL = eodSwapPositions.Sum(s => s.RealizedPnl);
eod_Swap.TdRealizedPnL = eod_Swap.RealizedPnL - (preEodSwap?.RealizedPnL ?? 0);
eod_Swap.TdCloseQty = positions.Sum(s => s.TdCloseQty);
var initMargin = Convert.ToDecimal(tradeSpan?.InitialMargin ?? 0);
var maintainMargin = Convert.ToDecimal(tradeSpan?.WorstCastClientPayable ?? 0);
if (initMargin < 0)
{
eod_Swap.InitMarginLoss = Math.Abs(initMargin);
}
else
{
eod_Swap.InitMarginGain = Math.Abs(initMargin);
}
if (maintainMargin < 0)
{
eod_Swap.PostionMarginLoss = Math.Abs(maintainMargin);
}
else
{
eod_Swap.PostionMarginGain = Math.Abs(maintainMargin);
}
UpdateDbOption(eod_Swap);
if (eod_Swap.id == 0)
{
DbContext.eod_swap.Add(eod_Swap);
}
}
/// <summary>
/// 单标的修改当天 框架合约信息
/// </summary>
/// <param name="td"></param>
/// <param name="settleDate"></param>
private void UpdateEodSwap(trade td, DateTime settleDate)
{
eod_swap eod_Swap = DbContext.eod_swap.FirstOrDefault(x => x.SwapTradeId == td.id && x.ValueDate == settleDate);
if (eod_Swap == null)
{
eod_Swap = new eod_swap();
eod_Swap.SwapTradeId = td.id;
eod_Swap.ValueDate = settleDate;
DbContext.eod_swap.Add(eod_Swap);
}
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList();
var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿
var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿
eod_Swap.NotionalValue = Convert.ToDecimal(td.StockEqvNotional);
eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
eod_Swap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);
eod_Swap.MarketValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.UnderlyingMarketValue);
eod_Swap.MarketValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.UnderlyingMarketValue);
eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum);
interestPositions.ForEach(x =>
{
decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(x.InterestMode))
{
ratio = -ratio;
}
eod_Swap.InterestPnL += x.InterestProfitSum * ratio;
});
eodSwapPositions.ForEach(x =>
{
decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
if (marginTypes.Contains(x.InterestMode))
{
ratio = -ratio;
}
eod_Swap.TdRealizedPnL += x.TdCloseMtmPnl + x.TdCloseDividend + x.TdCloseFee + x.TdCloseInterest * ratio + x.TdCloseInterestFee;
});
eod_Swap.PostionValue = eodSwapPositions.Sum(s => s.SwapPositionValue);
eod_Swap.RealizedPnL = eodSwapPositions.Sum(s => s.RealizedMtmPnL + s.RealizedDividend + s.RealizedFee + s.RealizedInterest + s.RealizedInterestFee);
eod_Swap.TdCloseQty = positions.Sum(s => s.TdCloseQty);
var tradeInitMarginObj = DbContext.trade_initial_margin.FirstOrDefault(x => x.TradeId == td.id);
var initMarginList = interestPositions.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金 && x.HappenDate == settleDate).ToList();
var addMarginList = interestPositions.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金 && x.HappenDate == settleDate).ToList();
eod_Swap.InitMarginGain += initMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.收取).ToList().Sum(s => s.InterestPrincipalFix);
eod_Swap.InitMarginLoss += initMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.支付).ToList().Sum(s => s.InterestPrincipalFix);
eod_Swap.PostionMarginGain += addMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.收取).ToList().Sum(s => s.InterestPrincipalFix);
eod_Swap.PostionMarginLoss += addMarginList.Where(s => s.InterestDirection == (int)SwapDirectionEnum.支付).ToList().Sum(s => s.InterestPrincipalFix);
UpdateDbOption(eod_Swap);
DbContext.SaveChanges();
}
/// <summary>
/// 获取多空组合 平仓详细
/// </summary>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
public SwapLongShortCloseModel GetCloseDetails(int tradeId, DateTime valueDate)
{
SwapLongShortCloseModel closeModel = new SwapLongShortCloseModel();
var eodPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId && x.ValueDate == valueDate && !x.Invalid).ToList();
var flowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.EventDate == valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成 && x.EventType == (int)SwapEventTypeEnum.平仓 && string.IsNullOrEmpty(x.UnderlyingCode)).ToList();
closeModel.DealPositions = eodPositions.Where(x => x.TdCloseQty != 0).ToList();
closeModel.DealInterests = flowEvents;
return closeModel;
}
/// <summary>
/// 查询互换日终持仓风险-明细
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public SearchListResult<SwapPositionResponse> SearchEodPositionList(EodSwapPositionQueryRequest req)
{
var predicate = PredicateBuilder.Create<eod_swap_position>(n => !n.Invalid);
var tradePredicate = PredicateBuilder.Create<trade>(n => n.TradeType == "收益互换"
&& n.ValidState != "InValid");
if (req.ValueDate.HasValue)
{
predicate = predicate.And(n => n.ValueDate == req.ValueDate);
//tradePredicate = tradePredicate.And(n => n.StartDate <= req.ValueDate);
}
if (!string.IsNullOrEmpty(req.TradeNumber))
{
tradePredicate = tradePredicate.And(n => n.TradeNumber.Contains(req.TradeNumber.Trim()));
}
if (req.UserAssets != null || req.UserClients != null)
{
tradePredicate = tradePredicate.And(q => req.UserAssets.Contains(q.AssetId) || req.UserClients.Contains(q.ClientId));
}
if (req.ClientIds != null && req.ClientIds.Any(x => x > 0))
{
tradePredicate = tradePredicate.And(q => req.ClientIds.Contains(q.ClientId));
}
if (req.BookIds != null && req.BookIds.Any(x => x > 0))
{
tradePredicate = tradePredicate.And(q => req.BookIds.Contains(q.AssetId));
}
if (req.UnderlyingIds != null)
{
var underlyingCodes = new List<string>();
foreach (var id in req.UnderlyingIds)
{
var un = UnderlyingDataProvider.GetUnderlying(id);
if (un != null)
{
underlyingCodes.Add(un.UnderlyingCode);
}
}
predicate = predicate.And(n => underlyingCodes.Contains(n.UnderlyingCode));
}
var positionQuery = DbContext.eod_swap_position.Where(predicate);
var tradeQuery = DbContext.trade.Where(tradePredicate);
var query = from position in positionQuery
join td in tradeQuery on position.SwapTradeId equals td.id
select new SwapPositionResponse
{
eodPosition = position,
TradeDate = td.StartDate.Value,
SwapTradeNo = td.TradeNumber,
StructureType = td.StructureType,
ClientName = td.ClientName,
ClientId = td.ClientId,
};
if (string.IsNullOrEmpty(req.sidx))
{
req.sidx = "SwapTradeNo,eodPosition.id";
req.sord = "asc";
}
var retListResult = query.ToSearchList(req);
foreach (var item in retListResult.rows)
{
var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId);
item.SwapTradeTypeStr = client?.SwapTradeTypeStr;
var multiplier = ConsGlobal.InstrumentType.IsBond(item.eodPosition.UnderlyingInstrumentType) ? 100 : 1;
item.eodPosition.PosiNetPrice *= multiplier;
item.eodPosition.PosiGrossPrice *= multiplier;
item.eodPosition.PosiNetFeePrice *= multiplier;
item.eodPosition.PosiNetNoFeePrice *= multiplier;
item.eodPosition.UnderlyingPrice *= multiplier;
}
return retListResult;
}
/// <summary>
/// 查询互换日终持仓风险-框架合约
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public SearchListResult<EodSwapResponse> SearchEodSwapList(EodSwapQueryRequest req)
{
var predicate = PredicateBuilder.Create<eod_swap>(n => 1 == 1);
var tradePredicate = PredicateBuilder.Create<trade>(n => n.TradeType == "收益互换"
&& n.ValidState != "InValid");
if (req.ValueDate.HasValue)
{
predicate = predicate.And(n => n.ValueDate == req.ValueDate);
// tradePredicate = tradePredicate.And(n=>n.StartDate<=req.ValueDate);
}
if (!string.IsNullOrEmpty(req.TradeNumber))
{
tradePredicate = tradePredicate.And(n => n.TradeNumber.Contains(req.TradeNumber.Trim()));
}
if (req.UserAssets != null || req.UserClients != null)
{
tradePredicate = tradePredicate.And(q => req.UserAssets.Contains(q.AssetId) || req.UserClients.Contains(q.ClientId));
}
if (req.ClientIds != null && req.ClientIds.Any(x => x > 0))
{
tradePredicate = tradePredicate.And(q => req.ClientIds.Contains(q.ClientId));
}
if (req.BookIds != null && req.BookIds.Any(x => x > 0))
{
tradePredicate = tradePredicate.And(q => req.BookIds.Contains(q.AssetId));
}
var positionQuery = DbContext.eod_swap.Where(predicate);
var tradeQuery = DbContext.trade.Where(tradePredicate);
var query = from position in positionQuery
join td in tradeQuery on position.SwapTradeId equals td.id
select new EodSwapResponse
{
position = position,
TradeDate = td.StartDate.Value,
SwapTradeNo = td.TradeNumber,
StructureType = td.StructureType,
ClientName = td.ClientName,
AssetBookName = td.AssetBookName,
ClientId = td.ClientId
};
if (string.IsNullOrEmpty(req.sidx))
{
req.sidx = "SwapTradeNo,position.id";
req.sord = "asc";
}
DbContext.SetDebugLog();
var retListResult = query.ToSearchList(req);
foreach (var item in retListResult.rows)
{
var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId);
item.SwapTradeTypeStr = client?.SwapTradeTypeStr;
}
return retListResult;
}
/// <summary>
/// 获取互换交易日终持仓数据
/// </summary>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
public List<eod_swap_position> GetPreEodPositions(int tradeId, DateTime valueDate)
{
return DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId && x.ValueDate == valueDate && !x.Invalid).ToList();
}
/// <summary>
/// 获取互换交易日终持仓数据集合
/// </summary>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
public List<eod_swap> GetEodSwaps(List<int> tradeIds, DateTime valueDate)
{
return DbContext.eod_swap.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate == valueDate).ToList();
}
/// <summary>
/// 获取互换交易某交易日区间框架合约数据
/// </summary>
/// <param name="tradeIds"></param>
/// <param name="valueDate"></param>
/// <param name="preValueDate"></param>
/// <returns></returns>
public List<eod_swap> GetEodSwaps(List<int> tradeIds, DateTime valueDate, DateTime preValueDate)
{
return DbContext.eod_swap.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate <= valueDate && x.ValueDate >= preValueDate).ToList();
}
/// <summary>
/// 获取互换交易某日终持仓数据
/// </summary>
/// <param name="tradeIds"></param>
/// <param name="valueDate"></param>
/// <param name="preValueDate"></param>
/// <returns></returns>
public List<eod_swap_position> GetEodPositions(List<int> tradeIds, DateTime valueDate, DateTime preValueDate)
{
return DbContext.eod_swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && !string.IsNullOrEmpty(x.UnderlyingCode) && x.ValueDate <= valueDate && x.ValueDate >= preValueDate && !x.Invalid).ToList();
}
/// <summary>
/// 获取互换交易某区间日终持仓估值-按产品要求
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public SearchListResult<SwapPositionResponse> SearchPositionList(ClientSwapPositionRequest req)
{
var retListResult = GetSearchPositionList(req);
var clientDataSource = DataCacheProvider.GetClientDataSource();
var underlyDataSource = DataCacheProvider.GetUnderlyingDataSource();
foreach (var item in retListResult.rows)
{
var client = clientDataSource.GetData(item.ClientId);
item.ClientNumber = client.Number;
if (!string.IsNullOrEmpty(item.eodPosition.UnderlyingCode))
{
var underly = underlyDataSource.GetData(item.eodPosition.UnderlyingCode);
if (underly != null)
{
item.eodPosition.UnderlyingName = underly.UnderlyingName;
}
}
}
return retListResult;
}
/// <summary>
/// 获取互换交易某区间日终持仓估值-按山证要求
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public SearchListResult<EodSwapPositionResponse> SearchEodPositionList(ClientSwapPositionRequest req)
{
var retListResult = GetSearchEodPositionList(req);
return retListResult;
}
/// <summary>
/// 互换持仓明细查询
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
private SearchListResult<SwapPositionResponse> GetSearchPositionList(ClientSwapPositionRequest req)
{
var predicate = PredicateBuilder.Create<eod_swap_position>(n => !n.Invalid && n.PositionType > 0 && n.PosiStatus == 0);
var tradePredicate = PredicateBuilder.Create<trade>(n => n.TradeType == "收益互换"
&& n.ValidState != "InValid");
if (req.ClientId > 0)
{
predicate = predicate.And(x => x.ClientId == req.ClientId);
}
if (req.ValueDate != null)
{
predicate = predicate.And(x => x.ValueDate == req.ValueDate);
tradePredicate = tradePredicate.And(n => n.StartDate <= req.ValueDate);
}
var positionQuery = DbContext.eod_swap_position.Where(predicate);
var tradeQuery = DbContext.trade.Where(tradePredicate);
var query = from position in positionQuery
join td in tradeQuery on position.SwapTradeId equals td.id
select new SwapPositionResponse
{
eodPosition = position,
TradeDate = td.StartDate.Value,
SwapTradeNo = td.TradeNumber,
StructureType = td.StructureType,
ClientName = td.ClientName,
ClientId = td.ClientId,
InitialMarginDirection = position.PosiDirection
};
if (string.IsNullOrEmpty(req.sidx))
{
req.sidx = "SwapTradeNo,eodPosition.id";
req.sord = "asc";
}
var retListResult = query.ToSearchList(req);
foreach (var item in retListResult.rows)
{
SetClientEodPosition(item.eodPosition);
SetPosiPrice(item.eodPosition);
}
return retListResult;
}
private SearchListResult<EodSwapPositionResponse> GetSearchEodPositionList(ClientSwapPositionRequest req)
{
var predicate = PredicateBuilder.Create<eod_swap_position>(n => !n.Invalid && n.PosiQuantity > 0);
var interestPredicate = PredicateBuilder.Create<eod_swap_position>(n => !n.Invalid && n.InterestDirection > 0);
var tradePredicate = PredicateBuilder.Create<trade>(n => n.StructureType == req.StructureType
&& n.ValidState != "InValid");
if (req.ClientId > 0)
{
predicate = predicate.And(x => x.ClientId == req.ClientId);
}
if (req.ValueDateFrom != null)
{
predicate = predicate.And(x => x.ValueDate >= req.ValueDateFrom);
}
if (req.ValueDate != null)
{
predicate = predicate.And(x => x.ValueDate == req.ValueDate);
tradePredicate = tradePredicate.And(x => req.ValueDate >= x.StartDate);
}
var positionQuery = DbContext.eod_swap_position.Where(predicate);
var tradeQuery = DbContext.trade.Where(tradePredicate);
var query = from position in positionQuery
join td in tradeQuery on position.SwapTradeId equals td.id
join tcrConfirm in DbContext.trade_contract_r.Where(O => O.Type == ContractTypeEnum.Trade && O.IsValid) on td.id equals tcrConfirm.TradeId into tcrConfirms
from tcrConfirm in tcrConfirms.DefaultIfEmpty()
select new EodSwapPositionResponse
{
position = position,
ClientName = td.ClientName,
ConfrimNo = tcrConfirm.ContractCode,
TradeNumber = td.TradeNumber,
StructureType = td.StructureType,
UnwindDate = td.UnWindDate,
TradeStatus = td.TradeStatus
};
if (string.IsNullOrEmpty(req.sidx))
{
req.sidx = "position.id";
req.sord = "asc";
}
var retListResult = query.ToSearchList(req);
var tradeIds = retListResult.rows.Select(s => s.position.SwapTradeId).ToList();
interestPredicate = interestPredicate.And(x => tradeIds.Contains(x.SwapTradeId));
var valueDates = retListResult.rows.Select(s => s.position.ValueDate).Distinct().ToList();
var eodPositions = DbContext.eod_swap_position.Where(interestPredicate).ToList();
var positions = DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.InterestMode == (int)InterestModeEnum.初始预付金 && x.IsInitial && !x.Invalid).ToList();
var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList();
Dictionary<string, bool> tradeDic = new Dictionary<string, bool>();
foreach (var item in retListResult.rows)
{
var tradeExtend = tradeExtends.FirstOrDefault(x => x.TradeId == item.position.SwapTradeId);
var eventDate = item.position.ValueDate;
if (tradeExtend != null)
{
eventDate = QdpCalendarHelper.GetNonHoliday(eventDate.AddDays(tradeExtend.ExtendObj.SettlementRules));
}
item.DayCount = (eventDate - item.position.PosiStartDate).Days+1;
//item.position.PosiProfitSum += item.position.VTradingFee-item.position.PosiFeePending;
SetClientEodPosition(item.position);
//item.position.PosiProfitSum += item.TradingFee;
var posiProfitSum= item.position.PosiProfitSum;
//item.position.PosiProfitSum 不需要加交易费用
item.position.PosiProfitSum = item.position.PosiProfitSum - item.position.PosiFeePending-item.position.PosiDividendSum;
item.NetSettmentAmount = item.position.PosiProfitSum+ item.position.PosiDividendSum+ item.position.PosiFeePending;
item.PeriodAmount = item.position.PosiDividendSum;
var margins = positions.Where(x => x.SwapTradeId == item.position.SwapTradeId);
var interests = eodPositions.Where(x => x.SwapTradeId == item.position.SwapTradeId && x.ValueDate == item.position.ValueDate);
var eodMargins = interests.Where(x => marginTypes.Contains(x.InterestMode));
var eodInterests = interests.Where(x => !marginTypes.Contains(x.InterestMode));
var floatRateInterest = eodInterests.Where(x => !string.IsNullOrEmpty(x.FloatRateUnderlyingCode)).FirstOrDefault();
item.position.FloatRateUnderlyingCode = floatRateInterest.FloatRateUnderlyingCode;
item.position.FloatRate= floatRateInterest.FloatRate;
item.OpenMarginAmount = margins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
item.OpenMarginRate = margins.Sum(s => s.InterestRateDefault * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
item.MarginInterestAmount = eodMargins.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
item.InterestAmount = eodInterests.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? -1 : 1));
item.InterestRate = eodInterests.Sum(s => s.InterestRateDefault);
item.NetSettmentAmount += item.InterestAmount + item.MarginInterestAmount + eodMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
item.NetSettmentAmount = decimal.Parse(item.NetSettmentAmount.ToString("0.00"));
if (item.position.PosiNotionalValue != 0 && item.position.PosiNetPrice != 0)
{
item.FloatRateAbs = item.position.PosiNotionalValue == 0 ? 0 : item.InterestAmount / item.position.PosiNotionalValue;
}
SetPosiPrice(item.position);
}
return retListResult;
}
/// <summary>
/// 设置客户视角
/// </summary>
/// <param name="position"></param>
private void SetClientEodPosition(eod_swap_position position)
{
position.TdCloseDividend = -position.TdCloseDividend;
position.TdCloseMtmPnl = -position.TdCloseMtmPnl;
position.TdCloseFee = -position.TdCloseFee;
position.TdCloseInterest = -position.TdCloseInterest;
position.TdCloseInterestFee = -position.TdCloseInterestFee;
position.RealizedMtmPnL = -position.RealizedMtmPnL;
position.RealizedDividend = -position.RealizedDividend;
position.RealizedFee = -position.RealizedFee;
position.RealizedInterest = -position.RealizedInterest;
position.RealizedInterestFee = -position.RealizedInterestFee;
position.RealizedPnl = -position.RealizedPnl;
position.InterestProfitSum = -position.InterestProfitSum;
position.PosiProfitSum = -position.PosiProfitSum;
position.VTradingFee = -position.VTradingFee;
position.PosiFeePending = -position.PosiFeePending;
position.SwapPositionValue = -position.SwapPositionValue;
position.PosiDividendSum = -position.PosiDividendSum;
}
private void SetPosiPrice(eod_swap_position position)
{
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(position.UnderlyingCode);
if (um != null && um.IsBond())
{
position.PosiNetPrice *= 100;
position.UnderlyingPrice *= 100;
position.PosiGrossPrice *= 100;
position.PosiNetFeePrice *= 100;
position.PosiNetNoFeePrice *= 100;
return;
}
}
/// <summary>
/// 获取客户互换持仓信息
/// </summary>
/// <param name="clientId"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
public List<eod_position> GetSwapPositions(int clientId, DateTime valueDate)
{
var trades = DbContext.trade.Where(x => x.TradeType == "收益互换"
&& x.ClientId == clientId
&& ConsTrade.LiveTradeStatusList.Contains(x.TradeStatus)
&& x.ValidState != "InValid").ToList();
var tradeIds = trades.Select(s => s.id).ToList();
var eodSwaps = DbContext.eod_swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.ValueDate == valueDate).ToList();
var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList();
return ConvertEodPnl(eodSwaps, trades, tradeExtends);
}
private List<eod_position> ConvertEodPnl(List<eod_swap_position> eodSwaps, List<trade> trades, List<trade_extend> tradeExtends)
{
List<eod_position> list = new List<eod_position>();
foreach (var item in eodSwaps)
{
var tradeOrigin = trades.First(x => x.id == item.SwapTradeId);
var realizedPnL = item.RealizedMtmPnL + item.RealizedInterest;
var tdExtend = tradeExtends.First(x => x.TradeId == item.SwapTradeId);
eod_position model = new eod_position()
{
TradeId = tradeOrigin.id,
TradeType = tradeOrigin.TradeType,
ClientId = tradeOrigin.ClientId,
TradeNumber = tradeOrigin.TradeNumber,
TradeDate = tradeOrigin.TradeDate,
ExerciseDate = tradeOrigin.ExerciseDate,
PrincipalRate = tradeOrigin.PrincipalRate ?? 0,
BasisUnderlyingCode = tradeOrigin.BasisUnderlyingCode,
UnderlyingCode = item.UnderlyingCode,
BasisGap = tradeOrigin.BasisGap ?? 0,
Lots = Convert.ToDouble(item.PosiQuantity),
ParticipationRate = tradeOrigin.ParticipationRate ?? 0,
NoRiskRate = tradeOrigin.NoRiskRate ?? 0,
UnderlyingPrice = Convert.ToDouble(item.UnderlyingPrice),
Pv = Convert.ToDouble(item.UnderlyingMarketValue) * -1,
RoundedPv = Math.Round(Convert.ToDouble(item.UnderlyingMarketValue), 2) * -1,
Pnl = Convert.ToDouble(realizedPnL) * -1,
RoundedPnl = Math.Round(Convert.ToDouble(realizedPnL), 2) * -1,
ValueDate = item.ValueDate,
PvDouble = Convert.ToDouble(item.UnderlyingMarketValue),
PnlDouble = Convert.ToDouble(realizedPnL),
PositionRelizedAmount = Convert.ToDouble(realizedPnL) * -1,
InstrumentType = tradeOrigin.UnderlyingInstrumentType,
IsGroup = tradeOrigin.IsGroup,
SettlementType = tradeOrigin.SettlementType,
SettlementFlag = tradeOrigin.SettlementFlag,
tradeOrigin = tradeOrigin.Clone(),
Vol = 0,
Delta = 0,
Gamma = 0,
Theta = 0,
Vega = 0,
Rho = 0,
GammaCash = 0
};
SetDicValue(model, item, tdExtend.ExtendObj.AnnualDays);
list.Add(model);
}
return list;
}
/// <summary>
/// 设置持仓导出字典信息
/// </summary>
/// <param name="model"></param>
/// <param name="item"></param>
/// <param name="annualDays"></param>
private void SetDicValue(eod_position model, eod_swap_position item, int annualDays)
{
var extDic = model.trade.MetaDic;
if (!string.IsNullOrEmpty(item.UnderlyingCode))
{
var underlyingAssetName = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode)?.UnderlyingName;
decimal posiTradingFeeUnit = 0;
if (item.PosiQuantity != 0)
{
posiTradingFeeUnit = item.PosiTradingFee / item.PosiQuantity;
}
if (item.PosiDirection == (int)SwapDirectionEnum.支付)
{
extDic["互换_支付方标的代码"] = item.UnderlyingCode;
extDic["互换_支付方标的名称"] = underlyingAssetName;
extDic["互换_支付方期初标的价格"] = item.PosiGrossPrice.OtcFormatUmPrice();
extDic["互换_支付方交易数量"] = item.PosiQuantity.OtcFormatNotional();
extDic["互换_支付方到期标的价格"] = item.UnderlyingPrice.OtcFormatUmPrice();
extDic["互换_支付方单位交易费用"] = posiTradingFeeUnit.OtcFormatUmPrice();
extDic["互换_支付方初始预付金"] = item.RealizedFee.OtcFormatPercent();
extDic["互换_支付方交易费用"] = item.PosiTradingFee.OtcFormatUmPrice();
extDic["互换_支付方多空方向"] = item.PositionType == (int)PositionTypeFlag.Long ? "多头" : "空头";
}
else
{
extDic["互换_收取方标的代码"] = item.UnderlyingCode;
extDic["互换_收取方标的名称"] = underlyingAssetName;
extDic["互换_收取方期初标的价格"] = item.PosiGrossPrice.OtcFormatUmPrice();
extDic["互换_收取方交易数量"] = item.PosiQuantity.OtcFormatNotional();
extDic["互换_收取方到期标的价格"] = item.UnderlyingPrice.OtcFormatUmPrice();
extDic["互换_收取方单位交易费用"] = posiTradingFeeUnit.OtcFormatUmPrice();
extDic["互换_收取方初始预付金"] = "";
extDic["互换_收取方交易费用"] = item.PosiTradingFee.OtcFormatUmPrice();
extDic["互换_收取方多空方向"] = item.PositionType == (int)PositionTypeFlag.Long ? "多头" : "空头";
}
}
else
{
if (item.InterestDirection == (int)SwapDirectionEnum.支付)
{
extDic["互换_支付方互换利率"] = item.InterestRateDefault.OtcFormatPercent();
extDic["互换_支付方固定收益"] = "";
}
else
{
extDic["互换_收取方互换利率"] = item.InterestRateDefault.OtcFormatPercent();
extDic["互换_收取方固定收益"] = "";
}
}
extDic["互换_互换日期"] = "";
extDic["年化天数"] = annualDays.ToString();
}
}
}