using YLErp.BLL; using YLErp.Commons; using YLErp.Configuration; using YLErp.Enums; using YLErp.Modules.CalculationModule; using YLErp.Modules.DataProviderModule; using YLErp.Modules.StructureModule; namespace YLErp.Modules.ApiModule { public static class TradeCalcApiHelper { static readonly HttpClientWrap _httpClientWrap; public const string PV = "pv"; public const string DELTA = "delta"; public const string GAMMA = "gamma"; public const string VEGA = "vega"; public const string RHO = "rho"; public const string THETA = "theta"; public const string MARGIN = "margin"; static TradeCalcApiHelper() { _httpClientWrap = new HttpClientWrap(PS.Config.ErpElement.ExternalAPIForCustomCalc, PS.Config.ErpElement.ExternalAPIForCustomCalcTimeOut); } public static string PostJson(string apiPath, object postData) { if (_httpClientWrap.BaseAddress == null) { throw new ServiceException("ExternalAPI未配置"); } try { return _httpClientWrap.PostJson(apiPath, postData, null); } catch (Exception ex) { LogFactory.GetLogger("ExternalAPI-Post").Error(apiPath, ex); throw; } } /// /// 调用外部接口计算自定义交易 /// /// 估值日期 /// 交易信息 /// 价格 /// 是否结算,会影响结算时无风险利率和分红率是否需要从历史记录中取 /// 要计算的要素 /// public static ReturnInfo CalculateCustomizedTrade(DateTime valueDate, trade t , double spotPrice, double volValue, bool isSettle, CalcScenarioEnum calcScenario, params string[] calcMethod) { if (t is null) { throw new ArgumentNullException(nameof(t)); } if (t.TradeType != "自定义交易") { throw new Exception("只支持自定义交易计算"); } if (PS.Config.Company == CompanyEnum.广期资本 && !(t.StructureType.StartsWith("API-"))) { TempValueResult temp=new TempValueResult(); var r = Return.Success(temp, ""); r.Content.Pv = 0; r.Content.Delta = 0; r.Content.Gamma = 0; r.Content.Vega = 0; r.Content.CalendarDayTheta = 0; r.Content.TradingDayTheta = 0; r.Content.Rho = 0; r.Content.DeltaCash = 0; r.Content.GammaCash = 0; r.Content.VegaCash = 0; r.Content.Rho = 0; return r; } var umInfo = DataCacheProvider.GetUnderlyingDataSource().GetData(t.UnderlyingCode ?? ""); var varietyInfo = DataCacheProvider.GetVarietyDataSource().GetData(umInfo?.UnderlyingTypeId ?? 0); var service = new TradeHisDataProvider(valueDate); var noRiskRate = service.GetNoRiskRate(t.id); var dividendRate = service.GetDividendRate(t.id); if (!isSettle || !noRiskRate.HasValue) { noRiskRate = t.NoRiskRate ?? valuedateBLL.SysRiskFreeRate(); dividendRate = t.DividendRate; } var structureList = new StructureService(OptUserInfo.SystemUser).QueryStructure_Details(t.StructureType); var extendInfo = new Dictionary(); foreach (var item in t.Propertys) { var columnType = structureList.Where(O => O.ColumnName == item.name).Select(O => O.ColumnType).FirstOrDefault(); switch (columnType) { case StructureColumnTypeEnum.NUMBER: if (double.TryParse(item.value, out var value)) { extendInfo[item.name] = value; } else { throw new ServiceException($"{t.TradeNumber}交易中 {item.name} 字段值:{item.value} 无法转换为数值类型"); } break; case StructureColumnTypeEnum.DATE: if (DateTime.TryParse(item.value, out var date)) { extendInfo[item.name] = date; } else { throw new ServiceException($"{t.TradeNumber}交易中 {item.name} 字段值{item.value} 无法转换为日期类型"); } break; case StructureColumnTypeEnum.TEXT: case StructureColumnTypeEnum.COMBO_BOX_SINGLE: default: extendInfo[item.name] = item.value; break; } } var postData = new Dictionary { ["CalcId"] = string.IsNullOrWhiteSpace(t.TradeNumber) ? Guid.NewGuid().ToString("N") : t.TradeNumber, ["ValueDate"] = valueDate.ToString("yyyy-MM-dd"), ["CalcMethods"] = calcMethod, ["StructureType"] = t.StructureType ?? "", ["CallPut"] = t.CallPut, ["StartDate"] = t.StartDate, ["ExerciseDate"] = t.ExerciseDate, ["BuySell"] = "买入", ["InitialSpotPrice"] = t.SpotPrice, ["Strike"] = t.IsMoneynessOptionData ? t.Strike * t.SpotPrice : t.Strike, ["UnderlyingCode"] = t.UnderlyingCode, ["UnderlyingPrice"] = spotPrice, ["RiskFreeRate"] = noRiskRate, ["DividendRate"] = dividendRate, ["UpDownLimit"] = NumberHelper.TryParse(umInfo?.UpDownLimit, out var pval, out var isPercent) ? pval : varietyInfo?.UpLimitValue ?? 0, ["Volatility"] = volValue }; var baseInfo = t.Clone(); TradeHelper2.ReduceTradeExt(baseInfo); baseInfo.CalcId = null; baseInfo.StructureType = null; baseInfo.OptionType = null; baseInfo.StartDate = null; baseInfo.ExerciseDate = null; baseInfo.BuySell = null; baseInfo.InitialSpotPrice = null; baseInfo.Strike = null; baseInfo.UnderlyingCode = null; baseInfo.UnderlyingPrice = null; baseInfo.NoRiskRate = null; baseInfo.DividendRate = null; baseInfo.Vol = null; baseInfo.ExtendInfo = null; postData["BaseInfo"] = baseInfo; postData["ExtendInfo"] = extendInfo; //计算场景 postData["CalcScenario"] = calcScenario.ToString(); var log = LogFactory.GetLogger(); var jsonStr = JsonHelper.Serialize(postData); log.Info("调用外部接口计算自定义交易请求" + jsonStr); var res = PostJson("api/v1/structure/calculate", postData); ReturnInfo result; try { LogFactory.GetLogger().Info(res); var obj = JsonHelper.Deserialize(res); if (obj == null) { return Return.Fail("返回信息无法解析:" + res); } if (obj.errcode == 0) { result = Return.Success(obj, obj.errmsg); result.Content.Pv = result.Content.Pv * t.Notional; result.Content.Delta = result.Content.Delta * t.Notional; result.Content.Gamma = result.Content.Gamma * t.Notional; result.Content.Vega = result.Content.Vega * t.Notional; result.Content.CalendarDayTheta = result.Content.CalendarDayTheta * t.Notional; result.Content.TradingDayTheta = result.Content.TradingDayTheta * t.Notional; result.Content.Rho = result.Content.Rho * t.Notional; if (t.BuySell == "卖出") { result.Content.Pv = -result.Content.Pv; result.Content.Delta = -result.Content.Delta; result.Content.Gamma = -result.Content.Gamma; result.Content.Vega = -result.Content.Vega; result.Content.CalendarDayTheta = -result.Content.CalendarDayTheta; result.Content.TradingDayTheta = -result.Content.TradingDayTheta; result.Content.Rho = -result.Content.Rho; } result.Content.DeltaCash = result.Content.Delta * spotPrice; result.Content.GammaCash = result.Content.Gamma * Math.Pow(spotPrice, 2) / 100; result.Content.VegaCash = result.Content.Vega * spotPrice; //广期计算的Rho是100bp的结果,这里需要改为1bp; result.Content.Rho = result.Content.Rho / 100; } else { result = Return.Fail(obj.errmsg, obj); } } catch (Exception ex) { result = Return.Fail("调用外部接口失败:" + ex.Message); } return result; } internal class TempValueResult { public int errcode { get; set; } public string errmsg { get; set; } public string CalcId { get; set; } public double Pv { get; set; } public double Vega { get; set; } public double Delta { get; set; } public double Gamma { get; set; } public double Rho { get; set; } public double Theta { get; set; } public double Margin { get; set; } public double Volatility { get; set; } public static implicit operator TradeValueResult(TempValueResult obj) { return new TradeValueResult() { Pv = obj.Pv, Delta = obj.Delta, Gamma = obj.Gamma, Vega = obj.Vega, Rho = obj.Rho, CalendarDayTheta = obj.Theta, TradingDayTheta = obj.Theta, Margin = obj.Margin, Vol = obj.Volatility, }; } } } }