Files
zszq-trs/YLErpWeb/Controllers/PricingController.cs
T

1003 lines
40 KiB
C#

using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Library.Equity.Engines.Analytical;
using System.Text.RegularExpressions;
using YLErp.BLL.MarginCalculation;
using YLErp.Configuration;
using YLErp.DBModels;
using YLErp.DBModels.Consts;
using YLErp.Enums;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.PricingModule;
using YLErp.Modules.PricingModule.Models;
using YLErp.Modules.StructureModule;
using YLErp.Modules.SystemModule;
using YLErp.Modules.TagModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.OrderModule;
using YLErp.Modules.UnderlyingModule;
using YLErp.Modules.VolatilityModule;
using YLErp.Office;
using YLErp.QdpModule;
namespace YLErp.Web.Controllers
{
public class PricingController : BaseController
{
/// <summary>
/// 组合报价
/// </summary>
[MyAuthorize("报价管理-结构化交易定价")]
public ActionResult Structure()
{
var otcTrade = new OtcOptionTradeFull()
{
TraderId = CurUser.UserId,
TraderName = CurUser.UserName,
BuySell = "卖出",
VolType = "交易",
TradeType = "香草期权",
OptionType = "看涨",
ExerciseMode = "European",
TradeDate = valuedateBLL.ValueDate,
UnderlyingInstrumentType = AppHelper.OtcConfig.StockFirst ? "Stock" : "CommodityFutures",
SettlementType = (int)SettlementTypeEnum.ClosePrice,
NoRiskRate = valuedateBLL.SystemDate.RiskFreeRate / 100,
ParticipationRate = 1,
AnnualizeFactor = 1,
MarginTemplateName = "系统默认",
CouponIncludeStartDate = false,
CouponUsePaymentDate = false
};
var model = new Models.PricingModel(CurUser, UserBLL.IsTradeOfCurrentLogin(CurUser.UserId)) { Trade = otcTrade };
if (model.NumOfSmoothingDaysCfg == "ONE")
{
model.Trade.NumOfSmoothingDays = 1;
}
//获取自定义结构信息
var structureTypes =
new StructureService(CurUser)
.QueryStructureMap(StructureRangeEnum.BALCK_TRADE);
var structureTypeMap = new Dictionary<string, List<Structure_Details>>() {
{ "气囊结构",new List<Structure_Details>() }
};
foreach (var item in structureTypes)
{
structureTypeMap[item.Key] = item.Value;
}
ViewBag.StructureTypeMap = structureTypeMap;
var map = new StructureService(CurUser)
.QueryStructureMap(StructureRangeEnum.EXTEND_INFO);
ViewBag.ExtendInfoMap = new Dictionary<string, List<Structure_Details>>();
ViewBag.ExtendInfoMap[""] = new List<Structure_Details>();
foreach (var item in map)
{
ViewBag.ExtendInfoMap[item.Key] = item.Value;
}
using (var tagService = new TagService(CurUser))
{
ViewBag.TagList = tagService.GetTagListByType(TagTypeEnum.Trade);
}
return View(model);
}
/// <summary>
/// 组合报价
/// </summary>
[MyAuthorize("报价管理-结构化交易定价")]
public ActionResult Structure_DZ()
{
var otcTrade = new OtcOptionTradeFull()
{
TraderId = CurUser.UserId,
TraderName = CurUser.UserName,
BuySell = "卖出",
VolType = "交易",
TradeType = "香草期权",
OptionType = "看涨",
ExerciseMode = "European",
TradeDate = valuedateBLL.ValueDate,
UnderlyingInstrumentType = AppHelper.OtcConfig.StockFirst ? "Stock" : "CommodityFutures",
SettlementType = (int)SettlementTypeEnum.ClosePrice,
NoRiskRate = valuedateBLL.SystemDate.RiskFreeRate / 100,
ParticipationRate = 1,
AnnualizeFactor = 1,
MarginTemplateName = "系统默认",
CouponIncludeStartDate = false,
CouponUsePaymentDate = false
};
var model = new Models.PricingModel(CurUser, UserBLL.IsTradeOfCurrentLogin(CurUser.UserId)) { Trade = otcTrade };
if (model.NumOfSmoothingDaysCfg == "ONE")
{
model.Trade.NumOfSmoothingDays = 1;
}
//获取自定义结构信息
var structureTypes =
new StructureService(CurUser)
.QueryStructureMap(StructureRangeEnum.BALCK_TRADE);
var structureTypeMap = new Dictionary<string, List<Structure_Details>>() {
{ "气囊结构",new List<Structure_Details>() }
};
foreach (var item in structureTypes)
{
structureTypeMap[item.Key] = item.Value;
}
ViewBag.StructureTypeMap = structureTypeMap;
return View(model);
}
/// <summary>
/// 组合报价导入
/// </summary>
public ActionResult StructureImport(string EncryptId)
{
var tradeId = DecryptInt(EncryptId);
var otcTrade = new TradeDetailsQueryService(CurUser).GetOtcTradeFull(tradeId);
if (otcTrade == null)
{
return ShowError("没有找到交易数据");
}
if (!otcTrade.IsOption() && otcTrade.TradeType != "结构化产品")
{
return ShowError("期权定价只支持期权交易");
}
ResetOtcOptionTrade(otcTrade);
otcTrade.ValueDate = valuedateBLL.ValueDate;
if (PS.Config.IsTradeVol && PS.Config.Company == CompanyEnum.厦门象屿)
{
var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay
? DayCountMode.CalendarDay : DayCountMode.TradingDay;
//新增交易当天的持仓波动率需要划掉一天,修改后的持仓波动率不需要再划一天
var vol = AnalyticalOptionTradeVolInterp.tradeVolLinearInterp(
valuationDate: new Qdp.Foundation.Implementations.Date(otcTrade.ValueDate),
tradeOpenVol: otcTrade.TradeOpenVolatility ?? 0,
tradeCloseVol: otcTrade.TradeCloseVolatility ?? 0,
startDate: new Qdp.Foundation.Implementations.Date(otcTrade.StartDate.Value),
maturityDate: new Qdp.Foundation.Implementations.Date(otcTrade.ExerciseDate.Value),
numOfSmoothingDays: otcTrade.NumOfSmoothingDays ?? 0,
dayCountMode: daycountMode,
calendar: CalendarImpl.Get("chn"),
includeStartDate: false);
otcTrade.TradeOpenVolatility = otcTrade.TradeCloseVolatility = vol;
}
var model = new Models.PricingModel(CurUser, UserBLL.IsTradeOfCurrentLogin(CurUser.UserId)) { IsImport = true, Trade = otcTrade };
//获取自定义结构信息
var structureTypes =
new StructureService(CurUser)
.QueryStructureMap(StructureRangeEnum.BALCK_TRADE);
var structureTypeMap = new Dictionary<string, List<Structure_Details>>() {
{ "气囊结构",new List<Structure_Details>() }
};
var dict = new Dictionary<string, List<Structure_Details>>();
foreach (var item in structureTypes)
{
structureTypeMap[item.Key] = item.Value;
}
ViewBag.StructureTypeMap = structureTypeMap;
var map = new StructureService(CurUser)
.QueryStructureMap(StructureRangeEnum.EXTEND_INFO);
ViewBag.ExtendInfoMap = new Dictionary<string, List<Structure_Details>>();
ViewBag.ExtendInfoMap[""] = new List<Structure_Details>();
foreach (var item in map)
{
ViewBag.ExtendInfoMap[item.Key] = item.Value;
}
if (PS.Config.Is润和)
{
return View(nameof(Structure_DZ), model);
}
return View(nameof(Structure), model);
}
#region----定价模板----
/// <summary>
/// 加载定价模板
/// </summary>
public JsonResult AjaxGetTemplateList()
{
var datas = new SysUserConfigService(CurUser).GetConfigInfos();
return JsonSuccessData(datas);
}
/// <summary>
/// 加载定价模板
/// </summary>
public JsonResult AjaxGetTemplate(string name, string TemplateType)
{
var t_Type = SysUserConfigType.PricingTemplateV2;
if (TemplateType == "公共模板")
{
t_Type = SysUserConfigType.CommonTemplate;
}
var configData = new SysUserConfigService(CurUser).GetConfigData(t_Type, name);
if (string.IsNullOrWhiteSpace(configData))
{
return JsonError("模板数据不存在");
}
return JsonSuccessData(configData);
}
/// <summary>
/// 删除定价模板
/// </summary>
public JsonResult AjaxRemoveTemplate(int sysUserConfigId)
{
if (sysUserConfigId == 0)
{
return JsonError("请求参数为空");
}
new SysUserConfigService(CurUser).RemoveData(sysUserConfigId, SysUserConfigType.PricingTemplateV2, Server.CacheProvider);
return JsonSuccess("成功删除");
}
/// <summary>
/// 保存定价模板
/// </summary>
/// <param name="name"></param>
/// <param name="dataJson"></param>
/// <param name="_override">是否可以覆盖</param>
/// <param name="_CommonTemplate">公共模板</param>
/// <returns></returns>
public JsonResult AjaxSaveTemplate(string name, string dataJson, bool _override = true, bool _CommonTemplate = false)
{
if (string.IsNullOrWhiteSpace(name))
{
return JsonError("错误参数:name");
}
try
{
new SysUserConfigService(CurUser).SaveData(SysUserConfigType.PricingTemplateV2, name, dataJson, Server.CacheProvider, enableOverride: _override, enableCommtemplate: _CommonTemplate);
}
catch (ServiceException ex)
{
if (ex.Message.Contains("覆盖"))
{
return JsonError("模板已经存在,请填写其它名称或着选择覆盖已存在的模板");
}
}
return JsonSuccess("保存成功");
}
/// <summary>
/// 管理模板列表
/// </summary>
public ActionResult TemplateList()
{
return View();
}
#endregion
/// <summary>
/// 根据报价或定价的参数,获取一个波动率的值,只在Normal模式下使用。
/// SkewMap模式使用GetBaseVolValue
/// </summary>
public JsonResult AjaxGetVol(SingleVolReq req)
{
var userGroup = UserBLL.GetUserGroup(UserId);
if (PS.Config.ErpElement.SkewMapVolConstruction)
{
try
{
if (req.IsMoneynessOption == "是")
{
req.Strike = req.SpotPrice * req.Strike;
}
var volType = req.VolType;
req.Vols = VolatilityHelper.GetVol(req.TradeDate, "交易", req.UnderlyingCode, userGroup);
if (req.Vols == null || string.IsNullOrEmpty(req.Vols.VolSurfaceMode))
{
throw new InvalidOperationException($"找不到波动率曲面{req.UnderlyingCode}");
}
if (req.BaseVol == null || req.BaseVol <= 0)
{
req.BaseVol = SkewMapVolHelper.GetSkewMapBaseVolForTrade(req.TradeDate, req.UnderlyingCode, req.Vols, req.UnderlyingTypeId, req.ExerciseDate);
}
int? varValue = null;
switch (volType)
{
case "报价Ask":
varValue = req.AskVar;
break;
case "报价Bid":
varValue = req.BidVar;
break;
}
if (!varValue.HasValue)
{
var vol = VolatilityHelper.GetVol(req.TradeDate, "交易", req.UnderlyingCode, CurUser.UserGroup);
if (vol == null)
{
return JsonError("找不到波动率:" + req.UnderlyingCode);
}
switch (volType)
{
case "报价Ask":
req.AskVar = varValue = (int)(vol.GetAskVar() ?? 0);
break;
case "报价Bid":
req.BidVar = varValue = (int)(vol.GetBidVar() ?? 0);
break;
}
}
req.VolType = volType;
var TargetVol = SingleVolService.GetSingleVol(req, UserId).ToString();
return JsonSuccessData(new
{
var = varValue,
vol = TargetVol,
baseVol = req.BaseVol
});
}
catch (ArgumentException ex)
{
if (Regex.IsMatch(ex.Message, @"列.\d+.不属于表"))
{
return JsonError("Var值超出SkewMap可选范围");
}
return JsonError(ex.Message);
}
catch (Exception ex)
{
return JsonError(ex.Message);
}
}
else
{
if (ConsUserGroup.HasGroup && string.IsNullOrWhiteSpace(userGroup))
{
return JsonSuccessData(new { vol = 0 });
}
var vol = SingleVolService.GetSingleVol(req, UserId);
if (PS.Config.Is润和)
{
req.VolType = "交易";
var Midvol = SingleVolService.GetSingleVol(req, UserId);
return JsonSuccessData(new { vol = vol, Midvol = Midvol });
}
return JsonSuccessData(new { vol });
}
}
/// <summary>
/// 获取一个标的信息
/// </summary>
public JsonResult AjaxGetUnderlying(UnderlyingGetRequest req)
{
underlying_manager underlying = null;
if (!string.IsNullOrWhiteSpace(req.UnderlyingCode))
{
underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
}
else
{
var query = DataCacheProvider.GetUnderlyingDataSource().AsQueryable()
.Where(n => n.LaunchState == "1" && n.UnderlyingState != "Matured");
if (!string.IsNullOrWhiteSpace(req.InstrumentType))
{
underlying = query.FirstOrDefault(n => n.UnderlyingInstrumentType == req.InstrumentType);
}
else if (req.VarietyId > 0)
{
underlying = query.FirstOrDefault(n => n.UnderlyingTypeId == req.VarietyId);
}
}
if (underlying != null)
{
// 获取不超过到期日的实行日
// 1个月,2周,1周,到日期这样的规则向前计算
if (!req.TradeDate.HasValue)
{
req.TradeDate = valuedateBLL.ValueDate;
}
var MaturityDate = underlying.MaturityDate ?? DateTime.Today;
if (underlying.CalcTypeIsStock() || underlying.IsCommoditySpot())
{
MaturityDate = DateTime.Today.AddYears(3);
}
underlying.ExerciseDate = QdpCalendarHelper.GetUnderlyingExerciseDate(MaturityDate, req.TradeDate.Value);
SyntheticPriceModel synthetic = null;
if (underlying.CommodityCode == "组合标的")
{
//返回组合标的中组成标的的现价
synthetic = new SyntheticUnderlyingPriceService(CurUser).GetPriceModel(underlying.UnderlyingCode);
underlying.Price = synthetic.Price;
}
var variety = DataCacheProvider.GetVariety(underlying.UnderlyingCode);
underlying.CountRatio = variety != null ? variety.CountRatio : 1;
return JsonSuccessData(new { underlying, synthetic });
}
return JsonError("标的信息缺失");
}
/// <summary>
/// 获取组合标的价格模型
/// </summary>
public JsonResult AjaxGetSyntheticPriceModel(string underlyingCode, DateTime? tradeDate = null)
{
var synthetic = new SyntheticUnderlyingPriceService(CurUser).GetPriceModel(underlyingCode);
if (synthetic != null && tradeDate != null && tradeDate != valuedateBLL.ValueDate
&& EodPriceQueryService.TryGetEodPrice(tradeDate.Value, underlyingCode, out var eodPrice))
{
synthetic.Price = eodPrice.ClosePrice;
}
return JsonSuccessData(synthetic);
}
/// <summary>
/// 获取系统设定的无风险利率
/// </summary>
public JsonResult AjaxGetNoRiskRate(DateTime? startDate, DateTime? endDate)
{
var riskFreeRate = valuedateBLL.GetRiskFreeRateFromCurve(startDate, endDate);
return JsonSuccessData(riskFreeRate / 100);
}
/// <summary>
/// 获取系统设定的股票分红率
/// </summary>
public JsonResult AjaxGetDividendRate(string underlyingCode, DateTime tradeDate, string tradetype, string optiontype)
{
if (string.IsNullOrEmpty(underlyingCode))
{
return JsonSuccessData(0);
}
var list = yldb.dividendrate_record.Where(x => x.TradeType.Contains(tradetype) && tradeDate >= x.ValueDate && (x.OptionType == optiontype || x.OptionType == "全部")).ToList();
var record = list.Where(x => x.UnderlyingCode.Split(',').Any(code => code == underlyingCode)).OrderByDescending(x => x.OptDate).OrderByDescending(x => x.ValueDate);
if (record.Any())
{
return JsonSuccessData(record.FirstOrDefault()?.DividendRate ?? 0);
}
tradeDate = tradeDate.Date;
var query = from n in yldb.underlying_manager
join m in yldb.UnderlyingDividend.Where(a => a.ValueDate >= tradeDate)
on n.id equals m.UnderlyingId into ms
from m in ms.DefaultIfEmpty()
where n.UnderlyingCode == underlyingCode
select m == null ? n.DividendRate : m.DividendRate;
return JsonSuccessData(query.FirstOrDefault() ?? 0);
}
public JsonResult AjaxGetExchangeOptionPrice(string optionCode)
{
var provider = new ExchangeOptionPriceProvider();
var price = provider.GetPrice(optionCode);
return JsonSuccessData(price);
}
/// <summary>
/// 获取标的价格
/// </summary>
public JsonResult AjaxGetUnderlyingPrice(string underlyingCode, DateTime? tradeDate = null)
{
double price = 0;
double netPrice = 0;
var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
SyntheticPriceModel synthetic = null;
if (udm != null)
{
if (udm.CommodityCode == "组合标的")
{
using var syntheService = new SyntheticUnderlyingPriceService(CurUser);
if (tradeDate != null)
{
synthetic = syntheService.GetPriceModel(underlyingCode, Convert.ToDateTime(tradeDate));
if (synthetic != null)
{
price = synthetic.Price;
}
}
else
{
synthetic = syntheService.GetPriceModel(underlyingCode);
price = synthetic.Price;
}
}
else
{
price = udm.Price ?? 0;
netPrice = price;
if (udm.IsBond())
{
if (EodPriceQueryService.TryGetBondEodPrice(valuedateBLL.ValueDate, underlyingCode, out var eodPrice))
{
price = eodPrice.ClosePrice;
netPrice = eodPrice.SettlePrice;
}
else
{
price = price * Convert.ToDouble(ConsGlobal.bondPriceMultiple);
netPrice = price;
}
}
}
if (tradeDate != null && tradeDate != valuedateBLL.ValueDate)
{
var date = Convert.ToDateTime(tradeDate);
if (udm.IsBond())
{
if (EodPriceQueryService.TryGetBondEodPrice(date, underlyingCode, out var eodPrice))
{
price = eodPrice.ClosePrice;
netPrice = eodPrice.SettlePrice;
}
}
else
{
if (EodPriceQueryService.TryGetEodPrice(date, underlyingCode, out var eodPrice))
{
price = eodPrice.ClosePrice;
netPrice = eodPrice.SettlePrice;
}
}
}
}
return JsonSuccessData(new { price, synthetic, netPrice });
}
//期权定价计算(带预付金计算)
public JsonResult AjaxCalcPrices(IEnumerable<OtcOptionTradeFull> trades, bool calcMargin, bool calcAutocallGreeks = false)
{
if (trades is null || !trades.Any())
{
return JsonSuccessData(Enumerable.Empty<TradeValueResult>());
}
var service = new PriceCalcService(CurUser);
var resultList = service.CalcOptionPrice(trades, calcMargin
, CalcScenarioEnum.Pricing, td => (td.TradeType != "凤凰期权" && td.TradeType != "雪球期权") || calcAutocallGreeks
, calcVersion: Request.Form["version"]);
return JsonSuccessData(resultList);
}
//期权定价计算(带预付金计算)
public JsonResult AjaxCalcPrice(OtcOptionTradeFull trade)
{
if (trade is null)
{
return JsonError("错误:请求参数为空");
}
var result = new PriceCalcService(CurUser).CalcOptionPrice(trade, false, CalcScenarioEnum.Pricing, false);
result.calcResult.Pv = result.calcResult.Pv - (trade.StockEqvNotional * (trade.PrepaymentRatio ?? 0) * (trade.BuySell == "卖出" ? -1 : 1));
result.calcResult.RoundedPv = result.calcResult.RoundedPv - (trade.StockEqvNotional * (trade.PrepaymentRatio ?? 0) * (trade.BuySell == "卖出" ? -1 : 1));
return JsonSuccessData(new { result.calcResult, result.Day1Pnl });
}
public JsonResult AjaxCalcTrades(CalcTradesRequest request)
{
var result = new PriceCalcService(CurUser).CalcTrades(request);
return JsonSuccessData(result);
}
public JsonResult AjaxCalcSwapTrades(CalcTradesRequest request)
{
var result = new PriceCalcService(CurUser).CalcSwapTrades(request);
return JsonSuccessData(result);
}
//保本雪球计算年化期权费率
public JsonResult AjaxCalcSnowballAnnualPremium(OtcOptionTradeFull trade)
{
if (trade is null)
{
return JsonError("错误:请求参数为空");
}
var result = new PriceCalcService(CurUser).CalcSnowballAnnualPremium(trade);
return JsonSuccessData(result);
}
/// <summary>
/// 根据权利金反算雪球票息
/// </summary>
public JsonResult AjaxCalcSnowballKORebate(OtcOptionTradeFull trade)
{
if (trade is null)
{
return JsonError("错误:请求参数为空");
}
var result = new PriceCalcService(CurUser).CalcSnowballKORebate(trade);
return JsonSuccessData(result);
}
/// <summary>
/// 根据权利金反算凤凰票息
/// </summary>
/// <param name="trade"></param>
/// <returns></returns>
public JsonResult AjaxCalcPhoenixCouponRate(OtcOptionTradeFull trade)
{
if (trade is null)
{
return JsonError("错误:请求参数为空");
}
var result = new PriceCalcService(CurUser).CalcPhoenixCouponRate(trade);
return JsonSuccessData(result);
}
//获取初始预付金
public JsonResult AjaxGetInitialMargin(OtcOptionTradeFull trade)
{
if (trade is null)
{
throw new ArgumentNullException(nameof(trade));
}
try
{
var tdConv = TradeConverter.ConvertOptionTrade(trade);
var realTradeId = tdConv.id;
tdConv.id = 0;
tdConv.OptId = UserId;
tdConv.VolType = "报价Bid";
tdConv.TradeCloseVolatility = null;//不设置为null会影响计算结果
if (!tdConv.TTMDays.HasValue)
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(tdConv.UnderlyingCode);
tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(tdConv.TradeDate.Value, tdConv.ExerciseDate.Value,
underlying?.UnderlyingTypeId ?? 0, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
var initialMargin = MarginDefault.GetInitialMargin(tdConv, realTradeId, true);
return JsonSuccessData(initialMargin);
}
catch (Exception ex)
{
LogFactory.GetLogger("AjaxGetInitialMargin").Error(ex);
return JsonError("获取初始预付金失败:" + ex.Message);
}
}
//保存交易
public JsonResult AjaxSaveTrades(IEnumerable<OtcOptionTradeFull> trades)
{
if (trades is null || !trades.Any())
{
return JsonError("没有交易数据");
}
if (PS.Config.Is润和)
{
foreach (var x in trades)
{
x.MetaDic["MidVol"] = x.MidVol.ToString();
x.MetaDic["Day1Pnl"] = x.Day1Pnl.ToString();
}
}
var dbTrades = new OtcTradeSaveService(CurUser).SaveOptionTradesFromPricing(trades);
var items = dbTrades.SelectMany(n =>
{
return n.TradeType == "结构化交易" ? n.SubTrades.Select(m => new { m.EncryptId }) : new[] { new { n.EncryptId } };
});
return JsonSuccess("录入交易成功", items);
}
//保存分组交易
public JsonResult AjaxSaveGroupTrade(trade trade, IEnumerable<OtcOptionTradeFull> subTrades)
{
var dbTrade = new OtcTradeSaveService(CurUser).SaveGroupTradeFromPricing(trade, subTrades);
return JsonSuccess("录入交易成功", dbTrade);
}
//计算组合的到期收益曲线
public JsonResult GetTradesPayoffLine(IEnumerable<OtcOptionTradeFull> trades)
{
var service = new OptionTradeAnalysisService(CurUser);
return Json(service.GetTradesPayoffLine(trades));
}
//计算组合的Pv曲线
public JsonResult GetTradesPvLine(IEnumerable<OtcOptionTradeFull> trades)
{
var service = new OptionTradeAnalysisService(CurUser);
return Json(service.GetTradesPvLine(trades));
}
/// <summary>
/// 计算某笔交易的Pv曲线
/// </summary>
public JsonResult GetTradesPvLine2(string enTradeId)
{
var intid = DecryptInt(enTradeId);
var service = new OptionTradeAnalysisService(CurUser);
return Json(service.GetTradesPvLine2(intid));
}
//同时返回到期收益、Pv、以及在一半ttm时的Pv,三条曲线
public JsonResult GetTradesLifePvLine(IEnumerable<OtcOptionTradeFull> trades)
{
var service = new OptionTradeAnalysisService(CurUser);
return Json(service.GetTradePvLifeLine(trades));
}
/// <summary>
/// 计算组合Greeks随时间变化的曲线
/// </summary>
/// <param name="trades"></param>
/// <returns></returns>
public JsonResult GetTradesGreeksForLifetime(IEnumerable<OtcOptionTradeFull> trades)
{
var service = new OptionTradeAnalysisService(CurUser);
return Json(service.GetTradesGreeksForLifetime(trades));
}
/// <summary>
/// 根据交易编号获取otctradefull
/// </summary>
public JsonResult AjaxGetOtcTradeFull(string tradeNumber)
{
var otcTrade = new TradeDetailsQueryService(CurUser).GetOtcTradeFull(tradeNumber);
ResetOtcOptionTrade(otcTrade);
return JsonSuccessData(otcTrade);
}
/// <summary>
/// 获取不超过到期日的执行日
/// 1个月,2周,1周,到日期这样的规则向前计算
/// </summary>
public JsonResult AjaxGetExerciseDate(DateTime? underlyingMaturityDate, DateTime? tradeDate = null)
{
if (!tradeDate.HasValue)
{
tradeDate = valuedateBLL.ValueDate;
}
var ExerciseDate = QdpCalendarHelper.GetUnderlyingExerciseDate(underlyingMaturityDate ?? DateTime.Today.AddYears(1), tradeDate.Value);
return JsonSuccessData(new { ExerciseDate });
}
/// <summary>
/// 获取隐含波动率
/// </summary>
public JsonResult GetImpliedVol(OtcOptionTradeFull trade)
{
if (trade == null)
{
return JsonError("所传数据不能为空");
}
if (string.IsNullOrEmpty(trade.UnderlyingCode))
{
return JsonError("请选择标的代码");
}
if (string.IsNullOrWhiteSpace(trade.TradeType))
{
return JsonError("请填写结构类型");
}
if (trade.TradeType != "香草期权")
{
return JsonError("暂时只支持香草期权类型");
}
if (string.IsNullOrWhiteSpace(trade.OptionType))
{
return JsonError("请填写看涨看跌");
}
if (!trade.TradeDate.HasValue)
{
return JsonError("请填写交易日期");
}
if (!trade.ExerciseDate.HasValue)
{
return JsonError("请填写到期日期");
}
if (!trade.Strike.HasValue)
{
return JsonError("请填写行权价");
}
if (!trade.IsMoneynessOptionData && !trade.SpotPrice.HasValue)
{
return JsonError("请填写期初标的价格");
}
if (!trade.TradeSinglePrice.HasValue)
{
return JsonError("请填写权利金");
}
if (trade.ExerciseDate <= trade.TradeDate)
{
return JsonError("到期日期不能小于成交日期");
}
var sysDate = valuedateBLL.ValueDate;
var valDate = trade.ValueDate ?? trade.TradeDate ?? valuedateBLL.ValueDate;
trade.VolType = "交易";
var impliedVol = VolatilityHelper.GetImpliedVol(valDate, trade, trade.TTMDays, trade.UnderlyingPrice ?? trade.SpotPrice ?? 0, sysDate > valDate);
return JsonSuccessData(Math.Abs(impliedVol));
}
/// <summary>
///
/// </summary>
private void ResetOtcOptionTrade(OtcOptionTradeFull otcTrade)
{
if (otcTrade is null)
{
return;
}
otcTrade.id = 0;
otcTrade.TradeNumber = string.Empty;
otcTrade.Notional = otcTrade.OriginalNotional ?? 0;
otcTrade.StockEqvNotional = otcTrade.OriginalStockEqvNotional ?? 0;
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(otcTrade.UnderlyingCode);
otcTrade.TradeAmount = otcTrade.Notional / (um?.CountRatio ?? 1);
otcTrade.ParentTradeId = 0;
otcTrade.IsGroup = 0;
otcTrade.UnWindDate = null;
otcTrade.FinalPrice = null;
otcTrade.UnWindNotional = null;
otcTrade.HasPartialUnWind = null;
otcTrade.KnockInOutDate = null;
otcTrade.KnockInOutStatus = null;
otcTrade.CheckStatus = null;
otcTrade.ProcessOrderId = 0;
otcTrade.ProcessOptDate = null;
otcTrade.ProcessStatus = null;
otcTrade.DividendDate = new DateTime(2000, 1, 1);
otcTrade.StructureType = string.Empty;
otcTrade.StructureIntroduction = string.Empty;
otcTrade.TraderId = CurUser.UserId;
otcTrade.TraderName = CurUser.UserName;
otcTrade.StockEqvNotional = otcTrade.StockEqvNotional.OtcFormatValue(OtcFormatFlag.StockEqvNotional);
otcTrade.StockEqvNotionalReal = otcTrade.StockEqvNotionalReal.OtcFormatValue(OtcFormatFlag.StockEqvNotional);
otcTrade.CouponIncludeStartDate ??= false;
otcTrade.CouponUsePaymentDate ??= false;
otcTrade.IsApproval = false;
otcTrade.CountRatio = DataCacheProvider.GetUnderlyingDataSource().GetData(otcTrade.UnderlyingCode)?.CountRatio ?? 1;
}
public ActionResult HistoricalBacktest(bool isLayer = false)
{
var datas =
new SysUserConfigService(CurUser).GetConfigInfos()
.GroupBy(O => O.ConfigType)
.ToDictionary(
K => K.Key == SysUserConfigType.CommonTemplate ? "公共定价模板" : "个人定价模板",
V => V.Select(O => new { O.EncryptId, O.ConfigName }));
ViewBag.isLayer = isLayer;
ViewBag.pageObj = new
{
dataSourceList = datas,
dataSourceTypeList = datas.Keys,
defaultType = datas.Keys.FirstOrDefault() ?? "",
calcRange = new[] { "估值日期", "标的名称", "时间序列-标的价格" },
calcIndex = new[] { "delta", "deltaCash", "deltaInLots", "gamma", "vega", "rho", "theta" },
};
return View();
}
public JsonResult ConfigInfos(string encryptId)
{
var id = DecryptInt(encryptId);
var obj = new SysUserConfigService(CurUser).GetConfigInfos(id);
return JsonSuccess("ok", obj);
}
public JsonResult UploadPrice(IFormFile file)
{
try
{
using var stream = file.OpenReadStream();
var priceList = HistoricalBacktestService.AnalysisPrice(stream);
if (priceList.Any(O => O.TimeSeries == default))
{
throw new ServiceException("文件中存在错误的日期格式");
}
if (priceList.Any(O => string.IsNullOrWhiteSpace(O.UnderlyingCode)))
{
throw new ServiceException("标的代码不应为空");
}
if (priceList.Any(O => double.IsNaN(O.Price)))
{
throw new ServiceException("价格不应为空");
}
var codes = priceList.Select(O => O.UnderlyingCode).ToHashSet();
var list = new List<string>();
foreach (var item in codes)
{
if (DataCacheProvider.GetUnderlyingDataSource().GetData(item) == null)
{
list.Add(item);
}
}
if (list.Count > 0)
{
return JsonError($"导入失败:标的 {string.Join(",", list)} 不存在");
}
Server.CacheProvider.Remove("backtest_" + UserId);
Server.CacheProvider.Set("backtest_" + UserId, priceList, DateTime.Now.AddHours(6));
return JsonSuccess("导入成功");
}
catch (ServiceException)
{
throw;
}
catch (Exception ex)
{
LogFactory.GetLogger("HistoricalBacktest").Error(ex);
return JsonError("导入失败");
}
}
public FileResult OutputHistoricalBacktest(HistoricalBacktestReq req)
{
var priceInfos = Server.CacheProvider.Get("backtest_" + UserId) as List<HistoricalBacktestPriceModel>;
if (priceInfos == null)
{
throw new ServiceException("请先导入要计算的维度序列");
}
var id = DecryptInt(req.encryptId);
var result = new HistoricalBacktestService(CurUser).Execute(id, priceInfos);
var modelDict = new Dictionary<string, object>
{
["Sheet1"] = new
{
InfoList = result
}
};
var sourcePath = OtcAppContext.MapPath("~/App_Docs/导出模板/");
var settleDocName = "历史回测导出模板.xlsx";
var sourceFileName = Path.Combine(sourcePath, settleDocName);
var buffer = new ExcelTemplateGenerator().SetTemplateFile(sourceFileName).SetTemplateData(modelDict).Output();
return File(buffer, "application/ms-excel", $"历史回测-{DateTime.Now:yyyy-MM-dd}.xlsx");
}
public JsonResult calcHistoricalBacktest(HistoricalBacktestReq req)
{
var priceInfos = Server.CacheProvider.Get("backtest_" + UserId) as List<HistoricalBacktestPriceModel>;
if (priceInfos == null)
{
return JsonError("请先导入要计算的维度序列");
}
var id = DecryptInt(req.encryptId);
var result = new HistoricalBacktestService(CurUser).Execute(id, priceInfos);
return JsonSuccess("计算成功", result);
}
}
}