using BaseOUDAL; using DocumentFormat.OpenXml.Bibliography; using ExcelDataReader.Log; using YLErp.DBModels; using YLErp.Helpers; namespace YLErp.Modules.EodModule { /// /// 债券期间付息服务 /// public class BondPaymentService : YLBaseService { private static IYcLogger Log = LogFactory.GetLogger(nameof(BondPaymentService)); public BondPaymentService(OptUserInfo userInfo) : base(userInfo) { } public SearchListResult SearchList(BondPaymentReq req) { var valueDtStart = req.ValueDateStart.Year > 2000 ? req.ValueDateStart : DateTime.Today.AddYears(-1); var valueDtEnd = req.ValueDateEnd.Year > 2000 ? req.ValueDateEnd.AddDays(1) : DateTime.Today.AddYears(1); var predicatUn = PredicateBuilder.Create(d => d.LaunchState == "1"); var predicatEoc = PredicateBuilder.Create(source => source.payment_date >= valueDtStart && source.payment_date < valueDtEnd); if (!string.IsNullOrEmpty(req.DataSource)) { predicatEoc = predicatEoc.And(d => d.channel_source.Contains(req.DataSource)); } if (!string.IsNullOrEmpty(req.MarketName)) { predicatUn = predicatUn.And(d => d.MarketName == req.MarketName); } if (!string.IsNullOrEmpty(req.UnderlyingCode)) { predicatEoc = predicatEoc.And(d => d.underlyingCode.Contains(req.UnderlyingCode)); } if (string.IsNullOrEmpty(req.sidx)) { req.sidx = "payment_date"; req.sord = "desc"; } var queryUn = DbContext.underlying_manager.Where(predicatUn).Select(n => new { n.id, n.MarketName, n.UnderlyingCode, n.UnderlyingName, n.UnderlyingInstrumentType, n.InnerCode }); var query = from un in queryUn join source in DbContext.bondPayment.Where(predicatEoc) on un.UnderlyingCode equals source.underlyingCode select new BondPaymentDto { id = source.id, channel_source = source.channel_source, MarketName = un.MarketName, security_id = un.UnderlyingCode, symbol = un.UnderlyingName, coupon_rate = source.coupon_rate, payment_date = source.payment_date, payment_interest = source.payment_interest, payment_parvalue = source.payment_parvalue, create_time = source.create_time, update_time = source.update_time }; var result = query.ToSearchList(req); return result; } public BondPayment SaveBondPayment(BondPayment req) { if (req is null) { throw new ArgumentNullException(nameof(req)); } BondPayment dbmodel; if (req.id == 0) { DbContext.bondPayment.Add(dbmodel = req); } else { dbmodel = DbContext.bondPayment.Find(req.id); if (dbmodel == null) { throw new ServiceException("数据不存在"); } UpdateChanges(dbmodel, req); } dbmodel.update_time = DateTime.Now; DbContext.SaveChanges(); return dbmodel; } /// /// 获取某债券的期间付息情况集合 /// /// /// /// /// public List GetBondPayments(string underlyingCode, DateTime startDate, DateTime endDate) { var result = DbContext.bondPayment.Where(x => x.underlyingCode == underlyingCode && x.payment_date > startDate && x.payment_date <= endDate).AsNoTracking().ToList(); return result; } public List GetTargetDatePayments(string underlyingCode, DateTime targetDate) { var startDate = targetDate.Date; var endDate = startDate.AddDays(1); return DbContext.bondPayment.AsNoTracking().Where(x => x.underlyingCode == underlyingCode && x.payment_date >= startDate && x.payment_date < endDate).ToList(); } /// /// 计算某债券某段时间的期间付息 /// /// 债券代码 /// 计息开始日 /// 计息结束日 /// 持仓数量 /// 多空方向 /// 收支方向 /// public decimal CalcPayment( string underlyingCode, DateTime startDate, DateTime endDate, decimal qty, decimal longRatio, decimal payDirection, bool useBondPriceScale = true) { var payments = GetBondPayments(underlyingCode, startDate, endDate); return CalcPayment(payments, qty, longRatio, payDirection, useBondPriceScale); } /// /// 计算某标的期间现金流。债券与 Stock/Fund 公司行为共用 bond_payment_info, /// 但通过 useBondPriceScale 明确区分两种入库金额单位。 /// /// 期间付息集合 /// 持仓数量 /// 多空方向 /// 收支方向 /// /// 是否按债券报价的百分比口径换算。债券的 payment_interest 是每 100 元面值的票息, /// 需要继续通过 BondPriceConverter 转成入库金额;Fund/Stock 的公司行为现金分红 /// 在 bond_payment_info 中按每 10 份存储,payment_interest * qty 已经是实际现金, /// 不能再做一次 /100。默认 true 是为了保持所有历史债券调用方的原有口径。 /// /// public decimal CalcPayment( List payments, decimal qty, decimal longRatio, decimal payDirection, bool useBondPriceScale = true) { var interest = payments.Sum(s => s.payment_interest ?? 0); var paymentAmount = interest * qty; // 债券:interest 为每 100 元面值的票息,×qty 后需 ÷100 转为实际金额。 // Fund/Stock 公司行为:interest 已由【同步任务】写成 GiveCashAmount/10, // ×qty 就是“每 10 份派现额 × 持仓份额”,必须保留原金额,不能套债券的 /100。 var actualAmount = useBondPriceScale ? BondPriceConverter.ToStorage(paymentAmount) : paymentAmount; return actualAmount * longRatio * payDirection; } } /// /// /// public class BondPaymentReq : BaseSearchReq { /// /// 数据来源 /// public string DataSource { get; set; } /// /// 标的代码 /// public string UnderlyingCode { get; set; } public DateTime ValueDateStart { get; set; } public DateTime ValueDateEnd { get; set; } // 市场 public string MarketName { get; set; } } public class BondPaymentDto : BondPayment { public string MarketName { get; set; } } }