738 lines
34 KiB
C#
738 lines
34 KiB
C#
using Qdp.Pricing.Base.Utilities;
|
|
using YLErp.Abstract.DataProviders;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.Commons;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.TradeDalModule;
|
|
using YLErp.Office.ExcelModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.EodModule
|
|
{
|
|
public class eod_varReq
|
|
{
|
|
/// <summary>
|
|
/// 估值日期
|
|
/// </summary>
|
|
public DateTime ValueDate { get; set; }
|
|
|
|
/// <summary>
|
|
/// 估值范围结束日期
|
|
/// </summary>
|
|
public DateTime? EndValueDate { get; set; }
|
|
|
|
public int VaRType { get; set; }
|
|
|
|
public double PvPercent { get; set; }
|
|
|
|
public string UnderlyingIds { get; set; }
|
|
|
|
public int? AssetId { get; set; }
|
|
|
|
public string TradeType { get; set; }
|
|
|
|
public bool IsNeedExport { get; set; }
|
|
|
|
public int DaysCount { get; set; }
|
|
}
|
|
|
|
public class eod_varResult
|
|
{
|
|
public string UnderlyingCode { get; set; }
|
|
public string AssetName { get; set; }
|
|
public double RealPv { get; set; }
|
|
public double CalcPv { get; set; }
|
|
public double WinLoss { get; set; }
|
|
public double WinLossPercent { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class EodVaRService : YLBaseService
|
|
{
|
|
public EodVaRService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
public EodVaRService(YLBaseService baseService) : base(baseService)
|
|
{
|
|
}
|
|
|
|
public void SaveEodVaR(eod_var record)
|
|
{
|
|
if (record.TradeType == null)
|
|
{
|
|
record.TradeType = "";
|
|
}
|
|
var r = DbContext.eod_var.FirstOrDefault(x => x.ValueDate == record.ValueDate && x.VaRType == record.VaRType && x.UnderlyingCode == record.UnderlyingCode && x.TradeType == record.TradeType && x.AssetId == record.AssetId);
|
|
if (r != null)
|
|
{
|
|
r.ValueDate = record.ValueDate;
|
|
r.PvObject = record.PvObject;
|
|
r.VaRValue = record.VaRValue;
|
|
r.RealPv = record.RealPv;
|
|
r.OptId = record.OptId;
|
|
r.OptName = record.OptName;
|
|
r.OptDate = record.OptDate;
|
|
}
|
|
else
|
|
{
|
|
DbContext.eod_var.Add(record);
|
|
}
|
|
|
|
DbContext.SaveChanges();
|
|
}
|
|
|
|
public void CalcEodVaR(eod_varReq req)
|
|
{
|
|
//目前修改dayCount是覆盖之前数据的,不再另存 8/4
|
|
if (req.TradeType == null)
|
|
{
|
|
req.TradeType = "";
|
|
}
|
|
var PvList = new List<double>();
|
|
var query = DbContext.eod_trade.Where(t => t.ValueDate == req.ValueDate);
|
|
if (!string.IsNullOrWhiteSpace(req.TradeType))
|
|
{
|
|
var tradeType = GlobalDicionary.SupportTradeTypes.Where(O => O != "收益互换" && O != "远期").ToList();
|
|
if (req.TradeType == "场外")
|
|
{
|
|
query = query.Where(O => tradeType.Contains(O.TradeType));
|
|
}
|
|
else if (req.TradeType == "互换")
|
|
{
|
|
query = query.Where(O => O.TradeType == "收益互换");
|
|
}
|
|
else if (req.TradeType == "远期")
|
|
{
|
|
query = query.Where(O => O.TradeType == "远期");
|
|
}
|
|
}
|
|
var eodTradeList = query.ToList();
|
|
|
|
//过滤只计算支持的交易
|
|
var tradeList = eodTradeList.Where(t => t.trade != null && t.trade.TradeType != "自定义交易" && t.trade.TradeType != "结构化交易" && GlobalDicionary.SupportTradeTypes.Contains(t.trade.TradeType) && !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).Select(t => t.trade).ToList();
|
|
|
|
var trades = DbContext.eod_trade_position
|
|
.Where(t => t.ValueDate == req.ValueDate && ConsTrade.TradeTypesForHedge.Contains(t.TradeType))
|
|
.Select(et => new tradeDto
|
|
{
|
|
id = et.TradeId,
|
|
ClientId = et.ClientId,
|
|
AssetId = et.BookId,
|
|
TradeType = et.TradeType,
|
|
UnderlyingId = et.UnderlyingId,
|
|
UnderlyingCode = et.UnderlyingCode,
|
|
BuySell = et.PositionType == "long" ? "买入" : "卖出",
|
|
IsMoneynessOption = "否",
|
|
Notional = et.Amount,
|
|
ExchangeOptionCode = et.ExchangeOptionCode
|
|
}).ToList();
|
|
|
|
//var attachTrades = DbContext.trade.Where(t => trades.Keys.Contains(t.id) && t.TradeType != "自定义交易" && GlobalDicionary.SupportTradeTypes.Contains(t.TradeType) && ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus)).ToList();
|
|
|
|
if (string.IsNullOrWhiteSpace(req.TradeType))
|
|
{
|
|
tradeList.AddRange(trades);
|
|
}
|
|
|
|
if (req.TradeType == "场内")
|
|
{
|
|
tradeList = new List<trade>();
|
|
trades = trades.Where(o => o.TradeType == "场内期权" || o.TradeType == "股票").ToList();
|
|
tradeList.AddRange(trades);
|
|
}
|
|
|
|
if (req.TradeType == "商品")
|
|
{
|
|
tradeList = new List<trade>();
|
|
trades = trades.Where(o => o.TradeType == "商品期货" || o.TradeType == "商品现货").ToList();
|
|
tradeList.AddRange(trades);
|
|
}
|
|
|
|
if (req.AssetId != null && req.AssetId != 0)
|
|
{
|
|
tradeList = tradeList.Where(o => req.AssetId == o.AssetId).ToList();
|
|
}
|
|
|
|
if (!tradeList.Any())
|
|
{
|
|
throw new Exception("该交易日未收盘或未找到持仓交易,无法算出VaR值");
|
|
}
|
|
|
|
foreach (var td in tradeList)
|
|
{
|
|
if (td.TradeType == "场内期权")
|
|
{
|
|
var exchangeOption = DataCacheProvider.GetExchangeListOptionDataSource().GetData(td.ExchangeOptionCode);
|
|
if (exchangeOption != null)
|
|
{
|
|
td.ExerciseDate = exchangeOption.MaturityDate;
|
|
td.ExerciseMode = exchangeOption.ExerciseMode;
|
|
td.OptionType = exchangeOption.OptionType;
|
|
td.Strike = exchangeOption.Strike;
|
|
td.TradeDate = td.StartDate = exchangeOption.OpenDate;
|
|
}
|
|
}
|
|
}
|
|
|
|
tradeBLL.SetFieldsByTradeType(tradeList);
|
|
new TradeDalService(this).SetSubTradeList(tradeList);
|
|
var underlyingDics = tradeList.Select(t => new { t.UnderlyingId, t.UnderlyingCode }).ToHashSet();
|
|
var underlyingIds = underlyingDics.Select(o => o.UnderlyingId);
|
|
var underlyingCodes = underlyingDics.Select(o => o.UnderlyingCode);
|
|
//合成价差期权对应的组合标的
|
|
//var tradeListAssociation = tradeList.Where(x => x.TradeType == "合成价差期权");
|
|
//var underlyingIdsAssociation = tradeListAssociation.Select(t => t.UnderlyingId).ToHashSet();
|
|
//var underlyingCodesAssociation = tradeListAssociation.Select(t => t.UnderlyingCode).ToHashSet();
|
|
var syntheticUnderlyings = DbContext.synthetic_underlying.Where(x => underlyingCodes.Contains(x.Name)).ToList();
|
|
var underlyingCodesAssociation = syntheticUnderlyings.Select(t => t.Name);
|
|
var underlyingIdsAssociation = underlyingDics.Where(t => underlyingCodesAssociation.Contains(t.UnderlyingCode)).Select(t => t.UnderlyingId).ToHashSet();
|
|
var underlyingIdsAssociationChildren = syntheticUnderlyings.Where(x => x.UnderlyingId1 != null).Select(x => x.UnderlyingId1).ToList();
|
|
underlyingIdsAssociationChildren.AddRange(syntheticUnderlyings.Where(x => x.UnderlyingId2 != null).Select(x => x.UnderlyingId2));
|
|
underlyingIdsAssociationChildren.AddRange(syntheticUnderlyings.Where(x => x.UnderlyingId3 != null).Select(x => x.UnderlyingId3));
|
|
underlyingIdsAssociationChildren.AddRange(syntheticUnderlyings.Where(x => x.UnderlyingId4 != null).Select(x => x.UnderlyingId4));
|
|
underlyingIdsAssociationChildren = underlyingIdsAssociationChildren.Distinct().ToList();
|
|
|
|
var useClosePrice = TradeCalcHelper.UseClosePrice();
|
|
var teodPriceProvider = new EodPriceProvider(req.ValueDate).Initialize();
|
|
var priceList = teodPriceProvider.GetEodPriceList();
|
|
var tdPriceProvider = teodPriceProvider.GetPriceProvider(useClosePrice ? SettlementTypeEnum.ClosePrice : SettlementTypeEnum.SettlePrice);
|
|
|
|
//当日持仓交易的收盘价
|
|
var dayEodPrices = priceList.Where(x => underlyingIds.Contains(x.UnderlyingId)).ToList();
|
|
//当日持仓合成价差期权交易组合标的对应的子标的的收盘价
|
|
var dayEodPricesAssociationChildren = priceList.Where(x => underlyingIdsAssociationChildren.Contains(x.UnderlyingId)).ToList();
|
|
|
|
//试算的每一个244天
|
|
var ValueDateCalc = req.ValueDate;
|
|
IPriceProvider dayEodPricesCalc = null;
|
|
IPriceProvider dayEodPricesCalcBefore = null;
|
|
var underlyingPvs = new Dictionary<string, List<double>>();
|
|
var tradePvs = new Dictionary<string, List<double>>();
|
|
for (var i = 0; i < req.DaysCount; i++)
|
|
{
|
|
#region 试算的每一天的标的相应的价格
|
|
|
|
//试算交易日的前一工作日
|
|
var ValueDateCalcBefore = valuedateBLL.GetNonHolidayDefore(ValueDateCalc.AddDays(-1));
|
|
dayEodPricesCalc = dayEodPricesCalcBefore ?? tdPriceProvider;
|
|
dayEodPricesCalcBefore = new EodPriceProvider(ValueDateCalcBefore).GetPriceProvider(SettlementTypeEnum.ClosePrice);
|
|
var calcTrades = tradeList;
|
|
var eodPrices = new ManualPriceProvider();
|
|
if (dayEodPrices != null && dayEodPrices.Any())
|
|
{
|
|
dayEodPrices.ForEach(t =>
|
|
{
|
|
//组合标的的模拟价格
|
|
if (underlyingIdsAssociation.Contains(t.UnderlyingId))
|
|
{
|
|
var syntheticUnderlying = syntheticUnderlyings.FirstOrDefault(x => x.Name == t.UnderlyingCode);
|
|
if (syntheticUnderlying == null)
|
|
{
|
|
throw new Exception($"组合标的{t.UnderlyingCode}不存在,计算失败");
|
|
}
|
|
double price1 = 0, price2 = 0, price3 = 0, price4 = 0;
|
|
if (!string.IsNullOrEmpty(syntheticUnderlying.UnderlyingCode1)
|
|
&& tdPriceProvider.TryGetPrice(syntheticUnderlying.UnderlyingCode1, out var price))
|
|
{
|
|
price1 = GetCalcUnderlyingPrice(syntheticUnderlying.UnderlyingCode1,
|
|
price, ValueDateCalcBefore, dayEodPricesCalc, dayEodPricesCalcBefore);
|
|
}
|
|
if (!string.IsNullOrEmpty(syntheticUnderlying.UnderlyingCode2)
|
|
&& tdPriceProvider.TryGetPrice(syntheticUnderlying.UnderlyingCode2, out price))
|
|
{
|
|
price2 = GetCalcUnderlyingPrice(syntheticUnderlying.UnderlyingCode2,
|
|
price, ValueDateCalcBefore, dayEodPricesCalc, dayEodPricesCalcBefore);
|
|
}
|
|
if (!string.IsNullOrEmpty(syntheticUnderlying.UnderlyingCode3)
|
|
&& tdPriceProvider.TryGetPrice(syntheticUnderlying.UnderlyingCode3, out price))
|
|
{
|
|
price3 = GetCalcUnderlyingPrice(syntheticUnderlying.UnderlyingCode3,
|
|
price, ValueDateCalcBefore, dayEodPricesCalc, dayEodPricesCalcBefore);
|
|
}
|
|
if (!string.IsNullOrEmpty(syntheticUnderlying.UnderlyingCode4)
|
|
&& tdPriceProvider.TryGetPrice(syntheticUnderlying.UnderlyingCode4, out price))
|
|
{
|
|
price4 = GetCalcUnderlyingPrice(syntheticUnderlying.UnderlyingCode4,
|
|
price, ValueDateCalcBefore, dayEodPricesCalc, dayEodPricesCalcBefore);
|
|
}
|
|
|
|
price = price1 * (syntheticUnderlying.Coefficient1 ?? 0) + price2 * (syntheticUnderlying.Coefficient2 ?? 0)
|
|
+ price3 * (syntheticUnderlying.Coefficient3 ?? 0) + price4 * (syntheticUnderlying.Coefficient4 ?? 0) + (syntheticUnderlying.Constant ?? 0);
|
|
if (price != 0)
|
|
{
|
|
eodPrices.SetPrice(t.UnderlyingCode, price);
|
|
}
|
|
else
|
|
{
|
|
calcTrades = calcTrades.Where(o => o.UnderlyingCode != t.UnderlyingCode).ToList();
|
|
}
|
|
}
|
|
//普通标的的模拟价格
|
|
else
|
|
{
|
|
var price = GetCalcUnderlyingPrice(t.UnderlyingCode,
|
|
useClosePrice ? t.ClosePrice : t.SettlePrice, ValueDateCalcBefore, dayEodPricesCalc, dayEodPricesCalcBefore);
|
|
|
|
if (price != 0)
|
|
{
|
|
eodPrices.SetPrice(t.UnderlyingCode, price);
|
|
}
|
|
else
|
|
{
|
|
calcTrades = calcTrades.Where(o => o.UnderlyingCode != t.UnderlyingCode).ToList();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
var tradeRiskResult = CalculatorHelper.CalculateRisksForTrades(
|
|
//根据模拟价格,对下一个交易日进行模拟试算
|
|
valueDate: QdpCalendarHelper.GetNonHoliday(req.ValueDate.AddDays(1)),
|
|
tradeList: calcTrades,
|
|
calcScenario: Enums.CalcScenarioEnum.EodSettlement,
|
|
priceProvider: eodPrices,
|
|
pricingRequest: QdpPricingRequest.PV_ONLY,
|
|
settlementType: useClosePrice ? SettlementTypeEnum.ClosePrice : SettlementTypeEnum.SettlePrice,
|
|
isUseTradeVol: PS.Config.IsTradeVol,
|
|
preciseTimeMode: false);
|
|
|
|
foreach (var underlyingPv in underlyingPvs)
|
|
{
|
|
underlyingPv.Value.Add(0);
|
|
}
|
|
foreach (var result in tradeRiskResult.Results)
|
|
{
|
|
foreach (var underlying in result.Underlyings)
|
|
{
|
|
if (underlying == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (underlyingPvs.ContainsKey(underlying.UnderlyingCode))
|
|
{
|
|
var index = underlyingPvs[underlying.UnderlyingCode].Count() - 1;
|
|
underlyingPvs[underlying.UnderlyingCode][index] = underlyingPvs[underlying.UnderlyingCode].Last() + (double.IsNaN(result.ValueResult.Pv) ? 0 : result.ValueResult.Pv);
|
|
}
|
|
else
|
|
{
|
|
underlyingPvs.Add(underlying.UnderlyingCode, new List<double>());
|
|
underlyingPvs[underlying.UnderlyingCode].Add(double.IsNaN(result.ValueResult.Pv) ? 0 : result.ValueResult.Pv);
|
|
}
|
|
}
|
|
if (req.IsNeedExport)
|
|
{
|
|
var t = result.Trade;
|
|
var postionside = t.BuySell == "买入" ? "long" : "short";
|
|
var code = !ConsTrade.TradeTypesForHedge.Contains(t.TradeType)
|
|
? t.TradeNumber
|
|
: HedgePnlCalc.GetHedgeUniqueCode(t.AssetId, t.TradeType, t.BuySell, t.UnderlyingCode, t.ExchangeOptionCode);
|
|
if (!tradePvs.ContainsKey(code))
|
|
{
|
|
tradePvs.Add(code, new List<double>() { result.ValueResult.Pv });
|
|
}
|
|
else
|
|
{
|
|
tradePvs[code].Add(result.ValueResult.Pv);
|
|
}
|
|
}
|
|
}
|
|
|
|
PvList.Add(tradeRiskResult.Results.Sum(x => double.IsNaN(x.ValueResult.Pv) ? 0 : x.ValueResult.Pv));
|
|
|
|
ValueDateCalc = ValueDateCalcBefore;
|
|
}
|
|
var tradeRiskResultToday = CalculatorHelper.CalculateRisksForTrades(
|
|
valueDate: req.ValueDate,
|
|
tradeList: tradeList,
|
|
calcScenario: Enums.CalcScenarioEnum.EodSettlement,
|
|
priceProvider: tdPriceProvider,
|
|
pricingRequest: QdpPricingRequest.PV_ONLY,
|
|
settlementType: useClosePrice ? SettlementTypeEnum.ClosePrice : SettlementTypeEnum.SettlePrice,
|
|
isUseTradeVol: PS.Config.IsTradeVol,
|
|
preciseTimeMode: false);
|
|
if (req.IsNeedExport)
|
|
{
|
|
ExportTradePvs(tradeRiskResultToday, tdPriceProvider, req.ValueDate, tradePvs);
|
|
}
|
|
var realPv = tradeRiskResultToday.Results.Sum(x => double.IsNaN(x.ValueResult.Pv) ? 0 : x.ValueResult.Pv);
|
|
var underlyingRealPvs = new Dictionary<string, double>();
|
|
foreach (var result in tradeRiskResultToday.Results)
|
|
{
|
|
foreach (var underlying in result.Underlyings)
|
|
{
|
|
if (underlying == null)
|
|
{
|
|
continue;
|
|
}
|
|
if (underlyingRealPvs.ContainsKey(underlying.UnderlyingCode))
|
|
{
|
|
underlyingRealPvs[underlying.UnderlyingCode] += (double.IsNaN(result.ValueResult.Pv) ? 0 : result.ValueResult.Pv);
|
|
}
|
|
else
|
|
{
|
|
underlyingRealPvs.Add(underlying.UnderlyingCode, 0);
|
|
underlyingRealPvs[underlying.UnderlyingCode] = (double.IsNaN(result.ValueResult.Pv) ? 0 : result.ValueResult.Pv);
|
|
}
|
|
}
|
|
}
|
|
var record = new eod_var()
|
|
{
|
|
ValueDate = req.ValueDate,
|
|
PvObject = string.Join(",", PvList),
|
|
RealPv = realPv,
|
|
VaRType = (int)EnumVaRType.HistoricalSimulation,
|
|
OptName = UserName,
|
|
OptId = UserId,
|
|
OptDate = DateTime.Now,
|
|
UnderlyingCode = "",
|
|
TradeType = req.TradeType,
|
|
AssetId = req.AssetId ?? 0
|
|
|
|
};
|
|
SaveEodVaR(record);
|
|
foreach (var underlyingRealPv in underlyingRealPvs)
|
|
{
|
|
if (underlyingPvs.TryGetValue(underlyingRealPv.Key, out var underlyingPv))
|
|
{
|
|
record = new eod_var()
|
|
{
|
|
ValueDate = req.ValueDate,
|
|
PvObject = string.Join(",", underlyingPv),
|
|
RealPv = underlyingRealPv.Value,
|
|
VaRType = (int)EnumVaRType.HistoricalSimulation,
|
|
OptName = UserName,
|
|
OptId = UserId,
|
|
OptDate = DateTime.Now,
|
|
UnderlyingCode = underlyingRealPv.Key,
|
|
TradeType = req.TradeType,
|
|
AssetId = req.AssetId ?? 0
|
|
};
|
|
SaveEodVaR(record);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CalcSimpleDeltaGammaVaR(eod_varReq req)
|
|
{
|
|
//从日终风险表获得DeltaCash和GammaCash,并按照标的资产合并
|
|
var eodRisks = DbContext.eod_trade_risk.AsNoTracking().Where(r => r.ValueDate == req.ValueDate).ToList();
|
|
if (req.AssetId != null && req.AssetId != 0)
|
|
{
|
|
var tradeIds = DbContext.trade.Where(o => ConsTrade.PositionTradeStatusList.Contains(o.TradeStatus) && o.AssetId == req.AssetId).Select(o => o.id);
|
|
eodRisks = eodRisks.Where(o => tradeIds.Contains(o.TradeId)).ToList();
|
|
}
|
|
var totalPv = (double)eodRisks.Sum(r => r.Pv);
|
|
var underlyingCodes = eodRisks.Where(r => r.Exposure != null).Select(r => r.Exposure).Distinct().ToList();
|
|
var deltaCashDict = underlyingCodes.ToDictionary(c => c, x => 0.0);
|
|
var gammaCashDict = underlyingCodes.ToDictionary(c => c, x => 0.0);
|
|
var thetaDict = underlyingCodes.ToDictionary(c => c, x => 0.0);
|
|
var pvDict = underlyingCodes.ToDictionary(c => c, x => 0.0);
|
|
|
|
foreach (var risk in eodRisks)
|
|
{
|
|
if (risk.Exposure == null)
|
|
{
|
|
continue;
|
|
}
|
|
deltaCashDict[risk.Exposure] += risk.DeltaCash;
|
|
gammaCashDict[risk.Exposure] += risk.GammaCash;
|
|
thetaDict[risk.Exposure] += (double)risk.Theta;
|
|
//underlying totalPv的计算
|
|
pvDict[risk.Exposure] += (double)risk.Pv;
|
|
}
|
|
|
|
//获取各标的过去30个交易日的历史波动率
|
|
var volDict = underlyingCodes.ToDictionary(c => c, x => 0.0);
|
|
var underlyings = DbContext.underlying_manager.AsNoTracking().Where(u => underlyingCodes.Contains(u.UnderlyingCode)).ToList();
|
|
|
|
var volCalculator = new VolCaculator();
|
|
var daysInYear = (int)valuedateBLL.TradeDayCount.ToDayCountImpl().DaysInYear();
|
|
foreach (var underlying in underlyings)
|
|
{
|
|
var priceDict = volCalculator.GetHistoricalPrices(underlying, req.ValueDate, 30 + 1);
|
|
var prices = priceDict.Select(f => f.Value).ToList();
|
|
var lnDiffs = volCalculator.GetLnDiffList(prices);
|
|
var vol = volCalculator.GetVolFromUnderlyingLogDiffs(lnDiffs, daysInYear);
|
|
volDict[underlying.UnderlyingCode] = vol;
|
|
}
|
|
|
|
//计算各标的资产的VaR
|
|
var varDict = new Dictionary<string, double>();
|
|
var sqrtT = Math.Sqrt(daysInYear);
|
|
foreach (var code in underlyingCodes)
|
|
{
|
|
if (gammaCashDict[code] < 0)
|
|
{
|
|
if (double.IsNaN(volDict[code]))
|
|
{
|
|
volDict[code] = 0;
|
|
}
|
|
var priceChange = 3.0 * volDict[code] / sqrtT;
|
|
varDict[code] = -(Math.Abs(deltaCashDict[code] * priceChange) + 0.5 * gammaCashDict[code] * priceChange * priceChange);
|
|
}
|
|
else
|
|
{
|
|
varDict[code] = thetaDict[code];
|
|
}
|
|
}
|
|
var totalVar = varDict.Values.Sum();
|
|
foreach (var code in underlyingCodes)
|
|
{
|
|
var value = new eod_var()
|
|
{
|
|
ValueDate = req.ValueDate,
|
|
UnderlyingCode = code,
|
|
RealPv = pvDict[code],
|
|
VaRType = (int)EnumVaRType.SimpleDeltaGamma,
|
|
VaRValue = varDict[code],
|
|
OptName = UserName,
|
|
OptId = UserId,
|
|
OptDate = DateTime.Now,
|
|
TradeType = "",
|
|
AssetId = req.AssetId ?? 0
|
|
};
|
|
SaveEodVaR(value);
|
|
}
|
|
var record = new eod_var()
|
|
{
|
|
ValueDate = req.ValueDate,
|
|
UnderlyingCode = "",
|
|
RealPv = totalPv,
|
|
VaRType = (int)EnumVaRType.SimpleDeltaGamma,
|
|
VaRValue = totalVar,
|
|
OptName = UserName,
|
|
OptId = UserId,
|
|
OptDate = DateTime.Now,
|
|
TradeType = "",
|
|
AssetId = req.AssetId ?? 0
|
|
};
|
|
|
|
SaveEodVaR(record);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据模拟交易日的标的变化率,试算出一个标的价格
|
|
/// </summary>
|
|
private double GetCalcUnderlyingPrice(string underlyingCode, double? settlePrice,
|
|
DateTime valueDateCalcBefore, IPriceProvider dayEodPricesCalc, IPriceProvider dayEodPricesCalcBefore)
|
|
{
|
|
double price = 0;
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
|
|
if (dayEodPricesCalc.TryGetPrice(underlyingCode, out var dayEodPriceCalc) &&
|
|
dayEodPricesCalcBefore.TryGetPrice(underlyingCode, out var dayEodPriceCalcBefore))
|
|
{
|
|
if (dayEodPriceCalcBefore > 0)
|
|
{
|
|
price = (settlePrice ?? 0.0) * dayEodPriceCalc / dayEodPriceCalcBefore;
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException($"标的{underlyingCode}在交易日{valueDateCalcBefore:yyyy-MM-dd}的标的结算价不合法,试算失败");
|
|
}
|
|
}
|
|
else if (um.UnderlyingInstrumentType == "Stock")
|
|
{
|
|
if (!dayEodPricesCalc.TryGetPrice(underlyingCode, out dayEodPriceCalc))
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
//取不到前一天的,就取那天的价格
|
|
return dayEodPriceCalc;
|
|
}
|
|
}
|
|
else//标的不存在收盘价时,用主标的合约的变化率来计算
|
|
{
|
|
var underlyingCodeArray = underlyingCode.TakeWhile(x => char.IsLetter(x)).ToArray();
|
|
var mainUnderlyingCode = new string(underlyingCodeArray) + "00";
|
|
|
|
if (!dayEodPricesCalc.TryGetPrice(mainUnderlyingCode, out dayEodPriceCalc))
|
|
{
|
|
return 0;
|
|
}
|
|
else if (!dayEodPricesCalcBefore.TryGetPrice(mainUnderlyingCode, out dayEodPriceCalcBefore))
|
|
{
|
|
//取不到前一天的,就取那天的价格
|
|
return dayEodPriceCalc;
|
|
}
|
|
|
|
if (dayEodPriceCalcBefore > 0)
|
|
{
|
|
price = (settlePrice ?? 0.0) * dayEodPriceCalc / dayEodPriceCalcBefore;
|
|
}
|
|
else
|
|
{
|
|
throw new ServiceException($"标的{underlyingCode}对应的主标的合约{mainUnderlyingCode}在交易日{valueDateCalcBefore:yyyy-MM-dd}的标的结算价不合法,试算失败");
|
|
}
|
|
}
|
|
return price;
|
|
}
|
|
|
|
private void ExportTradePvs(TradeRiskResult resultToday, IPriceProvider priceProvider, DateTime date, Dictionary<string, List<double>> tradePvs)
|
|
{
|
|
var list = resultToday.Results;
|
|
var templateFile = OtcAppContext.MapPath("~/App_Docs/导出模板/EodVar中间数据导出.xlsx");
|
|
var dicList = new List<Dictionary<string, string>>();
|
|
foreach (var result in list)
|
|
{
|
|
var t = result.Trade;
|
|
var postionside = t.BuySell == "买入" ? "long" : "short";
|
|
var code = t.TradeNumber ?? HedgePnlCalc.GetHedgeUniqueCode(t.AssetId, t.TradeType, t.BuySell, t.UnderlyingCode, t.ExchangeOptionCode);
|
|
var dic = new Dictionary<string, string>();
|
|
priceProvider.TryGetPrice(result.Trade.UnderlyingCode, out var underlyingPrice);
|
|
dic["TradeNumber"] = code;
|
|
dic["UnderlyingCode"] = result.Trade.UnderlyingCode;
|
|
dic["UnderlyingPrice"] = underlyingPrice.OtcFormatMoney();
|
|
dic["Vol"] = result.ValueResult.Vol.OtcFormatPercent();
|
|
dic["RealPv"] = result.ValueResult.Pv.OtcFormatMoney();
|
|
dic["Pvs"] = string.Join(",", tradePvs[code]);
|
|
|
|
dicList.Add(dic);
|
|
}
|
|
|
|
var list2 = dicList.Select(n => new ExpandoDictionary<string, string>(n)).ToArray();
|
|
|
|
var bytes = ExcelGenerator.UseTemplateGenerator(templateFile).AddVariable(new { list = list2 }).GenerateBytes();
|
|
|
|
var dicPath = OtcAppContext.MapPath(@"\App_Docs\Temp");
|
|
Directory.CreateDirectory(dicPath);
|
|
var xlsxFilePath = Path.Combine(dicPath, $"EodVar中间数据{date:yyyyMMdd}.xlsx");
|
|
File.WriteAllBytes(xlsxFilePath, bytes);
|
|
}
|
|
public List<eod_varResult> GetEodVaR(eod_varReq req)
|
|
{
|
|
if (req.TradeType == null)
|
|
{
|
|
req.TradeType = "";
|
|
}
|
|
//从js里移过来的逻辑;
|
|
req.PvPercent = 1 - req.PvPercent;
|
|
req.AssetId = req.AssetId == null ? 0 : req.AssetId;
|
|
var eodVaRs = DbContext.eod_var.Where(x => x.ValueDate == req.ValueDate && x.VaRType == req.VaRType && x.TradeType == req.TradeType && x.AssetId == req.AssetId);
|
|
List<string> underlyingCodes = new List<string>();
|
|
if (!string.IsNullOrWhiteSpace(req.UnderlyingIds))
|
|
{
|
|
var underlyingIds = req.UnderlyingIds.Split(',');
|
|
|
|
foreach (var id in underlyingIds)
|
|
{
|
|
UnderlyingDataProvider _umProvider = new UnderlyingDataProvider();
|
|
var um = _umProvider.GetUnderlying(int.Parse(id));
|
|
underlyingCodes.Add(um.UnderlyingCode);
|
|
}
|
|
eodVaRs = eodVaRs.Where(o => underlyingCodes.Contains(o.UnderlyingCode));
|
|
}
|
|
|
|
if (eodVaRs == null || eodVaRs.Count() < 1)
|
|
{
|
|
var service = new EodVaRService(OptUser);
|
|
if (req.EndValueDate == null || req.EndValueDate.Value == req.ValueDate)
|
|
{
|
|
service.CalcEodVaR(req);
|
|
}
|
|
else
|
|
{
|
|
var dayCount = (req.EndValueDate.Value - req.ValueDate).TotalDays;
|
|
for (var i = 0; i <= dayCount; i++)
|
|
{
|
|
|
|
if (!QdpModule.QdpCalendarHelper.IsHoliday(req.ValueDate))
|
|
{
|
|
service.CalcEodVaR(req);
|
|
}
|
|
}
|
|
}
|
|
eodVaRs = DbContext.eod_var.Where(x => x.ValueDate == req.ValueDate && x.VaRType == req.VaRType && x.TradeType == req.TradeType && x.AssetId == req.AssetId);
|
|
if (!string.IsNullOrWhiteSpace(req.UnderlyingIds))
|
|
{
|
|
eodVaRs = eodVaRs.Where(o => underlyingCodes.Contains(o.UnderlyingCode));
|
|
}
|
|
|
|
}
|
|
List<eod_varResult> results = new List<eod_varResult>();
|
|
double calcPv = 0;
|
|
var totalVaR = eodVaRs.FirstOrDefault(o => o.UnderlyingCode == null || o.UnderlyingCode == "");
|
|
|
|
eodVaRs = eodVaRs.Where(o => o.UnderlyingCode != null && o.UnderlyingCode != "");
|
|
|
|
if (underlyingCodes != null && underlyingCodes.Count > 0)
|
|
{
|
|
foreach (var eodVaR in eodVaRs)
|
|
{
|
|
var AssetName = assetunitBLL.GetAllAssetUnitModel().FirstOrDefault(o => o.id == eodVaR.AssetId)?.Name;
|
|
AssetName = string.IsNullOrEmpty(AssetName) ? "-- 总计 --" : AssetName;
|
|
if (req.VaRType == (int)EnumVaRType.HistoricalSimulation)
|
|
{
|
|
var pvList = eodVaR.PvObject.Split(',').Select(x => double.TryParse(x, out var temp) ? temp : 0).OrderBy(x => x).ToArray();
|
|
var index = (int)Math.Floor(pvList.Length * req.PvPercent) - 1;
|
|
if (index < 0)
|
|
{
|
|
index = 0;
|
|
}
|
|
calcPv = pvList[index];
|
|
}
|
|
else
|
|
{
|
|
calcPv = eodVaR.VaRValue ?? 0;
|
|
}
|
|
|
|
var tempResult = new eod_varResult()
|
|
{
|
|
UnderlyingCode = eodVaR.UnderlyingCode,
|
|
AssetName = AssetName,
|
|
RealPv = eodVaR.RealPv ?? 0,
|
|
CalcPv = calcPv,
|
|
WinLoss = calcPv - (eodVaR.RealPv ?? 0),
|
|
WinLossPercent = (eodVaR.RealPv ?? 0) == 0 ? 0 : (calcPv - eodVaR.RealPv.Value) / Math.Abs(eodVaR.RealPv.Value)
|
|
};
|
|
results.Add(tempResult);
|
|
}
|
|
}
|
|
|
|
if (totalVaR != null)
|
|
{
|
|
var AssetName = assetunitBLL.GetAllAssetUnitModel().FirstOrDefault(o => o.id == totalVaR.AssetId)?.Name;
|
|
AssetName = string.IsNullOrEmpty(AssetName) ? "-- 总计 --" : AssetName;
|
|
if (req.VaRType == (int)EnumVaRType.HistoricalSimulation)
|
|
{
|
|
var pvList = totalVaR.PvObject.Split(',').Select(x => double.TryParse(x, out var temp) ? temp : 0).OrderBy(x => x).ToArray();
|
|
var index = (int)Math.Floor(pvList.Count() * req.PvPercent) - 1;
|
|
if (index < 0)
|
|
{
|
|
index = 0;
|
|
}
|
|
calcPv = pvList[index];
|
|
}
|
|
else
|
|
{
|
|
calcPv = totalVaR.VaRValue ?? 0;
|
|
}
|
|
results.Add(new eod_varResult()
|
|
{
|
|
UnderlyingCode = "-- 总计 --",
|
|
AssetName = AssetName,
|
|
RealPv = totalVaR.RealPv ?? 0,
|
|
CalcPv = calcPv,
|
|
WinLoss = calcPv - (totalVaR.RealPv ?? 0),
|
|
WinLossPercent = (totalVaR.RealPv ?? 0) == 0 ? 0 : (calcPv - totalVaR.RealPv.Value) / Math.Abs(totalVaR.RealPv.Value)
|
|
});
|
|
return results;
|
|
}
|
|
else
|
|
{
|
|
return results;
|
|
}
|
|
}
|
|
}
|
|
}
|