fix(swap): 修复互换交易中债券价格精度处理问题,扩充到11位计算
- 统一债券价格四舍五入规则,使用AwayFromZero模式 - 为债券类型添加特殊的精度处理逻辑 - 修复前端价格显示精度计算问题 - 优化价格存储精度控制,区分债券和其他产品类型 - 修复初始化YTM和净价精度处理问题 - 更新价格验证逻辑以支持动态精度设置
This commit is contained in:
@@ -1386,7 +1386,7 @@ namespace YLErp.BLL.Eod
|
||||
ClientId = item.client_id ?? 0,
|
||||
ClientName = item.client_name,
|
||||
TradingQty = (item.order_qty ?? 0) - (item.last_shares ?? 0),
|
||||
TradingAmountAvg = BondPriceConverter.ToStorage(item.full_price ?? 0),
|
||||
TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.full_price ?? 0), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero),
|
||||
TradingAmountFeeAvg = BondPriceConverter.ToStorage(item.full_price ?? 0),
|
||||
TradingFee = 0
|
||||
};
|
||||
|
||||
@@ -137,7 +137,13 @@ namespace YLErp.Modules.SwapModule
|
||||
swapFlow.OptTime = result.OptTime;
|
||||
swapFlow.SettleDate = result.SettleDate;
|
||||
swapFlow.TradingAmount = result.TradingAmount;
|
||||
swapFlow.TradingAmountAvg = Math.Round(result.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
var underlying = string.IsNullOrEmpty(result.UnderlyingCode)
|
||||
? null
|
||||
: DataCacheProvider.GetUnderlyingDataSource().GetData(result.UnderlyingCode);
|
||||
var storagePriceRound = underlying?.IsBond() == true
|
||||
? ConsGlobal.PriceRound
|
||||
: ConsGlobal.SwapDeliveryPriceRound;
|
||||
swapFlow.TradingAmountAvg = Math.Round(result.TradingAmountAvg, storagePriceRound, MidpointRounding.AwayFromZero);
|
||||
swapFlow.TradingAmountFeeAvg = result.TradingAmountFeeAvg;
|
||||
swapFlow.TradingAmountNet = result.TradingAmountNet;
|
||||
swapFlow.TradingAmountNetFee = result.TradingAmountNetFee;
|
||||
|
||||
@@ -48,6 +48,20 @@ namespace YLErp.Modules.SwapModule
|
||||
unwindData.CloseNotionalValue = Math.Round(unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
private static int GetStorageDeliveryPriceRound(swap_flow_event flowEvent)
|
||||
{
|
||||
if (ConsGlobal.InstrumentType.IsBond(flowEvent?.UnderlyingInstrumentType))
|
||||
{
|
||||
return ConsGlobal.PriceRound;
|
||||
}
|
||||
if (string.IsNullOrEmpty(flowEvent?.UnderlyingCode))
|
||||
{
|
||||
return ConsGlobal.SwapDeliveryPriceRound;
|
||||
}
|
||||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(flowEvent?.UnderlyingCode);
|
||||
return underlying?.IsBond() == true ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound;
|
||||
}
|
||||
|
||||
private static void ValidateDeliveryPrices(UnwindData unwindData)
|
||||
{
|
||||
if (unwindData.FlowEvents == null)
|
||||
@@ -56,7 +70,7 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
foreach (var item in unwindData.FlowEvents.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)))
|
||||
{
|
||||
var roundedPrice = Math.Round(item.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
var roundedPrice = Math.Round(item.TradingAmountAvg, GetStorageDeliveryPriceRound(item), MidpointRounding.AwayFromZero);
|
||||
if (item.TradingAmountAvg != roundedPrice)
|
||||
{
|
||||
throw new ServiceException($"期末交割价最多保留{ConsGlobal.SwapDeliveryPriceRound}位小数");
|
||||
@@ -73,7 +87,7 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
foreach (var item in flowEvents.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)))
|
||||
{
|
||||
item.TradingAmountAvg = Math.Round(item.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
item.TradingAmountAvg = Math.Round(item.TradingAmountAvg, GetStorageDeliveryPriceRound(item), MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1310,7 +1324,6 @@ namespace YLErp.Modules.SwapModule
|
||||
/// <param name="unwindPriceFee"></param>
|
||||
public void AuotoSwapUnwind(int tradeid, decimal unwindPrice, decimal unwindPriceFee, decimal unwindNetFee, decimal unwindNet, DateTime valueDate, decimal unwindQty, decimal closeFee)
|
||||
{
|
||||
unwindPrice = Math.Round(unwindPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
unwindPriceFee = decimal.Parse(unwindPriceFee.ToString("F10"));
|
||||
var td = DbContext.trade.Find(tradeid);
|
||||
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && !x.Invalid);
|
||||
@@ -1319,6 +1332,10 @@ namespace YLErp.Modules.SwapModule
|
||||
var tradeExtend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
|
||||
td.trade_extend = tradeExtend;
|
||||
var position = positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode) && !x.IsInitial).FirstOrDefault();
|
||||
var storagePriceRound = ConsGlobal.InstrumentType.IsBond(position?.UnderlyingInstrumentType)
|
||||
? ConsGlobal.PriceRound
|
||||
: ConsGlobal.SwapDeliveryPriceRound;
|
||||
unwindPrice = Math.Round(unwindPrice, storagePriceRound, MidpointRounding.AwayFromZero);
|
||||
var preDealDate = GetPreDealDate(td.id, dealDate, eventTypes);
|
||||
swap_flow_event floatEvent = new swap_flow_event();
|
||||
UnwindData unwindData = new UnwindData();
|
||||
|
||||
@@ -32,13 +32,29 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
}
|
||||
|
||||
private int GetStorageDeliveryPriceRound(string underlyingInstrumentType, string underlyingCode)
|
||||
{
|
||||
if (ConsGlobal.InstrumentType.IsBond(underlyingInstrumentType))
|
||||
{
|
||||
return ConsGlobal.PriceRound;
|
||||
}
|
||||
if (string.IsNullOrEmpty(underlyingCode))
|
||||
{
|
||||
return ConsGlobal.SwapDeliveryPriceRound;
|
||||
}
|
||||
return GetUnderlyingData(underlyingCode)?.IsBond() == true
|
||||
? ConsGlobal.PriceRound
|
||||
: ConsGlobal.SwapDeliveryPriceRound;
|
||||
}
|
||||
|
||||
#region 可测试化接缝(Seams)——override 这些虚方法可在测试中替换 DB/外部调用,生产代码行为不变
|
||||
|
||||
/// <summary>持久化 eod 持仓记录(生产: DbContext.Add;测试: 收集到列表)</summary>
|
||||
protected virtual void PersistEodSwapPosition(eod_swap_position position)
|
||||
{
|
||||
position.PosiGrossPrice = Math.Round(position.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
position.UnderlyingPrice = Math.Round(position.UnderlyingPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
var storagePriceRound = GetStorageDeliveryPriceRound(position.UnderlyingInstrumentType, position.UnderlyingCode);
|
||||
position.PosiGrossPrice = Math.Round(position.PosiGrossPrice, storagePriceRound, MidpointRounding.AwayFromZero);
|
||||
position.UnderlyingPrice = Math.Round(position.UnderlyingPrice, storagePriceRound, MidpointRounding.AwayFromZero);
|
||||
position.PosiNotionalValue = Math.Round(position.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||||
if (position.id == 0)
|
||||
{
|
||||
@@ -179,7 +195,7 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
var normalizedPrice = BondPriceConverter.ToStorage(price);
|
||||
Log.Error($"互换债券日终价格按展示态返回,已转换为存储态: UnderlyingCode={code}, ValueDate={settleDate:yyyy-MM-dd}, PosiGrossPrice={posiGrossPrice}, SourcePrice={price}, NormalizedPrice={normalizedPrice}");
|
||||
return Math.Round(normalizedPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
return Math.Round(normalizedPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
return Math.Round(price, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
@@ -1471,7 +1487,10 @@ namespace YLErp.Modules.SwapModule
|
||||
newEodPayPosition.ContractSize = eventFlow.ContractSize;
|
||||
newEodPayPosition.CountRatio = eventFlow.CountRatio;
|
||||
newEodPayPosition.PosiNetPrice = netPrice;
|
||||
newEodPayPosition.PosiGrossPrice = Math.Round(grossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
newEodPayPosition.PosiGrossPrice = Math.Round(
|
||||
grossPrice,
|
||||
GetStorageDeliveryPriceRound(eventFlow.UnderlyingInstrumentType, eventFlow.UnderlyingCode),
|
||||
MidpointRounding.AwayFromZero);
|
||||
newEodPayPosition.PosiNetFeePrice = netFeePrice;
|
||||
newEodPayPosition.PosiNetNoFeePrice = netNoFeePrice;
|
||||
newEodPayPosition.PosiQuantity = payQty;
|
||||
@@ -1770,7 +1789,10 @@ namespace YLErp.Modules.SwapModule
|
||||
posiQty = 0;
|
||||
}
|
||||
curretEod.PosiGrossPrice = (eod.PosiGrossPrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountAvg)) / (eod.PosiQuantity + openQty);
|
||||
curretEod.PosiGrossPrice = Math.Round(curretEod.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
curretEod.PosiGrossPrice = Math.Round(
|
||||
curretEod.PosiGrossPrice,
|
||||
GetStorageDeliveryPriceRound(curretEod.UnderlyingInstrumentType, curretEod.UnderlyingCode),
|
||||
MidpointRounding.AwayFromZero);
|
||||
curretEod.PosiNetPrice = (eod.PosiNetPrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountFeeAvg)) / (eod.PosiQuantity + openQty);
|
||||
curretEod.PosiNetPrice = Math.Round(curretEod.PosiNetPrice, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
curretEod.PosiNetNoFeePrice = (eod.PosiNetNoFeePrice * eod.PosiQuantity + openFlowEvents.Sum(a => a.Quantity * a.TradingAmountNetAvg)) / (eod.PosiQuantity + openQty);
|
||||
@@ -1896,7 +1918,7 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
if (data.IsBond())
|
||||
{
|
||||
return Math.Round(BondPrice(data, settleDate, out vobp), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
return Math.Round(BondPrice(data, settleDate, out vobp), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
var price = data.Price ?? 0;
|
||||
if (EodPriceQueryService.TryGetEodPrice(settleDate, code, out var eodPrice))
|
||||
|
||||
@@ -51,6 +51,21 @@ namespace YLErp.Modules.SwapModule
|
||||
protected virtual underlying_manager GetUnderlying(string underlyingCode)
|
||||
=> DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
||||
|
||||
private int GetStorageDeliveryPriceRound(string underlyingInstrumentType, string underlyingCode)
|
||||
{
|
||||
if (ConsGlobal.InstrumentType.IsBond(underlyingInstrumentType))
|
||||
{
|
||||
return ConsGlobal.PriceRound;
|
||||
}
|
||||
if (string.IsNullOrEmpty(underlyingCode))
|
||||
{
|
||||
return ConsGlobal.SwapDeliveryPriceRound;
|
||||
}
|
||||
return GetUnderlying(underlyingCode)?.IsBond() == true
|
||||
? ConsGlobal.PriceRound
|
||||
: ConsGlobal.SwapDeliveryPriceRound;
|
||||
}
|
||||
|
||||
protected virtual DateTime GetNextBusinessDay(DateTime date)
|
||||
=> QdpCalendarHelper.GetNonHoliday(date);
|
||||
|
||||
@@ -63,7 +78,10 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
if (!string.IsNullOrEmpty(evt.UnderlyingCode))
|
||||
{
|
||||
evt.TradingAmountAvg = Math.Round(evt.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
evt.TradingAmountAvg = Math.Round(
|
||||
evt.TradingAmountAvg,
|
||||
GetStorageDeliveryPriceRound(evt.UnderlyingInstrumentType, evt.UnderlyingCode),
|
||||
MidpointRounding.AwayFromZero);
|
||||
}
|
||||
DbContext.swap_flow_event.Add(evt);
|
||||
}
|
||||
@@ -325,7 +343,7 @@ namespace YLErp.Modules.SwapModule
|
||||
DataState = 1,
|
||||
EventDate = flow_merge.OccurTime,
|
||||
UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)),
|
||||
TradingAmountAvg = Math.Round(TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
|
||||
TradingAmountAvg = Math.Round(TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flow_merge.UnderlyingCode), MidpointRounding.AwayFromZero),
|
||||
TradingAmountFeeAvg = TradingAmountFeeAvg,
|
||||
TradingAmountNetFeeAvg = TradingAmountNetFeeAvg,
|
||||
TradingAmountNetAvg = flow_merge.TradingAmountNetAvg,
|
||||
@@ -379,7 +397,10 @@ namespace YLErp.Modules.SwapModule
|
||||
DataState = 1,
|
||||
EventDate = flow_merge.OccurTime,
|
||||
UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)),
|
||||
TradingAmountAvg = Math.Round(flow_merge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
|
||||
TradingAmountAvg = Math.Round(
|
||||
flow_merge.TradingAmountAvg,
|
||||
GetStorageDeliveryPriceRound(null, flow_merge.UnderlyingCode),
|
||||
MidpointRounding.AwayFromZero),
|
||||
TradingAmountFeeAvg = flow_merge.TradingAmountFeeAvg,
|
||||
TradingFeePending = flow_merge.TradingFeePending,
|
||||
ClientId = flow_merge.ClientId
|
||||
@@ -413,7 +434,10 @@ namespace YLErp.Modules.SwapModule
|
||||
DataState = (int)SwapFlowDateStateEnum.完成,
|
||||
EventDate = td.TradeDate.Value,
|
||||
UnwindDate = td.StartDate.Value,
|
||||
TradingAmountAvg = Math.Round(position.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
|
||||
TradingAmountAvg = Math.Round(
|
||||
position.PosiGrossPrice,
|
||||
GetStorageDeliveryPriceRound(position.UnderlyingInstrumentType, position.UnderlyingCode),
|
||||
MidpointRounding.AwayFromZero),
|
||||
TradingAmountFeeAvg = position.PosiNetPrice,
|
||||
TradingAmountNetFeeAvg = position.PosiNetFeePrice,
|
||||
TradingAmountNetAvg = position.PosiNetNoFeePrice,
|
||||
@@ -466,7 +490,7 @@ namespace YLErp.Modules.SwapModule
|
||||
DataState = 100,
|
||||
EventDate = td.TradeDate.Value,
|
||||
UnwindDate = QdpCalendarHelper.GetNonHoliday(td.TradeDate.Value.AddDays(1)),
|
||||
TradingAmountAvg = Math.Round(flowMerge.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
|
||||
TradingAmountAvg = Math.Round(flowMerge.TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flowMerge.UnderlyingCode), MidpointRounding.AwayFromZero),
|
||||
TradingAmountFeeAvg = flowMerge.TradingAmountFeeAvg,
|
||||
TradingAmountNetFeeAvg = flowMerge.TradingAmountNetFeeAvg,
|
||||
TradingAmountNetAvg = flowMerge.TradingAmountNetAvg,
|
||||
|
||||
@@ -118,7 +118,8 @@ namespace YLErp.Modules.SwapModule
|
||||
swap_flow.ytm = reader.GetDecimalOrPercent("成交收益率",false,true) ?? 0;
|
||||
swap_flow.TradingAmountNet = reader.GetDecimal("成交净价") ?? 0;
|
||||
swap_flow.TradingAmountNetFee = TradeFeeHelper.CalcPriceWithFee(swap_flow.TradingFee, swap_flow.TradingAmountNet??0, swap_flow.TradingQty, swap_flow.BsType);
|
||||
if (underlying != null && underlying.IsBond())
|
||||
var isBond = underlying != null && underlying.IsBond();
|
||||
if (isBond)
|
||||
{
|
||||
// 成交流水债券报价(×100形式)转入库小数(×0.01),统一走 BondPriceConverter
|
||||
swap_flow.TradingAmountAvg = BondPriceConverter.ToStorage(swap_flow.TradingAmountAvg);
|
||||
@@ -129,7 +130,10 @@ namespace YLErp.Modules.SwapModule
|
||||
if (swap_flow.TradingAmountNetFee.HasValue)
|
||||
swap_flow.TradingAmountNetFee = BondPriceConverter.ToStorage(swap_flow.TradingAmountNetFee.Value);
|
||||
}
|
||||
swap_flow.TradingAmountAvg = Math.Round(swap_flow.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_flow.TradingAmountAvg = Math.Round(
|
||||
swap_flow.TradingAmountAvg,
|
||||
isBond ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
|
||||
MidpointRounding.AwayFromZero);
|
||||
if (!string.IsNullOrEmpty(clientName))
|
||||
{
|
||||
var client = DataCacheProvider.GetClientDataSource().AsQueryable(x=>x.Name== clientName).FirstOrDefault();
|
||||
|
||||
@@ -473,7 +473,7 @@ namespace YLErp.Modules.SwapModule
|
||||
swap_Flow.UnderlyingCode = req.UnderlyingCode;
|
||||
swap_Flow.BsType = req.BsType;
|
||||
swap_Flow.ContractSize = req.ContractSize;
|
||||
swap_Flow.TradingAmountAvg = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_Flow.TradingAmountAvg = req.TradingAmountAvg;
|
||||
swap_Flow.TradingAmountFeeAvg = TradeFeeHelper.CalcPriceWithFee(req.TradingFee,req.TradingAmountAvg,req.TradingQty,req.BsType);
|
||||
swap_Flow.ClientId = req.ClientId;
|
||||
swap_Flow.ytm = req.ytm;
|
||||
@@ -540,7 +540,7 @@ namespace YLErp.Modules.SwapModule
|
||||
swap_Flow.BsType = req.BsType;
|
||||
swap_Flow.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
||||
swap_Flow.ContractSize = req.ContractSize;
|
||||
swap_Flow.TradingAmountAvg = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_Flow.TradingAmountAvg = req.TradingAmountAvg;
|
||||
swap_Flow.TradingAmountFeeAvg = req.TradingAmountFeeAvg;
|
||||
UpdateDbOption(swap_Flow);
|
||||
if (req.id == 0)
|
||||
@@ -695,7 +695,7 @@ namespace YLErp.Modules.SwapModule
|
||||
TradingFee = gourpItem.Sum(s => s.TradingFee),
|
||||
DataState = (int)SwapFlowDateStateEnum.等待完成,
|
||||
TradingAmountFeeAvg = gourpItem.Average(s => s.TradingAmountFeeAvg),
|
||||
TradingAmountAvg = Math.Round(gourpItem.Average(s => s.TradingAmountAvg), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero),
|
||||
TradingAmountAvg = Math.Round(gourpItem.Average(s => s.TradingAmountAvg), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero),
|
||||
ContractSize = swapflow.ContractSize
|
||||
};
|
||||
UpdateDbOption(swap_flow_summary);
|
||||
@@ -757,7 +757,8 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
throw new ServiceException("没有找到标的信息:" + req.UnderlyingCode);
|
||||
}
|
||||
if (underlying != null && underlying.IsBond())
|
||||
var isBond = underlying.IsBond();
|
||||
if (isBond)
|
||||
{
|
||||
// 债券报价(×100)转入库小数(×0.01),价格字段统一走 BondPriceConverter
|
||||
req.TradingAmountAvg = BondPriceConverter.ToStorage(req.TradingAmountAvg);
|
||||
@@ -769,7 +770,10 @@ namespace YLErp.Modules.SwapModule
|
||||
// 数量×100(万手→手),与价格维度无关,保留常量
|
||||
req.TradingQty *= ConsGlobal.bondShowPriceMultiple;
|
||||
}
|
||||
req.TradingAmountAvg = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
req.TradingAmountAvg = Math.Round(
|
||||
req.TradingAmountAvg,
|
||||
isBond ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
|
||||
MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace YLErp.Modules.SwapModule
|
||||
swapFlow.DataState = (int)SwapFlowDateStateEnum.等待完成;
|
||||
}
|
||||
// 债券报价(×100)转入库小数(×0.01),统一走 BondPriceConverter
|
||||
swapFlow.TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.deal_full_price ?? 0), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
swapFlow.TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.deal_full_price ?? 0), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
swapFlow.TradingAmountFeeAvg = BondPriceConverter.ToStorage(item.deal_full_price_include_fee ?? 0);
|
||||
swapFlow.TradingAmount = swapFlow.TradingQty * swapFlow.ContractSize * swapFlow.TradingAmountAvg;
|
||||
swapFlow.ClientId = Convert.ToInt32(item.client_id ?? 0);
|
||||
@@ -451,7 +451,7 @@ namespace YLErp.Modules.SwapModule
|
||||
swap_flow_summary.FirstFlowTime = swapflow.OptTime;
|
||||
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
|
||||
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty;
|
||||
swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
|
||||
swap_flow_summary.TradingAmountFeeAvg = Math.Round(swap_flow_summary.TradingAmountFeeAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty;
|
||||
@@ -508,7 +508,7 @@ namespace YLErp.Modules.SwapModule
|
||||
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
|
||||
swap_flow_summary.TradingAmount = swap_flow_summary.TradingQty * swap_flow_summary.ContractSize;
|
||||
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.FullPrice * s.TradingQty) / swap_flow_summary.TradingQty;
|
||||
swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
|
||||
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.NetPrice * s.TradingQty) / swap_flow_summary.TradingQty;
|
||||
swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
|
||||
@@ -1082,7 +1082,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var ratio = flowMergeClone.BsType == 1 ? 1 : -1;
|
||||
var oriRatio = flowMergeClone.BsType == 1 ? -1 : 1;
|
||||
flowMergeClone.TradingFeePending = flowMergeClone.TradingQty / origin.TradingQty * origin.TradingFeePending;
|
||||
flowMergeClone.TradingAmountAvg = Math.Round(origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
|
||||
flowMergeClone.TradingAmountAvg = Math.Round(origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
flowMergeClone.TradingAmountNetAvg = origin.TradingAmountNetAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty;
|
||||
|
||||
flowMergeClone.TradingAmountFeeAvg = flowMergeClone.TradingAmountAvg + ratio * flowMergeClone.TradingFeePending / flowMergeClone.TradingQty;
|
||||
|
||||
@@ -62,6 +62,13 @@ namespace YLErp.Modules.SwapModule
|
||||
return roundedPrice;
|
||||
}
|
||||
|
||||
private static decimal? RoundSwapBondNetPriceAndYtm(decimal? value)
|
||||
{
|
||||
return value.HasValue
|
||||
? Math.Round(value.Value, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero)
|
||||
: null;
|
||||
}
|
||||
|
||||
#region 互换交易保存
|
||||
/// <summary>
|
||||
/// 新版收益互换预付金校验
|
||||
@@ -361,7 +368,7 @@ namespace YLErp.Modules.SwapModule
|
||||
td.ValidState = "Valid";
|
||||
td.TradeSource = "系统交易";
|
||||
td.TradeStatus = ConsTrade.确认成交;
|
||||
td.InitYtm = flowMerge.InitYtm ?? 0;
|
||||
td.InitYtm = RoundSwapBondNetPriceAndYtm(flowMerge.InitYtm) ?? 0;
|
||||
return td;
|
||||
}
|
||||
/// <summary>
|
||||
@@ -400,7 +407,7 @@ namespace YLErp.Modules.SwapModule
|
||||
OptId = UserInfo.UserId,
|
||||
OptName = UserInfo.UserName,
|
||||
UnderlyingInstrumentType = underlying.UnderlyingInstrumentType,
|
||||
InitYtm = flowMerge.InitYtm
|
||||
InitYtm = RoundSwapBondNetPriceAndYtm(flowMerge.InitYtm)
|
||||
};
|
||||
td.swap_positions.Add(floatPosition);
|
||||
swap_position interestPosition = new swap_position()
|
||||
@@ -593,7 +600,7 @@ namespace YLErp.Modules.SwapModule
|
||||
dbTrade.trade_extend = req.trade_extend;
|
||||
dbTrade.swap_positions = req.swap_positions;
|
||||
dbTrade.MetaDic = req.MetaDic;
|
||||
dbTrade.InitYtm = req.swap_positions.FirstOrDefault(p => p.InitYtm != null)?.InitYtm;
|
||||
dbTrade.InitYtm = RoundSwapBondNetPriceAndYtm(req.swap_positions.FirstOrDefault(p => p.InitYtm != null)?.InitYtm);
|
||||
InnerSaveTrade(false, dbTrade, changsStr, changeConfirmStatus);
|
||||
|
||||
return dbTrade;
|
||||
@@ -1380,7 +1387,7 @@ namespace YLErp.Modules.SwapModule
|
||||
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;
|
||||
position.PosiNetNoFeePrice = RoundSwapBondNetPriceAndYtm(swap.PosiNetNoFeePrice);
|
||||
position.PosiNetFeePrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiNetNoFeePrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
|
||||
position.PosiNetFeePrice = Math.Round(position.PosiNetFeePrice??0, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
|
||||
position.PosiNotionalValue = Math.Round(swap.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||||
@@ -1405,10 +1412,10 @@ namespace YLErp.Modules.SwapModule
|
||||
position.FloatRateUnderlyingCode = swap.FloatRateUnderlyingCode;
|
||||
position.interest_rest_days = swap.interest_rest_days;
|
||||
position.interest_rule = swap.interest_rule;
|
||||
position.InitYtm = swap.InitYtm;
|
||||
if (swap.InitYtm != null && swap.InitYtm > 0)
|
||||
position.InitYtm = RoundSwapBondNetPriceAndYtm(swap.InitYtm);
|
||||
if (position.InitYtm != null && position.InitYtm > 0)
|
||||
{
|
||||
td.InitYtm = swap.InitYtm;
|
||||
td.InitYtm = position.InitYtm;
|
||||
}
|
||||
|
||||
if (position.id == 0)
|
||||
|
||||
@@ -455,10 +455,10 @@
|
||||
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapBondDeliveryPrice" v-on:input="changeSpotPrice(item)"></vue-number-input>
|
||||
</td>
|
||||
<td v-if="trade.StructureType!='普通收益互换'">
|
||||
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiNetNoFeePrice')" v-model="item.PosiNetNoFeePrice" v-bind:format="inputFormatMarginRateNoPercent"></vue-number-input>
|
||||
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiNetNoFeePrice')" v-model="item.PosiNetNoFeePrice" v-bind:format="inputFormatSwapBondNetPriceAndYtm"></vue-number-input>
|
||||
</td>
|
||||
<td v-if="trade.StructureType!='普通收益互换'">
|
||||
<vue-number-input :key="getPosiPriceFormatKey(item,'InitYtm')" v-model="item.InitYtm" v-bind:format="inputFormatMarginRateNoPercent"></vue-number-input>
|
||||
<vue-number-input :key="getPosiPriceFormatKey(item,'InitYtm')" v-model="item.InitYtm" v-bind:format="inputFormatSwapBondNetPriceAndYtm"></vue-number-input>
|
||||
</td>
|
||||
<td v-if="trade.StructureType=='普通收益互换'">
|
||||
<vue-number-input :key="getPosiPriceFormatKey(item,'normalPosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapDeliveryPrice" v-on:input="changeSpotPrice(item)"></vue-number-input>
|
||||
|
||||
@@ -79,7 +79,8 @@ const vue = new Vue({
|
||||
return SwapCalc.getPriceScale(this.multiplier);
|
||||
},
|
||||
getStorageDeliveryPrice() {
|
||||
return _.round(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), 9);
|
||||
const precision = inputFormatSwapDeliveryPrice.precision + (this.multiplier === 100 ? 2 : 0);
|
||||
return SwapCalc.roundHalfAwayFromZero(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), precision);
|
||||
},
|
||||
initDeal() {
|
||||
var positions = model.FlowEvents.filter((item) => {
|
||||
|
||||
@@ -13,9 +13,10 @@ const inputFormatSwapRate = Object.freeze({ precision: otcformat.trading.premium
|
||||
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradePrice.precision, negative: true, append: '' });
|
||||
const inputFormatTradeSinglePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '', percent: false });
|
||||
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
|
||||
const inputFormatMarginRateNoPercent = Object.freeze({ precision: otcformat.trading.umpriceP.precision, append: '', percent:true });
|
||||
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: false });
|
||||
const inputFormatSwapBondDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
|
||||
const inputFormatSwapBondNetPriceAndYtm = Object.freeze({ precision: 9, negative: true, append: '', percent: true });
|
||||
const swapBondStoragePricePrecision = inputFormatSwapBondDeliveryPrice.precision + 2;
|
||||
|
||||
const consUnderlyingFlagBase = (function () {
|
||||
let unSelFlag = tradeHelper.UnderlyingSelectFlag;
|
||||
@@ -221,6 +222,15 @@ const vue = new Vue({
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
roundStorageDeliveryPrice(item, price) {
|
||||
const precision = tradeHelper.IsBond(item && item.UnderlyingInstrumentType)
|
||||
? swapBondStoragePricePrecision
|
||||
: inputFormatSwapDeliveryPrice.precision;
|
||||
return _.round(Number(price), precision);
|
||||
},
|
||||
roundStorageBondNetPriceAndYtm(value) {
|
||||
return value == null ? value : _.round(Number(value), swapBondStoragePricePrecision);
|
||||
},
|
||||
getPosiPriceFormatKey(item, field) {
|
||||
const index = item && item.index != null ? item.index : '';
|
||||
const isBond = tradeHelper.IsBond(item && item.UnderlyingInstrumentType);
|
||||
@@ -279,7 +289,7 @@ const vue = new Vue({
|
||||
//计算数量
|
||||
if (this.paySwapList.length > 0) {
|
||||
var item = this.paySwapList[0];
|
||||
var deliveryPrice = _.round(Number(item.PosiGrossPrice), 9);
|
||||
var deliveryPrice = this.roundStorageDeliveryPrice(item, item.PosiGrossPrice);
|
||||
var notional = deliveryPrice * item.ContractSize;
|
||||
item.PosiQuantity = notional == 0 ? 0 : _.round(this.trade.StockEqvNotional / notional, page.otcFormatConfig.StockEqvNotional.precision);
|
||||
this.calcNotional();
|
||||
@@ -361,7 +371,7 @@ const vue = new Vue({
|
||||
}
|
||||
var national = payItem.PosiQuantity * payItem.ContractSize;
|
||||
// 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional
|
||||
var deliveryPrice = _.round(Number(payItem.PosiGrossPrice), 9);
|
||||
var deliveryPrice = this.roundStorageDeliveryPrice(payItem, payItem.PosiGrossPrice);
|
||||
var stockEqvNotional = SwapCalc.calcStockEqvNotional(deliveryPrice, national);//名义本金=期初价格*数量*乘数
|
||||
this.trade.StockEqvNotional = otcformat.trading.stockEqvNotional(stockEqvNotional);
|
||||
payItem.PosiNotionalValue = this.trade.StockEqvNotional;
|
||||
@@ -506,7 +516,9 @@ const vue = new Vue({
|
||||
errorcount++;
|
||||
return false;
|
||||
}
|
||||
x.PosiGrossPrice = _.round(Number(x.PosiGrossPrice), 9);
|
||||
x.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(x, x.PosiGrossPrice);
|
||||
x.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(x.PosiNetNoFeePrice);
|
||||
x.InitYtm = x.InitYtm == null ? null : thisObj.roundStorageBondNetPriceAndYtm(x.InitYtm);
|
||||
thisObj.trade.swap_positions.push(x);
|
||||
});
|
||||
} else {
|
||||
@@ -632,8 +644,8 @@ const vue = new Vue({
|
||||
var thisObj = this;
|
||||
main.post("/pricing/AjaxGetUnderlyingPrice", { underlyingCode: underlyingCode, tradeDate: StartDate })
|
||||
.done(function (resp) {
|
||||
item.PosiNetNoFeePrice = otcformat.trading.umprice(resp.obj.netPrice);
|
||||
item.PosiGrossPrice = _.round(Number(resp.obj.price), 9);
|
||||
item.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(resp.obj.netPrice);
|
||||
item.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(item, resp.obj.price);
|
||||
thisObj.calcNotional();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -42,7 +42,8 @@ const vue = new Vue({
|
||||
return this.multiplier == 100 ? 0.01 : 1;
|
||||
},
|
||||
getStorageDeliveryPrice() {
|
||||
return _.round(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), 9);
|
||||
const precision = inputFormatSwapDeliveryPrice.precision + (this.multiplier === 100 ? 2 : 0);
|
||||
return SwapCalc.roundHalfAwayFromZero(Number(this.floatPosition.TradingAmountAvg) * this.getPriceScale(), precision);
|
||||
},
|
||||
initDeal() {
|
||||
var positions = model.FlowEvents.filter((item) => {
|
||||
|
||||
Reference in New Issue
Block a user