#EQD-5718 【缺陷转需求】-国联民生-利息端计息方式与结算规则扩充
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using MoreLinq.Extensions;
|
||||
using MoreLinq.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using System.Linq.Expressions;
|
||||
using YLErp.BLL;
|
||||
@@ -14,6 +14,11 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
public class SwapDealService : SwapTradeBaseService
|
||||
{
|
||||
protected virtual bool TryGetFloatRate(DateTime valueDate, string underlyingCode, out double rate)
|
||||
{
|
||||
return EodPriceQueryService.TryGetPrice(valueDate, underlyingCode, out rate);
|
||||
}
|
||||
|
||||
public SwapDealService(OptUserInfo optUser) : base(optUser)
|
||||
{
|
||||
|
||||
@@ -36,7 +41,7 @@ namespace YLErp.Modules.SwapModule
|
||||
bool commodity = ConsGlobal.InstrumentType.CalcTypeIsFutures(um.UnderlyingInstrumentType);
|
||||
List<int> eventTyps = new List<int>() { (int)SwapEventTypeEnum.自动互换, (int)SwapEventTypeEnum.互换 };
|
||||
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
|
||||
CheckLastEod(dealDate, td.TradeDate.Value, tradeId);
|
||||
//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();
|
||||
@@ -122,7 +127,28 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
var td = DbContext.trade.Find(tradeId);
|
||||
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
|
||||
CheckLastEod(dealDate, td.StartDate.Value, tradeId);
|
||||
//CheckLastEod(dealDate, td.StartDate.Value, tradeId);
|
||||
}
|
||||
/// <summary>
|
||||
/// 校验收益结算操作(不检查收盘限制)
|
||||
/// </summary>
|
||||
/// <param name="tradeId"></param>
|
||||
public void CheckEodTradeForIncome(int tradeId)
|
||||
{
|
||||
var td = DbContext.trade.Find(tradeId);
|
||||
// 收益结算不检查收盘限制,只检查交易状态
|
||||
if (td.TradeType != "收益互换")
|
||||
{
|
||||
throw new ServiceException("该交易不是收益互换类型");
|
||||
}
|
||||
if (td.ValidState == "InValid")
|
||||
{
|
||||
throw new ServiceException("该交易已无效");
|
||||
}
|
||||
if (td.TradeStatus != ConsTrade.确认成交 && td.TradeStatus != ConsTrade.提前终止拒绝)
|
||||
{
|
||||
throw new ServiceException($"该交易状态为【{td.TradeStatus}】,无法进行收益结算");
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 多空组合 平仓初始化
|
||||
@@ -140,7 +166,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && x.IsInitial && !x.Invalid);
|
||||
List<int> eventTyps = new List<int>() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
|
||||
var dealDate = valuedateBLL.ValueDate <= td.ExerciseDate.Value ? valuedateBLL.ValueDate : td.ExerciseDate.Value;
|
||||
CheckLastEod(dealDate, td.TradeDate.Value, tradeId);
|
||||
//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);
|
||||
@@ -194,7 +220,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||||
List<int> eventTypes = new List<int>() { (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();
|
||||
@@ -296,16 +322,17 @@ namespace YLErp.Modules.SwapModule
|
||||
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == tradeId);
|
||||
List<int> eventTypes = new List<int>() { (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<eod_swap_position> 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 orginPv = lastEod != null ? lastEod.NotionalValue : stockEqvNotional;
|
||||
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);
|
||||
var calcLastNew = tradeExtend?.ExtendObj?.InterestCalcMode?.EndsWith("1") ?? true;
|
||||
interests = GetInterests(td, tradeExtend, valueDate, unwindDate, lastEodPositions, positions, stockEqvNotional, posiLongNotionalValue, posiShortNotionalValue, posiNotionalValue, closePercent, eventType, tdClose, false, grossPrice ?? 0, orginPv, true, calcLastNew, false);
|
||||
return interests;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -348,120 +375,198 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
List<swap_flow_event> interests = new List<swap_flow_event>();
|
||||
var annualDays = tradeExtend == null ? 365 : tradeExtend.ExtendObj.AnnualDays;
|
||||
bool calcFirst = tradeExtend?.ExtendObj.InterestCalcMode?.StartsWith("1") ?? true;
|
||||
|
||||
foreach (var position in positions)
|
||||
{
|
||||
var _closePosiNotionalValue = closePosiNotionalValue;
|
||||
var _posiNotionalValue = posiNotionalValue;
|
||||
var preEodPosition = eodPositions.FirstOrDefault(x => x.PositionId == position.id);
|
||||
DateTime? preDealDate = null;
|
||||
// 初始化持仓信息
|
||||
var preEodPosition = eodPositions.FirstOrDefault(x => x.PositionId == position.id) ?? new eod_swap_position();
|
||||
var positionClone = position.Clone();
|
||||
var newClosePercent = closePrecent;
|
||||
if (preEodPosition != null)
|
||||
DateTime? preDealDate = preEodPosition.id != 0 ? preEodPosition.ValueDate : null;
|
||||
|
||||
// 计算计息区间
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
bool swap = InitInterestDate(unwindDate, preDealDate, td, tdClose, calcLast, out DateTime startDate, out DateTime endDate);
|
||||
|
||||
// 计算名义本金
|
||||
var (closePrincipal, posiPrincipal, newClosePercent) = CalcNotionalByMode(position, closePrecent, posiNotionalValue, posiLongNotionalValue, posiShortNotionalValue);
|
||||
if ((InterestModeEnum)position.InterestMode == InterestModeEnum.追加预付金 || (InterestModeEnum)position.InterestMode == InterestModeEnum.初始预付金)
|
||||
{
|
||||
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);
|
||||
// 获取利率
|
||||
decimal rate = GetFixedRate(position, startDate);
|
||||
decimal floatRate = GetFloatRate(position, preEodPosition, td.StartDate.Value, endDate, interestPeriod, swap, positionClone);
|
||||
|
||||
// 如果不需要重置,并且上一日已有 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)//当日无适用观察日
|
||||
// 根据场景计算利息
|
||||
if (settment)
|
||||
{
|
||||
var swapInterval = position.SwapIntervalList.Where(x => x.Date > startDate).OrderBy(o => o.Date).FirstOrDefault();
|
||||
if (swapInterval != null)
|
||||
{
|
||||
rate = swapInterval.Rate;
|
||||
}
|
||||
// 收盘归档场景,使用 CalcEodInterest
|
||||
interests.Add(CalcEodInterest(td, valueDate, positionClone, rate, floatRate, closePrincipal, posiPrincipal, annualDays, calcFirst, calcLast, preEodPosition, eventType, add));
|
||||
}
|
||||
else
|
||||
{
|
||||
rate = swapIntervalToday.Rate;
|
||||
// 盘中互换场景,使用 CalcUnwindInterest
|
||||
interests.Add(CalcUnwindInterest(td, valueDate, endDate, positionClone, rate, floatRate, posiPrincipal, closePrincipal, newClosePercent, annualDays, preEodPosition, eventType, add, swap, orginPv));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定条件获取 rateDate
|
||||
/// 根据计息模式计算名义本金
|
||||
/// </summary>
|
||||
private DateTime GetRateDate(int? interest_rule, DateTime startDate, DateTime endDate, int days, int interestPeriod)
|
||||
private (decimal close, decimal posi, decimal closePct) CalcNotionalByMode(swap_position position, decimal closePercent, decimal posiNotional, decimal posiLong, decimal posiShort)
|
||||
{
|
||||
// 判断是否达到重置周期
|
||||
if (days % interestPeriod == 0)
|
||||
decimal closePrincipal = posiNotional;
|
||||
decimal posiPrincipal = posiNotional;
|
||||
decimal newClosePercent = closePercent;
|
||||
|
||||
switch ((InterestModeEnum)position.InterestMode)
|
||||
{
|
||||
return QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(interest_rule ?? 0));
|
||||
case InterestModeEnum.固定值:
|
||||
closePrincipal = posiPrincipal = position.InterestPrincipalFix;
|
||||
newClosePercent = 1m;
|
||||
break;
|
||||
case InterestModeEnum.多头存续名义本金:
|
||||
closePrincipal = posiLong * closePercent;
|
||||
posiPrincipal = posiLong;
|
||||
break;
|
||||
case InterestModeEnum.空头存续名义本金:
|
||||
closePrincipal = posiShort * closePercent;
|
||||
posiPrincipal = posiShort;
|
||||
break;
|
||||
case InterestModeEnum.标的期初全价:
|
||||
closePrincipal = posiNotional * closePercent;
|
||||
break;
|
||||
case InterestModeEnum.追加预付金:
|
||||
case InterestModeEnum.初始预付金:
|
||||
closePrincipal = position.InterestPrincipalFix * closePercent;
|
||||
posiPrincipal = position.InterestPrincipalFix;
|
||||
break;
|
||||
}
|
||||
return (closePrincipal, posiPrincipal, newClosePercent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取固定利率
|
||||
/// </summary>
|
||||
private decimal GetFixedRate(swap_position position, DateTime startDate)
|
||||
{
|
||||
var swapIntervalToday = position.SwapIntervalList?.Where(x => x.Date <= startDate).OrderByDescending(o => o.Date).FirstOrDefault();
|
||||
if (swapIntervalToday != null) return swapIntervalToday.Rate;
|
||||
var nextInterval = position.SwapIntervalList?.Where(x => x.Date > startDate).OrderBy(o => o.Date).FirstOrDefault();
|
||||
return nextInterval?.Rate ?? position.InterestRateDefault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取浮动利率
|
||||
/// </summary>
|
||||
private decimal GetFloatRate(swap_position position, eod_swap_position preEod, DateTime startDate, DateTime endDate, int period, bool swap, swap_position positionClone)
|
||||
{
|
||||
if (string.IsNullOrEmpty(position.FloatRateUnderlyingCode)) return position.FloatRate;
|
||||
|
||||
int days = (endDate - startDate).Days;
|
||||
DateTime rateDate = days % period == 0
|
||||
? QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0))
|
||||
: QdpCalendarHelper.GetNonHolidayDefore(startDate.AddDays(position.interest_rule ?? 0));
|
||||
|
||||
if (preEod.id != 0 && days % period != 0)
|
||||
{
|
||||
position.FloatRate = positionClone.FloatRate = preEod.FloatRate;
|
||||
return preEod.FloatRate;
|
||||
}
|
||||
|
||||
// 如果不在重置周期内,使用 td.StartDate 来获取 rateDate
|
||||
return QdpCalendarHelper.GetNonHolidayDefore(startDate.AddDays(interest_rule ?? 0));
|
||||
if (TryGetFloatRate(rateDate, position.FloatRateUnderlyingCode, out double rate))
|
||||
{
|
||||
position.FloatRate = positionClone.FloatRate = Convert.ToDecimal(rate);
|
||||
return position.FloatRate;
|
||||
}
|
||||
if (!swap) throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{rateDate:yyyy年MM月dd日}的价格");
|
||||
return 0m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算收盘利息(EOD)
|
||||
/// </summary>
|
||||
private swap_flow_event CalcEodInterest(trade td, DateTime valueDate, swap_position position, decimal rate, decimal floatRate, decimal closePrincipal, decimal posiPrincipal, int annualDays, bool calcFirst, bool calcLast, eod_swap_position preEod, int eventType, bool add)
|
||||
{
|
||||
// 判断当日是否计息:首日不算头或到期日不算尾则不计息
|
||||
bool calcToday = true;
|
||||
if (calcFirst == false && valueDate == td.StartDate.Value) calcToday = false; // 首日不算头
|
||||
if (calcLast == false && valueDate == td.ExerciseDate.Value) calcToday = false; // 到期日不算尾
|
||||
|
||||
// 初始化EOD持仓信息
|
||||
if (preEod.id == 0)
|
||||
{
|
||||
preEod.FloatRate = floatRate;
|
||||
preEod.TdInterestPrincipal = posiPrincipal;
|
||||
preEod.PosiNotionalValue = posiPrincipal;
|
||||
}
|
||||
|
||||
// 构建利息事件
|
||||
var interest = new swap_flow_event
|
||||
{
|
||||
SwapTradeId = td.id,
|
||||
SwapTradeNo = td.TradeNumber,
|
||||
EventType = eventType,
|
||||
EventReason = "交易",
|
||||
EventDate = valueDate,
|
||||
PositionId = position.id,
|
||||
InterestDirection = position.InterestDirection,
|
||||
InterestRate = rate,
|
||||
InterestPrincipal = closePrincipal,
|
||||
InterestSwapInterval = position.InterestSwapInterval,
|
||||
InterestMode = position.InterestMode,
|
||||
FloatRate = floatRate,
|
||||
DataState = (int)SwapFlowDateStateEnum.完成,
|
||||
ClientId = td.ClientId,
|
||||
UnwindDate = valueDate
|
||||
};
|
||||
// 收盘场景使用 preEod.FloatRate(历史浮动利率),与 InitSwapDealInterest 收盘场景保持一致
|
||||
decimal eodFloatRate = preEod.id != 0 ? preEod.FloatRate : floatRate;
|
||||
decimal interestAmount = 0;
|
||||
decimal tdInterestAmount = 0;
|
||||
if (calcToday)
|
||||
{
|
||||
|
||||
if (position.InterestType == (int)InterestTypeEnum.复利)
|
||||
{
|
||||
// 复利计算
|
||||
CalcDailyCompoundInterestByEod(preEod, valueDate, td.StartDate.Value, position, closePrincipal, posiPrincipal, interest, annualDays, false, eodFloatRate, 1m, posiPrincipal, ref interestAmount, ref tdInterestAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 单利计算
|
||||
CalcDailySimpleInterestByEod(preEod, valueDate, td.StartDate.Value, position, closePrincipal, posiPrincipal, interest, annualDays, false, eodFloatRate, 1m, posiPrincipal, ref interestAmount, ref tdInterestAmount);
|
||||
}
|
||||
}
|
||||
// 四舍五入并赋值
|
||||
interest.InterestAmount = Math.Round(interestAmount, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
interest.TdInterestAmount = Math.Round(tdInterestAmount, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
// 计算InterestClosePnL(方向:收取=1为正,支付=-1为负)
|
||||
var interestRatio = position.InterestDirection == 1 ? 1m : -1m;
|
||||
interest.InterestClosePnL = interest.InterestAmount * interestRatio;
|
||||
|
||||
if (add) UpdateDbOption(interest);
|
||||
return interest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算盘中利息(平仓/互换)
|
||||
/// </summary>
|
||||
private swap_flow_event CalcUnwindInterest(trade td, DateTime valueDate, DateTime endDate, swap_position position, decimal rate, decimal floatRate, decimal posiPrincipal, decimal closePrincipal, decimal closePercent, int annualDays, eod_swap_position preEod, int eventType, bool add, bool swap, decimal orginPv)
|
||||
{
|
||||
if (preEod.id == 0)
|
||||
{
|
||||
preEod.FloatRate = floatRate;
|
||||
preEod.TdInterestPrincipal = posiPrincipal;
|
||||
preEod.PosiNotionalValue = posiPrincipal;
|
||||
preEod.ValueDate = td.TradeDate.Value;
|
||||
}
|
||||
|
||||
return InitSwapDealInterest(td, valueDate, endDate, rate, position, add, swap, posiPrincipal, closePrincipal, closePercent, annualDays, eventType, preEod, false, orginPv);
|
||||
}
|
||||
/// <summary>
|
||||
/// 初始化利息腿信息
|
||||
@@ -492,11 +597,9 @@ namespace YLErp.Modules.SwapModule
|
||||
int eventType,
|
||||
eod_swap_position preEodPosition,
|
||||
bool needPrice,
|
||||
bool settment,
|
||||
decimal orginPv
|
||||
decimal orginPv
|
||||
)
|
||||
{
|
||||
DateTime lastSwapDate = preEodPosition.ValueDate;
|
||||
decimal interestProfitSum = preEodPosition.InterestProfitSum;
|
||||
swap_flow_event interest = new swap_flow_event();
|
||||
interest.SwapTradeId = td.id;
|
||||
@@ -514,8 +617,6 @@ namespace YLErp.Modules.SwapModule
|
||||
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;
|
||||
@@ -528,43 +629,14 @@ namespace YLErp.Modules.SwapModule
|
||||
decimal InterestAmount = 0;
|
||||
decimal TdInterestAmount = 0;
|
||||
var interestRatio = position.InterestDirection == 1 ? 1m : -1m;
|
||||
var floateRate = preEodPosition.FloatRate;
|
||||
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);
|
||||
}
|
||||
CalcDailyCompoundInterest(preEodPosition, endDate, position, closePosiNotionalValue, posiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, orginPv, ref InterestAmount, ref TdInterestAmount);
|
||||
}
|
||||
else
|
||||
{
|
||||
decimal aDays = position.IsAnnualized ? annualDays : 1;
|
||||
InterestAmount = closePosiNotionalValue * (interest.InterestRate + position.FloatRate);
|
||||
TdInterestAmount = posiNotionalValue * (interest.InterestRate + position.FloatRate);
|
||||
if (settment)
|
||||
{
|
||||
InterestAmount = InterestAmount * ((decimal)itemDays / aDays);
|
||||
TdInterestAmount = TdInterestAmount * ((decimal)itemDays / aDays);
|
||||
InterestAmount = (interestProfitSum * closePrecent) + InterestAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (endDate > lastSwapDate)//日期超算情况
|
||||
{
|
||||
InterestAmount = InterestAmount * ((decimal)itemDays / aDays);
|
||||
TdInterestAmount = TdInterestAmount * ((decimal)itemDays / aDays);
|
||||
InterestAmount += (interestProfitSum * closePrecent);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterestAmount = interestProfitSum * closePrecent;
|
||||
}
|
||||
}
|
||||
|
||||
CalcDailySimpleInterest(preEodPosition, endDate, position, closePosiNotionalValue, posiNotionalValue, interest, annualDays, needPrice, floateRate, closePrecent, orginPv, ref InterestAmount, ref TdInterestAmount);
|
||||
}
|
||||
|
||||
interest.InterestAmount = Math.Round(InterestAmount, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
@@ -582,16 +654,34 @@ namespace YLErp.Modules.SwapModule
|
||||
/// </summary>
|
||||
/// <param name="lastSwapDate">上一互换日</param>
|
||||
/// <param name="endDate">结算日期</param>
|
||||
/// <param name="tradeDate">开仓日</param>
|
||||
/// <param name="floatUnderylingCode">浮动标的</param>
|
||||
/// <param name="principal">计息基数</param>
|
||||
/// <param name="interestRate">固定利率</param>
|
||||
/// <param name="isAnnualized">是否年化</param>
|
||||
/// <param name="annualDays">年化天数</param>
|
||||
/// <returns></returns>
|
||||
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)
|
||||
public void CalcDailyCompoundInterest(eod_swap_position preEodPosition, DateTime endDate, 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;
|
||||
// 复利:利息并入本金
|
||||
CalcDailyInterest(preEodPosition, endDate, position, principal, posiPrincipal, flowEvent, annualDays, needPrice, floateRate, closePercent, orginPv, compoundInterest: true, ref InterestAmount, ref TdInterestAmount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算单利 盘中(按重置天数分段,每段使用对应浮动利率)
|
||||
/// </summary>
|
||||
public void CalcDailySimpleInterest(eod_swap_position preEodPosition, DateTime endDate, 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)
|
||||
{
|
||||
// 单利:利息不并入本金
|
||||
CalcDailyInterest(preEodPosition, endDate, position, principal, posiPrincipal, flowEvent, annualDays, needPrice, floateRate, closePercent, orginPv, compoundInterest: false, ref InterestAmount, ref TdInterestAmount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用日度利息计算方法(单利/复利共用)
|
||||
/// </summary>
|
||||
/// <param name="compoundInterest">是否复利:true=利息并入本金,false=单利</param>
|
||||
private void CalcDailyInterest(eod_swap_position preEodPosition, DateTime endDate, swap_position position, decimal principal, decimal posiPrincipal, swap_flow_event flowEvent, int annualDays, bool needPrice, decimal floateRate, decimal closePercent, decimal orginPv, bool compoundInterest, ref decimal InterestAmount, ref decimal TdInterestAmount)
|
||||
{
|
||||
var startDate = position.PosiStartDate;
|
||||
decimal interestProfitSum = preEodPosition.InterestProfitSum;
|
||||
var TdInterestPrincipal = preEodPosition.TdInterestPrincipal;
|
||||
decimal interest = interestProfitSum * closePercent;
|
||||
@@ -599,21 +689,26 @@ namespace YLErp.Modules.SwapModule
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
decimal dynomicPrincipal = principal;
|
||||
decimal tdDynomicPrincipal = posiPrincipal;
|
||||
var calcDays = (endDate - lastSwapDate).Days;
|
||||
var calcDays = (endDate - startDate).Days;
|
||||
double floatRate = Convert.ToDouble(floateRate);
|
||||
for (int i = 0; i <= calcDays; i++)
|
||||
{
|
||||
var rateDate = lastSwapDate.AddDays(i);
|
||||
if (rateDate > lastSwapDate || endDate == lastSwapDate)
|
||||
var accrueDate = startDate.AddDays(i);
|
||||
if (accrueDate > preEodPosition.ValueDate)
|
||||
{
|
||||
if (i % interestPeriod == 0)
|
||||
{
|
||||
dynomicPrincipal = dynomicPrincipal + interest;
|
||||
tdDynomicPrincipal = tdDynomicPrincipal + interest;
|
||||
// 复利时:利息并入本金
|
||||
if (compoundInterest)
|
||||
{
|
||||
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))
|
||||
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(accrueDate.AddDays(position.interest_rule ?? 0));
|
||||
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
|
||||
{
|
||||
if (floatRate1 != 0)
|
||||
{
|
||||
@@ -624,7 +719,6 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
|
||||
}
|
||||
|
||||
}
|
||||
flowEvent.InterestPrincipal = tdDynomicPrincipal * closePercent;
|
||||
TdInterestPrincipal = tdDynomicPrincipal;
|
||||
@@ -645,9 +739,7 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
interest += interest1;
|
||||
tdinterest += tdinterest1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
InterestAmount = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
TdInterestAmount = Math.Round(tdinterest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
@@ -668,8 +760,8 @@ namespace YLErp.Modules.SwapModule
|
||||
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;
|
||||
decimal interest = interestProfitSum * closePercent;
|
||||
decimal tdinterest = interestProfitSum * closePercent;
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
decimal tdDynomicPrincipal = posiPrincipal;
|
||||
double floatRate = Convert.ToDouble(floateRate);
|
||||
@@ -681,7 +773,7 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
// 获取合适的 rateDate
|
||||
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0)); // 获取前一工作日;
|
||||
if (EodPriceQueryService.TryGetPrice(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
|
||||
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double floatRate1))
|
||||
{
|
||||
if (floatRate1 != 0)
|
||||
{
|
||||
@@ -721,6 +813,59 @@ namespace YLErp.Modules.SwapModule
|
||||
TdInterestAmount = Math.Round(tdinterest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算单利 收盘(按重置天数分段,每段使用对应浮动利率)
|
||||
/// </summary>
|
||||
public void CalcDailySimpleInterestByEod(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;
|
||||
int interestPeriod = position.interest_rest_days ?? 1;
|
||||
double floatRate = Convert.ToDouble(floateRate);
|
||||
var calcDays = (endDate - tradeDate).Days;
|
||||
// 修复:首次操作时(preEodPosition.id == 0),TdInterestPrincipal 需要正确初始化
|
||||
if (preEodPosition.id == 0)
|
||||
{
|
||||
preEodPosition.TdInterestPrincipal = posiPrincipal;
|
||||
}
|
||||
|
||||
// 检查是否到达重置周期
|
||||
if (calcDays % interestPeriod == 0)
|
||||
{
|
||||
// 获取新的浮动利率
|
||||
if (!string.IsNullOrEmpty(position.FloatRateUnderlyingCode))
|
||||
{
|
||||
var fr007RateDate = QdpCalendarHelper.GetNonHolidayDefore(endDate.AddDays(position.interest_rule ?? 0));
|
||||
if (TryGetFloatRate(fr007RateDate, position.FloatRateUnderlyingCode, out double newFloatRate))
|
||||
{
|
||||
if (newFloatRate != 0)
|
||||
{
|
||||
floatRate = newFloatRate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"获取不到{position.FloatRateUnderlyingCode}在{fr007RateDate:yyyy年MM月dd日}的价格");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flowEvent.FloatRate = Convert.ToDecimal(floatRate);
|
||||
var baseTdInterestPrincipal = preEodPosition.TdInterestPrincipal + posiPrincipal - orginPv;
|
||||
var baseInterestPrincipal = baseTdInterestPrincipal * closePercent;
|
||||
|
||||
// 修复:正确计算本次利息(基于实际持仓本金)
|
||||
decimal interest = baseInterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
|
||||
decimal tdinterest = baseTdInterestPrincipal * (flowEvent.InterestRate + Convert.ToDecimal(floatRate));
|
||||
if (position.IsAnnualized)
|
||||
{
|
||||
interest /= annualDays;
|
||||
tdinterest /= annualDays;
|
||||
}
|
||||
|
||||
InterestAmount = Math.Round(interest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
TdInterestAmount = Math.Round(tdinterest, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单标的平仓
|
||||
/// </summary>
|
||||
@@ -733,7 +878,7 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId);
|
||||
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
bool cofirm = false;
|
||||
try
|
||||
@@ -1232,7 +1377,7 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new ServiceException("未找到交易信息");
|
||||
}
|
||||
CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId);
|
||||
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
|
||||
var trans = DbContext.Database.BeginTransaction();
|
||||
bool confirm = false;
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user