using ClosedXML.Report.Utils;
using CsvHelper;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using MoreLinq.Extensions;
using Newtonsoft.Json;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Controls;
using Org.BouncyCastle.Asn1.Ocsp;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Core.Modules;
using System.Linq.Expressions;
using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
using YLErp.Model;
using YLErp.Models;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.EodModule.QueryModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
using static Microsoft.IO.RecyclableMemoryStreamManager;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.SwapModule
{
public class SwapDealService : SwapTradeBaseService
{
public SwapDealService(OptUserInfo optUser) : base(optUser)
{
}
public SwapDealService(YLBaseService baseService) : base(baseService)
{
}
///
/// 平仓初始化
///
///
///
///
public UnwindData InitUnwind(int tradeId)
{
var td = DbContext.trade.Find(tradeId);
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid);
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
bool commodity = ConsGlobal.InstrumentType.CalcTypeIsFutures(um.UnderlyingInstrumentType);
List eventTyps = new List() { (int)SwapEventTypeEnum.自动互换, (int)SwapEventTypeEnum.互换 };
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
CheckLastEod(dealDate, td.TradeDate.Value, tradeId);
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
td.trade_extend = tradeExtend;
var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault();
var oriPosition = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial).FirstOrDefault();
var preDealDate = GetPreDealDate(tradeId, dealDate, eventTyps);
var hasProcess = HasTradeProcess();
swap_flow_event floatEvent = new swap_flow_event();
UnwindData unwindData = new UnwindData();
if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && td.TradeStatus == ConsTrade.平仓待复核)
{
var swapEvent = GetSwapEvent(tradeId, (int)SwapEventTypeEnum.平仓);
if (swapEvent == null)
{
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
}
unwindData = swapEvent.unwindData;
}
else
{
unwindData.CloseType = commodity ? 1 : 2;
unwindData.StartDate = td.TradeDate.Value;
if (preDealDate.HasValue)
{
unwindData.StartDate = preDealDate.Value;
}
unwindData.ValueDate = dealDate;
unwindData.UnwindDate = dealDate;
floatEvent.EventDate = dealDate;
floatEvent.UnwindDate = unwindData.UnwindDate;
floatEvent.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules));
unwindData.PayDate = floatEvent.PayDate;
floatEvent.SwapTradeId = tradeId;
floatEvent.SwapTradeNo = td.TradeNumber;
unwindData.SwapTradeId = tradeId;
unwindData.StructureType = td.StructureType;
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.NotionalQty = positions.Where(x => x.IsInitial).Sum(s => s.PosiQuantity);
unwindData.PosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional);
unwindData.PositionQty = position != null ? position.PosiQuantity : Convert.ToDecimal(td.TradeAmount);
unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
unwindData.CloseMethod = (int)CloseMethodEnum.全部平仓;
unwindData.ClosePercent = 1;
unwindData.CloseNotionalValue = unwindData.PosiNotionalValue;
unwindData.CloseQty = unwindData.PositionQty;
if (position != null)
{
floatEvent.PositionId = position.PositionId;
floatEvent.EventType = (int)SwapEventTypeEnum.平仓;
floatEvent.EventReason = "交易";
floatEvent.DividendIn = 0;
floatEvent.UnderlyingCode = position.UnderlyingCode;
floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
floatEvent.CloseFee = 0;
floatEvent.BeforeCloseFee = oriPosition.PosiTradingFeePending;
floatEvent.MarkClosePnl = 0;
floatEvent.PayDirection = position.PosiDirection;
floatEvent.PosiGrossPrice = position.PosiGrossPrice;
floatEvent.PosiNetPrice = position.PosiNetPrice;
floatEvent.TradingAmountNetAvg = position.PosiNetNoFeePrice;
floatEvent.PositionType = position.PositionType;
floatEvent.TradingAmountNetFeeAvg = position.PosiNetFeePrice;
floatEvent.Quantity = position.PosiQuantity;
floatEvent.PositionQty = 0;
floatEvent.ContractSize = position.ContractSize;
floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize;
var ratio = position.PosiDirection == (int)SwapDirectionEnum.收取 ? -1m : 1m;
floatEvent.TradingFeePending = position.PosiTradingFeePending;
floatEvent.DataState = (int)SwapFlowDateStateEnum.完成;
floatEvent.InterestMode = position.InterestMode;
floatEvent.ClientId = td.ClientId;
floatEvent.SetOpt(UserInfo);
}
unwindData.FlowEvents.Add(floatEvent);
}
return unwindData;
}
///
/// 校验上日是否收盘
///
///
public void CheckEodTrade(int tradeId)
{
var td = DbContext.trade.Find(tradeId);
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
CheckLastEod(dealDate, td.StartDate.Value, tradeId);
}
///
/// 多空组合 平仓初始化
///
///
///
///
public UnwindData InitLongShortUnwind(int tradeId, SwapEventTypeEnum eventTypeEnum)
{
var td = DbContext.trade.Find(tradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.IsInitial && !x.Invalid);
List eventTyps = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
CheckLastEod(dealDate, td.TradeDate.Value, tradeId);
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
td.trade_extend = tradeExtend;
var preDealDate = GetPreDealDate(tradeId, dealDate, eventTyps);
double stockEqvNotional = td.StockEqvNotional;//剩余名义本金
var hasProcess = HasTradeProcess();
swap_flow_event floatEvent = new swap_flow_event();
UnwindData unwindData = new UnwindData();
if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && (td.TradeStatus == ConsTrade.平仓待复核 || td.TradeStatus == ConsTrade.互换待复核))
{
var swapEvent = GetSwapEvent(tradeId, (int)eventTypeEnum);
if (swapEvent == null)
{
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
}
unwindData = swapEvent.unwindData;
}
else
{
unwindData.StartDate = td.TradeDate.Value;
if (preDealDate.HasValue)
{
unwindData.StartDate = preDealDate.Value;
}
unwindData.ValueDate = dealDate;
unwindData.UnwindDate = unwindData.UnwindDate;
unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules));
unwindData.SwapTradeId = tradeId;
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.NotionalQty = positions.Sum(s => s.PosiQuantity);
unwindData.PosiNotionalValue = Convert.ToDecimal(stockEqvNotional);
unwindData.PositionQty = 0;//平仓只做了结为0,互换用不上
unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
if (eventTypeEnum == SwapEventTypeEnum.平仓)
{
unwindData.FlowEvents = GetUnwindInterests(dealDate, unwindData.UnwindDate.Value, tradeId, 1, (int)SwapEventTypeEnum.平仓);
}
}
return unwindData;
}
///
/// 平仓初始化
///
///
///
///
public UnwindData InitIncome(int tradeId)
{
var checkEventTypes = new List() { (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
var td = DbContext.trade.Find(tradeId);
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid);
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
List eventTypes = new List() { (int)SwapFlowEventTypeEnum.互换, (int)SwapFlowEventTypeEnum.自动互换 };
var dealDate = valuedateBLL.ValueDate < td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
CheckLastEod(dealDate, td.TradeDate.Value, tradeId);
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
td.trade_extend = tradeExtend;
var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault();
//var preSettleDate = CheckLastEod(dealDate, td.StartDate.Value, tradeId);//上一交易日期
var preDealDate = GetPreDealDate(tradeId, dealDate, eventTypes);
var hasProcess = HasTradeProcess();
swap_flow_event floatEvent = new swap_flow_event();
UnwindData unwindData = new UnwindData();
if (((valuedateBLL.SystemDate.CloseReCheck == 1) || (valuedateBLL.SystemDate.CloseReApprove == 1 && hasProcess)) && td.TradeStatus == ConsTrade.互换待复核)
{
var swapEvent = GetSwapEvent(tradeId, (int)SwapEventTypeEnum.互换);
if (swapEvent == null)
{
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
}
unwindData = swapEvent.unwindData;
}
else
{
unwindData.StartDate = td.TradeDate.Value;
if (preDealDate.HasValue)
{
unwindData.StartDate = preDealDate.Value;
}
unwindData.ValueDate = dealDate;
unwindData.UnwindDate = dealDate;
floatEvent.UnwindDate = unwindData.UnwindDate;
floatEvent.EventDate = dealDate;
unwindData.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules));
floatEvent.PayDate = unwindData.PayDate;
floatEvent.SwapTradeId = tradeId;
unwindData.SwapTradeId = tradeId;
unwindData.StructureType = td.StructureType;
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.NotionalQty = positions.Where(x => x.IsInitial).Sum(s => s.PosiQuantity);
unwindData.PosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional);
unwindData.PositionQty = Convert.ToDecimal(td.TradeAmount);
unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
unwindData.ClosePercent = unwindData.PosiNotionalValue / unwindData.NotionalValue;
unwindData.CloseNotionalValue = unwindData.PosiNotionalValue;
if (position != null)
{
floatEvent.EventType = (int)SwapEventTypeEnum.互换;
floatEvent.EventReason = "交易";
floatEvent.PositionId = position.PositionId;
floatEvent.DividendIn = 0;
floatEvent.UnderlyingCode = position.UnderlyingCode;
floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
floatEvent.CloseFee = 0;
floatEvent.MarkClosePnl = 0;
floatEvent.PayDirection = position.PosiDirection;
floatEvent.PosiGrossPrice = position.PosiGrossPrice;
floatEvent.PosiNetPrice = position.PosiNetPrice;
floatEvent.TradingAmountNetAvg = position.PosiNetNoFeePrice;
floatEvent.TradingAmountNetFeeAvg = position.PosiNetFeePrice;
floatEvent.PositionType = position.PositionType;
floatEvent.Quantity = position.PosiQuantity;
floatEvent.PositionQty = 0;
floatEvent.ContractSize = position.ContractSize;
floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize;
floatEvent.ClientId = td.ClientId;
floatEvent.DataState = (int)SwapFlowDateStateEnum.完成;
}
unwindData.FlowEvents.Add(floatEvent);
}
return unwindData;
}
///
/// 获取平仓利息端信息
///
/// 平仓日期
/// 交易id
/// 平仓比例
///
///
public List GetUnwindInterests(DateTime valueDate, DateTime unwindDate, int tradeId, decimal closePercent, int eventType)
{
List interests = new List();
if (closePercent > 1)
{
closePercent = 1;//防篡改
}
else if (closePercent < 0)
{
closePercent = 0;
}
var td = DbContext.trade.Find(tradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
var allpositions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid).ToList();
var origPositions = allpositions.Where(x => x.IsInitial).ToList();
var realPostitions = allpositions.Where(x => !x.IsInitial).ToList();
var positions = origPositions.Where(x => x.PosiDirection == 0).ToList();
var fpositions = origPositions.Where(x => x.PosiDirection > 0).ToList();
var longPositions = fpositions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).ToList();
var shortPositions = fpositions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).ToList();
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
List eventTypes = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
var lastEod = DbContext.eod_swap.Where(x => x.ValueDate < unwindDate && x.SwapTradeId == tradeId).OrderByDescending(o => o.ValueDate).FirstOrDefault();
var orginPv = lastEod != null ? lastEod.NotionalValue : 0;
var _preSetteDate = lastEod == null ? unwindDate.AddDays(-1) : lastEod.ValueDate;
List lastEodPositions = new SwapEodPositionService(this).GetPreEodPositions(tradeId, _preSetteDate);//上一交易数据
var posiLongNotionalValue = longPositions.Sum(s => s.PosiNotionalValue);// 剩余名义本金
var posiShortNotionalValue = shortPositions.Sum(s => s.PosiNotionalValue);// 剩余名义本金
var stockEqvNotional = realPostitions.Where(x => x.PosiDirection > 0).Sum(s => s.PosiNotionalValue);
var posiNotionalValue = stockEqvNotional * closePercent;//剩余名义本金
var grossPrice = realPostitions.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice;
bool tdClose = DbContext.swap_flow_event.Any(x => x.SwapTradeId == tradeId && x.UnwindDate == unwindDate && eventTypes.Contains(x.EventType) && x.DataState == (int)SwapFlowDateStateEnum.完成);
interests = GetInterests(td, tradeExtend, valueDate, unwindDate, lastEodPositions, positions, stockEqvNotional, posiLongNotionalValue, posiShortNotionalValue, posiNotionalValue, closePercent, eventType, tdClose, false, grossPrice ?? 0, orginPv, true, false, false);
return interests;
}
///
/// 计算利息腿计息详细
///
/// 交易
/// 交易扩展数据
/// 操作日期
/// 上一日终持仓
/// 期初利率端
/// 持仓名义本金
/// 多头持仓名义本金
/// 空头持仓名义本金
/// 平仓名义本金
///
///
///
///
///
public List GetInterests(
trade td,
trade_extend tradeExtend,
DateTime valueDate,
DateTime unwindDate,
List eodPositions,
List positions,
decimal posiNotionalValue,
decimal posiLongNotionalValue,
decimal posiShortNotionalValue,
decimal closePosiNotionalValue,
decimal closePrecent,
int eventType,
bool tdClose,
bool needPrice,
decimal grossPrice,
decimal orginPv,
bool add = false,
bool calcLast = true,
bool settment = true)
{
List interests = new List();
var annualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
foreach (var position in positions)
{
var _closePosiNotionalValue = closePosiNotionalValue;
var _posiNotionalValue = posiNotionalValue;
var preEodPosition = eodPositions.FirstOrDefault(x => x.PositionId == position.id);
DateTime? preDealDate = null;
var positionClone = position.Clone();
var newClosePercent = closePrecent;
if (preEodPosition != null)
{
preDealDate = preEodPosition.ValueDate;
}
var swap = InitInterestDate(unwindDate, preDealDate, td, tdClose, calcLast, out DateTime startDate, out DateTime endDate);//不算头或不算尾情况,无利息
if (!preDealDate.HasValue)
{
preEodPosition = new eod_swap_position();
preEodPosition.PosiStartDate = position.PosiStartDate;
preEodPosition.ValueDate = position.PosiStartDate;
}
var swapIntervalToday = position.SwapIntervalList.Where(x => x.Date <= startDate).OrderByDescending(o => o.Date).FirstOrDefault();
if (position.InterestMode == (int)InterestModeEnum.固定值)
{
_closePosiNotionalValue = position.InterestPrincipalFix;
_posiNotionalValue = position.InterestPrincipalFix;
newClosePercent = 1m;
}
else if (position.InterestMode == (int)InterestModeEnum.多头存续名义本金)
{
_closePosiNotionalValue = posiLongNotionalValue * closePrecent;
_posiNotionalValue = posiLongNotionalValue;
}
else if (position.InterestMode == (int)InterestModeEnum.空头存续名义本金)
{
_closePosiNotionalValue = posiShortNotionalValue * closePrecent;
_posiNotionalValue = posiShortNotionalValue;
}
else if (position.InterestMode == (int)InterestModeEnum.标的期初全价)
{
_closePosiNotionalValue = _posiNotionalValue * closePrecent;
_posiNotionalValue = _posiNotionalValue;
}
else if (position.InterestMode == (int)InterestModeEnum.追加预付金 || position.InterestMode == (int)InterestModeEnum.初始预付金)
{
_closePosiNotionalValue = position.InterestPrincipalFix * closePrecent;
_posiNotionalValue = position.InterestPrincipalFix * closePrecent;
positionClone.InterestDirection = position.InterestDirection == (int)SwapDirectionEnum.收取 ? (int)SwapDirectionEnum.支付 : (int)SwapDirectionEnum.收取;
}
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
// 获取重置频率,如果为空则默认为1
int interestPeriod = position.interest_rest_days ?? 1;
// 计算从 td.StartDate 到 endDate 的天数
var days = (endDate - td.StartDate.Value).Days;
// 获取合适的 rateDate
DateTime rateDate = GetRateDate(position.interest_rule, td.StartDate.Value, endDate, days, interestPeriod);
// 如果不需要重置,并且上一日已有 FloatRate,则不再查找
if (preEodPosition.id != 0 && days % interestPeriod != 0)
{
position.FloatRate = preEodPosition.FloatRate;
positionClone.FloatRate = preEodPosition.FloatRate;
}
else
{
// 如果没有 preEodPosition 数据或需要查找新 Rate,则去查询最新的浮动利率
if (EodPriceQueryService.TryGetPrice(rateDate, position.FloatRateUnderlyingCode, out double floatRate))
{
position.FloatRate = Convert.ToDecimal(floatRate);
positionClone.FloatRate = position.FloatRate;
}
else if(!swap)
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格");
}
}
}
decimal rate = position.InterestRateDefault;
if (swapIntervalToday == null)//当日无适用观察日
{
var swapInterval = position.SwapIntervalList.Where(x => x.Date > startDate).OrderBy(o => o.Date).FirstOrDefault();
if (swapInterval != null)
{
rate = swapInterval.Rate;
}
}
else
{
rate = swapIntervalToday.Rate;
}
if (preEodPosition.id == 0)
{
preEodPosition.FloatRate = positionClone.FloatRate;
preEodPosition.TdInterestPrincipal = _posiNotionalValue;
preEodPosition.PosiNotionalValue = _posiNotionalValue;
}
swap_flow_event interest = InitSwapDealInterest(td, valueDate, endDate, rate, positionClone, add, swap, _posiNotionalValue, _closePosiNotionalValue, newClosePercent, annualDays, eventType, preEodPosition, needPrice, settment, orginPv);
interests.Add(interest);
}
return interests;
}
///
/// 根据给定条件获取 rateDate
///
private DateTime GetRateDate(int? interest_rule, DateTime startDate, DateTime endDate, int days, int interestPeriod)
{
// 判断是否达到重置周期
if (days % interestPeriod == 0)
{
return QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(interest_rule ?? 0));
}
// 如果不在重置周期内,使用 td.StartDate 来获取 rateDate
return QdpCalendarHelper.GetNonHolidayDefore(startDate.AddDays(interest_rule ?? 0));
}
///
/// 初始化利息腿信息
///
/// 交易编码
/// 计息开始日期
/// 计息结束日期
/// 计息年化利率
/// 利息腿
/// 是否新增
/// 是否已互换
/// 上一日终归档
/// 当日适用名义本金
/// 当日平仓名义本金
/// 年化天数
///
private swap_flow_event InitSwapDealInterest(trade td,
DateTime valueDate,
DateTime endDate,
decimal rate,
swap_position position,
bool add,
bool swap,
decimal posiNotionalValue,
decimal closePosiNotionalValue,
decimal closePrecent,
int annualDays,
int eventType,
eod_swap_position preEodPosition,
bool needPrice,
bool settment,
decimal orginPv
)
{
DateTime lastSwapDate = preEodPosition.ValueDate;
decimal interestProfitSum = preEodPosition.InterestProfitSum;
swap_flow_event interest = new swap_flow_event();
interest.SwapTradeId = td.id;
interest.SwapTradeNo = td.TradeNumber;
interest.EventType = eventType;
interest.EventReason = "交易";
interest.EventDate = valueDate;
interest.PositionId = position.id;
interest.InterestDirection = position.InterestDirection;
interest.InterestRate = rate;
interest.InterestPrincipal = closePosiNotionalValue;
interest.InterestSwapInterval = position.InterestSwapInterval;
interest.InterestMode = position.InterestMode;
interest.FloatRate = position.FloatRate;
interest.DataState = (int)SwapFlowDateStateEnum.完成;
interest.ClientId = td.ClientId;
interest.UnwindDate = endDate;
var itemDays = (endDate - lastSwapDate).Days;
itemDays = itemDays == 0 ? 1 : itemDays;
if (swap)
{
interest.InterestAmount = 0;
interest.TdInterestAmount = 0;
interest.InterestAmount = 0;
interest.InterestClosePnL = 0;
}
else
{
decimal InterestAmount = 0;
decimal TdInterestAmount = 0;
var interestRatio = position.InterestDirection == 1 ? 1m : -1m;
if (position.InterestType == (int)InterestTypeEnum.复利)
{
var floateRate = preEodPosition.FloatRate;
if (settment)//收盘利息计算
{
CalcDailyCompoundInterestByEod(preEodPosition, endDate, td.StartDate.Value, position, closePosiNotionalValue, posiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, orginPv, ref InterestAmount, ref TdInterestAmount);
}
else
{
CalcDailyCompoundInterest(preEodPosition, endDate, td.StartDate.Value, position, closePosiNotionalValue, posiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, orginPv, ref InterestAmount, ref TdInterestAmount);
}
}
else
{
InterestAmount = closePosiNotionalValue * (interest.InterestRate + position.FloatRate);
TdInterestAmount = posiNotionalValue * (interest.InterestRate + position.FloatRate);
if (settment)
{
if (position.IsAnnualized)
{
InterestAmount = InterestAmount * ((decimal)itemDays / annualDays);
TdInterestAmount = TdInterestAmount * ((decimal)itemDays / annualDays);
}
InterestAmount = (interestProfitSum * closePrecent) + InterestAmount;
}
else
{
if (itemDays > 1)//日期超算情况
{
decimal days = (decimal)itemDays - 1m;
if (position.IsAnnualized)
{
InterestAmount = InterestAmount * (days / annualDays);
TdInterestAmount = TdInterestAmount * (days / annualDays);
InterestAmount += (interestProfitSum * closePrecent);
}
}
else
{
InterestAmount = interestProfitSum * closePrecent;
}
}
}
interest.InterestAmount = Math.Round(InterestAmount, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
interest.TdInterestAmount = Math.Round(TdInterestAmount, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
interest.InterestClosePnL = interest.InterestAmount * interestRatio;
}
if (add)
{
UpdateDbOption(interest);
}
return interest;
}
///
/// 计算复利 盘中
///
/// 上一互换日
/// 结算日期
/// 开仓日
/// 浮动标的
/// 计息基数
/// 固定利率
/// 是否年化
/// 年化天数
///
public void CalcDailyCompoundInterest(eod_swap_position preEodPosition, DateTime endDate, DateTime tradeDate, swap_position position, decimal principal, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, ref decimal InterestAmount, ref decimal TdInterestAmount)
{
DateTime lastSwapDate = preEodPosition.ValueDate;
decimal interestProfitSum = preEodPosition.InterestProfitSum;
var TdInterestPrincipal = preEodPosition.TdInterestPrincipal;
decimal interest = 0;
decimal tdinterest = 0;
int interestPeriod = position.interest_rest_days ?? 1;
decimal dynomicPrincipal = principal;
decimal tdDynomicPrincipal = posiPrincipal;
var calcDays = (endDate - tradeDate).Days;
double floatRate = Convert.ToDouble(floateRate);
for (int i = 0; i <= calcDays; i++)
{
var rateDate = tradeDate.AddDays(i);
if (rateDate > lastSwapDate || endDate == tradeDate)
{
if (i % interestPeriod == 0)
{
dynomicPrincipal = dynomicPrincipal + interest;
tdDynomicPrincipal = tdDynomicPrincipal + interest;
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日;
if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
{
if (floatRate1 != 0)
{
floatRate = floatRate1;
}
}
else
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
}
}
flowEvent.InterestPrincipal = tdDynomicPrincipal * closePercent;
TdInterestPrincipal = tdDynomicPrincipal;
}
else
{
flowEvent.InterestPrincipal = (preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv) * closePercent;
tdDynomicPrincipal = flowEvent.InterestPrincipal;
TdInterestPrincipal = (preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv);
}
flowEvent.FloatRate = Convert.ToDecimal(floatRate);
var interest1 = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
var tdinterest1 = TdInterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
if (position.IsAnnualized)
{
interest1 /= annualDays;
tdinterest1 /= annualDays;
}
interest += interest1;
tdinterest += tdinterest1;
}
else if (lastSwapDate >= rateDate)
{
interest = interestProfitSum * closePercent;
tdinterest = interestProfitSum * closePercent;
}
}
InterestAmount = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
TdInterestAmount = Math.Round(tdinterest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
///
/// 计算复利 收盘
///
/// 上一互换日
/// 结算日期
/// 开仓日
/// 浮动标的
/// 计息基数
/// 固定利率
/// 是否年化
/// 年化天数
///
public void CalcDailyCompoundInterestByEod(eod_swap_position preEodPosition, DateTime endDate, DateTime tradeDate, swap_position position, decimal principal, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, ref decimal InterestAmount, ref decimal TdInterestAmount)
{
decimal interestProfitSum = preEodPosition.InterestProfitSum;
decimal interest = preEodPosition.TdInterestIncome;
decimal tdinterest = preEodPosition.TdInterestIncome;
int interestPeriod = position.interest_rest_days ?? 1;
decimal tdDynomicPrincipal = posiPrincipal;
double floatRate = Convert.ToDouble(floateRate);
var days = (endDate - tradeDate).Days;
if (days % interestPeriod == 0)
{
tdDynomicPrincipal = tdDynomicPrincipal + interestProfitSum;
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
{
// 获取合适的 rateDate
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日;
if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
{
if (floatRate1 != 0)
{
floatRate = floatRate1;
}
}
else
{
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
}
}
flowEvent.InterestPrincipal = tdDynomicPrincipal * closePercent;
interest = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
tdinterest = tdDynomicPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
if (position.IsAnnualized)
{
interest /= annualDays;
tdinterest /= annualDays;
}
}
else
{
flowEvent.InterestPrincipal = (preEodPosition.TdInterestPrincipal + tdDynomicPrincipal - orginPv) * closePercent;
var interest1 = flowEvent.InterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
var tdinterest1 = (preEodPosition.TdInterestPrincipal + tdDynomicPrincipal - orginPv) * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
if (position.IsAnnualized)
{
interest1 /= annualDays;
tdinterest1 /= annualDays;
}
interest = interest1;
tdinterest = tdinterest1;
}
flowEvent.FloatRate = Convert.ToDecimal(floatRate);
InterestAmount = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
TdInterestAmount = Math.Round(tdinterest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
///
/// 单标的平仓
///
///
///
public void SwapUnwind(UnwindData unwindData)
{
var td = DbContext.trade.Find(unwindData.SwapTradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId);
var trans = DbContext.Database.BeginTransaction();
bool cofirm = false;
try
{
int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate);
if (unwindData.SwapMarginAmount != 0)
{
AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate);
}
if (unwindData.SwapMarginRebatePnl != 0)
{
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
}
DealFloatPosition(unwindData);
var flowList = new List(unwindData.FlowEvents);
var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓");
if (unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓)
{
td.TradeStatus = "已平仓";
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
{
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, unwindData.ValueDate, 0);
}
}
else
{
td.HasPartialUnWind = 1;
}
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
DbContext.SaveChanges();
trans.Commit();
cofirm = true;
}
catch (Exception ex)
{
trans.Rollback();
throw new Exception(ex.Message, ex);
}
finally
{
trans.Dispose();
}
if (cofirm)
{
Task.Run(() =>
{
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
});
}
}
///
/// 自动全平仓
///
///
///
///
public void AuotoSwapUnwind(int tradeid, decimal unwindPrice, decimal unwindPriceFee, decimal unwindNetFee, decimal unwindNet, DateTime valueDate, decimal unwindQty)
{
unwindPriceFee = decimal.Parse(unwindPriceFee.ToString("F10"));
var td = DbContext.trade.Find(tradeid);
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && !x.Invalid);
List eventTypes = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 };
var dealDate = valueDate;
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
td.trade_extend = tradeExtend;
var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault();
var preDealDate = GetPreDealDate(td.id, dealDate, eventTypes);
swap_flow_event floatEvent = new swap_flow_event();
UnwindData unwindData = new UnwindData();
unwindData.CloseType = 2;
unwindData.StartDate = td.TradeDate.Value;
if (preDealDate.HasValue)
{
unwindData.StartDate = preDealDate.Value;
}
unwindData.ValueDate = dealDate;
unwindData.UnwindDate = QdpCalendarHelper.GetNonHoliday(dealDate.AddDays(1));
floatEvent.EventDate = dealDate;
floatEvent.UnwindDate = unwindData.UnwindDate;
floatEvent.PayDate = QdpCalendarHelper.GetNonHoliday(unwindData.UnwindDate.Value.AddDays(td.trade_extend.ExtendObj.SettlementRules));
unwindData.PayDate = floatEvent.PayDate;
floatEvent.SwapTradeId = td.id;
floatEvent.SwapTradeNo = td.TradeNumber;
unwindData.SwapTradeId = td.id;
unwindData.StructureType = td.StructureType;
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.NotionalQty = positions.Where(x => x.IsInitial).Sum(s => s.PosiQuantity);
unwindData.PosiNotionalValue = position != null ? position.PosiNotionalValue : Convert.ToDecimal(td.StockEqvNotional);
unwindData.PositionQty = Convert.ToDecimal(td.TradeAmount);
var unwindPercent = unwindData.PositionQty == 0 ? 0 : unwindQty / unwindData.PositionQty;
unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
unwindData.CloseMethod = unwindQty == unwindData.PositionQty ? (int)CloseMethodEnum.全部平仓 : (int)CloseMethodEnum.部分平仓;
unwindData.ClosePercent = unwindData.PositionQty == 0 ? 0 : unwindPercent;
unwindData.CloseNotionalValue = position == null ? 0 : unwindQty * position.PosiGrossPrice * position.ContractSize;
unwindData.CloseQty = unwindQty;
if (position != null)
{
decimal floatRatio = position.PosiDirection == 1 ? 1m : -1m;
decimal longRatio = position.PositionType == 1 ? 1m : -1m;
floatEvent.PositionId = position.PositionId;
floatEvent.EventType = (int)SwapFlowEventTypeEnum.平仓;
floatEvent.EventReason = "交易";
floatEvent.DividendIn = 0;
floatEvent.UnderlyingCode = position.UnderlyingCode;
floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
floatEvent.CloseFee = 0;
floatEvent.BeforeCloseFee = position.PosiTradingFee + position.PosiTradingFeePending;
floatEvent.PayDirection = position.PosiDirection;
floatEvent.PosiGrossPrice = position.PosiGrossPrice;
floatEvent.PosiNetPrice = position.PosiNetPrice;
floatEvent.PositionType = position.PositionType;
floatEvent.Quantity = unwindData.CloseQty;
floatEvent.PositionQty = unwindData.PositionQty - unwindData.CloseQty;
floatEvent.ContractSize = position.ContractSize;
floatEvent.DataState = (int)SwapFlowDateStateEnum.完成;
floatEvent.InterestMode = position.InterestMode;
floatEvent.TradingAmountAvg = unwindPrice;
floatEvent.TradingAmountFeeAvg = unwindPriceFee;
floatEvent.TradingAmountNetFeeAvg = unwindNetFee;
floatEvent.TradingAmountNetAvg = unwindNet;
floatEvent.TradingFeePending = position.PosiTradingFeePending * unwindData.ClosePercent;
floatEvent.TradingFeePending = Math.Round(floatEvent.TradingFeePending, 2, MidpointRounding.AwayFromZero);
floatEvent.TradingFee = floatEvent.TradingFeePending;
floatEvent.MarkClosePnl = (unwindPrice - position.PosiGrossPrice) * unwindQty * floatRatio * longRatio;
floatEvent.MarkClosePnl = Math.Round(floatEvent.MarkClosePnl + (floatEvent.TradingFeePending*2 * floatRatio * -1), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
floatEvent.TradingAmount = unwindPrice * floatEvent.Quantity * floatEvent.ContractSize;
floatEvent.OptLog = "流水自动";
floatEvent.ClientId = td.ClientId;
floatEvent.SetOpt(UserInfo);
}
unwindData.FlowEvents.Add(floatEvent);
var interestPositions = GetUnwindInterests(unwindData.ValueDate, unwindData.UnwindDate.Value, td.id, unwindPercent, (int)SwapEventTypeEnum.平仓);
interestPositions.ForEach(item =>
{
item.OptLog = "流水自动";
});
unwindData.FlowEvents.AddRange(interestPositions);
CalcCloseAmount(unwindData);
DealUnwind(unwindData, td);
}
///
/// 衡泰新增平仓事件
///
///
///
///
///
///
public void AutoSwapUnwindFromConsumer(trade td, DateTime valueDate, DateTime payDate, decimal markClosePnl, decimal tradeinfFee, decimal interestAmount, decimal fee, decimal unwindQty, bool allClose)
{
List eventTypes = new List() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 };
var dealDate = valueDate;
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
td.trade_extend = tradeExtend;
var position = DbContext.swap_position.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial && !x.Invalid).FirstOrDefault();
var preDealDate = GetPreDealDate(td.id, dealDate, eventTypes);
swap_flow_event floatEvent = new swap_flow_event();
UnwindData unwindData = new UnwindData();
unwindData.CloseType = 2;
unwindData.StartDate = td.TradeDate.Value;
if (preDealDate.HasValue)
{
unwindData.StartDate = preDealDate.Value;
}
unwindData.ValueDate = dealDate;
floatEvent.EventDate = dealDate;
unwindData.UnwindDate = QdpCalendarHelper.GetNonHoliday(dealDate.AddDays(1));
floatEvent.UnwindDate = unwindData.UnwindDate;
floatEvent.PayDate = payDate;
unwindData.PayDate = floatEvent.PayDate;
floatEvent.SwapTradeId = td.id;
floatEvent.SwapTradeNo = td.TradeNumber;
unwindData.SwapTradeId = td.id;
unwindData.StructureType = td.StructureType;
unwindData.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? 0);
unwindData.NotionalQty = position.PosiQuantity;
unwindData.PosiNotionalValue = Convert.ToDecimal(td.StockEqvNotional);
unwindData.PositionQty = Convert.ToDecimal(td.TradeAmount);
unwindData.AnnualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
unwindData.CloseMethod = allClose ? (int)CloseMethodEnum.全部平仓 : (int)CloseMethodEnum.部分平仓;
unwindData.ClosePercent = allClose ? 1 : unwindQty / unwindData.NotionalQty;
unwindData.CloseNotionalValue = allClose ? unwindData.PosiNotionalValue : unwindQty;
unwindData.CloseQty = allClose ? unwindData.PositionQty : unwindQty;
if (position != null)
{
decimal floatRatio = position.PosiDirection == 1 ? 1m : -1m;
floatEvent.PositionId = position.id;
floatEvent.EventType = (int)SwapEventTypeEnum.平仓;
floatEvent.EventReason = "接口合约终止交易";
floatEvent.DividendIn = 0;
floatEvent.UnderlyingCode = position.UnderlyingCode;
floatEvent.UnderlyingInstrumentType = position.UnderlyingInstrumentType;
floatEvent.CloseFee = 0;
floatEvent.BeforeCloseFee = position.PosiTradingFee + position.PosiTradingFeePending;
floatEvent.PayDirection = position.PosiDirection;
floatEvent.PosiGrossPrice = position.PosiGrossPrice;
floatEvent.PosiNetPrice = position.PosiNetPrice;
floatEvent.TradingAmountNetAvg = position.PosiNetNoFeePrice;
floatEvent.TradingFeePending = position.PosiTradingFeePending * unwindData.ClosePercent;
floatEvent.TradingFee = tradeinfFee - floatEvent.TradingFeePending;
floatEvent.MarkClosePnl = markClosePnl;
floatEvent.TradingAmount = floatEvent.Quantity * floatEvent.ContractSize;
floatEvent.PositionType = position.PositionType;
floatEvent.Quantity = position.PosiQuantity;
floatEvent.PositionQty = 0;
floatEvent.ContractSize = position.ContractSize;
floatEvent.DataState = (int)SwapFlowDateStateEnum.完成;
floatEvent.InterestMode = position.InterestMode;
floatEvent.TradingAmount = unwindData.CloseQty;
floatEvent.ClientId = td.ClientId;
floatEvent.OptLog = "衡泰同步";
floatEvent.SetOpt(UserInfo);
}
unwindData.FlowEvents.Add(floatEvent);
var interestPositions = GetUnwindInterestsByHT(unwindData, td, interestAmount, fee);
unwindData.FlowEvents.AddRange(interestPositions);
CalcCloseAmount(unwindData);
DealUnwind(unwindData, td, "合约终止接口回执");
}
private List GetUnwindInterestsByHT(UnwindData unwindData, trade td, decimal interestAmount, decimal fee)
{
List interests = new List();
var allpositions = DbContext.swap_position.Where(x => x.SwapTradeId == unwindData.SwapTradeId && !x.Invalid && x.IsInitial && x.PosiDirection > 0).ToList();
var position = allpositions.Where(x => ConsTrade.InterestModels.Contains(x.InterestMode)).FirstOrDefault();
if (position == null)
{
return interests;
}
var grossPrice = allpositions.Where(x => x.PosiDirection > 0).FirstOrDefault()?.PosiGrossPrice ?? 0;
var _closePosiNotionalValue = unwindData.CloseNotionalValue;
var _posiNotionalValue = unwindData.PosiNotionalValue;
var newClosePercent = unwindData.ClosePercent;
foreach (var item in allpositions)
{
var positionClone = item.Clone();
var swapIntervalToday = position.SwapIntervalList.OrderByDescending(o => o.Date).FirstOrDefault();
if (item.InterestMode == (int)InterestModeEnum.固定值)
{
_closePosiNotionalValue = item.InterestPrincipalFix;
_posiNotionalValue = item.InterestPrincipalFix;
newClosePercent = 1m;
}
else if (item.InterestMode == (int)InterestModeEnum.标的期初全价)
{
_closePosiNotionalValue = _posiNotionalValue * grossPrice * newClosePercent;
_posiNotionalValue = _posiNotionalValue * grossPrice;
}
else if (item.InterestMode == (int)InterestModeEnum.追加预付金 || item.InterestMode == (int)InterestModeEnum.初始预付金)
{
_closePosiNotionalValue = 0;
positionClone.InterestDirection = position.InterestDirection == (int)SwapDirectionEnum.收取 ? (int)SwapDirectionEnum.支付 : (int)SwapDirectionEnum.收取;
}
decimal rate = item.InterestRateDefault;
if (swapIntervalToday != null)//当日无适用观察日
{
rate = swapIntervalToday.Rate;
}
swap_flow_event interest = new swap_flow_event();
interest.SwapTradeId = td.id;
interest.SwapTradeNo = td.TradeNumber;
interest.EventType = (int)SwapEventTypeEnum.平仓;
interest.EventReason = "衡泰同步平仓";
interest.EventDate = unwindData.ValueDate;
interest.PositionId = item.id;
interest.InterestDirection = positionClone.InterestDirection;
interest.InterestRate = rate;
interest.InterestPrincipal = _closePosiNotionalValue;
interest.InterestSwapInterval = item.InterestSwapInterval;
interest.InterestMode = item.InterestMode;
interest.FloatRate = item.FloatRate;
interest.DataState = (int)SwapFlowDateStateEnum.完成;
interest.ClientId = td.ClientId;
interest.UnwindDate = unwindData.ValueDate;
interest.PayDate = unwindData.PayDate;
if (position != null && item.id == position.id)
{
interest.InterestAmount = interestAmount;
interest.TdInterestAmount = interestAmount;
interest.InterestClosePnL = interestAmount;
interest.InterestFee = fee;
}
UpdateDbOption(interest);
interests.Add(interest);
}
return interests;
}
private void DealUnwind(UnwindData unwindData, trade td, string actionMsg = "系统操作_自动平仓")
{
int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate);
if (unwindData.SwapMarginAmount != 0)
{
AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate);
}
//if (unwindData.SwapMarginRebatePnl != 0)
//{
// AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
//}
var flowList = new List(unwindData.FlowEvents);
var eventId = SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, actionMsg);
if (unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓)
{
td.TradeStatus = "已平仓";
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
{
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, unwindData.ValueDate, 0);
}
}
else
{
td.HasPartialUnWind = 1;
}
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
td.Notional = td.TradeAmount;
DbContext.SaveChanges();
}
///
/// 计算平仓总额
///
///
private void CalcCloseAmount(UnwindData unwindData)
{
var floatPosition = unwindData.FlowEvents.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode));
var interestList = unwindData.FlowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode));
decimal floatRatio = floatPosition.PayDirection == 1 ? 1m : -1m;
var pnl = floatPosition.MarkClosePnl;
unwindData.SwapCloseAmount = pnl;
unwindData.SwapRealizedPnL = pnl;
unwindData.SwapMarginRebatePnl = 0;
unwindData.SwapMarginAmount = 0;
if (interestList != null)
{
interestList.ForEach(x =>
{
if (x.InterestMode == (int)InterestModeEnum.追加预付金 || x.InterestMode == (int)InterestModeEnum.初始预付金)
{
decimal interestRatio = x.InterestDirection == 1 ? -1m : 1m;
unwindData.SwapMarginRebatePnl += x.InterestClosePnL;
unwindData.SwapMarginAmount += x.InterestPrincipal * interestRatio;
}
unwindData.SwapRealizedPnL += x.InterestClosePnL;
unwindData.SwapCloseAmount += x.InterestClosePnL;
});
}
unwindData.SwapCloseAmount = decimal.Parse(unwindData.SwapCloseAmount.ToString("0.00"));
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
}
///
/// 多空组合平仓
///
///
///
public void SwapLongShortUnwind(UnwindData unwindData)
{
var td = DbContext.trade.Find(unwindData.SwapTradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
var trans = DbContext.Database.BeginTransaction();
try
{
int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_平仓费, unwindData.ValueDate);
if (unwindData.SwapMarginAmount != 0)
{
AddClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapMarginAmount), ClientCashInCashOut.系统操作_应付预付金, unwindData.ValueDate);
}
if (unwindData.SwapMarginRebatePnl != 0)
{
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
}
SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.平仓, clientCashId, "系统操作_平仓");
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional = 0;
td.TradeStatus = "已平仓";
DbContext.SaveChanges();
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
finally
{
trans.Dispose();
}
}
///
/// 多空组合互换
///
///
///
public void SwapLongShort(UnwindData unwindData)
{
var td = DbContext.trade.Find(unwindData.SwapTradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
var trans = DbContext.Database.BeginTransaction();
try
{
int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.互换, clientCashId, "系统操作_互换");
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
finally
{
trans.Dispose();
}
}
///
/// 互换
///
///
///
public void SwapIncome(UnwindData unwindData)
{
var td = DbContext.trade.Find(unwindData.SwapTradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId);
var trans = DbContext.Database.BeginTransaction();
bool confirm = false;
try
{
int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapRealizedPnL), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
if (unwindData.SwapMarginRebatePnl != 0)
{
AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapMarginRebatePnl), ClientCashInCashOut.系统操作_预付金返息, unwindData.ValueDate);
}
foreach (var item in unwindData.FlowEvents)
{
item.OptLog = "手工操作";
}
SaveSwapDeal(unwindData, (int)SwapEventTypeEnum.互换, clientCashId, "系统操作_互换");
if (td.ExerciseDate <= unwindData.ValueDate)
{
td.Notional = 0;
td.StockEqvNotional = 0;
td.TradeStatus = "已到期";
}
td.UnWindDate = unwindData.UnwindDate;
DbContext.SaveChanges();
trans.Commit();
confirm = true;
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
finally
{
trans.Dispose();
}
}
///
/// 互换/平仓审核通过
///
///
///
///
public void ApproveSwapTrade(trade td, int eventType)
{
Expression> eventExpression = x => x.SwapTradeId == td.id && !x.Invalid && x.EventType == eventType;
var swapEvent = DbContext.swap_event.Where(eventExpression).OrderByDescending(o => o.id).FirstOrDefault();
if (swapEvent == null)
{
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
}
swapEvent.unwindData = JsonConvert.DeserializeObject(swapEvent.EventData);
var flowList = DbContext.swap_flow_event.Where(x => x.EventId == swapEvent.id).ToList();
string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费;
int clientCashId = CloseTrade_ClientCashInCashOut(td, Convert.ToDouble(swapEvent.unwindData.SwapCloseAmount), action, swapEvent.unwindData.ValueDate);
swapEvent.ClientCashId = clientCashId;
if (td.StructureType != "多空组合")
{
if (eventType == (int)SwapEventTypeEnum.互换)
{
if (td.ExerciseDate <= swapEvent.unwindData.ValueDate)
{
td.Notional = 0;
td.StockEqvNotional = 0;
td.TradeStatus = ConsTrade.已到期;
}
else
{
td.TradeStatus = ConsTrade.确认成交;
}
}
else
{
if (swapEvent.unwindData.CloseMethod == (int)CloseMethodEnum.全部平仓)
{
td.TradeStatus = ConsTrade.已平仓;
td.Notional = 0;
td.TradeAmount = 0;
}
else
{
td.TradeStatus = ConsTrade.确认成交;
td.HasPartialUnWind = 1;
td.TradeAmount -= Convert.ToDouble(swapEvent.unwindData.CloseQty);
}
td.StockEqvNotional -= Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue);
}
td.UnWindDate = swapEvent.unwindData.UnwindDate;
UpdateInitalPosition(flowList, swapEvent.unwindData, eventType);
DbContext.SaveChanges();
}
else
{
if (eventType == (int)SwapEventTypeEnum.平仓)
{
td.UnWindDate = swapEvent.unwindData.UnwindDate;
td.StockEqvNotional = 0;
td.TradeStatus = "已平仓";
}
UpdateInitalPosition(flowList, swapEvent.unwindData, eventType);
DbContext.SaveChanges();
}
}
///
/// 互换/平仓提交审核
///
///
///
///
public void ApplySwapTrade(UnwindData unwindData, int eventType)
{
var td = DbContext.trade.Find(unwindData.SwapTradeId);
if (td == null)
{
throw new ServiceException("未找到交易信息");
}
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
var trans = DbContext.Database.BeginTransaction();
string action = eventType == (int)SwapEventTypeEnum.互换 ? ClientCashInCashOut.系统操作_互换 : ClientCashInCashOut.系统操作_平仓费;
try
{
new TradeUnwindService(this).CloseReCheck_SetTrade(unwindData.SwapTradeId, eventType == (int)SwapEventTypeEnum.互换, true);
SaveSwapDeal(unwindData, eventType, 0, action, true);
DbContext.SaveChanges();
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
finally
{
trans.Dispose();
}
}
///
/// 保存平仓/互换事件
///
///
///
private long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
{
var flowList = new List(unwindData.FlowEvents);
unwindData.FlowEvents.Clear();
string data = JsonConvert.SerializeObject(unwindData);
var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, eventType, data, clientCashId, true, eventResason);//将平仓、互换总额存入事件
foreach (var item in flowList)
{
if (item.PositionType > 0 && eventType == (int)SwapEventTypeEnum.平仓)
{
item.Quantity = unwindData.CloseQty;
item.PositionQty = unwindData.ClosePercent == 1 ? 0 : unwindData.PositionQty - unwindData.CloseQty;
}
item.PayDate = unwindData.PayDate;
item.UnwindDate = unwindData.UnwindDate;
item.EventDate = unwindData.ValueDate;
item.EventId = swapEvent.id;
DbContext.swap_flow_event.Add(item);
}
if (!approve)
{
UpdateInitalPosition(flowList, unwindData, eventType);
}
DbContext.SaveChanges();
return swapEvent.id;
}
///
/// 计算平仓数据
///
///
private void DealFloatPosition(UnwindData unwindData)
{
foreach (var item in unwindData.FlowEvents)
{
item.OptLog = "手工操作";
if (item.PositionType > 0 && item.EventType == (int)SwapEventTypeEnum.平仓)
{
decimal shortRatio = item.PositionType == (int)PositionTypeFlag.Long ? -1m : 1m;
item.TradingAmountFeeAvg = item.TradingAmountAvg + item.TradingFeePending / unwindData.CloseQty * shortRatio;
item.TradingAmountNetFeeAvg = item.TradingAmountNetAvg + item.TradingFeePending / unwindData.CloseQty * shortRatio;
}
}
}
///
/// 单标的互换更新实时持仓信息
///
///
private void UpdateInitalPosition(List flowList, UnwindData unwindData, int eventType)
{
var positions = DbContext.swap_position.Where(x => !x.IsInitial && x.SwapTradeId == unwindData.SwapTradeId && !x.Invalid);
foreach (var position in positions)
{
if (!string.IsNullOrEmpty(position.UnderlyingCode))
{
position.PosiQuantity -= unwindData.CloseQty;
position.PosiNotionalValue = position.PosiGrossPrice * position.PosiQuantity * position.ContractSize;
position.PosiTradingFee -= position.PosiTradingFee * unwindData.ClosePercent;
}
else
{
var interest = flowList.FirstOrDefault(x => x.PositionId == position.PositionId);
if (interest != null)
{
position.InterestAmount += interest.InterestAmount;
position.InterestFeePending += interest.InterestFee;
if ((interest.InterestMode == (int)InterestModeEnum.追加预付金 || interest.InterestMode == (int)InterestModeEnum.初始预付金) && eventType == (int)SwapEventTypeEnum.平仓)
{
position.InterestPrincipalFix -= interest.InterestPrincipal;
}
}
}
}
}
}
}