#EQD-5718 【缺陷转需求】-国联民生-利息端计息方式与结算规则扩充
This commit is contained in:
@@ -60,6 +60,18 @@ namespace YLErp.DBModels
|
||||
[DisplayName("观察起始日")]
|
||||
[Column("observation_start")]
|
||||
public DateTime? ObservationStart { get; set; }
|
||||
/// <summary>
|
||||
/// 交易日历
|
||||
/// </summary>
|
||||
[DisplayName("交易日历")]
|
||||
[Column("observation_calendar")]
|
||||
public string ObservationCalendar { get; set; }
|
||||
/// <summary>
|
||||
/// 结算规则
|
||||
/// </summary>
|
||||
[DisplayName("结算规则")]
|
||||
[Column("observation_settlement_rules")]
|
||||
public int? ObservationSettlementRules { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 互换观察日集合
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -20,5 +20,9 @@ namespace YLErp.Models
|
||||
/// 是否结算 0:否 1:是
|
||||
/// </summary>
|
||||
public int Settlement { get; set; }
|
||||
/// <summary>
|
||||
/// 结算日期(观察日不一定等于结算日)
|
||||
/// </summary>
|
||||
public DateTime? SettlementDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -136,7 +136,15 @@ namespace YLErp.Modules.SwapModule
|
||||
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);
|
||||
//获取自动互换的 interval 信息,用于确定结算日期
|
||||
IntervalModel autoInterval = null;
|
||||
foreach (var interest in interestList)
|
||||
{
|
||||
autoInterval = interest.SwapIntervalList.FirstOrDefault(x => x.Date == settleDate && x.Settlement == 1);
|
||||
if (autoInterval != null)
|
||||
break;
|
||||
}
|
||||
DealAutoInterests(autoInterests, td, settleDate, preDealDate, posiLongNotional + posiShortNotional, autoInterval);
|
||||
//多空组合判断是否已到到期日且无持仓信息
|
||||
if (longShort && td.ExerciseDate.Value == settleDate && allPositionQty == 0)
|
||||
{
|
||||
@@ -361,7 +369,8 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <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)
|
||||
/// <param name="interval">自动互换观察日信息,用于获取结算日期</param>
|
||||
private void DealAutoInterests(List<swap_flow_event> autoInterests, trade td, DateTime settleDate, DateTime? preDealDate, decimal StockEqvNotional, IntervalModel interval)
|
||||
{
|
||||
if (autoInterests.Count == 0)
|
||||
{
|
||||
@@ -387,17 +396,20 @@ namespace YLErp.Modules.SwapModule
|
||||
unwindData.SwapCloseAmount = unwindData.SwapCloseAmount + x.InterestClosePnL;
|
||||
unwindData.SwapRealizedPnL = unwindData.SwapCloseAmount;
|
||||
});
|
||||
SaveAutoSwapDeal(td, autoInterests, unwindData);
|
||||
SaveAutoSwapDeal(td, autoInterests, unwindData, interval);
|
||||
}
|
||||
/// <summary>
|
||||
/// 保存自动互换数据信息
|
||||
/// </summary>
|
||||
/// <param name="td"></param>
|
||||
/// <param name="swap_Deal"></param>
|
||||
private long SaveAutoSwapDeal(trade td, List<swap_flow_event> flowEvents, UnwindData unwindData)
|
||||
/// <param name="interval">自动互换观察日信息,用于获取结算日期</param>
|
||||
private long SaveAutoSwapDeal(trade td, List<swap_flow_event> flowEvents, UnwindData unwindData, IntervalModel interval)
|
||||
{
|
||||
//td.UnWindDate = unwindData.ValueDate;
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, unwindData.ValueDate);
|
||||
//优先使用 interval.SettlementDate 作为资金记录发生日期,如果没有则使用 ValueDate
|
||||
var cashHappenDate = interval?.SettlementDate ?? unwindData.ValueDate;
|
||||
int clientCashId = AddClientCashInCashOut(td, Convert.ToDouble(-unwindData.SwapCloseAmount), ClientCashInCashOut.系统操作_互换, cashHappenDate);
|
||||
string data = JsonConvert.SerializeObject(unwindData);
|
||||
var swapEvent = new SwapEventService(this).AddSwapEventDate(unwindData.ValueDate, unwindData.SwapTradeId, (int)SwapEventTypeEnum.自动互换, data, clientCashId, true, "系统操作-自动互换");//将互换总额存入事件
|
||||
flowEvents.ForEach(x =>
|
||||
@@ -777,7 +789,7 @@ namespace YLErp.Modules.SwapModule
|
||||
positions.Add(position);
|
||||
List<eod_swap_position> preEodPositions = new List<eod_swap_position>();
|
||||
preEodPositions.Add(eodPayPosition);
|
||||
if (position.InterestMode == (int)InterestModeEnum.固定值)
|
||||
if (position.InterestMode == (int)InterestModeEnum.固定值||position.InterestMode == (int)InterestModeEnum.初始预付金 || position.InterestMode == (int)InterestModeEnum.追加预付金)
|
||||
{
|
||||
orginPv = eodPayPosition.InterestPrincipalFix;
|
||||
}
|
||||
@@ -872,7 +884,7 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition = eodPayPosition.Clone();
|
||||
newEodPayPosition.id = 0;
|
||||
}
|
||||
if (position.InterestMode == (int)InterestModeEnum.固定值)
|
||||
if (position.InterestMode == (int)InterestModeEnum.固定值 || position.InterestMode == (int)InterestModeEnum.初始预付金 || position.InterestMode == (int)InterestModeEnum.追加预付金)
|
||||
{
|
||||
orginPv = eodPayPosition.InterestPrincipalFix;
|
||||
}
|
||||
@@ -911,7 +923,6 @@ namespace YLErp.Modules.SwapModule
|
||||
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;
|
||||
//利息端估值用信息
|
||||
@@ -1013,7 +1024,7 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.id = 0;
|
||||
newEodPayPosition.PositionId = position.id;
|
||||
}
|
||||
if (position.InterestMode == (int)InterestModeEnum.固定值)
|
||||
if (position.InterestMode == (int)InterestModeEnum.固定值 || position.InterestMode == (int)InterestModeEnum.初始预付金 || position.InterestMode == (int)InterestModeEnum.追加预付金)
|
||||
{
|
||||
orginPv = eodPayPosition.InterestPrincipalFix;
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="interestMode">计息方式</param>
|
||||
/// <param name="interestStart">计息开始日期</param>
|
||||
/// <param name="interestEnd">计息结束日期</param>
|
||||
public bool InitInterestDate(DateTime valueDate, DateTime? preSettleDate, trade td, bool tdClose,bool calcLastNew,out DateTime interestStart, out DateTime interestEnd)
|
||||
public bool InitInterestDate(DateTime valueDate, DateTime? preSettleDate, trade td, bool tdClose,bool calcLastNew, out DateTime interestStart, out DateTime interestEnd)
|
||||
{
|
||||
interestStart = td.StartDate.Value;
|
||||
var exerciseDate = td.ExerciseDate.Value;
|
||||
@@ -382,6 +382,19 @@ namespace YLErp.Modules.SwapModule
|
||||
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate >= valueDate).ToList();
|
||||
DbContext.eod_swap_position.RemoveRange(eodSwapPositions);
|
||||
DbContext.swap_flow_event.RemoveRange(swapFlowEvents);
|
||||
|
||||
// 删除自动互换产生的资金记录(client_cash_in_out)
|
||||
var swapEventIds = swapEvents.Select(s => s.id).ToList();
|
||||
if (swapEventIds.Any())
|
||||
{
|
||||
// 通过 swap_event 的 ClientCashId 删除对应的资金记录
|
||||
var clientCashIds = swapEvents.Where(s => s.ClientCashId > 0).Select(s => s.ClientCashId).ToList();
|
||||
if (clientCashIds.Any())
|
||||
{
|
||||
var clientCashRecords = DbContext.ClientCashInCashOut.Where(x => clientCashIds.Contains(x.id)).ToList();
|
||||
DbContext.ClientCashInCashOut.RemoveRange(clientCashRecords);
|
||||
}
|
||||
}
|
||||
}
|
||||
DbContext.swap_event.RemoveRange(swapEvents);
|
||||
DbContext.eod_swap.RemoveRange(eodSwaps);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Qdp.Foundation.Implementations;
|
||||
using Qdp.Foundation.Implementations;
|
||||
using Qdp.Pricing.Base.Enums;
|
||||
using Qdp.Pricing.Base.Implementations;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -220,5 +220,199 @@ namespace YLErp.QdpModule
|
||||
var monthlyDates = GetDefaultKoObservationDatesForSnowbal(startDate, endDate);
|
||||
return monthlyDates.Select(x => (Date)x).ToArray();
|
||||
}
|
||||
|
||||
public static DateTime[] GetDatesWithFixedTerm(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
string termStr,
|
||||
string calendarStr,
|
||||
BusinessDayConvention bdc = BusinessDayConvention.None,
|
||||
bool alignEnd = false,
|
||||
string calcMode = "01")
|
||||
{
|
||||
if (!Term.IsTerm(termStr))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var calendarName = string.IsNullOrEmpty(calendarStr) ? "chn" : calendarStr.ToLower();
|
||||
return GetDatesWithFixedTerm(startDate, endDate, new Term(termStr), calendarName, bdc, alignEnd, calcMode);
|
||||
}
|
||||
|
||||
public static DateTime[] GetDatesWithFixedTerm(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
Term term,
|
||||
string calendarStr,
|
||||
BusinessDayConvention bdc = BusinessDayConvention.None,
|
||||
bool alignEnd = false,
|
||||
string calcMode = "01")
|
||||
{
|
||||
var calendarName = string.IsNullOrEmpty(calendarStr) ? "chn" : calendarStr.ToLower();
|
||||
return alignEnd
|
||||
? GetDatesWithFixedTermStartAlignEnd(startDate, endDate, term, calendarName, bdc, calcMode)
|
||||
: GetDatesWithFixedTermStartAlignStart(startDate, endDate, term, calendarName, bdc, calcMode);
|
||||
}
|
||||
|
||||
private static DateTime[] GetDatesWithFixedTermStartAlignEnd(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
Term term,
|
||||
string calendarName,
|
||||
BusinessDayConvention bdc = BusinessDayConvention.None,
|
||||
string calcMode = "01")
|
||||
{
|
||||
var qdpStart = new Date(startDate);
|
||||
var qdpEnd = new Date(endDate);
|
||||
var dates = new List<Date>();
|
||||
var calendar = CalendarImpl.Get(calendarName);
|
||||
|
||||
if (calcMode == "11")
|
||||
{
|
||||
while (qdpEnd >= qdpStart)
|
||||
{
|
||||
dates.Add(qdpEnd);
|
||||
qdpEnd = term.Prev(qdpEnd);
|
||||
}
|
||||
}
|
||||
else if (calcMode == "10")
|
||||
{
|
||||
qdpEnd = term.Prev(qdpEnd);
|
||||
while (qdpEnd >= qdpStart)
|
||||
{
|
||||
dates.Add(qdpEnd);
|
||||
qdpEnd = term.Prev(qdpEnd);
|
||||
}
|
||||
}
|
||||
else if (calcMode == "01")
|
||||
{
|
||||
while (qdpEnd > qdpStart)
|
||||
{
|
||||
dates.Add(qdpEnd);
|
||||
qdpEnd = term.Prev(qdpEnd);
|
||||
}
|
||||
}
|
||||
else if (calcMode == "00")
|
||||
{
|
||||
qdpEnd = term.Prev(qdpEnd);
|
||||
while (qdpEnd > qdpStart)
|
||||
{
|
||||
dates.Add(qdpEnd);
|
||||
qdpEnd = term.Prev(qdpEnd);
|
||||
}
|
||||
}
|
||||
|
||||
if (dates.Count == 0)
|
||||
{
|
||||
dates.Add(new Date(endDate));
|
||||
}
|
||||
|
||||
dates = dates.Select(d => calendar.Adjust(d, bdc)).Distinct().ToList();
|
||||
dates.Reverse();
|
||||
return dates.Select(x => x.DateTime).ToArray();
|
||||
}
|
||||
|
||||
private static DateTime[] GetDatesWithFixedTermStartAlignStart(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
Term term,
|
||||
string calendarName,
|
||||
BusinessDayConvention bdc = BusinessDayConvention.None,
|
||||
string calcMode = "01")
|
||||
{
|
||||
var qdpStart = new Date(startDate);
|
||||
var qdpEnd = new Date(endDate);
|
||||
var dates = new List<Date>();
|
||||
var calendar = CalendarImpl.Get(calendarName);
|
||||
|
||||
if (calcMode == "11")
|
||||
{
|
||||
while (qdpStart <= qdpEnd)
|
||||
{
|
||||
dates.Add(qdpStart);
|
||||
qdpStart = term.Next(qdpStart);
|
||||
}
|
||||
}
|
||||
else if (calcMode == "10")
|
||||
{
|
||||
while (qdpStart < qdpEnd)
|
||||
{
|
||||
dates.Add(qdpStart);
|
||||
qdpStart = term.Next(qdpStart);
|
||||
}
|
||||
}
|
||||
else if (calcMode == "01")
|
||||
{
|
||||
qdpStart = term.Next(qdpStart);
|
||||
while (qdpStart <= qdpEnd)
|
||||
{
|
||||
dates.Add(qdpStart);
|
||||
qdpStart = term.Next(qdpStart);
|
||||
}
|
||||
}
|
||||
else if (calcMode == "00")
|
||||
{
|
||||
qdpStart = term.Next(qdpStart);
|
||||
while (qdpStart < qdpEnd)
|
||||
{
|
||||
dates.Add(qdpStart);
|
||||
qdpStart = term.Next(qdpStart);
|
||||
}
|
||||
}
|
||||
|
||||
dates = dates.Select(d => calendar.Adjust(d, bdc)).Distinct().ToList();
|
||||
|
||||
if (dates.Count == 0)
|
||||
{
|
||||
dates.Add(qdpEnd);
|
||||
}
|
||||
|
||||
return dates.Select(x => x.DateTime).ToArray();
|
||||
}
|
||||
|
||||
public static ObservationSettleDateResult[] GetObservationAndSettleDates(
|
||||
DateTime startDate,
|
||||
DateTime endDate,
|
||||
string termStr,
|
||||
string calendarStr,
|
||||
int settlementRules,
|
||||
BusinessDayConvention bdc = BusinessDayConvention.None,
|
||||
bool alignEnd = false,
|
||||
string calcMode = "01")
|
||||
{
|
||||
if (!Term.IsTerm(termStr))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var calendarName = string.IsNullOrEmpty(calendarStr) ? "chn" : calendarStr.ToLower();
|
||||
var calendar = CalendarImpl.Get(calendarName);
|
||||
var term = new Term(termStr);
|
||||
|
||||
DateTime[] observationDates = alignEnd
|
||||
? GetDatesWithFixedTermStartAlignEnd(startDate, endDate, term, calendarName, bdc, calcMode)
|
||||
: GetDatesWithFixedTermStartAlignStart(startDate, endDate, term, calendarName, bdc, calcMode);
|
||||
|
||||
var results = new List<ObservationSettleDateResult>();
|
||||
foreach (var obsDate in observationDates)
|
||||
{
|
||||
var settleDate = obsDate.AddDays(settlementRules);
|
||||
settleDate = calendar.Adjust(new Date(settleDate), bdc).DateTime;
|
||||
|
||||
results.Add(new ObservationSettleDateResult
|
||||
{
|
||||
ObservationDate = obsDate,
|
||||
SettleDate = settleDate
|
||||
});
|
||||
}
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public class ObservationSettleDateResult
|
||||
{
|
||||
public DateTime ObservationDate { get; set; }
|
||||
public DateTime SettleDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,17 @@ namespace YLErp.Web.Controllers
|
||||
return JsonSuccess("");
|
||||
}
|
||||
/// <summary>
|
||||
/// 校验收益结算操作(不检查收盘限制)
|
||||
/// </summary>
|
||||
/// <param name="enid"></param>
|
||||
/// <returns></returns>
|
||||
public JsonResult CheckEodTradeForIncome(string enid)
|
||||
{
|
||||
var intid = DecryptInt(enid);
|
||||
new SwapDealService(CurUser).CheckEodTradeForIncome(intid);
|
||||
return JsonSuccess("");
|
||||
}
|
||||
/// <summary>
|
||||
/// 收益互换 平仓
|
||||
/// </summary>
|
||||
/// <param name="enid"></param>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using iTextSharp.text;
|
||||
using iTextSharp.text;
|
||||
using iTextSharp.text.pdf;
|
||||
using NPOI.POIFS.Crypt;
|
||||
using Qdp.Pricing.Base.Enums;
|
||||
@@ -3456,6 +3456,14 @@ namespace YLErp.Web.Controllers
|
||||
return JsonSuccess("", QdpObservationHelper.GetDatesWithFixedTerm(req.startDate, req.endDate, req.termStr, bdc, req.alignEnd, req.calcMode));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public JsonResult GetSwapObservationDateList(GetSwapObservationDateRequest req)
|
||||
{
|
||||
var bdc = (BusinessDayConvention)Enum.Parse(typeof(BusinessDayConvention), req.holidayAdjustment);
|
||||
var results = QdpObservationHelper.GetObservationAndSettleDates(req.startDate, req.endDate, req.termStr, req.calendar, req.settlementRules, bdc, req.alignEnd, req.calcMode);
|
||||
return JsonSuccess("", results);
|
||||
}
|
||||
|
||||
public ActionResult StructureList(string structure, string CalcId, bool onlyshow = false)
|
||||
{
|
||||
var ret = new DZStructureService(CurUser).getStructureList(structure);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace YLErp.Web.Models
|
||||
{
|
||||
public class GetSwapObservationDateRequest
|
||||
{
|
||||
public DateTime startDate { get; set; }
|
||||
|
||||
public DateTime endDate { get; set; }
|
||||
|
||||
public string termStr { get; set; }
|
||||
|
||||
public string holidayAdjustment { get; set; }
|
||||
|
||||
public bool alignEnd { get; set; }
|
||||
|
||||
public string calcMode { get; set; }
|
||||
|
||||
public string calendar { get; set; }
|
||||
|
||||
public int settlementRules { get; set; }
|
||||
}
|
||||
|
||||
public class SwapObservationDateResult
|
||||
{
|
||||
public DateTime ObservationDate { get; set; }
|
||||
|
||||
public DateTime SettleDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@using YLErp.Web.Models.JsModels;
|
||||
@using YLErp.Web.Models.JsModels;
|
||||
@model trade
|
||||
@{
|
||||
ViewBag.Title = "交易信息 | 编辑";
|
||||
@@ -294,7 +294,7 @@
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" v-on:click="initObservationDates(item,1)">设置观察日</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" v-on:click="initObservationDates(item,0)">设置观察日</button>
|
||||
</td>
|
||||
<td>
|
||||
<a href="javascript:void(0);" title="删除" v-on:click="deleteMarginSwapRate(item.index)"><span class="glyphicon glyphicon-minus" style="color:#ff0000"></span></a>
|
||||
@@ -363,7 +363,7 @@
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<vue-number-input v-model="item.interest_rest_days" style="width:70px;" v-bind:format="inputFormatInteger" :disabled="item.InterestType==0"></vue-number-input>
|
||||
<vue-number-input v-model="item.interest_rest_days" style="width:70px;" v-bind:format="inputFormatInteger"></vue-number-input>
|
||||
</td>
|
||||
<td>
|
||||
<select v-model="item.interest_rule" :disabled="item.FloatRateUnderlyingCode=='--'||item.FloatRateUnderlyingCode==''||item.FloatRateUnderlyingCode==null">
|
||||
@@ -372,7 +372,7 @@
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" v-on:click="initObservationDates(item,0)">设置观察日</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" v-on:click="initObservationDates(item,1)">设置观察日</button>
|
||||
</td>
|
||||
<td>
|
||||
<a href="javascript:void(0);" title="删除" v-on:click="deleteSwapRate(item.index)"><span class="glyphicon glyphicon-minus" style="color:#ff0000"></span></a>
|
||||
@@ -512,7 +512,22 @@
|
||||
<option v-for="item in page.timeUnits" :value="item.Value">{{item.Text}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12"></div>
|
||||
<div class="col-6 mb-2" v-show="observationType==1">
|
||||
<span>交易日历</span>
|
||||
<select id="ObservationCalendar" style="width: 126px;margin-left:10px" v-model="observation.ObservationCalendar">
|
||||
<option value="Chn">系统日历</option>
|
||||
<option value="IB">银行间日历</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6 mb-2" v-show="observationType==1">
|
||||
<span>结算规则</span>
|
||||
<select id="ObservationSettlementRules" style="width: 126px;margin-left:13px" v-model="observation.ObservationSettlementRules">
|
||||
<option value=0>T+0</option>
|
||||
<option value=1>T+1</option>
|
||||
<option value=2>T+2</option>
|
||||
<option value=3>T+3</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-6 mb-2">
|
||||
<span>节假日调整</span>
|
||||
<select id="ObservationHolidayType" style="width:120px" v-model="observation.ObservationHolidayType">
|
||||
@@ -538,12 +553,20 @@
|
||||
<div class="div-group">
|
||||
<div id="divItems" class="searchdiv" style="text-align: center;padding-left: 0px;padding-right: 0px;margin: 0px;">
|
||||
<ol id="ol" style="text-align:left;">
|
||||
<span style="min-width:100px;display: inline-block;">日期</span>
|
||||
<span style="display: inline-block;">互换利率</span>
|
||||
<span style="min-width:100px;display: inline-block;">观察日期</span>
|
||||
<span v-show="observationType==1" style="min-width:100px;display: inline-block;">结算日期</span>
|
||||
<span style="min-width:120px;display: inline-block;">互换利率</span>
|
||||
<span style="display: inline-block;"><input type="checkbox" id="checkAllclose" v-on:click="checkedAll()" :checked="observation.CheckedAll">是否结算</span>
|
||||
<li v-for="item in observation.ObservationDataList">
|
||||
<input type="text" class="field1" style="min-width:86px;margin-right:1rem;" v-model="item.date">
|
||||
<input type="text" class="field2" style="" v-model="item.val">%
|
||||
<input v-show="observationType==1" type="text" class="field3" style="min-width:86px;margin-right:1rem;" v-model="item.SettlementDate" readonly>
|
||||
<template v-if="observationType==1">
|
||||
<span style="display:inline-block;text-align:center;margin-right:2px;">{{item.floatRateCode}}</span>+
|
||||
<input type="text" class="field4" style="width:50px;" v-model="item.val">%
|
||||
</template>
|
||||
<template v-else>
|
||||
<input type="text" class="field2" style="" v-model="item.val">%
|
||||
</template>
|
||||
<input type="checkbox" name="checkclose" class="field5" v-model="item.itemChecked" v-on:click="checkedItem(item)" :disabled="item.disabled">
|
||||
<a href="#" class="delete" v-on:click="deleteObItem(item)">
|
||||
<span class="glyphicon glyphicon-remove"></span>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@using YLErp.Enums;
|
||||
@using YLErp.Enums;
|
||||
@model TradeViewModel
|
||||
|
||||
@{
|
||||
@@ -265,7 +265,7 @@
|
||||
<td>@item.InterestRateDefault.OtcFormat(OtcFormatFlag.marginRateP)</td>
|
||||
<td>@(item.IsAnnualized ? "是" : "否")</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" onclick="showSwapRate('@(item.InterestSwapInterval)')" style="height:22px;">查看</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" onclick="showSwapRate('@(item.InterestSwapInterval)', true)" style="height:22px;">查看</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -337,7 +337,7 @@
|
||||
<td>@item.interest_rest_days</td>
|
||||
<td>@((item.interest_rule != null) ? (SwapInterestRule)item.interest_rule : "")</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" onclick="showSwapRate('@(item.InterestSwapInterval)')" style="height:22px;">查看</button>
|
||||
<button class="btn btn-sm btn-outline-danger" type="button" onclick="showSwapRate('@(item.InterestSwapInterval)', false)" style="height:22px;">查看</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -1013,7 +1013,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<td> 观察日期 </td>
|
||||
<td> 互换利率 </td>
|
||||
<td v-if="!isMarginLeg"> 结算日期 </td>
|
||||
<td> 互换利率</td>
|
||||
<td> 是否结算 </td>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1021,7 +1022,11 @@
|
||||
<tbody>
|
||||
<tr v-for="item in SwapIntervalList">
|
||||
<td>{{formatDate(item.Date)}}</td>
|
||||
<td>{{item.Rate}}</td>
|
||||
<td v-if="!isMarginLeg">{{formatDate(item.SettlementDate)}}</td>
|
||||
<td>
|
||||
<span v-if="item.FloatRateCode">{{item.FloatRateCode}}+</span>
|
||||
<span>{{item.Rate}}</span>
|
||||
</td>
|
||||
<td>{{item.Settlement?"是":"否"}}</td>
|
||||
|
||||
</tr>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//otcformat禁止千分位分组
|
||||
//otcformat禁止千分位分组
|
||||
window.otcformat.options.disableGrouping = true;
|
||||
|
||||
const consClients = ylotc.clients;
|
||||
@@ -126,6 +126,8 @@ const vue = new Vue({
|
||||
ObservationUnit: 'D',
|
||||
ObservationHolidayType: 'Following',
|
||||
ObservationAlignEnd: true,
|
||||
ObservationCalendar: 'Chn',
|
||||
ObservationSettlementRules: 0,
|
||||
DefaultTitle1Value: 0.1,
|
||||
ObservationDataList: [],
|
||||
CheckedAll: false,
|
||||
@@ -344,7 +346,7 @@ const vue = new Vue({
|
||||
}
|
||||
x.SwapIntervalList.push(interval);
|
||||
}
|
||||
if (x.InterestType == 1 && x.interest_rest_days<=0) {
|
||||
if (x.interest_rest_days != null && x.interest_rest_days <= 0) {
|
||||
main.message("利息端第" + (index + 1) + "行重置频率必须大于0");
|
||||
errorcount++;
|
||||
return false;
|
||||
@@ -461,9 +463,7 @@ const vue = new Vue({
|
||||
}
|
||||
},
|
||||
changeInterestType(item) {
|
||||
if (item.InterestType == 0) {
|
||||
item.interest_rest_days = null;
|
||||
} else {
|
||||
if (!item.interest_rest_days) {
|
||||
item.interest_rest_days = 1;
|
||||
}
|
||||
},
|
||||
@@ -553,7 +553,7 @@ const vue = new Vue({
|
||||
arr[index].Rate = swapRate;
|
||||
});
|
||||
var observationDates = JSON.stringify(item.SwapIntervalList);
|
||||
item.SwapIntervals = observationDates;
|
||||
item.InterestSwapInterval = observationDates;
|
||||
|
||||
});
|
||||
} else {
|
||||
@@ -582,6 +582,10 @@ const vue = new Vue({
|
||||
thisObj.observation.ObservationUnit = observation ? observation.ObservationUnit : 'D';
|
||||
thisObj.observation.ObservationHolidayType = observation ? observation.ObservationHolidayType : 'Following';
|
||||
thisObj.observation.ObservationAlignEnd = observation ? observation.ObservationAlignEnd : true;
|
||||
if (type == 1) {
|
||||
thisObj.observation.ObservationCalendar = observation ? observation.ObservationCalendar : 'Chn';
|
||||
thisObj.observation.ObservationSettlementRules = observation ? observation.ObservationSettlementRules : 0;
|
||||
}
|
||||
thisObj.observation.IsDeductPrincipal = observation ? observation.IsDeductPrincipal : true;
|
||||
if (type == 0) {
|
||||
thisObj.observation.IsDeductPrincipal = false;
|
||||
@@ -603,6 +607,7 @@ const vue = new Vue({
|
||||
thisObj.observation.ObservationStart = endTime;
|
||||
}
|
||||
thisObj.observation.ObservationDataList = [];
|
||||
var floatRateCode = type == 1 ? (item.FloatRateUnderlyingCode || '--') : '';
|
||||
thisObj.observation.IntervalList.forEach((value, num, arr) => {
|
||||
var val = value.Rate;
|
||||
var _date = "";
|
||||
@@ -616,24 +621,47 @@ const vue = new Vue({
|
||||
disabled = true;
|
||||
itemChecked = false;
|
||||
}
|
||||
var obdate = {
|
||||
date: _date,
|
||||
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
var obdate = {};
|
||||
if (type == 1) {
|
||||
var SettlementDate = value.Date;
|
||||
if (value.SettlementDate!=null) {
|
||||
SettlementDate = value.SettlementDate;
|
||||
}
|
||||
obdate = {
|
||||
date: _date,
|
||||
SettlementDate: _date,
|
||||
floatRateCode: floatRateCode,
|
||||
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
};
|
||||
} else {
|
||||
obdate = {
|
||||
date: _date,
|
||||
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
};
|
||||
}
|
||||
thisObj.observation.ObservationDataList.push(obdate);
|
||||
});
|
||||
// thisObj.initObservationCheckedAll();
|
||||
var area = type == 1 ? ['800px', '600px'] : ['750px', '600px'];
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: ['580px', '560px'],
|
||||
area: area,
|
||||
title: "设置互换日期",
|
||||
shadeClose: false,
|
||||
shade: 0.4,
|
||||
content: $("#observationInfosEdit")
|
||||
});
|
||||
},
|
||||
//计算结算日期
|
||||
calcSettleDate(observationDate, settlementRules) {
|
||||
if (!observationDate) return "";
|
||||
var m = new moment(observationDate);
|
||||
var settleDate = m.add(settlementRules, 'days').format("YYYY-MM-DD");
|
||||
return settleDate;
|
||||
},
|
||||
//生成观察日操作
|
||||
GetObservationDates() {
|
||||
var thisObj = this;
|
||||
@@ -641,43 +669,97 @@ const vue = new Vue({
|
||||
var observationUnit = thisObj.observation.ObservationUnit;
|
||||
var observationHolidayType = thisObj.observation.ObservationHolidayType;
|
||||
var alignEnd = thisObj.observation.ObservationAlignEnd;
|
||||
var postData = {
|
||||
startDate: thisObj.observation.ObservationStart, endDate: thisObj.trade.ExerciseDate, termStr: observationNum + observationUnit,
|
||||
holidayAdjustment: observationHolidayType, alignEnd: alignEnd, calcMode: thisObj.trade.trade_swap.RateCalcMode
|
||||
};
|
||||
var settlementRules = thisObj.observation.ObservationSettlementRules;
|
||||
var calendar = thisObj.observation.ObservationCalendar;
|
||||
var floatRateCode = this.observationType == 1 ? (thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--') : '';
|
||||
|
||||
thisObj.observation.ObservationDataList = [];
|
||||
main.post("/trade/GetObservationDateList", postData).done(
|
||||
function (res) {
|
||||
$.each(res.obj, function (i) {
|
||||
var _date = "";
|
||||
var m = new moment(this);
|
||||
if (!isNaN(m.date())) {
|
||||
_date = m.format("YYYY-MM-DD");
|
||||
}
|
||||
var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value));
|
||||
var itemChecked = true;
|
||||
var disabled = false;
|
||||
if (_date == thisObj.trade.ExerciseDate) {
|
||||
disabled = true;
|
||||
itemChecked = false;
|
||||
}
|
||||
var obdate = {
|
||||
date: _date,
|
||||
val: val,
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
}
|
||||
thisObj.observation.ObservationDataList.push(obdate);
|
||||
|
||||
if (this.observationType == 1) {
|
||||
var postData = {
|
||||
startDate: thisObj.observation.ObservationStart,
|
||||
endDate: thisObj.trade.ExerciseDate,
|
||||
termStr: observationNum + observationUnit,
|
||||
holidayAdjustment: observationHolidayType,
|
||||
alignEnd: alignEnd,
|
||||
calcMode: thisObj.trade.trade_swap.RateCalcMode,
|
||||
calendar: calendar,
|
||||
settlementRules: settlementRules
|
||||
};
|
||||
main.post("/trade/GetSwapObservationDateList", postData).done(
|
||||
function (res) {
|
||||
$.each(res.obj, function (i) {
|
||||
var _date = "";
|
||||
var m = new moment(this.ObservationDate);
|
||||
if (!isNaN(m.date())) {
|
||||
_date = m.format("YYYY-MM-DD");
|
||||
}
|
||||
var _settleDate = "";
|
||||
var sm = new moment(this.SettleDate);
|
||||
if (!isNaN(sm.date())) {
|
||||
_settleDate = sm.format("YYYY-MM-DD");
|
||||
}
|
||||
var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value));
|
||||
var itemChecked = true;
|
||||
var disabled = false;
|
||||
if (_date == thisObj.trade.ExerciseDate) {
|
||||
disabled = true;
|
||||
itemChecked = false;
|
||||
}
|
||||
var obdate = {
|
||||
date: _date,
|
||||
SettlementDate: _settleDate,
|
||||
floatRateCode: floatRateCode,
|
||||
val: val,
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
};
|
||||
thisObj.observation.ObservationDataList.push(obdate);
|
||||
});
|
||||
thisObj.initObservationCheckedAll();
|
||||
});
|
||||
thisObj.initObservationCheckedAll();
|
||||
});
|
||||
} else {
|
||||
var postData = {
|
||||
startDate: thisObj.observation.ObservationStart,
|
||||
endDate: thisObj.trade.ExerciseDate,
|
||||
termStr: observationNum + observationUnit,
|
||||
holidayAdjustment: observationHolidayType,
|
||||
alignEnd: alignEnd,
|
||||
calcMode: thisObj.trade.trade_swap.RateCalcMode
|
||||
};
|
||||
main.post("/trade/GetObservationDateList", postData).done(
|
||||
function (res) {
|
||||
$.each(res.obj, function (i) {
|
||||
var _date = "";
|
||||
var m = new moment(this);
|
||||
if (!isNaN(m.date())) {
|
||||
_date = m.format("YYYY-MM-DD");
|
||||
}
|
||||
var val = parseFloat(consNumberFormat.umpriceP(thisObj.observation.DefaultTitle1Value));
|
||||
var itemChecked = true;
|
||||
var disabled = false;
|
||||
if (_date == thisObj.trade.ExerciseDate) {
|
||||
disabled = true;
|
||||
itemChecked = false;
|
||||
}
|
||||
var obdate = {
|
||||
date: _date,
|
||||
val: val,
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
};
|
||||
thisObj.observation.ObservationDataList.push(obdate);
|
||||
});
|
||||
thisObj.initObservationCheckedAll();
|
||||
});
|
||||
}
|
||||
},
|
||||
//编辑观察日功能数据处理
|
||||
SetObservationDates() {
|
||||
var observationStr = "";
|
||||
if (this.observation.ObservationDataList != null) {
|
||||
this.observation.ObservationDataList.forEach(item => {
|
||||
observationStr = observationStr + item.date + ", " + (item.val * 0.01).toFixed(6) + ", " + item.itemChecked + ";\n";
|
||||
observationStr = observationStr + item.date + ", " + item.SettlementDate + ", " + (item.val * 0.01).toFixed(6) + ", " + item.itemChecked + ";\n";
|
||||
});
|
||||
}
|
||||
this.observation.ObservationInterval = observationStr;
|
||||
@@ -712,18 +794,21 @@ const vue = new Vue({
|
||||
var observationDates = thisObj.observation.ObservationInterval;
|
||||
var items = observationDates.split(";").filter(o => o);
|
||||
thisObj.observation.ObservationDataList = [];
|
||||
var floatRateCode = thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--';
|
||||
items.forEach(function (item) {
|
||||
if (item) {
|
||||
var values = item.split(",");
|
||||
var itemChecked = JSON.parse(values[2].trim());
|
||||
var itemChecked = JSON.parse(values[3].trim());
|
||||
var disabled = false;
|
||||
if (values[0] == thisObj.trade.ExerciseDate) {
|
||||
disabled = true;
|
||||
itemChecked = false;
|
||||
}
|
||||
var val = values[1].trim();
|
||||
var val = values[2].trim();
|
||||
var obdate = {
|
||||
date: values[0],
|
||||
SettlementDate: values[1] || "",
|
||||
floatRateCode: floatRateCode,
|
||||
val: _.toString(val) ? parseFloat(consNumberFormat.umpriceP(val)) : "",
|
||||
itemChecked: itemChecked,
|
||||
disabled: disabled
|
||||
@@ -758,11 +843,24 @@ const vue = new Vue({
|
||||
//添加观察日
|
||||
addnewitem() {
|
||||
var thisObj = this;
|
||||
var obdate = {
|
||||
date: '',
|
||||
val: 0,
|
||||
itemChecked: true,
|
||||
disabled: false
|
||||
var obdate = {};
|
||||
if (this.observationType == 1) {
|
||||
var floatRateCode = thisObj.getSwapList[thisObj.observation.index]?.FloatRateUnderlyingCode || '--';
|
||||
obdate = {
|
||||
date: '',
|
||||
SettlementDate: '',
|
||||
floatRateCode: floatRateCode,
|
||||
val: 0,
|
||||
itemChecked: true,
|
||||
disabled: false
|
||||
};
|
||||
} else {
|
||||
obdate = {
|
||||
date: '',
|
||||
val: 0,
|
||||
itemChecked: true,
|
||||
disabled: false
|
||||
};
|
||||
}
|
||||
thisObj.observation.ObservationDataList.push(obdate);
|
||||
},
|
||||
@@ -792,7 +890,8 @@ const vue = new Vue({
|
||||
var observation = {
|
||||
Date: item.date,
|
||||
Rate: item.val * 0.01,
|
||||
Settlement: item.itemChecked ? 1 : 0
|
||||
Settlement: item.itemChecked ? 1 : 0,
|
||||
SettlementDate: item.SettlementDate || null // 结算日期
|
||||
}
|
||||
observationArr.push(observation);
|
||||
|
||||
@@ -805,11 +904,11 @@ const vue = new Vue({
|
||||
alert("请输入正确的数字格式");
|
||||
return false;
|
||||
}
|
||||
if (this.observationType == 0) {
|
||||
if (this.observationType == 1) {
|
||||
thisObj.getSwapList.forEach((val, num, arr) => {
|
||||
if (val.index == thisObj.observation.index) {
|
||||
arr[num].SwapIntervals = JSON.stringify(observationArr);
|
||||
thisObj.observation.ObservationInterval = arr[num].SwapIntervals;
|
||||
arr[num].InterestSwapInterval = JSON.stringify(observationArr);
|
||||
thisObj.observation.ObservationInterval = arr[num].InterestSwapInterval;
|
||||
arr[num].SwapIntervalList = observationArr;
|
||||
arr[num].Obervation = JSON.parse(JSON.stringify(thisObj.observation));
|
||||
}
|
||||
@@ -817,8 +916,8 @@ const vue = new Vue({
|
||||
} else {
|
||||
thisObj.marginSwapList.forEach((val, num, arr) => {
|
||||
if (val.index == thisObj.observation.index) {
|
||||
arr[num].SwapIntervals = JSON.stringify(observationArr);
|
||||
thisObj.observation.ObservationInterval = arr[num].SwapIntervals;
|
||||
arr[num].InterestSwapInterval = JSON.stringify(observationArr);
|
||||
thisObj.observation.ObservationInterval = arr[num].InterestSwapInterval;
|
||||
arr[num].SwapIntervalList = observationArr;
|
||||
arr[num].Obervation = JSON.parse(JSON.stringify(thisObj.observation));
|
||||
}
|
||||
@@ -1081,11 +1180,23 @@ const vue = new Vue({
|
||||
thisObj.getSwapList = thisObj.trade.swap_positions.filter(x => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.IsInitial && (x.InterestMode == 1 || x.InterestMode == 2 || x.InterestMode == 7 || x.InterestMode == 8 || x.InterestMode == 9)) return x; });
|
||||
thisObj.getSwapList.forEach((val, num, arr) => {
|
||||
arr[num].index = num;
|
||||
// 解析 InterestSwapInterval 为 SwapIntervalList
|
||||
if (arr[num].InterestSwapInterval && !arr[num].SwapIntervalList) {
|
||||
try {
|
||||
arr[num].SwapIntervalList = JSON.parse(arr[num].InterestSwapInterval);
|
||||
} catch (e) { }
|
||||
}
|
||||
});
|
||||
thisObj.marginSwapList = thisObj.trade.swap_positions.filter(x => { if ((x.UnderlyingCode == null || x.UnderlyingCode.length == 0) && x.IsInitial && (x.InterestMode == 5 || x.InterestMode == 6)) return x; });
|
||||
thisObj.marginSwapList.forEach((val, num, arr) => {
|
||||
arr[num].index = num;
|
||||
arr[num].index = 1000 + num; // 保证金列表使用 1000+ 偏移,避免与利息腿冲突
|
||||
arr[num].HappenDate = thisObj.formatDate(arr[num].HappenDate);
|
||||
// 解析 InterestSwapInterval 为 SwapIntervalList
|
||||
if (arr[num].InterestSwapInterval && !arr[num].SwapIntervalList) {
|
||||
try {
|
||||
arr[num].SwapIntervalList = JSON.parse(arr[num].InterestSwapInterval);
|
||||
} catch (e) { }
|
||||
}
|
||||
});
|
||||
if (thisObj.trade.StructureType == '多空组合') {
|
||||
thisObj.paySwapList = [];
|
||||
|
||||
@@ -44,8 +44,8 @@ function downloadFiles(files) {
|
||||
window.open(files);
|
||||
}
|
||||
}
|
||||
function showSwapRate(timeRate) {
|
||||
vueDetails.initSwapIntervalList(timeRate);
|
||||
function showSwapRate(timeRate, isMarginLeg) {
|
||||
vueDetails.initSwapIntervalList(timeRate, isMarginLeg);
|
||||
$("#swapIntervalModal").modal("show");
|
||||
}
|
||||
var layerIndex = -1;
|
||||
@@ -192,7 +192,7 @@ function unWindSwapTrade(id) {
|
||||
function unWindSwap(id) {
|
||||
var title = "收益结算";
|
||||
var srcurl = "/swaptrade2/SwapIncome/?enid=" + id;
|
||||
main.post("/swaptrade2/CheckEodTrade?enid=" + id).done(function (res) {
|
||||
main.post("/swaptrade2/CheckEodTradeForIncome?enid=" + id).done(function (res) {
|
||||
if (res.success) {
|
||||
main.open(title,
|
||||
srcurl,
|
||||
@@ -375,7 +375,8 @@ function reload() {
|
||||
var vueDetails = new Vue({
|
||||
el: "#swapIntervalModal",
|
||||
data: {
|
||||
SwapIntervalList:[]
|
||||
SwapIntervalList:[],
|
||||
isMarginLeg: false // 是否是保证金腿
|
||||
},
|
||||
created: function () {
|
||||
},
|
||||
@@ -383,13 +384,18 @@ var vueDetails = new Vue({
|
||||
closeModal: function () {
|
||||
$("#swapIntervalModal").modal("hide");
|
||||
},
|
||||
initSwapIntervalList(swapIntervals) {
|
||||
initSwapIntervalList(swapIntervals, isMarginLeg) {
|
||||
var that = this;
|
||||
that.isMarginLeg = isMarginLeg;
|
||||
that.SwapIntervalList = [];
|
||||
if (swapIntervals.length>0) {
|
||||
if (swapIntervals && swapIntervals.length>0) {
|
||||
that.SwapIntervalList = JSON.parse(swapIntervals);
|
||||
that.SwapIntervalList.forEach((item, index) => {
|
||||
that.SwapIntervalList[index].Rate = otcformat.trading.marginRateP(item.Rate);
|
||||
// 如果结算日期为空,默认等于观察日期
|
||||
if (!item.SettlementDate) {
|
||||
that.SwapIntervalList[index].SettlementDate = item.Date;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user