Merge remote-tracking branch 'dest/glms/feature/1.4.2' into test

This commit is contained in:
lisong
2026-08-21 16:00:48 +08:00
81 changed files with 5362 additions and 136 deletions
@@ -1,4 +1,4 @@
using BaseOUDAL;
using BaseOUDAL;
using DocumentFormat.OpenXml.Bibliography;
using ExcelDataReader.Log;
using YLErp.DBModels;
@@ -104,7 +104,53 @@ namespace YLErp.Modules.EodModule
.Where(x => x.reg_date > startDate && x.reg_date <= endDate)
.AsNoTracking().ToList();
Log.Info($"[分红-登记日口径] GetBondPayments underlyingCode={underlyingCode} 区间=({startDate:yyyy-MM-dd},{endDate:yyyy-MM-dd}] 按reg_date过滤, 命中 {result.Count} 条: " +
string.Join(",", result.Select(r => r.reg_date?.ToString("yyyy-MM-dd"))));
string.Join(",", result.Select(r => r.reg_date?.ToString("yyyy-MM-dd"))));
// 让 Copy/Update EOD 始终只依赖 BondPaymentService,而不必在收盘链路直接累加 ex_dividend_info。
// 口径约定:bond_payment_info.payment_interest 对 Stock/Fund 统一按“每 10 份派现金额”存储,
// 即直接存 ex_dividend_info.GiveCashAmount 原值,不做 /10
// 最后的 /10 由 CalcPayment 非债券分支完成。
// 去重:镜像任务完成后的正式记录带 jsid = -dividend.id;此处先按该负号标记,
// 或用“同一实际付息日 + 同一派现金额”兜底去重,避免镜像完成后重复计息。
var corporatePayments = (from dividend in DbContext.ex_dividend_info.AsNoTracking()
join underlying in DbContext.underlying_manager.AsNoTracking()
on dividend.UnderlyingCode equals underlying.UnderlyingCode
where dividend.ValidStatus
&& dividend.EffectiveDate.HasValue
&& dividend.EffectiveDate.Value > startDate
&& dividend.EffectiveDate.Value <= endDate
&& dividend.GiveCashAmount != 0
&& dividend.UnderlyingCode == underlyingCode
&& (underlying.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Stock
|| underlying.UnderlyingInstrumentType == ConsGlobal.InstrumentType.Fund)
select dividend).ToList();
foreach (var dividend in corporatePayments)
{
// 按“每 10 份派现金额”口径,直接存 GiveCashAmount 原值,与同步任务/CalcPayment 保持一致。
var paymentInterest = dividend.GiveCashAmount;
var hasMirroredPayment = result.Any(payment =>
payment.jsid == -dividend.id
|| (payment.payment_date.HasValue
&& payment.payment_date.Value.Date == dividend.EffectiveDate.Value.Date
&& payment.payment_interest == paymentInterest));
if (hasMirroredPayment)
{
continue;
}
result.Add(new BondPayment
{
underlyingCode = dividend.UnderlyingCode,
payment_date_pl = dividend.EffectiveDate,
payment_date = dividend.EffectiveDate,
payment_interest = paymentInterest,
paying_price = paymentInterest,
channel_source = ExDividendDataSources.Manual,
jsid = -dividend.id,
create_time = dividend.OptDate,
update_time = dividend.OptDate
});
}
return result;
}
@@ -132,24 +178,50 @@ namespace YLErp.Modules.EodModule
/// <param name="longRatio">多空方向</param>
/// <param name="payDirection">收支方向</param>
/// <returns></returns>
public decimal CalcPayment(string underlyingCode, DateTime startDate, DateTime endDate, decimal qty, decimal longRatio, decimal payDirection)
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);
return CalcPayment(payments, qty, longRatio, payDirection, useBondPriceScale);
}
/// <summary>
/// 计算某债券期间付息
/// 计算某标的期间现金流。债券与 Stock/Fund 公司行为共用 bond_payment_info
/// 但通过 useBondPriceScale 明确区分两种入库金额单位。
/// </summary>
/// <param name="payments">期间付息集合</param>
/// <param name="qty">持仓数量</param>
/// <param name="longRatio">多空方向</param>
/// <param name="payDirection">收支方向</param>
/// <param name="useBondPriceScale">
/// 是否按债券报价的百分比口径换算。债券的 payment_interest 是每 100 元面值的票息,
/// 需要继续通过 BondPriceConverter 转成入库金额;Fund/Stock 的公司行为现金分红
/// 在 bond_payment_info 中按“每 10 份派现金额”存储,payment_interest * qty / 10 才是实际现金,
/// 不能再套债券的 /100。默认 true 是为了保持所有历史债券调用方的原有口径。
/// </param>
/// <returns></returns>
public decimal CalcPayment(List<BondPayment> payments, decimal qty, decimal longRatio, decimal payDirection)
public decimal CalcPayment(
List<BondPayment> payments,
decimal qty,
decimal longRatio,
decimal payDirection,
bool useBondPriceScale = true)
{
var interest = payments.Sum(s => s.payment_interest ?? 0);
// interest 为每 100 元面值的票息,×qty 后需 ÷100 转为实际金额(与入库价格 bondPriceMultiple 同口径)
return BondPriceConverter.ToStorage(interest * qty) * longRatio * payDirection;
var paymentAmount = interest * qty;
// 债券:interest 为每 100 元面值的票息,×qty 后需 ÷100 转为实际金额。
// Fund/Stock 公司行为:payment_interest 存的是“每 10 份派现金额”(GiveCashAmount 原值),
// interest × qty 得到“每 10 份派现额 × 持仓份数”,需再 ÷10 才是实际现金;
// 既不能套债券的 /100,也不能直接返回 paymentAmount(那样会放大 10 倍)。
var actualAmount = useBondPriceScale
? BondPriceConverter.ToStorage(paymentAmount)
: paymentAmount / 10;
return actualAmount * longRatio * payDirection;
}
}