2117 lines
120 KiB
C#
2117 lines
120 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.PosiGrossPrice);
|
|
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);
|
|
decimal tax = um.ValueAddedTax ?? 0;
|
|
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
|
|
if (valueDate > td.StartDate.Value && (curretEod.PosiQuantity > 0 || valueDate == td.UnWindDate))
|
|
{
|
|
decimal payment = bondPaymentService.CalcPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio);
|
|
// 考虑增值税
|
|
curretEod.TdPosiDividend = payment / (1 - tax) * (1 + tax);
|
|
}
|
|
curretEod.PosiDividendSum = eod.PosiDividendSum + curretEod.TdPosiDividend;
|
|
curretEod.PosiQuantity = eod.PosiQuantity;
|
|
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.TdPosiDividend = 0;
|
|
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;
|
|
|
|
decimal tax = um.ValueAddedTax ?? 0;
|
|
BondPaymentService bondPaymentService = new BondPaymentService(UserInfo);
|
|
decimal payment = bondPaymentService.CalcPayment(curretEod.UnderlyingCode, eod.ValueDate, valueDate, curretEod.PosiQuantity, shortRatio, directionRatio);
|
|
// 考虑增值税
|
|
curretEod.TdPosiDividend = payment / (1 - tax) * (1 + tax);
|
|
|
|
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;
|
|
var closeQty = unwindEvents.Where(x => x.EventType == (int)SwapFlowEventTypeEnum.平仓).ToList().Sum(s => s.Quantity);
|
|
// 等于 平仓数量/昨天剩余平仓数量 * 昨日浮动端分红
|
|
curretEod.TdCloseDividend = closeQty / eod.PosiQuantity * eod.TdPosiDividend;
|
|
curretEod.RealizedDividend = curretEod.RealizedDividend + curretEod.TdCloseDividend;
|
|
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 = unwindFlowEvents.Sum(x => x.TradingFee+x.TradingFeePending);
|
|
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??0;
|
|
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 = Math.Round(item.NetSettmentAmount, ConsGlobal.MoneyRound,MidpointRounding.AwayFromZero);
|
|
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();
|
|
}
|
|
}
|
|
}
|