Files
zszq-trs/YLErpDAL/Modules/EodModule/SettlementModule/EodSettlementService.cs
T
张名锐 5a51d66970 refactor(dividend): 优化除权除息计算逻辑并改进数据类型精度
- 将除权除息相关数值字段从 double 类型改为 decimal 类型以提高精度
- 重构了红利比率计算方法 GetRatio,新增 GetRatioDecimal 方法使用 decimal 计算
- 修改价格和持仓数量计算逻辑,统一使用 decimal 进行高精度运算
- 更新数据库查询逻辑,将篮子标的判断从 IsBasket() 方法改为 CommodityCode 条件
- 优化 AddDividendInfos 方法中的批量处理逻辑,增加业务键冲突检测
- 添加数据源标识字段 DataSource 和来源更新时间字段 SourceUpdatedAt
- 新增 FindExDividendByBusinessKey 和 MergeNonZeroDividendValues 辅助方法
- 更新结算服务中除权除息信息的获取方式,使用字典查找替代 LINQ JOIN
- 修复前端保存除权信息时的响应处理逻辑
- 为基金类型也开放除权功能,不仅限于股票类型
- 添加单元测试验证篮子标的查询翻译逻辑的正确性
2026-08-13 10:53:57 +08:00

153 lines
6.8 KiB
C#

using System.Linq.Expressions;
using YLErp.BLL;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.Modules.EodModule
{
/// <summary>
/// 日终结算服务
/// </summary>
public class EodSettlementService : YLBaseService
{
public EodSettlementService(YLBaseService baseService) : base(baseService)
{
}
public EodSettlementService(OptUserInfo userInfo) : base(userInfo)
{
}
public EodSettlementService(OptUserInfo userInfo, YLContext dbContext) : base(userInfo, dbContext)
{
}
/// <summary>
/// 获取日终持仓数据
/// </summary>
public IEnumerable<T> GetEodTradePosition<T>(DateTime settleDate, Expression<Func<T, bool>> predicate = null)
where T : EodTradePosition
{
var bodDate = QdpCalendarHelper.GetNonHoliday(settleDate.AddDays(1));
if (predicate == null)
{
predicate = PredicateBuilder.Create<T>(n => n.ValueDate == settleDate);
}
else
{
predicate = PredicateBuilder.Create<T>(n => n.ValueDate == settleDate).And(predicate);
}
var query = from eod in DbContext.Set<T>().Where(predicate)
join bod in DbContext.BodTradePosition.Where(n => n.ValueDate == bodDate)
on new { eod.BookId, eod.TradeType, eod.PositionType, eod.UnderlyingCode, ExchangeOptionCode = eod.ExchangeOptionCode ?? string.Empty }
equals new { bod.BookId, bod.TradeType, bod.PositionType, bod.UnderlyingCode, ExchangeOptionCode = bod.ExchangeOptionCode ?? string.Empty } into t_bod
from bod in t_bod.DefaultIfEmpty()
select new
{
eod,
bod = bod == null ? null : new
{
bod.Amount,
bod.Cost,
//bod.AveragePrice
}
};
var datas = query.ToArray();
var diviService = new TradeModule.DealModule.DividendService(OptUser);
var dividendDict = diviService.GetExDividendQuery(settleDate)
.ToDictionary(O => O.UnderlyingCode, O => O, StringComparer.OrdinalIgnoreCase);
var eodPriceProvider = new EodPriceProvider(settleDate);
return datas.Select(data =>
{
var eod = data.eod;
var bod = data.bod;
if (dividendDict.TryGetValue(eod.UnderlyingCode, out var dividend))
{
if (data.eod.TradeType == "股票")
{
var SettlePrice = eodPriceProvider.GetPrice(data.eod.UnderlyingCode, SettlementTypeEnum.ClosePrice);
SettlePrice = diviService.GetPrice(SettlePrice, dividend);
var amount = diviService.GetPositionAmount(data.eod.Amount, dividend);
eod.Pv = eod.Pv > 0 ? Math.Abs(amount * SettlePrice) : -Math.Abs(amount * SettlePrice);
}
}
if (bod != null)
{
eod.Amount = bod.Amount;
//eod.AveragePrice = bod.AveragePrice;
eod.Cost = bod.Cost;
}
return eod;
}).ToArray();
}
/// <summary>
/// 获取日终持仓数据
/// </summary>
public IEnumerable<EodTradePositionAndRisk> GetEodTradePosition<TPos, TRisk>(DateTime settleDate, Expression<Func<TPos, bool>> predicate = null)
where TPos : EodTradePosition
where TRisk : EodTradeRisk
{
var bodDate = QdpCalendarHelper.GetNonHoliday(settleDate.AddDays(1));
if (predicate == null)
{
predicate = PredicateBuilder.Create<TPos>(n => n.ValueDate == settleDate);
}
else
{
predicate = PredicateBuilder.Create<TPos>(n => n.ValueDate == settleDate).And(predicate);
}
var query = from eod in DbContext.Set<TPos>().AsNoTracking().Where(predicate)
join bod in DbContext.BodTradePosition.Where(n => n.ValueDate == bodDate)
on new { eod.BookId, eod.TradeType, eod.PositionType, eod.UnderlyingCode, ExchangeOptionCode = eod.ExchangeOptionCode ?? string.Empty }
equals new { bod.BookId, bod.TradeType, bod.PositionType, bod.UnderlyingCode, ExchangeOptionCode = bod.ExchangeOptionCode ?? string.Empty } into t_bod
from bod in t_bod.DefaultIfEmpty()
join risk in DbContext.Set<TRisk>().AsNoTracking().Where(n => n.ValueDate == settleDate && n.TradeId > 0) on new { eod.ValueDate, eod.TradeId } equals new { risk.ValueDate, risk.TradeId } into risk_t
from risk in risk_t.DefaultIfEmpty()
select new
{
eod,
bod = bod == null ? null : new
{
bod.Amount,
bod.Cost,
//bod.AveragePrice
},
risk
};
var datas = query.ToArray();
var diviService = new TradeModule.DealModule.DividendService(OptUser);
var dividendDict = diviService.GetExDividendQuery(settleDate)
.ToDictionary(O => O.UnderlyingCode, O => O, StringComparer.OrdinalIgnoreCase);
var eodPriceProvider = new EodPriceProvider(settleDate);
return datas.Select(data =>
{
var pos = data.eod;
var bod = data.bod;
if (dividendDict.TryGetValue(pos.UnderlyingCode, out var dividend))
{
if (data.eod.TradeType == "股票")
{
var settlePrice = eodPriceProvider.GetPrice(data.eod.UnderlyingCode, SettlementTypeEnum.ClosePrice);
settlePrice = diviService.GetPrice(settlePrice, dividend);
var amount = diviService.GetPositionAmount(data.eod.Amount, dividend);
pos.Pv = pos.Pv > 0 ? Math.Abs(amount * settlePrice) : -Math.Abs(amount * settlePrice);
}
}
if (bod != null)
{
pos.Amount = bod.Amount;
pos.Cost = bod.Cost;
}
return new EodTradePositionAndRisk(pos, data.risk);
}).ToArray();
}
}
}