fix(swap): 修复收益互换价格转换和初始保证金计算问题
- 移除债券价格显示态和存储态转换的错误判断逻辑 - 简化债券价格转换条件,只对债券标的进行存储态转换 - 添加初始保证金计算前的金额精度统一处理 - 重构初始保证金准备逻辑到独立方法中 - 修复因价格单位不一致导致的浮动损益计算错误
This commit is contained in:
@@ -162,11 +162,7 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <summary>
|
||||
/// 获取用于互换浮动腿盯市的标的价格。
|
||||
///
|
||||
/// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后
|
||||
/// PosiGrossPrice 为 0.2;而历史交易中仍可能存在直接保存为 20 的展示态价格。
|
||||
/// 中债估值正常经 EodPriceQueryService 转换后应为小数价格,但手工维护的历史
|
||||
/// 行情可能仍以展示态进入该服务,例如 2000 经一次转换后得到 20。若将 20
|
||||
/// 与 0.2 直接相减,会把 20% 的价格差误算成 1,980,000 的浮动损益。
|
||||
/// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后 PosiGrossPrice 为 0.2
|
||||
///
|
||||
/// 因此仅当交易期初价已经是小数口径、且当前债券价明显仍处于展示态时,再做
|
||||
/// 一次展示态到存储态转换。期初价本身是历史展示态口径的存量交易保持原价格,
|
||||
@@ -176,10 +172,10 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
var price = GetUnderlyingPrice(code, settleDate, out vobp);
|
||||
var underlying = GetUnderlyingData(code);
|
||||
var usesStoragePrice = Math.Abs(posiGrossPrice) < 2m;
|
||||
var usesDisplayPrice = Math.Abs(price) >= 10m;
|
||||
// var usesStoragePrice = Math.Abs(posiGrossPrice) < 2m;
|
||||
// var usesDisplayPrice = Math.Abs(price) >= 10m;
|
||||
|
||||
if (underlying?.IsBond() == true && usesStoragePrice && usesDisplayPrice)
|
||||
if (underlying?.IsBond() == true)
|
||||
{
|
||||
var normalizedPrice = BondPriceConverter.ToStorage(price);
|
||||
Log.Error($"互换债券日终价格按展示态返回,已转换为存储态: UnderlyingCode={code}, ValueDate={settleDate:yyyy-MM-dd}, PosiGrossPrice={posiGrossPrice}, SourcePrice={price}, NormalizedPrice={normalizedPrice}");
|
||||
|
||||
@@ -381,7 +381,10 @@ namespace YLErp.Modules.SwapModule
|
||||
CountRatio = underlying.CountRatio,
|
||||
ContractSize = Convert.ToDecimal(underlying.ContractSize),
|
||||
PosiNetPrice = flowMerge.TradingAmountFeeAvgAbs,
|
||||
PosiGrossPrice = Math.Round(flowMerge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
|
||||
PosiGrossPrice = Math.Round(
|
||||
flowMerge.TradingAmountAvg,
|
||||
underlying.IsBond() ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
|
||||
MidpointRounding.AwayFromZero),
|
||||
PosiNetFeePrice = flowMerge.TradingAmountNetFeeAvg ?? 0,
|
||||
PosiNetNoFeePrice = flowMerge.TradingAmountNetAvg ?? 0,
|
||||
PosiQuantity = flowMerge.TradingQtyAbs,
|
||||
@@ -684,11 +687,7 @@ namespace YLErp.Modules.SwapModule
|
||||
private bool PrepareTrade(trade req, TradeSourceEnum dataSource, underlying_manager um)
|
||||
{
|
||||
bool tradeNumberGenerated = false;
|
||||
req.InitialMargin = Convert.ToDouble(req.trade_Initial_Margin.MarginValue);
|
||||
if (req.trade_Initial_Margin.MarginType == 0)
|
||||
{
|
||||
req.InitialMargin = req.StockEqvNotional == 0 ? 0 : Convert.ToDouble(req.trade_Initial_Margin.MarginValue) * req.StockEqvNotional;
|
||||
};
|
||||
PrepareInitialMargin(req);
|
||||
var isAddNew = req.id == 0;
|
||||
if (isAddNew)
|
||||
{
|
||||
@@ -731,6 +730,17 @@ namespace YLErp.Modules.SwapModule
|
||||
return tradeNumberGenerated;
|
||||
|
||||
}
|
||||
|
||||
private static void PrepareInitialMargin(trade req)
|
||||
{
|
||||
// 初始预付金依赖最终入库的名义本金,须先统一金额精度,避免两者无法勾稽。
|
||||
req.StockEqvNotional = Math.Round(req.StockEqvNotional, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||||
req.InitialMargin = Convert.ToDouble(req.trade_Initial_Margin.MarginValue);
|
||||
if (req.trade_Initial_Margin.MarginType == 0)
|
||||
{
|
||||
req.InitialMargin = req.StockEqvNotional == 0 ? 0 : Convert.ToDouble(req.trade_Initial_Margin.MarginValue) * req.StockEqvNotional;
|
||||
}
|
||||
}
|
||||
//准备单个交易
|
||||
private trade PrepareSingleTrade(trade req, TradeSourceEnum dataSource, bool isAddNew, underlying_manager um)
|
||||
{
|
||||
@@ -1364,7 +1374,10 @@ namespace YLErp.Modules.SwapModule
|
||||
// position.PosiGrossPrice = string.IsNullOrEmpty(swap.UnderlyingCode)
|
||||
// ? Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero)
|
||||
// : ValidateDeliveryPrice(swap.PosiGrossPrice, "期初交割价");
|
||||
position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
var storagePriceRound = ConsGlobal.InstrumentType.IsBond(swap.UnderlyingInstrumentType)
|
||||
? ConsGlobal.PriceRound
|
||||
: ConsGlobal.SwapDeliveryPriceRound;
|
||||
position.PosiGrossPrice = Math.Round(swap.PosiGrossPrice, storagePriceRound, MidpointRounding.AwayFromZero);
|
||||
position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (position.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
|
||||
position.PosiNetPrice = Math.Round(position.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
position.PosiNetNoFeePrice = swap.PosiNetNoFeePrice;
|
||||
|
||||
Reference in New Issue
Block a user