Merge branch 'glms/feature/dotnumber' into glms/feature/1.4.2

# Conflicts:
#	YLErpWeb/Views/SwapTrade2/TradeEdit.cshtml
#	YLErpWeb/wwwroot/Scripts/app/swaptrade/swapTradeEdit.js
This commit is contained in:
张名锐
2026-07-23 17:53:37 +08:00
22 changed files with 319 additions and 109 deletions
+4
View File
@@ -64,6 +64,10 @@ namespace YLErp
public const int PriceRound = 11;
/// <summary>
/// 互换期初、期末交割价四舍五入保留位数
/// </summary>
public const int SwapDeliveryPriceRound = 9;
/// <summary>
/// 金额四舍五入保留位数
/// </summary>
@@ -237,5 +237,26 @@ namespace YLErp.Modules.SwapModule
Assert.AreEqual("确认成交", td.TradeStatus, "部分平仓 TradeStatus 保持不变");
Console.WriteLine($"UW_008: A=0.3→B={service.SaveSwapDealCalls[0].data.ClosePercent}, HasPartialUnWind={td.HasPartialUnWind} ✅");
}
[TestMethod]
public void UW_009_SwapUnwind_名义本金写入前舍入两位小数()
{
var td = SwapDealTestFactory.CreateTrade();
td.StockEqvNotional = 1000000.006;
var service = new TestableSwapDealService(td);
var unwindData = SwapDealTestFactory.CreateUnwindData(
swapRealizedPnL: 0m, closeMethod: (int)CloseMethodEnum., closePercent: 0.5m,
closeQty: 5000m, closeNotionalValue: 500000.004m, positionQty: 10000m);
unwindData.NotionalValue = 1000000.006m;
unwindData.PosiNotionalValue = 1000000.006m;
service.SwapUnwind(unwindData);
var savedData = service.SaveSwapDealCalls[0].data;
Assert.AreEqual(1000000.01m, savedData.NotionalValue, "期初名义本金应按两位小数写入事件");
Assert.AreEqual(1000000.01m, savedData.PosiNotionalValue, "剩余名义本金应按两位小数写入事件");
Assert.AreEqual(500000.00m, savedData.CloseNotionalValue, "平仓名义本金应按两位小数写入事件");
Assert.AreEqual(500000.01, td.StockEqvNotional, 0.000001, "trade 剩余名义本金应在扣减后舍入两位小数");
}
}
}
@@ -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 = result.TradingAmountAvg;
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;
+66 -4
View File
@@ -36,9 +36,61 @@ namespace YLErp.Modules.SwapModule
/// 原 private 改 protected virtual,使测试 stub 可整体 override,规避内部 new SwapEventService 连库。</summary>
protected virtual long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
{
NormalizeNotionalValues(unwindData);
NormalizeDeliveryPrices(unwindData.FlowEvents);
return SaveSwapDealInternal(unwindData, eventType, clientCashId, eventResason, approve);
}
private static void NormalizeNotionalValues(UnwindData unwindData)
{
unwindData.NotionalValue = Math.Round(unwindData.NotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
unwindData.PosiNotionalValue = Math.Round(unwindData.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
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)
{
return;
}
foreach (var item in unwindData.FlowEvents.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)))
{
var roundedPrice = Math.Round(item.TradingAmountAvg, GetStorageDeliveryPriceRound(item), MidpointRounding.AwayFromZero);
if (item.TradingAmountAvg != roundedPrice)
{
throw new ServiceException($"期末交割价最多保留{ConsGlobal.SwapDeliveryPriceRound}位小数");
}
item.TradingAmountAvg = roundedPrice;
}
}
private static void NormalizeDeliveryPrices(IEnumerable<swap_flow_event> flowEvents)
{
if (flowEvents == null)
{
return;
}
foreach (var item in flowEvents.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)))
{
item.TradingAmountAvg = Math.Round(item.TradingAmountAvg, GetStorageDeliveryPriceRound(item), MidpointRounding.AwayFromZero);
}
}
/// <summary>保存所有变更(生产: DbContext.SaveChanges;测试: 空操作)</summary>
protected virtual void SaveAllChanges()
{
@@ -1222,6 +1274,8 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
ValidateDeliveryPrices(unwindData);
NormalizeNotionalValues(unwindData);
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
ValidateFrontendPnL(unwindData, isIncome: false); // 只读校验告警,不阻断交易
// 前端按"占期初(original)"语义传 ClosePercent(A);后端全链路按"占剩余(remaining)"语义(B)消费。
@@ -1252,7 +1306,7 @@ namespace YLErp.Modules.SwapModule
td.HasPartialUnWind = 1;
}
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
SaveAllChanges();
cofirm = true;
@@ -1278,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();
@@ -1568,7 +1626,7 @@ namespace YLErp.Modules.SwapModule
td.HasPartialUnWind = 1;
}
td.UnWindDate = unwindData.UnwindDate;
td.StockEqvNotional -= Convert.ToDouble(unwindData.CloseNotionalValue);
td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount -= Convert.ToDouble(unwindData.CloseQty);
td.Notional = td.TradeAmount;
td.OptDate = DateTime.Now;
@@ -1701,6 +1759,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
ValidateDeliveryPrices(unwindData);
NormalizeIncomeUnwindDate(unwindData);
ValidateIncomeValueDate(unwindData, td);
//CheckLastEod(unwindData.ValueDate, td.StartDate.Value, unwindData.SwapTradeId); //去掉平仓收盘限制
@@ -1741,12 +1800,14 @@ namespace YLErp.Modules.SwapModule
throw new Exception("该笔交易状态为平仓待复核,未找到相关记录,请检查该笔交易是否有效");
}
swapEvent.unwindData = JsonConvert.DeserializeObject<UnwindData>(swapEvent.EventData);
NormalizeDeliveryPrices(swapEvent.unwindData.FlowEvents);
if (eventType == (int)SwapEventTypeEnum.)
{
NormalizeIncomeUnwindDate(swapEvent.unwindData);
ValidateIncomeValueDate(swapEvent.unwindData, td);
}
var flowList = FindFlowEventsByEventId(swapEvent.id);
NormalizeDeliveryPrices(flowList);
string action = eventType == (int)SwapEventTypeEnum. ? ClientCashInCashOut._互换 : ClientCashInCashOut._平仓费;
int clientCashId = AddClientCash(td, Convert.ToDouble(-swapEvent.unwindData.SwapRealizedPnL), action, swapEvent.unwindData.ValueDate);
if (swapEvent.unwindData.SwapMarginAmount != 0)
@@ -1767,7 +1828,7 @@ namespace YLErp.Modules.SwapModule
td.UnWindDate = swapEvent.unwindData.UnwindDate;
if (eventType != (int)SwapEventTypeEnum.)
{
td.StockEqvNotional -= Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue);
td.StockEqvNotional = Math.Round(td.StockEqvNotional - Convert.ToDouble(swapEvent.unwindData.CloseNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount -= Convert.ToDouble(swapEvent.unwindData.CloseQty);
}
@@ -1789,6 +1850,7 @@ namespace YLErp.Modules.SwapModule
{
throw new ServiceException("未找到交易信息");
}
ValidateDeliveryPrices(unwindData);
if (eventType == (int)SwapEventTypeEnum.)
{
NormalizeIncomeUnwindDate(unwindData);
@@ -1933,7 +1995,7 @@ namespace YLErp.Modules.SwapModule
{
// 平仓时才扣减持仓
position.PosiQuantity -= unwindData.CloseQty;
position.PosiNotionalValue -= unwindData.CloseNotionalValue;
position.PosiNotionalValue = Math.Round(position.PosiNotionalValue - unwindData.CloseNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.PosiTradingFee -= position.PosiTradingFee * unwindData.ClosePercent;
position.PosiTradingFeePending -= position.PosiTradingFeePending * unwindData.ClosePercent;
}
@@ -32,11 +32,30 @@ 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 Seamsoverride DB/
/// <summary>持久化 eod 持仓记录(生产: DbContext.Add;测试: 收集到列表)</summary>
protected virtual void PersistEodSwapPosition(eod_swap_position position)
{
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)
{
DbContext.eod_swap_position.Add(position);
@@ -156,34 +175,18 @@ namespace YLErp.Modules.SwapModule
return UnderlyingCodePrice(code, settleDate, out vobp);
}
/// <summary>
/// 获取用于互换浮动腿盯市的标的价格。
///
/// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后
/// PosiGrossPrice 为 0.2;而历史交易中仍可能存在直接保存为 20 的展示态价格。
/// 中债估值正常经 EodPriceQueryService 转换后应为小数价格,但手工维护的历史
/// 行情可能仍以展示态进入该服务,例如 2000 经一次转换后得到 20。若将 20
/// 与 0.2 直接相减,会把 20% 的价格差误算成 1,980,000 的浮动损益。
///
/// 因此仅当交易期初价已经是小数口径、且当前债券价明显仍处于展示态时,再做
/// 一次展示态到存储态转换。期初价本身是历史展示态口径的存量交易保持原价格,
/// 避免修改日终估值链路后改变其既有损益。
/// </summary>
private decimal GetSwapValuationPrice(string code, decimal posiGrossPrice, DateTime settleDate, out decimal vobp)
/// <summary>获取用于互换浮动腿盯市的标的价格。</summary>
private decimal GetSwapValuationPrice(string code, DateTime settleDate, out decimal vobp)
{
var price = GetUnderlyingPrice(code, settleDate, out vobp);
var underlying = GetUnderlyingData(code);
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}");
return normalizedPrice;
return Math.Round(price, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
}
return price;
return Math.Round(price, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
}
/// <summary>获取标的缓存数据(生产: DataCacheProvider;测试: 返回内存对象)</summary>
@@ -1472,7 +1475,10 @@ namespace YLErp.Modules.SwapModule
newEodPayPosition.ContractSize = eventFlow.ContractSize;
newEodPayPosition.CountRatio = eventFlow.CountRatio;
newEodPayPosition.PosiNetPrice = netPrice;
newEodPayPosition.PosiGrossPrice = grossPrice;
newEodPayPosition.PosiGrossPrice = Math.Round(
grossPrice,
GetStorageDeliveryPriceRound(eventFlow.UnderlyingInstrumentType, eventFlow.UnderlyingCode),
MidpointRounding.AwayFromZero);
newEodPayPosition.PosiNetFeePrice = netFeePrice;
newEodPayPosition.PosiNetNoFeePrice = netNoFeePrice;
newEodPayPosition.PosiQuantity = payQty;
@@ -1557,7 +1563,7 @@ namespace YLErp.Modules.SwapModule
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum. ? 1 : -1;
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
var price = GetSwapValuationPrice(eod.UnderlyingCode, eod.PosiGrossPrice, dealDate, out decimal vobp);
var price = GetSwapValuationPrice(eod.UnderlyingCode, dealDate, out decimal vobp);
curretEod.dv01 = Dv01Helper.CalcDv01(eod.UnderlyingCode, curretEod.PosiQuantity, eod.PosiDirection, eod.PositionType, vobp);
decimal tax = um.ValueAddedTax ?? 0;
if (valueDate > td.StartDate.Value && curretEod.PosiQuantity > 0)
@@ -1649,7 +1655,7 @@ namespace YLErp.Modules.SwapModule
var dealDate = curretEod.ValueDate;
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum. ? 1 : -1;
var price = GetSwapValuationPrice(eod.UnderlyingCode, eod.PosiGrossPrice, dealDate, out decimal vobp);
var price = GetSwapValuationPrice(eod.UnderlyingCode, dealDate, out decimal vobp);
var todayConsumedDividend = CalcConsumedDividend(curretEod, unwindEvents);
var originNotional = (decimal)td.OriginalStockEqvNotional / swapPosition.PosiNetPrice;
decimal totalPayment = CalcBondPayment(curretEod.UnderlyingCode, td.StartDate.Value, valueDate, (decimal)originNotional, shortRatio, directionRatio);
@@ -1771,7 +1777,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.PriceRound, 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);
@@ -1835,7 +1844,7 @@ namespace YLErp.Modules.SwapModule
curretEod.ContractSize = position.ContractSize;
curretEod.CountRatio = position.CountRatio;
curretEod.PosiTradingFee = position.PosiTradingFee;
curretEod.UnderlyingPrice = GetSwapValuationPrice(position.UnderlyingCode, position.PosiGrossPrice, dealDate, out decimal vobp);
curretEod.UnderlyingPrice = GetSwapValuationPrice(position.UnderlyingCode, dealDate, out decimal vobp);
SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, position);
curretEod.dv01 = Dv01Helper.CalcDv01(curretEod.UnderlyingCode, curretEod.PosiQuantity, curretEod.PosiDirection, curretEod.PositionType, vobp);
//if (settleDate == td.TradeDate)
@@ -1897,14 +1906,14 @@ namespace YLErp.Modules.SwapModule
}
if (data.IsBond())
{
return BondPrice(data, settleDate, out vobp);
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))
{
price = eodPrice.GetPrice(SettlementTypeEnum.ClosePrice);
}
return Convert.ToDecimal(price);
return Math.Round(Convert.ToDecimal(price), ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
}
/// <summary>
/// 获取债券收盘价格
@@ -1949,9 +1958,9 @@ namespace YLErp.Modules.SwapModule
var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿
// 框架合约的方向约定:多头为正、空头为负;总名义本金取交易原始规模,
// 不能直接用多空腿相加,否则会把对冲方向误当成合约规模变化。
eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
eod_Swap.NotionalValueShort = -Math.Abs(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue));
eod_Swap.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? td.StockEqvNotional);
eod_Swap.NotionalValueLong = Math.Round(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
eod_Swap.NotionalValueShort = Math.Round(-Math.Abs(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue)), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
eod_Swap.NotionalValue = Math.Round(Convert.ToDecimal(td.OriginalStockEqvNotional ?? td.StockEqvNotional), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
eod_Swap.SwapTradeId = td.id;
eod_Swap.SwapTradeNo = td.TradeNumber;
eod_Swap.ClientId = td.ClientId;
@@ -2026,9 +2035,9 @@ namespace YLErp.Modules.SwapModule
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList();
var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿
var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿
eod_Swap.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? td.StockEqvNotional);
eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
eod_Swap.NotionalValueShort = -Math.Abs(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue));
eod_Swap.NotionalValue = Math.Round(Convert.ToDecimal(td.OriginalStockEqvNotional ?? td.StockEqvNotional), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
eod_Swap.NotionalValueLong = Math.Round(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
eod_Swap.NotionalValueShort = Math.Round(-Math.Abs(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue)), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
eod_Swap.MarketValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.UnderlyingMarketValue);
eod_Swap.MarketValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.UnderlyingMarketValue);
eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum);
@@ -2318,24 +2327,23 @@ namespace YLErp.Modules.SwapModule
item.PeriodPaymentValuation = item.FloatingUnrealizedPnl + item.position.InterestPnL;
}
// eod_swap 的保证金本金来自 trade_span;缺少 span 数据时会被保存为 0。
// 本风险页改按日终保证金腿展示,且该页面保证金本金采用原始本金的 1/10 口径
// 利息仍使用原始本金累积值,不能同步缩放,否则会破坏保证金利息金额。
// 本风险页改按日终保证金腿的实际本金展示。
item.position.InitMarginGain = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.
&& x.InterestDirection == (int)SwapDirectionEnum.)
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
.Sum(x => Math.Abs(x.InterestPrincipalFix));
item.position.InitMarginLoss = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.
&& x.InterestDirection == (int)SwapDirectionEnum.)
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
.Sum(x => Math.Abs(x.InterestPrincipalFix));
item.position.PostionMarginGain = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.
&& x.InterestDirection == (int)SwapDirectionEnum.)
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
.Sum(x => Math.Abs(x.InterestPrincipalFix));
item.position.PostionMarginLoss = marginLegs
.Where(x => x.InterestMode == (int)InterestModeEnum.
&& x.InterestDirection == (int)SwapDirectionEnum.)
.Sum(x => Math.Abs(x.InterestPrincipalFix)) / 10m;
.Sum(x => Math.Abs(x.InterestPrincipalFix));
// 保证金本金方向与我方的利息现金流方向相反:原始“收取”保证金
// 表示我方占用客户资金,应向客户支付利息;支付金额按负数展示。
@@ -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);
@@ -61,6 +76,13 @@ namespace YLErp.Modules.SwapModule
{
foreach (var evt in events)
{
if (!string.IsNullOrEmpty(evt.UnderlyingCode))
{
evt.TradingAmountAvg = Math.Round(
evt.TradingAmountAvg,
GetStorageDeliveryPriceRound(evt.UnderlyingInstrumentType, evt.UnderlyingCode),
MidpointRounding.AwayFromZero);
}
DbContext.swap_flow_event.Add(evt);
}
DbContext.SaveChanges();
@@ -321,7 +343,7 @@ namespace YLErp.Modules.SwapModule
DataState = 1,
EventDate = flow_merge.OccurTime,
UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)),
TradingAmountAvg = TradingAmountAvg,
TradingAmountAvg = Math.Round(TradingAmountAvg, GetStorageDeliveryPriceRound(underlyingInstrumentType, flow_merge.UnderlyingCode), MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = TradingAmountFeeAvg,
TradingAmountNetFeeAvg = TradingAmountNetFeeAvg,
TradingAmountNetAvg = flow_merge.TradingAmountNetAvg,
@@ -375,7 +397,10 @@ namespace YLErp.Modules.SwapModule
DataState = 1,
EventDate = flow_merge.OccurTime,
UnwindDate = QdpCalendarHelper.GetNonHoliday(flow_merge.OccurTime.AddDays(1)),
TradingAmountAvg = flow_merge.TradingAmountAvg,
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
@@ -409,7 +434,10 @@ namespace YLErp.Modules.SwapModule
DataState = (int)SwapFlowDateStateEnum.,
EventDate = td.TradeDate.Value,
UnwindDate = td.StartDate.Value,
TradingAmountAvg = position.PosiGrossPrice,
TradingAmountAvg = Math.Round(
position.PosiGrossPrice,
GetStorageDeliveryPriceRound(position.UnderlyingInstrumentType, position.UnderlyingCode),
MidpointRounding.AwayFromZero),
TradingAmountFeeAvg = position.PosiNetPrice,
TradingAmountNetFeeAvg = position.PosiNetFeePrice,
TradingAmountNetAvg = position.PosiNetNoFeePrice,
@@ -462,7 +490,7 @@ namespace YLErp.Modules.SwapModule
DataState = 100,
EventDate = td.TradeDate.Value,
UnwindDate = QdpCalendarHelper.GetNonHoliday(td.TradeDate.Value.AddDays(1)),
TradingAmountAvg = flowMerge.TradingAmountAvg,
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,6 +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,
isBond ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
MidpointRounding.AwayFromZero);
if (!string.IsNullOrEmpty(clientName))
{
var client = DataCacheProvider.GetClientDataSource().AsQueryable(x=>x.Name== clientName).FirstOrDefault();
+12 -2
View File
@@ -695,7 +695,7 @@ namespace YLErp.Modules.SwapModule
TradingFee = gourpItem.Sum(s => s.TradingFee),
DataState = (int)SwapFlowDateStateEnum.,
TradingAmountFeeAvg = gourpItem.Average(s => s.TradingAmountFeeAvg),
TradingAmountAvg = gourpItem.Average(s => s.TradingAmountAvg),
TradingAmountAvg = Math.Round(gourpItem.Average(s => s.TradingAmountAvg), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero),
ContractSize = swapflow.ContractSize
};
UpdateDbOption(swap_flow_summary);
@@ -747,12 +747,18 @@ namespace YLErp.Modules.SwapModule
private void CheckValid(swap_flow req)
{
CheckRequired(req);
var roundedPrice = Math.Round(req.TradingAmountAvg, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
if (req.TradingAmountAvg != roundedPrice)
{
throw new ServiceException($"成交全价最多保留{ConsGlobal.SwapDeliveryPriceRound}位小数");
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
if (underlying == null)
{
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);
@@ -764,6 +770,10 @@ namespace YLErp.Modules.SwapModule
// 数量×100(万手→手),与价格维度无关,保留常量
req.TradingQty *= ConsGlobal.bondShowPriceMultiple;
}
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 = BondPriceConverter.ToStorage(item.deal_full_price ?? 0);
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);
@@ -508,6 +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.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;
@@ -1081,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 = origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty;
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,7 +62,7 @@ namespace YLErp.Modules.SwapModule
position.PosiGrossPrice = eodPayPosition.PosiGrossPrice;
position.PosiNetFeePrice = eodPayPosition.PosiNetFeePrice;
position.PosiNetNoFeePrice = eodPayPosition.PosiNetNoFeePrice;
position.PosiNotionalValue = eodPayPosition.PosiNotionalValue;
position.PosiNotionalValue = Math.Round(eodPayPosition.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.PosiQuantity = eodPayPosition.PosiQuantity;
position.PosiStartDate = eodPayPosition.PosiStartDate;
position.OptTime = DateTime.Now;
@@ -85,7 +85,7 @@ namespace YLErp.Modules.SwapModule
position.PosiGrossPrice = eodPayPosition.PosiGrossPrice;
position.PosiNetFeePrice = eodPayPosition.PosiNetFeePrice;
position.PosiNetNoFeePrice = eodPayPosition.PosiNetNoFeePrice;
position.PosiNotionalValue = eodPayPosition.PosiNotionalValue;
position.PosiNotionalValue = Math.Round(eodPayPosition.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.PosiTradingFeePending = eodPayPosition.PosiFeePending;
position.PosiQuantity = eodPayPosition.PosiQuantity;
position.PosiDirection = eodPayPosition.PosiDirection;
+56 -21
View File
@@ -51,6 +51,24 @@ namespace YLErp.Modules.SwapModule
{
}
private static decimal ValidateDeliveryPrice(decimal price, string fieldName)
{
var roundedPrice = Math.Round(price, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero);
if (price != roundedPrice)
{
throw new ServiceException($"{fieldName}最多保留{ConsGlobal.SwapDeliveryPriceRound}位小数");
}
return roundedPrice;
}
private static decimal? RoundSwapBondNetPriceAndYtm(decimal? value)
{
return value.HasValue
? Math.Round(value.Value, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero)
: null;
}
#region
/// <summary>
/// 新版收益互换预付金校验
@@ -334,7 +352,7 @@ namespace YLErp.Modules.SwapModule
AssetBookName = asset.Name,
Notional = Convert.ToDouble(flowMerge.TradingQtyAbs),
TradeAmount = Convert.ToDouble(flowMerge.TradingQtyAbs),
StockEqvNotional = Convert.ToDouble(flowMerge.TradingAmount),
StockEqvNotional = Math.Round(Convert.ToDouble(flowMerge.TradingAmount), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero),
IsAutoGenerate = true,
};
if (flowMerge.SettleDate.HasValue)
@@ -352,7 +370,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>
@@ -372,11 +390,14 @@ namespace YLErp.Modules.SwapModule
CountRatio = underlying.CountRatio,
ContractSize = Convert.ToDecimal(underlying.ContractSize),
PosiNetPrice = flowMerge.TradingAmountFeeAvgAbs,
PosiGrossPrice = flowMerge.TradingAmountAvg,
PosiGrossPrice = Math.Round(
flowMerge.TradingAmountAvg,
underlying.IsBond() ? ConsGlobal.PriceRound : ConsGlobal.SwapDeliveryPriceRound,
MidpointRounding.AwayFromZero),
PosiNetFeePrice = flowMerge.TradingAmountNetFeeAvg ?? 0,
PosiNetNoFeePrice = flowMerge.TradingAmountNetAvg ?? 0,
PosiQuantity = flowMerge.TradingQtyAbs,
PosiNotionalValue = flowMerge.TradingAmount,
PosiNotionalValue = Math.Round(flowMerge.TradingAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero),
PosiTradingFeePending = flowMerge.TradingFeePending,
PosiTradingFee = 0,
PosiTradingFeeUnit = 0,
@@ -388,7 +409,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()
@@ -581,7 +602,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;
@@ -675,11 +696,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)
{
@@ -722,6 +739,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)
{
@@ -764,6 +792,7 @@ namespace YLErp.Modules.SwapModule
req.SpotPrice = Convert.ToDouble(swapPosition.PosiNetPrice);
}
req.Strike = null;
req.StockEqvNotional = Math.Round(req.StockEqvNotional, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
req.OriginalStockEqvNotional = req.StockEqvNotional;
req.StockEqvNotionalReal = req.StockEqvNotional;
@@ -1351,13 +1380,19 @@ namespace YLErp.Modules.SwapModule
position.UnderlyingCode = swap.UnderlyingCode;
position.UnderlyingInstrumentType = swap.UnderlyingInstrumentType;
position.PosiDirection = swap.PosiDirection;
position.PosiGrossPrice = swap.PosiGrossPrice;
position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
// position.PosiGrossPrice = string.IsNullOrEmpty(swap.UnderlyingCode)
// ? Math.Round(swap.PosiGrossPrice, ConsGlobal.SwapDeliveryPriceRound, MidpointRounding.AwayFromZero)
// : ValidateDeliveryPrice(swap.PosiGrossPrice, "期初交割价");
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;
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 = swap.PosiNotionalValue;
position.PosiNotionalValue = Math.Round(swap.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
position.PosiQuantity = swap.PosiQuantity;
position.InterestDirection = swap.InterestDirection;
position.InterestMode = swap.InterestMode;
@@ -1379,10 +1414,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)
@@ -1481,7 +1516,7 @@ namespace YLErp.Modules.SwapModule
td.ProcessStatus = null;
if (backToBegin)
{
td.StockEqvNotional = td.OriginalStockEqvNotional ?? 0;
td.StockEqvNotional = Math.Round(td.OriginalStockEqvNotional ?? 0, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.UnWindDate = null;
td.HasPartialUnWind = null;
SingleTradeBackToBegin(td, swapPositions);
@@ -1643,10 +1678,10 @@ namespace YLErp.Modules.SwapModule
posi.PosiGrossPrice = eodPosi.PosiGrossPrice;
posi.PosiNetFeePrice = eodPosi.PosiNetFeePrice;
posi.PosiNetNoFeePrice = eodPosi.PosiNetNoFeePrice;
posi.PosiNotionalValue = eodPosi.PosiNotionalValue;
posi.PosiNotionalValue = Math.Round(eodPosi.PosiNotionalValue, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
if (posi.PosiDirection > 0)
{
td.StockEqvNotional = Convert.ToDouble(posi.PosiNotionalValue);
td.StockEqvNotional = Math.Round(Convert.ToDouble(posi.PosiNotionalValue), ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
td.TradeAmount = Convert.ToDouble(posi.PosiQuantity);
}
}
+1 -1
View File
@@ -163,7 +163,7 @@
<!-- 期初标的交割净价: TradingAmountNetAvg 字段名为"成交净价(期末语义)",但此处后端 InitIncome 实际装入的是期初净价(position.PosiNetNoFeePrice),值是期初值 -->
<td v-if="deal.StructureType!='普通收益互换'">{{priceFormat(floatPosition.TradingAmountNetAvg > 0 ? floatPosition.TradingAmountNetAvg : floatPosition.PosiNetPrice)}}</td>
<td>
<vue-number-input v-model="floatPosition.TradingAmountAvg" v-bind:format="inputFormatEqvNotional" v-on:input="changeUnderlyingPrice" style="width:107px;"></vue-number-input>
<vue-number-input v-model="floatPosition.TradingAmountAvg" v-bind:format="inputFormatSwapDeliveryPrice" v-on:input="changeUnderlyingPrice" style="width:107px;"></vue-number-input>
<a href="javascript:void(0)" v-on:click="refreshUnderlyingPrice()">
<span title="使用系统标的价格" class="glyphicon glyphicon-refresh"></span>
</a>
+1 -1
View File
@@ -200,7 +200,7 @@
</td>
<td>{{priceFormat(floatPosition.PosiGrossPrice)}}</td>
<td>
<vue-number-input v-model="floatPosition.TradingAmountAvg" v-bind:format="inputFormatEqvNotional" v-on:input="changeUnderlyingPrice" style="width:107px;"></vue-number-input>
<vue-number-input v-model="floatPosition.TradingAmountAvg" v-bind:format="inputFormatSwapDeliveryPrice" v-on:input="changeUnderlyingPrice" style="width:107px;"></vue-number-input>
<a href="javascript:void(0)" v-on:click="refreshUnderlyingPrice()">
<span title="使用系统标的价格" class="glyphicon glyphicon-refresh"></span>
</a>
@@ -243,7 +243,7 @@
</div>
<div class="form-group">
<label class="formlabel ">成交均价</label>
<vue-number-input v-model="swapflow.TradingAmountAvg" v-bind:format="inputFormatTradePrice" v-on:input="changeSpotPrice()"></vue-number-input>
<vue-number-input v-model="swapflow.TradingAmountAvg" v-bind:format="inputFormatSwapDeliveryPrice" v-on:input="changeSpotPrice()"></vue-number-input>
</div>
@*<div class="form-group">
<label class="formlabel ">成交均价含费</label>
+4 -4
View File
@@ -452,16 +452,16 @@
</a>
</td>
<td v-if="trade.StructureType!='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatMarginRateNoPercent" v-on:input="onDpPriceInput(item)"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='DP'">源</span><span class="glyphicon glyphicon-pencil" style="color:#ef6c00;font-size:11px;margin-left:4px;cursor:default;" v-if="item.isBond && item.bondManual && item.bondManual.DP" title="手动编辑:该字段由您填写,不会被计算器反算覆盖"></span>
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapBondDeliveryPrice" v-on:input="onDpPriceInput(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" v-on:input="onBondPriceInput(item,'CP')"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='CP'">源</span><span class="glyphicon glyphicon-pencil" style="color:#ef6c00;font-size:11px;margin-left:4px;cursor:default;" v-if="item.isBond && item.bondManual && item.bondManual.CP" title="手动编辑:该字段由您填写,不会被计算器反算覆盖"></span>
<vue-number-input :key="getPosiPriceFormatKey(item,'PosiNetNoFeePrice')" v-model="item.PosiNetNoFeePrice" v-bind:format="inputFormatSwapBondNetPriceAndYtm" v-on:input="onBondPriceInput(item,'CP')"></vue-number-input>
</td>
<td v-if="trade.StructureType!='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'InitYtm')" v-model="item.InitYtm" v-bind:format="inputFormatMarginRateNoPercent" v-on:input="onBondPriceInput(item,'YD')"></vue-number-input><span style="color:#2e7d32;font-size:11px;margin-left:4px;" v-if="item.isBond && item.bondDriverType==='YD'">源</span><span class="glyphicon glyphicon-pencil" style="color:#ef6c00;font-size:11px;margin-left:4px;cursor:default;" v-if="item.isBond && item.bondManual && item.bondManual.YD" title="手动编辑:该字段由您填写,不会被计算器反算覆盖"></span><a href="javascript:void(0);" v-on:click="resetBondCalc(item)" v-if="item.isBond" style="margin-left:6px;font-size:11px;color:#1565c0;">重算</a>
<vue-number-input :key="getPosiPriceFormatKey(item,'InitYtm')" v-model="item.InitYtm" v-bind:format="inputFormatSwapBondNetPriceAndYtm" v-on:input="onBondPriceInput(item,'YD')"></vue-number-input>
</td>
<td v-if="trade.StructureType=='普通收益互换'">
<vue-number-input :key="getPosiPriceFormatKey(item,'normalPosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatTradeSinglePrice" v-on:input="changeSpotPrice(item)"></vue-number-input>
<vue-number-input :key="getPosiPriceFormatKey(item,'normalPosiGrossPrice')" v-model="item.PosiGrossPrice" v-bind:format="inputFormatSwapDeliveryPrice" v-on:input="changeSpotPrice(item)"></vue-number-input>
</td>
<td>
<vue-number-input v-model="item.PosiQuantity" v-on:input="changeQuantity(item)" v-bind:format="inputFormatTradeAmount"></vue-number-input>{{item.underlying!=null?item.underlying.QuoteUnitString:''}}
@@ -1,5 +1,6 @@
//window.otcformat.options.disableGrouping = true;
const inputFormatTradePrice = Object.freeze({ precision: otcformat.trading.tradeSinglePrice.precision, negative: true, append: '' });
const inputFormatSwapDeliveryPrice = Object.freeze({ precision: 9, negative: true, append: '' });
const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.notional.precision, append: '' });
const inputFormatMarginRate = Object.freeze({ precision: otcformat.trading.marginRateP.precision, append: '%' });
var clients = ylotc.clients;
@@ -669,7 +670,7 @@ function getColModelGridStep4() {
label: '名义本金',
width: 160,
align: 'center',
formatter: otcformat.trading.umprice
formatter: otcformat.trading.StockEqvNotional
}
, {
name: 'position.PosiTradingFee',
@@ -1198,6 +1199,7 @@ var vue = new Vue({
},
postSwapflow() {
var thisObj = this;
thisObj.swapflow.TradingAmountAvg = _.round(Number(thisObj.swapflow.TradingAmountAvg), 9);
main.post("/swaptrade2/SaveSwapflow", { req: thisObj.swapflow, step: thisObj.step }).done(function (resp) {
if (resp.success) {
getList();
@@ -1221,4 +1223,4 @@ var vue = new Vue({
'vue-underlying': vueUnderlying()
}
});
window.reloadData = getList();
window.reloadData = getList();
@@ -8,6 +8,7 @@ const inputFormatEqvNotional = Object.freeze({ precision: otcformat.trading.Stoc
const inputFormatDividend = Object.freeze({ precision: 2, append: '', negative: true });
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, append: '', negative: true });
let ValueDate = model.ValueDate;
let MaxIncomeValueDate = model.MaxIncomeValueDate ? model.MaxIncomeValueDate.substr(0, 10) : ValueDate;
@@ -77,6 +78,10 @@ const vue = new Vue({
getPriceScale() {
return SwapCalc.getPriceScale(this.multiplier);
},
getStorageDeliveryPrice() {
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) => {
return item.UnderlyingCode;
@@ -161,7 +166,7 @@ const vue = new Vue({
{ code: thisObj.floatPosition.UnderlyingCode, valuedate: thisObj.deal.ValueDate })
.done(function (res) {
res.obj = res.obj * thisObj.multiplier;
thisObj.floatPosition.TradingAmountAvg = otcformat.trading.tradeSinglePrice(res.obj);
thisObj.floatPosition.TradingAmountAvg = _.round(Number(res.obj), 9);
thisObj.calcFloatClosePnl();
});
},
@@ -180,11 +185,11 @@ const vue = new Vue({
let TradingFee = thisObj.floatPosition.TradingFee == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFee);
let TradingFeePending = thisObj.floatPosition.TradingFeePending == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFeePending);
let DividendIn = thisObj.floatPosition.DividendIn == "" ? 0 : parseFloat(thisObj.floatPosition.DividendIn ?? 0);
let scale = thisObj.getPriceScale();
let deliveryPrice = thisObj.getStorageDeliveryPrice();
// 债券全价是单位价格,价差盈亏应按持仓数量×合约乘数计算;
// CloseNotionalValue 是期初全价折算后的名义本金,直接乘价差会重复包含期初价格。
let positionAmount = parseFloat(thisObj.floatPosition.Quantity) * parseFloat(thisObj.floatPosition.ContractSize || 1);
thisObj.floatPosition.MarkClosePnl = positionAmount * (thisObj.floatPosition.TradingAmountAvg * scale - thisObj.initPosiGrossPrice) * floatRatio;
thisObj.floatPosition.MarkClosePnl = positionAmount * (deliveryPrice - thisObj.initPosiGrossPrice) * floatRatio;
thisObj.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(thisObj.floatPosition.MarkClosePnl);//MarkClosePnl 纯盯市不要计算交易费用和分红
// 守卫: 浮动盈亏合计必须保留 2 位小数 → 对应历史 bug 3c5f25a5(原代码缺精度保留)
// 数值由 swapCalc.calcFloatPnlSum 计算, 此处 .toFixed(2) 仅保留字符串类型以兼容下游
@@ -205,13 +210,13 @@ const vue = new Vue({
thisObj.deal.SwapRealizedPnL = pnl;
thisObj.deal.SwapMarginRebatePnl = 0;
thisObj.deal.SwapMarginAmount = 0;
let scale = thisObj.getPriceScale();
thisObj.floatPosition.TradingAmount = parseFloat(thisObj.floatPosition.TradingAmountAvg) * parseFloat(thisObj.deal.CloseNotionalValue) * scale;
let deliveryPrice = thisObj.getStorageDeliveryPrice();
thisObj.floatPosition.TradingAmount = deliveryPrice * parseFloat(thisObj.deal.CloseNotionalValue);
thisObj.floatPosition.CloseFee = TradingFee;
if (thisObj.deal.CloseQty > 0) {
thisObj.floatPosition.TradingAmountFeeAvg = parseFloat(thisObj.floatPosition.TradingAmountAvg) * scale + (TradingFee / thisObj.deal.CloseQty) * floatRatio;
thisObj.floatPosition.TradingAmountFeeAvg = deliveryPrice + (TradingFee / thisObj.deal.CloseQty) * floatRatio;
} else {
thisObj.floatPosition.TradingAmountFeeAvg = parseFloat(thisObj.floatPosition.TradingAmountAvg) * scale;
thisObj.floatPosition.TradingAmountFeeAvg = deliveryPrice;
}
this.interestList.forEach(x => {
//let interestRatio = x.InterestDirection == 1 ? 1 : -1;
@@ -277,7 +282,7 @@ const vue = new Vue({
thisObj.floatPosition.EventDate = thisObj.deal.ValueDate;
let floatPosition = _.cloneDeep(thisObj.floatPosition);
floatPosition.Quantity = 0;
floatPosition.TradingAmountAvg = floatPosition.TradingAmountAvg * thisObj.getPriceScale();
floatPosition.TradingAmountAvg = thisObj.getStorageDeliveryPrice();
reqObj.FlowEvents.push(floatPosition);
var postData = { unwindData: reqObj };
var msg = "确认提交收益结算?";
@@ -13,7 +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;
@@ -219,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);
@@ -376,7 +388,8 @@ const vue = new Vue({
//计算数量
if (this.paySwapList.length > 0) {
var item = this.paySwapList[0];
var notional = item.PosiGrossPrice * item.ContractSize;
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();
}
@@ -457,7 +470,8 @@ const vue = new Vue({
}
var national = payItem.PosiQuantity * payItem.ContractSize;
// 守卫: 名义本金必须 round 到 2 位 → 对应历史 bug f873239a(缺 _.round); 外置到 swapCalc.calcStockEqvNotional
var stockEqvNotional = SwapCalc.calcStockEqvNotional(payItem.PosiGrossPrice, national);//名义本金=期初价格*数量*乘数
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;
}
@@ -601,6 +615,9 @@ const vue = new Vue({
errorcount++;
return false;
}
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 {
@@ -735,8 +752,8 @@ const vue = new Vue({
// 非 EodPrice 分支 ×bondPriceMultiple=0.01)。故此处仅做精度格式化,**不可**再 bondCalcPriceToStorage(÷100)
// 否则默认价 1.0 被除成 0.01,界面 percent:true 再 ×100 显示为 1"被自动除以100"bug)。
// 计算器(/Bond/CalcBond)返回的才是展示态,其 ÷100 落库逻辑在 calcBondForItem 内处理。
item.PosiNetNoFeePrice = otcformat.trading.umprice(resp.obj.netPrice);
item.PosiGrossPrice = otcformat.trading.umprice(resp.obj.price);
item.PosiNetNoFeePrice = thisObj.roundStorageBondNetPriceAndYtm(resp.obj.netPrice);
item.PosiGrossPrice = thisObj.roundStorageDeliveryPrice(item, resp.obj.price);
thisObj.calcNotional();
});
},
@@ -7,6 +7,7 @@ const inputFormatTradeAmount = Object.freeze({ precision: otcformat.trading.noti
const inputFormatEqvNotional = Object.freeze({ precision: otcformat.trading.StockEqvNotional.precision, append: '', negative: true });
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, append: '', negative: true });
let ValueDate = model.ValueDate;
const vue = new Vue({
el: '#vueDiv',
@@ -40,6 +41,10 @@ const vue = new Vue({
getPriceScale() {
return this.multiplier == 100 ? 0.01 : 1;
},
getStorageDeliveryPrice() {
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) => {
return item.UnderlyingCode;
@@ -84,7 +89,7 @@ const vue = new Vue({
this.deal.SwapCloseAmount = otcformat.trading.StockEqvNotional(this.deal.SwapCloseAmount);
//this.floatPosition.PosiNetPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiNetPrice);
//this.floatPosition.PosiGrossPrice = otcformat.trading.tradeSinglePrice(this.floatPosition.PosiGrossPrice);
this.floatPosition.TradingAmountAvg = otcformat.trading.tradeSinglePrice(this.floatPosition.TradingAmountAvg);
this.floatPosition.TradingAmountAvg = _.round(Number(this.floatPosition.TradingAmountAvg), 9);
this.floatPosition.TradingFee = otcformat.trading.StockEqvNotional(this.floatPosition.TradingFee);
this.floatPosition.TradingFeePending = otcformat.trading.StockEqvNotional(this.floatPosition.TradingFeePending);
this.floatPosition.DividendIn = parseFloat(this.floatPosition.DividendIn).toFixed(2);
@@ -212,7 +217,7 @@ const vue = new Vue({
{ code: thisObj.floatPosition.UnderlyingCode, valuedate: thisObj.deal.ValueDate })
.done(function (res) {
res.obj = res.obj * thisObj.multiplier;
thisObj.floatPosition.TradingAmountAvg = otcformat.trading.tradeSinglePrice(res.obj);
thisObj.floatPosition.TradingAmountAvg = _.round(Number(res.obj), 9);
thisObj.calcFloatClosePnl();
});
},
@@ -222,8 +227,8 @@ const vue = new Vue({
let longRatio = thisObj.floatPosition.PositionType == 1 ? 1 : -1;
let TradingFee = thisObj.floatPosition.TradingFee == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFee);
let TradingFeePending = thisObj.floatPosition.TradingFeePending == "" ? 0 : parseFloat(thisObj.floatPosition.TradingFeePending);
let scale = thisObj.getPriceScale();
thisObj.floatPosition.MarkClosePnl = Math.round(thisObj.deal.CloseQty * (thisObj.floatPosition.TradingAmountAvg * scale - thisObj.initPosiNetPrice) * floatRatio * longRatio * 10000) / 10000;
let deliveryPrice = thisObj.getStorageDeliveryPrice();
thisObj.floatPosition.MarkClosePnl = Math.round(thisObj.deal.CloseQty * (deliveryPrice - thisObj.initPosiNetPrice) * floatRatio * longRatio * 10000) / 10000;
thisObj.floatPosition.MarkClosePnl = Number(thisObj.floatPosition.MarkClosePnl.toFixed(2));//MarkClosePnl 纯盯市不要计算交易费用和分红
thisObj.floatPosition.MarkClosePnl = otcformat.trading.StockEqvNotional(thisObj.floatPosition.MarkClosePnl);
thisObj.floatPosition.FloatPnlSum = (parseFloat(thisObj.floatPosition.MarkClosePnl) + TradingFee + TradingFeePending + parseFloat(thisObj.floatPosition.DividendIn)).toFixed(2);
@@ -253,13 +258,13 @@ const vue = new Vue({
thisObj.deal.SwapRealizedPnL = pnl;
thisObj.deal.SwapMarginRebatePnl = 0;
thisObj.deal.SwapMarginAmount = 0;
let scale = thisObj.getPriceScale();
thisObj.floatPosition.TradingAmount = parseFloat(thisObj.floatPosition.TradingAmountAvg) * parseFloat(thisObj.deal.CloseQty) * scale;
let deliveryPrice = thisObj.getStorageDeliveryPrice();
thisObj.floatPosition.TradingAmount = deliveryPrice * parseFloat(thisObj.deal.CloseQty);
thisObj.floatPosition.CloseFee = TradingFee;
if (thisObj.deal.CloseQty == 0) {
thisObj.floatPosition.TradingAmountFeeAvg = 0;
} else {
thisObj.floatPosition.TradingAmountFeeAvg = parseFloat(thisObj.floatPosition.TradingAmountAvg) * scale + (TradingFee / thisObj.deal.CloseQty) * ratio;
thisObj.floatPosition.TradingAmountFeeAvg = deliveryPrice + (TradingFee / thisObj.deal.CloseQty) * ratio;
}
this.interestList.forEach(x => {
/*let interestRatio = x.InterestDirection == 1 ? 1 : -1;*/
@@ -360,7 +365,7 @@ const vue = new Vue({
thisObj.floatPosition.EventDate = thisObj.deal.ValueDate;
let floatPosition = _.cloneDeep(thisObj.floatPosition);
floatPosition.Quantity = reqObj.CloseQty;
floatPosition.TradingAmountAvg = floatPosition.TradingAmountAvg * thisObj.getPriceScale();
floatPosition.TradingAmountAvg = thisObj.getStorageDeliveryPrice();
reqObj.FlowEvents.push(floatPosition);
var postData = { unwindData: reqObj };
var msg = "确认提交平仓?";
@@ -276,6 +276,7 @@
tradeHelper.IsBond = function (instType) {
switch (instType) {
case "Bonds":
case "Bond":
case "TBonds":
case "CreditBonds":
case "OtherBonds":
@@ -897,7 +897,7 @@
<td>@tr.MetaDic["互换_收取方初始预付金"]</td>
<td>@tr.MetaDic["互换_收取方交易费用"]</td>
<td>@tr.MetaDic["互换_收取方多空方向"]</td>
<td>@tr.OriginalStockEqvNotional</td>
<td>@tr.TdDetail.OriginalStockEqvNotional.OtcFormat(OtcFormatFlag.StockEqvNotional)</td>
<td>@tr.MetaDic["年化天数"]</td>
<td>@tr.MetaDic["互换_互换日期"]</td>
</tr>