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

This commit is contained in:
张名锐
2026-07-31 16:20:42 +08:00
29 changed files with 380 additions and 99 deletions
+20 -5
View File
@@ -1,4 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;
using YLErp.Models;
namespace YLErp
@@ -135,6 +135,9 @@ namespace YLErp
public const string Shibor = "Shibor";
public const string FixingRepoRate = "FixingRepoRate";
public const string OtherRate = "OtherRate";
public const string RateYield = "RateYield"; //利率收益率
public const string BondIndex = "BondIndex"; // 债券指数
//public const string OtherUnderlying = "OtherUnderlying";
@@ -169,6 +172,8 @@ namespace YLErp
//case OtherUnderlying: return "其他标的";
case CreditBonds: return "信用债";
case OtherBonds: return "其它债券";
case RateYield: return "利率收益率";
case BondIndex: return "债券指数";
default: return instType;
}
}
@@ -204,6 +209,8 @@ namespace YLErp
//case "其他标的": return OtherUnderlying;
case "信用债": return CreditBonds;
case "其它债券": return OtherBonds;
case "利率收益率": return RateYield;
case "债券指数": return BondIndex;
default: throw new ServiceException("资产类型不存在");
}
}
@@ -264,6 +271,8 @@ namespace YLErp
new SelectItem { Text = "汇率" ,Value = ExRate },
new SelectItem { Text = "Shibor" ,Value = Shibor},
new SelectItem { Text = "银行间回购定盘" ,Value = FixingRepoRate},
new SelectItem { Text = "利率收益率" ,Value = RateYield},
new SelectItem { Text = "债券指数" ,Value = BondIndex},
//new SelectItem { Text = "其他利率" ,Value = OtherRate},
//new SelectItem { Text = "其他标的" ,Value = OtherUnderlying}
};
@@ -271,7 +280,7 @@ namespace YLErp
public static IEnumerable<string> FutureTypes()
{
return new[] { CommodityFutures, StockIF, GoldFutures, TBFutures, OtherFutures, AbroadFutures };
return new[] { CommodityFutures, StockIF, GoldFutures, TBFutures, OtherFutures, AbroadFutures, RateYield, BondIndex };
}
/// <summary>
@@ -371,8 +380,12 @@ namespace YLErp
/// 类型转换为计算所用类型
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string ConvertCalcType(string instType)
public static string ConvertCalcType(string instType,bool pringScene = false)
{
if (pringScene && PS.Config.Company == Configuration.CompanyEnum.)
{
return Bonds;
}
switch (instType)
{
case Stock:
@@ -390,6 +403,8 @@ namespace YLErp
case TBFutures:
case OtherFutures:
case AbroadFutures:
case RateYield:
case BondIndex:
return CommodityFutures;
case CommoditySpot:
case GoldSpot:
@@ -414,7 +429,7 @@ namespace YLErp
public static string[] GetFutureTypes()
{
return new[] { CommodityFutures, StockIF,
GoldFutures, TBFutures, AbroadFutures, OtherFutures };
GoldFutures, TBFutures, AbroadFutures, OtherFutures,RateYield,BondIndex };
}
/// <summary>
@@ -458,7 +473,7 @@ namespace YLErp
Bonds,
TBonds,
CreditBonds,
OtherBonds
OtherBonds,
};
}
}
+1 -1
View File
@@ -13,7 +13,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="NPOI" Version="2.5.6" />
<PackageReference Include="RestSharp" Version="108.0.3" />
<PackageReference Include="Qdp.Pricing.Base" Version="1.0.1" />
<PackageReference Include="Qdp.Pricing.Base" Version="1.1.5-glms" />
<PackageReference Include="TinyPinyin.Net" Version="1.0.2" />
<PackageReference Include="YieldChain.Core" Version="1.0.3" />
</ItemGroup>
@@ -1,4 +1,4 @@
using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
using Qdp.ComputeService.Data.CommonModels.ValuationParams.FixedIncome;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options;
@@ -59,7 +59,7 @@ namespace YLErp.BLL.Calculation.V2
{
underlying = underlying.Clone();
underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType);
underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType,true);
switch (trade.TradeType)
{
+22 -1
View File
@@ -1,4 +1,4 @@
using BaseOUDAL;
using BaseOUDAL;
using YLErp.Commons;
using YLErp.Helpers;
using YLErp.Modules;
@@ -211,6 +211,27 @@ namespace YLErp.Model
public double DividendRate { get; set; }
/// <summary>
/// Delta_R
/// </summary>
public double? Delta_r { get; set; }
public double? Delta_r_1bp { get; set; }
public double? Dv01 { get; set; }
public double? Gamma_r { get; set; }
public double? Gamma_r_1bp { get; set; }
public double? Vega_r { get; set; }
public double? Vega_r_1bp { get; set; }
/// <summary>
/// 维持预付金
/// </summary>
@@ -1,4 +1,4 @@
using MoreLinq;
using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -248,7 +248,14 @@ namespace YLErp.Modules.CalcPriceShowConfigModule
new CalcQuotaDto{Name="Vegacash",Tip=""},
new CalcQuotaDto{Name="PV*",Tip="*号标记的字段是所有涉及到敲出的期权结构,若当天为该期权的观察日,当盘中实时价格越过敲出价时,该笔结构的估值只包含Payoff,不需要再包含时间价值,同时该笔交易的Delta手数和Gamma手数都变成0\r\n\r\n系统默认在盘中的时候期权价值=内在价值+时间价值"},
new CalcQuotaDto{Name="Delta*",Tip="*号标记的字段是所有涉及到敲出的期权结构,若当天为该期权的观察日,当盘中实时价格越过敲出价时,该笔结构的估值只包含Payoff,不需要再包含时间价值,同时该笔交易的Delta手数和Gamma手数都变成0\r\n\r\n系统默认在盘中的时候期权价值=内在价值+时间价值"},
new CalcQuotaDto{Name="Gamma*",Tip="*号标记的字段是所有涉及到敲出的期权结构,若当天为该期权的观察日,当盘中实时价格越过敲出价时,该笔结构的估值只包含Payoff,不需要再包含时间价值,同时该笔交易的Delta手数和Gamma手数都变成0\r\n\r\n系统默认在盘中的时候期权价值=内在价值+时间价值"}
new CalcQuotaDto{Name="Gamma*",Tip="*号标记的字段是所有涉及到敲出的期权结构,若当天为该期权的观察日,当盘中实时价格越过敲出价时,该笔结构的估值只包含Payoff,不需要再包含时间价值,同时该笔交易的Delta手数和Gamma手数都变成0\r\n\r\n系统默认在盘中的时候期权价值=内在价值+时间价值"},
new CalcQuotaDto{Name="Delta_r",Tip=""},
new CalcQuotaDto{Name="Delta_r(1bp)",Tip=""},
new CalcQuotaDto{Name="Dv01",Tip=""},
new CalcQuotaDto{Name="Gamma_r",Tip=""},
new CalcQuotaDto{Name="Gamma_r(1bp)",Tip=""},
new CalcQuotaDto{Name="Vega_r",Tip=""},
new CalcQuotaDto{Name="Vega_r(1bp)",Tip=""},
};
}
@@ -1,4 +1,4 @@
using Qdp.Foundation.Implementations;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using YLErp.Enums;
@@ -105,7 +105,7 @@ namespace YLErp.Modules.CalculationModule
public string underlyingInstrumentType
{
get => _underlyingInstrumentType;
set => _underlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(value);
set => _underlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(value,true);
}
/// <summary>
@@ -1,4 +1,4 @@
using Qdp.Pricing.Base.Interfaces;
using Qdp.Pricing.Base.Interfaces;
namespace YLErp.Modules.CalculationModule
{
@@ -495,6 +495,26 @@ namespace YLErp.Modules.CalculationModule
/// Theta(轧差)
/// </summary>
public double ThetaNet { get; set; }
/// <summary>
/// Delta_R
/// </summary>
public double? Delta_r { get; set; }
public double? Delta_r_1bp { get; set; }
public double? Dv01 { get; set; }
public double? Gamma_r { get; set; }
public double? Gamma_r_1bp { get; set; }
public double? Vega_r { get; set; }
public double? Vega_r_1bp { get; set; }
}
public class TradeValueResultExtend
@@ -1,4 +1,4 @@
using Qdp.Foundation.Implementations;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Interfaces;
using Qdp.Pricing.Base.Utilities;
@@ -570,7 +570,7 @@ namespace YLErp.Modules.CalculationModule
{
underlying = underlying.Clone();
underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType);
underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType,true);
switch (trade.TradeType)
{
@@ -1,4 +1,4 @@
using YLErp.Abstract.DataProviders;
using YLErp.Abstract.DataProviders;
using YLErp.Helpers;
using YLErp.Models;
using YLErp.Modules.TradeModule.DealModule;
@@ -118,13 +118,13 @@ namespace YLErp.Modules.DataProviderModule
ValueDate = ValueDate,
UnderlyingId = um.id,
UnderlyingCode = um.UnderlyingCode,
ClosePrice = 0,
SettlePrice = 0,
ClosePrice = (double)(eodprice.dirty_price_close ?? 0) / 100,
SettlePrice = (double)(eodprice.net_price ?? 0) / 100,
HighPrice = 0,
LowPrice = 0,
UnderlyingStatus = "正常运行",
UnderlyingInstrumentType = "Bonds",
ReferencePrice = 0,
ReferencePrice = (double)(eodprice.yield ?? 0),
DeciSettlePrice = eodprice.dirty_price_close,
DeciClosePrice = eodprice.net_price,
DeciReferencePrice = eodprice.yield,
@@ -138,15 +138,15 @@ namespace YLErp.Modules.DataProviderModule
{
if (item.UnderlyingCode != null)
{
if (item.UnderlyingInstrumentType == "Bonds")
{
// [Layer2-待统一] 债券映射口径:SettlePrice=全价(dirty_price_close)ClosePrice=净价(net_price)。
// 注意:这与 EodPriceQueryService.GetBondPrice 的映射【完全相反】(GetBondPrice: ClosePrice=全价,SettlePrice=净价)。
// 两处对"债券收盘价/结算价"的净全价定义不一致属历史遗留,请勿随意改动单侧,需业务先定调后统一(见 TryGetSettlementEodPrice 注释)。
item.SettlePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciSettlePrice));
item.ClosePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciClosePrice));
item.ReferencePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciReferencePrice));
}
//if (item.UnderlyingInstrumentType == "Bonds")
//{
// // [Layer2-待统一] 债券映射口径:SettlePrice=全价(dirty_price_close)ClosePrice=净价(net_price)。
// // 注意:这与 EodPriceQueryService.GetBondPrice 的映射【完全相反】(GetBondPrice: ClosePrice=全价,SettlePrice=净价)。
// // 两处对"债券收盘价/结算价"的净全价定义不一致属历史遗留,请勿随意改动单侧,需业务先定调后统一(见 TryGetSettlementEodPrice 注释)。
// item.SettlePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciSettlePrice));
// item.ClosePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciClosePrice));
// item.ReferencePrice = Convert.ToDouble(BondPriceConverter.ToStorage(item.DeciReferencePrice));
//}
_priceDic[item.UnderlyingCode] = item;
}
}
@@ -1,4 +1,4 @@
using BaseOUDAL;
using BaseOUDAL;
using System;
using System.Data;
using System.Diagnostics;
@@ -339,7 +339,9 @@ namespace YLErp.Modules.EodModule
var tcActions = new[] { ClientCashInCashOut._平仓费,
ClientCashInCashOut._行权费,ClientCashInCashOut._票息,ClientCashInCashOut._互换,ClientCashInCashOut._其他};
var underlyingCodes = searchResult.rows.Select(O => O.UnderlyingCode).Distinct().ToList();
var greeksHandleService = new GLMSGreeksHandleService();
greeksHandleService.InitData(req.ValueDate, underlyingCodes);
foreach (var r in searchResult.rows)
{
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(r.UnderlyingCode);
@@ -470,6 +472,7 @@ namespace YLErp.Modules.EodModule
r.DeltaLots = (r.Delta ?? 0) / um.ContractSize;
}
r.Rho *= 100;
greeksHandleService.Handle(r,um);
if (r.trade == null)
{
r.TradeSinglePrice = r.etcTradePrice / r.TradeAmount;
@@ -1118,6 +1121,9 @@ namespace YLErp.Modules.EodModule
.AsEnumerable()
.GroupBy(O => O.ParentTradeId)
.ToDictionary(K => K.Key, V => V.Select(O => new xodTradeBase() { TradeJson = O.TradeJson }));
var underlyingCodes = searchResult.rows.Select(O => O.UnderlyingCode).Distinct().ToList();
var greeksHandleService = new GLMSGreeksHandleService();
greeksHandleService.InitData(req.ValueDate, underlyingCodes);
foreach (var r in searchResult.rows)
{
@@ -1200,6 +1206,7 @@ namespace YLErp.Modules.EodModule
}
r.Rho *= 100;
greeksHandleService.Handle(r,um);
r.CountRatio = um.CountRatio;
r.TradeOriginalAmount = r.OriginalNotional / um.CountRatio;
r.etcTradePrice = ConsTrade.TradeTypesForHedge.Contains(r.TradeType)
@@ -1706,10 +1713,10 @@ namespace YLErp.Modules.EodModule
var results = GetEodPositionRisksDataHandle(req);
var list2 = results.Select(n => new ExpandoDictionary<string, string>(n)).ToArray();
var templateFile = OtcAppContext.MapPath("~/App_Docs/导出模板/日终持仓风险.xlsx");
var templateFile = OtcAppContext.MapPath("~/App_Docs/导出模板/日终持仓风险_国联.xlsx");
if (PS.Config.ErpElement.NeedShowSpv)
{
templateFile = OtcAppContext.MapPath("~/App_Docs/导出模板/日终持仓风险-Spv.xlsx");
templateFile = OtcAppContext.MapPath("~/App_Docs/导出模板/日终持仓风险-Spv_国联.xlsx");
}
return ExcelGenerator.UseTemplateGenerator(templateFile).AddVariable(new { list = list2 }).GenerateBytes();
@@ -2016,6 +2023,17 @@ namespace YLErp.Modules.EodModule
dic.Add("Spv3", item.PnLDelta.OtcFormat(OtcFormatFlag.greek));
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
dic.Add("Delta_r", OtcFormatExtensions.OtcFormat(item.Delta_r, OtcFormatFlag.greek));
dic.Add("Delta_r_1bp", OtcFormatExtensions.OtcFormat(item.Delta_r_1bp, OtcFormatFlag.greek));
dic.Add("Dv01", OtcFormatExtensions.OtcFormat(item.Dv01, OtcFormatFlag.greek));
dic.Add("Gamma_r", OtcFormatExtensions.OtcFormat(item.Gamma_r, OtcFormatFlag.greek));
dic.Add("Gamma_r_1bp", OtcFormatExtensions.OtcFormat(item.Gamma_r_1bp, OtcFormatFlag.greek));
dic.Add("Vega_r", OtcFormatExtensions.OtcFormat(item.Vega_r, OtcFormatFlag.greek));
dic.Add("Vega_r_1bp", OtcFormatExtensions.OtcFormat(item.Vega_r_1bp, OtcFormatFlag.greek));
}
dic.Add("备注", item.Comments);
@@ -2115,6 +2133,17 @@ namespace YLErp.Modules.EodModule
dic.Add("Spv2", "");
dic.Add("Spv3", "");
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
dic.Add("Delta_r", "");
dic.Add("Delta_r_1bp", "");
dic.Add("Dv01", "");
dic.Add("Gamma_r", "");
dic.Add("Gamma_r_1bp", "");
dic.Add("Vega_r", "");
dic.Add("Vega_r_1bp", "");
}
results.Add(dic);
}
return results;
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Model;
using YLErp.Modules.CalculationModule;
namespace YLErp.Modules.EodModule
{
/// <summary>
/// 国联民生 希腊字母计算
/// </summary>
public class GLMSGreeksHandleService
{
public void InitData(DateTime valueDate,List<string> underlyingCodes)
{
}
public void Handle(EodPositionRisksDTO dto,underlying_manager um)
{
//对Delta_r Delta_r_1bp Dv01 Gamma_r Gamma_r_1bp Vega_r Vega_r_1bp
if ("GB10".Equals(um.UnderlyingCode))
{
dto.Delta_r = dto.Delta * -1;
}
}
public void Handle(trade td,TradeValueResult calRes)
{
calRes.Delta_r = calRes.Delta * -1;
}
}
}
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Linq.Dynamic.Core;
using YLErp.Modules.DataProviderModule;
@@ -48,7 +48,7 @@ namespace YLErp.Modules.EodModule.SettlementModule
if (request.IsSettleOtcTrades)
{
var tradePredicate = _context.PredicateBuilder.GetOtcTradePredicate()
.And(t => t.TradeType != "自定义交易" && t.TradeType != "收益互换" && t.UnderlyingCode != null);
.And(t => t.TradeType != "自定义交易" && t.UnderlyingCode != null);
//新增客户筛选 tw
if (clienIds != null)
@@ -58,6 +58,11 @@ namespace YLErp.Modules.EodModule.SettlementModule
allQuery = DbContext.trade.Where(tradePredicate).Select(n => n.UnderlyingCode).Distinct();
if (PS.Config.ErpElement.ForwardTradePriceModel == Configuration.Enums.ForwardTradePriceModel.STANDARD)
{
allQuery = allQuery.Union(DbContext.trade.Where(tradePredicate).Where(x => x.BasisUnderlyingCode != null && x.BasisUnderlyingCode != "").Select(n => n.BasisUnderlyingCode).Distinct());
}
var clientProductPredicate = _context.PredicateBuilder.GetClientProductPredicate();
//新增客户筛选 tw
@@ -74,26 +79,28 @@ namespace YLErp.Modules.EodModule.SettlementModule
}
//检查场内标的
//if (request.IsSettleExchangeTrades)
//{
// var exTradePredicate = _context.PredicateBuilder.GetExchangeTradePredicate();
if (request.IsSettleExchangeTrades)
{
var exTradePredicate = _context.PredicateBuilder.GetExchangeTradePredicate();
// var exTradeQuery = DbContext.ExchangeTrade.Where(exTradePredicate).Select(n => n.UnderlyingCode);
var exTradeQuery = DbContext.ExchangeTrade.Where(exTradePredicate).Select(n => n.UnderlyingCode);
// allQuery = allQuery == null ? exTradeQuery.Distinct() : allQuery.Union(exTradeQuery.Distinct());
allQuery = allQuery == null ? exTradeQuery.Distinct() : allQuery.Union(exTradeQuery.Distinct());
// var preSettleDate = _context.PreSettleDate;
// //最后一个交易日持仓信息
// var futureTypes = ConsGlobal.InstrumentType.GetFutureTypes();
// var positionQuery = from t in DbContext.eod_trade_position
// join um in DbContext.underlying_manager on t.UnderlyingCode equals um.UnderlyingCode
// where t.ValueDate == preSettleDate && t.Amount != 0
// && ConsTrade.TradeTypesForHedge.Contains(t.TradeType)
// && (!futureTypes.Contains(um.UnderlyingInstrumentType) || um.MaturityDate >= settleDate)
// select t.UnderlyingCode;
var preSettleDate = _context.PreSettleDate;
//最后一个交易日持仓信息
var futureTypes = ConsGlobal.InstrumentType.GetFutureTypes();
var positionQuery = from t in DbContext.eod_trade_position
join um in DbContext.underlying_manager on t.UnderlyingCode equals um.UnderlyingCode
where t.ValueDate == preSettleDate && t.Amount != 0
&& ConsTrade.TradeTypesForHedge.Contains(t.TradeType)
&& (!futureTypes.Contains(um.UnderlyingInstrumentType) || um.MaturityDate >= settleDate)
select t.UnderlyingCode;
// allQuery = allQuery.Union(positionQuery.Distinct());
//}
allQuery = allQuery.Union(positionQuery.Distinct());
}
if (allQuery == null)
{
@@ -103,12 +110,10 @@ namespace YLErp.Modules.EodModule.SettlementModule
//检查是否所有标的都在结算日有结算价格
var umCodeArr = allQuery.ToArray();
var swapUmCodeArr = GetSwapUnderlyingCodes();
var codeArr = umCodeArr.Union(swapUmCodeArr);
var eodPriceProvider = _context.GetEodPriceProvider().Initialize(codeArr);
// var preEodPriceProvidaer = _context.GetPreEodPriceProvider().Initialize(swapUmCodeArr);
var umCodes = codeArr.Where(n => !string.IsNullOrEmpty(n) && !eodPriceProvider.HasValue(n)).ToHashSet(StringComparer.OrdinalIgnoreCase);
//var swapUmCodes= swapUmCodeArr.Where(n => !string.IsNullOrEmpty(n) && !preEodPriceProvidaer.HasValue(n)).ToHashSet(StringComparer.OrdinalIgnoreCase);
var eodPriceProvider = _context.GetEodPriceProvider().Initialize(umCodeArr);
var umCodes = umCodeArr.Where(n => !string.IsNullOrEmpty(n) && !eodPriceProvider.HasValue(n)).ToHashSet(StringComparer.OrdinalIgnoreCase);
//排除掉节假日不需要结算的交易
foreach (var t in _context.HolidayTrades)
{
@@ -1,4 +1,4 @@
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Implementations;
using System.Text.RegularExpressions;
using YLErp.BLL;
using YLErp.BLL.Calculation.V2;
@@ -11,6 +11,7 @@ using YLErp.Enums;
using YLErp.Model;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.PricingModule.Models;
using YLErp.Modules.TradeModule;
using YLErp.QdpModule;
@@ -500,7 +501,7 @@ namespace YLErp.Modules.PricingModule
}
}
}
var greeksHandleService = new GLMSGreeksHandleService();
foreach (var item in tdList)
{
var td = item.tdConv;
@@ -529,6 +530,9 @@ namespace YLErp.Modules.PricingModule
}
}
greeksHandleService.Handle(td, calcResult);
results.Add(new CalcOptionPriceResult
{
BuySell = td.BuySell,
@@ -117,12 +117,6 @@ namespace YLErp.Modules.SuperviseReportModule.SAC.Common
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class SacDescriptionAttribute : Attribute
{
// Attribute.TypeId 本质是 System.TypeNewtonsoft 序列化会触发
// "Method may only be called on a Type for which Type.IsGenericParameter is true."
// 预览页用 ToJson() 序列化 SacInfo/List<SacInfo> 时崩溃,故显式跳过该成员。
// 13.0.1 安全版对 Type 成员序列化行为变更后暴露此问题)
public bool ShouldSerializeTypeId() => false;
/// <summary>
/// 字段名
/// </summary>
@@ -1,4 +1,4 @@
using BaseOUDAL;
using BaseOUDAL;
using YLErp.BLL;
using YLErp.Configuration;
using YLErp.Configuration.Enums;
@@ -993,10 +993,10 @@ namespace YLErp.Modules.TradeModule.OrderModule
throw new ServiceException($"客户'{client.Name}'为非高风险客户,没有卖出权限");
}
if (_underlying != null && _underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity))
{
throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行权益类的期权交易");
}
//if (_underlying != null && _underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity))
//{
// throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行权益类的期权交易");
//}
if (PS.Config.ErpElement.SecuritiesEnvironment)
{
+4 -4
View File
@@ -40,10 +40,10 @@
<PackageReference Include="Snowflake.Core" Version="2.0.0" />
<PackageReference Include="SocialExplorer.FastDBF" Version="1.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.0" />
<PackageReference Include="Qdp.Pricing.Library.Options" Version="1.0.5" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="YLErp.Office" Version="1.1.2" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.5" />
<PackageReference Include="Qdp.Pricing.Library.Options" Version="5.18.4-glms" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="YLErp.Office" Version="1.4.5" />
</ItemGroup>
<ItemGroup>
+13 -4
View File
@@ -37,15 +37,13 @@ namespace YLErp.Web.App
try
{
// 暴露完整异常(类型 + 所有内层 message + 堆栈),不再只取最内层 message,
// 便于定位根因(如 SacInfo 序列化 TypeId 触发的 IsGenericParameter 反射异常)。
message = exception.ToString();
message = GetInnerExceptionMessage(exception);
if (serviceExpcetion == null || serviceExpcetion.IsFaultError)
{
var result = await request.BodyReader.ReadAsync();
var reqBody = ConvertBufferToString(result.Buffer);
LogFactory.GetLogger(context.Request.Path.Value).Error(serviceExpcetion ?? exception, $"[query]:{request.QueryString.Value};[body]:{reqBody}\r\n{message}");
LogFactory.GetLogger(context.Request.Path.Value).Error(serviceExpcetion ?? exception, $"[query]:{request.QueryString.Value};[body]:{reqBody}");
}
}
catch (Exception ex)
@@ -79,5 +77,16 @@ namespace YLErp.Web.App
ReadOnlySpan<byte> span = readOnlySequence.IsSingleSegment ? readOnlySequence.First.Span : readOnlySequence.ToArray().AsSpan();
return System.Text.Encoding.UTF8.GetString(span);
}
private static string GetInnerExceptionMessage(Exception ex)
{
var exceptionStr = ex.Message;
while (ex.InnerException != null)
{
exceptionStr = ex.InnerException.Message;
ex = ex.InnerException;
}
return exceptionStr;
}
}
}
+2 -1
View File
@@ -7,12 +7,13 @@
<FunctionSub Name="波动率审核编辑" Type="Operate" Title="波动率审核"></FunctionSub>
<FunctionSub Name="利率曲线" Title="利率曲线"></FunctionSub>
<FunctionSub Name="基差曲线" Title="基差曲线"></FunctionSub>
<FunctionSub Name="定价计算指标显示设置" Type="Operate" Title="定价计算指标显示设置"></FunctionSub>
</FunctionParent>
<FunctionParent Name="交易管理" Title="交易管理">
<FunctionSub Name="交易搜索" Title="期权交易"></FunctionSub>
<FunctionSub Name="查看所有交易" Title="查看所有交易" Type="Operate" Note="是否可以查看所有交易,没有这个权限在交易列表中只能看到自己所在的簿记账户的交易" ></FunctionSub>
<FunctionSub Name="交易新增" Title="交易新增" Type="Operate" Note="是否有新增交易权限" ></FunctionSub>
<FunctionSub Name="交易续" Title="交易续" Type="Operate" Note="是否有续交易权限" ></FunctionSub>
<FunctionSub Name="交易续" Title="交易续" Type="Operate" Note="是否有续交易权限" ></FunctionSub>
<FunctionSub Name="交易编辑" Title="交易编辑" Type="Operate" Note="是否有交易编辑权限" ></FunctionSub>
<FunctionSub Name="交易删除" Title="交易删除" Type="Operate" Note="是否有交易删除权限" ></FunctionSub>
<FunctionSub Name="新增选择交易员" Title="新增交易选择交易员" Type="Operate" Note="新增交易时候是否可以选择自己以外的其他交易员" ></FunctionSub>
+2 -2
View File
@@ -407,9 +407,9 @@ namespace YLErp.Web
public bool _交易新增 => HasRight("交易管理-交易新增");
/// <summary>
/// 交易管理-交易续
/// 交易管理-交易续
/// </summary>
public bool _交易续 => HasRight("交易管理-交易续");
public bool _交易续 => HasRight("交易管理-交易续");
/// <summary>
/// 交易管理-交易编辑
+2 -2
View File
@@ -48,9 +48,9 @@ namespace YLErp.Web.Controllers
public ActionResult TradeEdit(string enid, string renewEnid = null, bool isUseApproval = false)
{
ViewBag.isUseApproval = isUseApproval;
if (!string.IsNullOrWhiteSpace(renewEnid) && !CurUser._交易续)
if (!string.IsNullOrWhiteSpace(renewEnid) && !CurUser._交易续)
{
return ShowError("没有续交易权限");
return ShowError("没有续交易权限");
}
// The new/renew flow uses the literal "0" to indicate that no trade exists yet.
var intid = enid == "0" ? 0 : DecryptInt(enid);
+4
View File
@@ -7055,6 +7055,10 @@ namespace YLErp.Web.Controllers
public JsonResult BatchDownloadEodPositionRisks(EodPositionRisksReq req)
{
req.BookIds = AssetUnitModel.IntersectAssetUnits(req.AssetIdGroupList, req.BookIds).ToList();
req.UserAssets = CurUser.GetAssetUnitIds();
req.UserClients = CurUser.GetClientIdsByCurUser(CurUser._查看所有交易);
if (CurUser._查看所有交易) { req.UserClients.Clear(); }
if (!new EodPositionRisksQueryService(CurUser).BatchDownloadEodPositionRisks(req))
{
return JsonError("线程正在被占用");
+50 -1
View File
@@ -1,4 +1,4 @@
@using YLErp.Modules.CalcPriceShowConfigModule
@using YLErp.Modules.CalcPriceShowConfigModule
@*定价模板*@
@model PricingModel
@{
@@ -1199,6 +1199,55 @@
<td>{{calcResult.GammaContainsKnockOut}}</td>
</tr>
}
else if ("Delta_r".Equals(quta.Name))
{
<tr>
<th title="Delta*">Delta_r</th>
<td>{{calcResult.Delta_r}}</td>
</tr>
}
else if ("Delta_r(1bp)".Equals(quta.Name))
{
<tr>
<th title="Delta*">Delta_r(1bp)</th>
<td>{{calcResult.Delta_r_1bp}}</td>
</tr>
}
else if ("Dv01".Equals(quta.Name))
{
<tr>
<th title="Delta*">Dv01</th>
<td>{{calcResult.Dv01}}</td>
</tr>
}
else if ("Gamma_r".Equals(quta.Name))
{
<tr>
<th title="Delta*">Gamma_r</th>
<td>{{calcResult.Gamma_r}}</td>
</tr>
}
else if ("Gamma_r(1bp)".Equals(quta.Name))
{
<tr>
<th title="Delta*">Gamma_r(1bp)</th>
<td>{{calcResult.Gamma_r_1bp}}</td>
</tr>
}
else if ("Vega_r".Equals(quta.Name))
{
<tr>
<th title="Delta*">Vega_r</th>
<td>{{calcResult.Vega_r}}</td>
</tr>
}
else if ("Vega_r(1bp)".Equals(quta.Name))
{
<tr>
<th title="Delta*">Vega_r(1bp)</th>
<td>{{calcResult.Vega_r_1bp}}</td>
</tr>
}
}
}
+1 -1
View File
@@ -157,7 +157,7 @@
{
@MyControls.Btn("收益结算", string.Format("unWindLongShortSwap('{0}')", tradeModel.EncryptId))
}
@if (CurUser.交易管理_交易续)
@if (CurUser.交易管理_交易续)
{
@MyControls.Btn("续作", string.Format("renewTrade('{0}')", tradeModel.EncryptId))
}
+3 -2
View File
@@ -1,4 +1,4 @@
@{
@{
ViewBag.Title = "日终持仓风险";
Layout = "~/Views/Shared/_MainLayout.cshtml";
var settleConfig = PS.Config.GetSettlementConfig();
@@ -19,7 +19,8 @@
IsUseDisplayNotional = PS.Config.IsUseDisplayNotional,
showGuohai = PS.Config.Is国海,
isGuoyuan = PS.Config.Company==CompanyEnum.国元固收,
isGuoJun = PS.Config.IsGuoJun
isGuoJun = PS.Config.IsGuoJun,
isGLMS = PS.Config.Company == CompanyEnum.国联
};
}
<style>
+7 -9
View File
@@ -30,7 +30,6 @@
<PackageReference Include="RazorEngineCore" Version="2022.8.1" />
<PackageReference Include="RazorLight-rpm" Version="3.0.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.6" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="6.0.1" />
<PackageReference Include="DocumentFormat.OpenXml" Version="2.7.2" />
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
</ItemGroup>
@@ -105,6 +104,9 @@
<None Update="App_Docs\导出模板\客户盈亏状况模板.xlsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="App_Docs\导出模板\日终持仓风险_国联.xlsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="App_Docs\导出模板\日终持仓风险.xlsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -242,10 +244,8 @@
<_RebuildScript>$(MSBuildProjectDirectory)\rebuild-bundles.ps1</_RebuildScript>
<_RebuildScriptSh>$(MSBuildProjectDirectory)/rebuild-bundles.sh</_RebuildScriptSh>
</PropertyGroup>
<Exec Condition="'$(OS)' == 'Windows_NT'"
Command="powershell -ExecutionPolicy Bypass -File &quot;$(_RebuildScript)&quot;" />
<Exec Condition="'$(OS)' != 'Windows_NT'"
Command="bash &quot;$(_RebuildScriptSh)&quot;" />
<Exec Condition="'$(OS)' == 'Windows_NT'" Command="powershell -ExecutionPolicy Bypass -File &quot;$(_RebuildScript)&quot;" />
<Exec Condition="'$(OS)' != 'Windows_NT'" Command="bash &quot;$(_RebuildScriptSh)&quot;" />
</Target>
<!-- 手动校验 targetdotnet build -t:VerifyBundles
@@ -257,10 +257,8 @@
<_RebuildScript>$(MSBuildProjectDirectory)\rebuild-bundles.ps1</_RebuildScript>
<_RebuildScriptSh>$(MSBuildProjectDirectory)/rebuild-bundles.sh</_RebuildScriptSh>
</PropertyGroup>
<Exec Condition="'$(OS)' == 'Windows_NT'"
Command="powershell -ExecutionPolicy Bypass -File &quot;$(_RebuildScript)&quot; -Verify" />
<Exec Condition="'$(OS)' != 'Windows_NT'"
Command="bash &quot;$(_RebuildScriptSh)&quot; --verify" />
<Exec Condition="'$(OS)' == 'Windows_NT'" Command="powershell -ExecutionPolicy Bypass -File &quot;$(_RebuildScript)&quot; -Verify" />
<Exec Condition="'$(OS)' != 'Windows_NT'" Command="bash &quot;$(_RebuildScriptSh)&quot; --verify" />
</Target>
<Target Name="CopyFilesAfterPublish" AfterTargets="Publish">
@@ -1,4 +1,4 @@
function getColModelGrid() {
function getColModelGrid() {
var i = 0;
var colModelGrid = [
{ name: 'id', hidden: true, optionHide: true },
@@ -494,6 +494,75 @@
colModelGrid = colModelGrid.concat(spvCols);
}
if (page.isGLMS) {
colModelGrid.push({
name: 'Delta_r',
label: 'Delta_r',
index: 'Delta_r',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
}, {
name: 'Delta_r_1bp',
label: 'Delta_r(1bp)',
index: 'Delta_r_1bp',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
}, {
name: 'Dv01',
label: 'Dv01',
index: 'Dv01',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
}, {
name: 'Gamma_r',
label: 'Gamma_r',
index: 'Gamma_r',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
}, {
name: 'Gamma_r_1bp',
label: 'Gamma_r(1bp)',
index: 'Gamma_r_1bp',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
}, {
name: 'Vega_r',
label: 'Vega_r',
index: 'Vega_r',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
}, {
name: 'Vega_r_1bp',
label: 'Vega_r(1bp)',
index: 'Vega_r_1bp',
width: 100,
align: 'center',
sortIndex: i++,
formatter: greeksFormat,
sortable: false
},);
}
return tradeHelper.getAmountToNotional(colModelGrid);
}
@@ -594,6 +663,13 @@ function TradeAmountFormat(cellValue, options, rowObject) {
return otcformat.trading.notional(cellValue);
}
function greeksFormat(cellValue, options, rowObject) {
if (cellValue) {
return otcformat.trading.greek(cellValue);
}
return "";
}
function SpotPriceFormat(cellValue, options, rowObject) {
if (rowObject.TradeType !== "场内期权" && rowObject.TradeType !== "商品期货" && rowObject.TradeType !== "股票" && rowObject.TradeType !== "商品现货") {
return otcformat.trading.umprice(cellValue)
@@ -1,4 +1,4 @@

//客户选择组件
const vueClient = function () {
return {
@@ -214,7 +214,7 @@ const vueMarginTemplateName = function () {
}
//计算结果字段
const consCalcFields = Object.freeze(['TotalMargin', 'Pv', 'Delta', 'Gamma', 'Vega', 'Theta', 'Rho', 'PvContainsKnockOut', 'DeltaContainsKnockOut', 'DeltaInLots', 'GammaInLots', 'DeltaCash', 'GammaCash', 'VegaCash', 'GammaContainsKnockOut']);
const consCalcFields = Object.freeze(['TotalMargin', 'Pv', 'Delta', 'Gamma', 'Vega', 'Theta', 'Rho', 'PvContainsKnockOut', 'DeltaContainsKnockOut', 'DeltaInLots', 'GammaInLots', 'DeltaCash', 'GammaCash', 'VegaCash', 'GammaContainsKnockOut', 'Delta_r', 'Delta_r_1bp', 'Dv01', 'Gamma_r', 'Gamma_r_1bp', 'Vega_r', 'Vega_r_1bp']);
var _trades, _tradeVues, _salesCommissionCtrl;
//交易保存
@@ -1913,6 +1913,13 @@ function createVue(index, baseVue, floating) {
calcResult.PvContainsKnockOut = pricingFormat.tradePrice(result.PvContainsKnockOut);
calcResult.DeltaContainsKnockOut = pricingFormat.greek(result.DeltaContainsKnockOut);
calcResult.GammaContainsKnockOut = pricingFormat.greek(result.GammaContainsKnockOut);
calcResult.Delta_r = result.Delta_r ? pricingFormat.greek(result.Delta_r) : "";
calcResult.Delta_r_1bp = result.Delta_r_1bp ? pricingFormat.greek(result.Delta_r_1bp) : "";
calcResult.Dv01 = result.Dv01 ? pricingFormat.greek(result.Dv01) : "";
calcResult.Gamma_r = result.Gamma_r ? pricingFormat.greek(result.Gamma_r) : "";
calcResult.Gamma_r_1bp = result.Gamma_r_1bp ? pricingFormat.greek(result.Gamma_r_1bp) : "";
calcResult.Vega_r = result.Vega_r ? pricingFormat.greek(result.Vega_r) : "";
calcResult.Vega_r_1bp = result.Vega_r_1bp ? pricingFormat.greek(result.Vega_r_1bp) : "";
calcResult.TotalMargin = 0;
if (data === this.datas[this.datas.length - 1]) {
calcResult.TotalMargin = pageVue.GetTotalMargin(_.map(this.datas, x => x.trade), this.structureType);