using Qdp.Pricing.Base.Implementations;
using System.Text.RegularExpressions;
using YLErp.BLL;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.Eod;
using YLErp.BLL.MarginCalculation;
using YLErp.Commons;
using YLErp.Configuration;
using YLErp.DBModels.Helpers;
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;
using static iTextSharp.text.pdf.AcroFields;
namespace YLErp.Modules.PricingModule
{
///
/// 计算服务
///
public class PriceCalcService : YLBaseService
{
public PriceCalcService(OptUserInfo userInfo) : base(userInfo)
{
}
///
/// 定价计算调用
///
public List GetOptionCalculatorV2(trade trade, underlying_manager underlying, OptionCalcParams para, CalcScenarioEnum calcScenario)
{
if (trade is null)
{
throw new ArgumentNullException(nameof(trade));
}
if (underlying is null)
{
throw new ArgumentNullException(nameof(underlying));
}
if (para is null)
{
throw new ArgumentNullException(nameof(para));
}
if (trade.TradeType != "合成价差期权" && trade.TradeType != "现金流交易" && trade.SpotPrice <= 0)
{
throw new Exception("请输入正确的标的价格");
}
if (!trade.TradeDate.HasValue)
{
throw new Exception("请输入正确的交易日期");
}
if (PS.Config.Is润和)
{
if (trade.TTMDays.HasValue)
{
var days = Math.Floor(trade.TTMDays.Value - 0.625) + 1;
//underlying.QuotationDate = trade.ExerciseDate.Value.AddDays(-days);
underlying.QuotationDate = QdpCalendarHelper.AddDate(trade.ExerciseDate.Value, -(int)days);
}
else
{
underlying.QuotationDate = DateTime.Today;
}
}
else if (para.ValueDate.HasValue)
{
GetAccQuotationDate(trade, para);
underlying.QuotationDate = para.ValueDate.Value;
}
else
{
GetAccQuotationDate(trade, para);
underlying.QuotationDate = trade.TradeDate.Value;
if (underlying.QuotationDate != valuedateBLL.ValueDate.Date)
{
underlying.QuotationDate = AdjustQuotationDate(underlying.QuotationDate.Value, trade.TradeType);
}
}
var QRList = new List();
var callputPara = new CallPutResultCalcParams(para);
try
{
//根据报价方式为波动率调整时,要用波动率ask,bid值计算call,put
//波动率调整不需要rule
if (para.QuotationType == "波动率调整")
{
//补丁 波动率调整不需要rule
callputPara.rules = GetRules(0);
trade.BuySell = "卖出";
var askovrs = GetCallPutResultV2(trade, underlying, callputPara.SetVol(para.AskVol), CalcScenarioEnum.Pricing);
trade.BuySell = "买入";
var bidovrs = GetCallPutResultV2(trade, underlying, callputPara.SetVol(para.BidVol), CalcScenarioEnum.Pricing);
QRList.Add(bidovrs[0]);
QRList.Add(askovrs[0]);
//call 针对波动率调整,TradePriceBid 和 TradePriceAsk应该一样(因为rule为0)
QRList[0].TradePriceBid = bidovrs[0].TradePriceBid;//askovrs[0].TradePriceBid; //系统买入
QRList[0].RoundedTradePriceBid = bidovrs[0].RoundedTradePriceBid;
QRList[0].TradePriceAsk = askovrs[0].TradePriceAsk; //系统卖出
QRList[0].RoundedTradePriceAsk = askovrs[0].RoundedTradePriceAsk;
//put
QRList[1].TradePriceBid = bidovrs[1].TradePriceBid; //系统买入
QRList[1].RoundedTradePriceBid = bidovrs[1].RoundedTradePriceBid;
QRList[1].TradePriceAsk = askovrs[1].TradePriceAsk; //系统卖出
QRList[1].RoundedTradePriceAsk = askovrs[1].RoundedTradePriceAsk;
}
else
{
//报价参数
callputPara.rules = GetRules(underlying.id);
callputPara.callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0;
trade.BuySell = "买入";
var bidovrs = GetCallPutResultV2(trade, underlying, callputPara, CalcScenarioEnum.Pricing);
//不用再计算一遍,直接把买入的计算结果取反
//trade.BuySell = "卖出";
//var askovrs = GetCallPutResultV2(trade, underlying, paraBuilder.Build(vol, vol));
var bid = bidovrs[0];
if (bid.Vega != 0 && bid.VegaCash == 0)
{
bid.VegaCash = bid.Vega * (trade.SpotPrice ?? 0);
}
var ask = new TradeValueResult
{
Pv = -bid.Pv,
Delta = -bid.Delta,
Gamma = -bid.Gamma,
GammaCash = -bid.GammaCash,
DeltaCash = -bid.DeltaCash,
VegaCash = -bid.VegaCash,
Vega = -bid.Vega,
CalendarDayTheta = -bid.CalendarDayTheta,
TradingDayTheta = -bid.TradingDayTheta,
Rho = -bid.Rho,
UnderlyingCode = bid.UnderlyingCode,
Strike = bid.Strike,
Vol = bid.Vol,
RoundedPv = -bid.RoundedPv,
KnockOutPayoff = -bid.KnockOutPayoff,
IsKnockOut = bid.IsKnockOut,
};
QRList.AddRange(new[] { bid, ask });
}
return QRList;
}
catch (Exception ex)
{
LogFactory.GetLogger("定价计算").Error(ex);
var message = Regex.Replace(ex.Message, "exercise date (.*?) is not in KOObsDates", "到期日 $1 不在敲出观察日列表中");
throw new Exception("计算出错," + message);
}
}
///
/// 获取累计包含首日定价日期
///
///
///
private static void GetAccQuotationDate(trade trade, OptionCalcParams para)
{
if (trade.TradeType == "累计期权" && trade.trade_accumulator_option != null && !string.IsNullOrWhiteSpace(trade.trade_accumulator_option.KOObservationDates))
{
var ko = QdpHelper.ParseAutocallCustomizedInfo(trade.trade_accumulator_option.KOObservationDates);
if (ko.Item1.Length > 0 && ko.Item1.Select(l => l.DateTime).ToArray().Contains(trade.TradeDate.Value))
{
para.ValueDate = QdpCalendarHelper.GetNonHolidayDefore((para.ValueDate ?? trade.TradeDate).Value.AddDays(-1));
}
}
}
private static List GetCallPutResultV2(trade trade, underlying_manager underlying, CallPutResultCalcParams para, CalcScenarioEnum calcScenario)
{
if (trade is null)
{
throw new ArgumentNullException(nameof(trade));
}
if (underlying is null)
{
throw new ArgumentNullException(nameof(underlying));
}
if (para is null)
{
throw new ArgumentNullException(nameof(para));
}
//交易日等于系统日期,是当天报价,要使用精确时间模式
//trade.TradeDate必然有值,在外部调用的方法中判断
var isPreciseTimeMode = underlying.QuotationDate.Value.Date == valuedateBLL.ValueDate.Date;
if (trade.TradeType == "气囊结构" || trade.TradeType == "收益增强结构" || trade.TradeType == "区间累积期权" || (trade.TradeType == "二元期权" && trade.ExerciseMode == "American"))
{
trade.OptionType = "看涨";
}
TradeValueResult pricingResult;
var valueDate = underlying.QuotationDate.Value;
var spotPrice = para.UnderlyingPrice ?? trade.SpotPrice ?? 0;
if (trade.TradeType == "自定义交易")
{
var result = TradeRiskCalcUtil.GetManualOptionValue(valueDate, trade, spotPrice, para.callVol, true, calcScenario);
if (result.manual == null)
{
throw new Exception(PS.Config.ErpElement.ExternalAPIForCustomCalcEnable ? "失败:接口计算失败;" : "失败:无法调用计算接口,请开启相应配置");
}
return new List { result.optionValue, result.optionValue };
}
string fixing = null;
if (trade.TradeType == "亚式期权" || trade.StructureType == "亚式熊市价差")
{
if (!string.IsNullOrEmpty(para.Fixings))
{
fixing = para.Fixings;
}
else
{
var avgStartDate = trade.trade_asian_option.AveragingPeriodStartDate ?? trade.TradeDate.Value;
if (valueDate > avgStartDate)
{
fixing = FixingService.GetFixingString(valueDate, trade, avgStartDate, trade.trade_asian_option.ObservationDates);
fixing = FixingService.AddOrReplaceLastDateSpotPrice(fixing, valueDate, spotPrice);
if (PS.Config.Is润和 && DateTime.Now.Hour < 15)
{
var index = fixing.IndexOf(valueDate.ToString("yyyy-MM-dd"));
if (index >= 0)
{
fixing = fixing.Remove(index).TrimEnd(';');
}
}
}
}
}
else if (trade.TradeType == "区间累积期权")
{
fixing = !string.IsNullOrEmpty(para.Fixings)
? para.Fixings
: FixingService.GetFixingString(valueDate: valueDate.AddDays(-1), otcTrade: trade, startDate: trade.StartDate ?? trade.TradeDate.Value, observationDates: trade.trade_rangeaccrual.ObservationDates);
}
if ("V2".Equals(para.CalcVersion, StringComparison.OrdinalIgnoreCase) || trade.TradeType == "累计期权" || "结构化产品".Equals(trade.TradeType) || trade.IsSnowballSpecialist())
{
var request = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate())
{
vols = new[] { para.callVol },
spotPrices = new[] { spotPrice },
engineName = para.EngineName,
preciseTimeMode = isPreciseTimeMode,
pricingRequest = para.IsCalcGreeks ? QdpPricingRequest.BASIC_GREEKS : QdpPricingRequest.PRICE_GREEKS,
quadratureFastMode = true,
calcScenario = CalcScenarioEnum.Pricing,
fixings = fixing,
timeToMaturityDays = trade.TTMDays
};
pricingResult = OptionCalculatorV2.GetOptionValueResult(valueDate, trade, request, out _);
}
else
{
pricingResult = ValueCalculator.GetOptionValueResultV2(
para.UserId,
underlying,
trade,
new double[] { para.callVol },
new double[] { spotPrice },
fixing: fixing,
preciseTimeMode: isPreciseTimeMode,
engineName: para.EngineName,
request: para.IsCalcGreeks ? QdpPricingRequest.BASIC_GREEKS : QdpPricingRequest.PRICE_GREEKS,
calcScenario: CalcScenarioEnum.Pricing,
quadratureFastMode: true);
}
if (pricingResult == null)
{
throw new Exception("失败:此交易无法计算 ");
}
//报价结果
var qr = new QuotationResult();
OptionQuote(ref pricingResult, para.rules.Where(r => underlying_parameter.callQuoteTypes.Contains(r.Type)).ToList(), trade.Notional, trade.TradeType, ref qr);
var payoffService = new TradeKnockOutPayoffCalcService(valueDate);
var payoffResult = payoffService.GetKnockOutPayoff(trade, spotPrice);
if (payoffResult != null)
{
pricingResult.IsKnockOut = payoffResult.IsKnockOut;
pricingResult.KnockOutPayoff = payoffResult.Payoff;
}
return new List { pricingResult, pricingResult };
}
///
/// 计算期权权利金
///
public IEnumerable CalcOptionPrice(IEnumerable trades, bool calcMargin, CalcScenarioEnum calcScenario
, Func calcGreeks = null, string calcVersion = null)
{
var results = new List();
var tdGroups = trades.GroupBy(n => n.CalcId.Split('-')[0]).ToArray();
var priceProvider = new ManualPriceProvider();
var req = new Lazy(() => new RunMarginCalculationReq(UserInfo)
{
forOtherSide = false,
hasOptionInfo = false,
CalcMarginType = CalcMarginTypeEnum.InitialMargin,
settleDate = DateTime.Now,
PriceProvider = priceProvider
});
foreach (var tdGroup in tdGroups)
{
var tdList = tdGroup.Select(td =>
{
if (!td.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (!td.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
var tdConv = TradeConverter.ConvertOptionTrade(td);
tdConv.CalcId = td.CalcId;
return new
{
tdConv,
td.IsTTMSystem,
td.ValueDate,
td.UnderlyingPrice,
EngineName = ConsTrade.GetEngineName(td.EngineName),
};
}).ToArray();
//结构化交易(用于预付金计算)
if (tdList.Length > 1)
{
var index = -2;
foreach (var item in tdList)
{
item.tdConv.id = index--;
item.tdConv.ParentTradeId = -1;
}
}
var tradeValueDict = new Dictionary();
//计算pv
foreach (var item in tdList)
{
var td = item.tdConv;
var buySell = td.BuySell;
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
if (td.TradeType == "现金流交易")
{
underlying = new underlying_manager();
}
if (underlying == null)
{
throw new ServiceException($"没有找到标的信息,标的代码:{td.UnderlyingCode}");
}
if (PS.Config.Is润和 && item.IsTTMSystem)
{
td.TTMDays = TradeCalcHelper.CalculateTTMDays(DateTime.Today, td.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
else if (!td.TTMDays.HasValue && td.TradeType != "现金流交易")
{
td.TTMDays = TradeCalcHelper.CalculateTTMDays(td.TradeDate.Value, td.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
//这个地方会把交易方向改变
var QRList = GetOptionCalculatorV2(td, underlying, new OptionCalcParams
{
UserId = UserId.ToString(),
IsCalcGreeks = calcGreeks == null || calcGreeks(td),
EngineName = item.EngineName,
CalcVersion = calcVersion,
UnderlyingPrice = item.UnderlyingPrice,
ValueDate = item.ValueDate,
}, calcScenario);
var calcResult = buySell == "卖出" ? QRList[1] : QRList[0];
calcResult.BuySell = buySell;
//期权本身的价值,不包含买卖方向
td.TradePrice = QRList[0].Pv;
td.AccurateTradePrice = QRList[0].Pv;
if (PS.Config.Is润和)
{
var tdmid = td.Clone();
tdmid.Vol = tdmid.MidVol;
tdmid.TradeOpenVolatility = tdmid.MidVol;
//这个地方会把交易方向改变
var QRList2 = GetOptionCalculatorV2(tdmid, underlying, new OptionCalcParams
{
UserId = UserId.ToString(),
IsCalcGreeks = calcGreeks == null || calcGreeks(tdmid),
EngineName = item.EngineName,
CalcVersion = calcVersion,
UnderlyingPrice = item.UnderlyingPrice,
ValueDate = item.ValueDate
}, calcScenario);
var calcResult2 = buySell == "卖出" ? QRList2[1] : QRList2[0];
td.Day1Pnl = calcResult2.Pv - calcResult.Pv;
calcResult2.Pv = calcResult.Pv;
tradeValueDict[td.id] = (calcResult2, underlying);
}
else
{
tradeValueDict[td.id] = (calcResult, underlying);
}
//if (PS.Config.Company == Configuration.CompanyEnum.天示)
//{
// calcResult.Pv = calcResult.Pv + td.PrincipalSum * (buySell == "卖出" ? -1 : 1);
//}
//恢复原有的交易方向,确保预付金计算正确
td.BuySell = buySell;
}
List tradeSpans = null;
//计算预付金
if (calcMargin)
{
var tdCloneList = tdList.Where(n => n.tdConv.TradeType != "现金流交易").Select(n =>
{
var tdClone = n.tdConv.Clone();
tdClone.OptId = UserId;
tdClone.VolType = "报价Bid";
tdClone.TradeCloseVolatility = null;
priceProvider.SetPrice(tdClone.UnderlyingCode, tdClone.SpotPrice ?? 0);
return tdClone;
}).ToList();
//结构化交易预付金计算可能出现的问题:结算日期或者期初标的价格不一致
//要正确处理上述问题,需要对现有代码做重新设计和开发,故暂时忽略上述情况
if (tdCloneList.Count() > 0)
{
req.Value.settleDate = tdCloneList[0].TradeDate ?? valuedateBLL.ValueDate;
req.Value.CalcMarginType = CalcMarginTypeEnum.InitialMargin;
tradeSpans = MarginDefault.RunMarginCalculation(req.Value.Clone(tdCloneList)).ToList();
if (PS.Config.Is国投 || PS.Config.Is润和 || PS.Config.Is华安 || PS.Config.Is招证)
{
GroupSpansCalc(tradeSpans);
}
if (PS.Config.Company == CompanyEnum.东吴)
{
foreach (var ts in tradeSpans)
{
if (ts.WorstCastClientPayable < 0)
{
ts.WorstCastClientPayable = 0;
}
}
}
}
}
var greeksHandleService = new GLMSGreeksHandleService();
foreach (var item in tdList)
{
var td = item.tdConv;
var initialMargin = 0d;
if (tradeSpans != null)
{
initialMargin = tradeSpans.FirstOrDefault(n => n.TradeId == td.id)?.WorstCastClientPayable ?? 0;
}
var (calcResult, underlying) = tradeValueDict[td.id];
if (calcResult != null)
{
if (calcResult.IsKnockOut)
{
calcResult.DeltaContainsKnockOut = 0;
calcResult.GammaContainsKnockOut = 0;
calcResult.PvContainsKnockOut = calcResult.KnockOutPayoff;
}
else
{
calcResult.PvContainsKnockOut = calcResult.Pv;
calcResult.DeltaContainsKnockOut = calcResult.Delta;
calcResult.GammaContainsKnockOut = calcResult.Gamma;
}
}
greeksHandleService.InitData(item.ValueDate ?? td.TradeDate ?? DateTime.Today, new List { td.UnderlyingCode });
greeksHandleService.Handle(td, calcResult, underlying);
results.Add(new CalcOptionPriceResult
{
BuySell = td.BuySell,
CalcId = td.CalcId,
calcResult = calcResult,
initialMargin = initialMargin,
countRatio = underlying.CountRatio,
contractSize = underlying.ContractSize,
Day1Pnl = td.Day1Pnl ?? 0,
TradePrice = td.TradePrice,
AccurateTradePrice = td.AccurateTradePrice,
TTMDays = td.TTMDays ?? 0
});
}
if (PS.Config.Is华安)
{
if (tradeSpans != null)
{
if (tradeSpans.Sum(x => x.WorstCastClientPayable) < 0 || results.Where(r => tdList.Select(td => td.tdConv.CalcId).Contains(r.CalcId)).Sum(t => t.TradePrice * (t.BuySell == "卖出" ? 1 : -1)) > 0)
{
tradeSpans.ForEach(x => x.WorstCastClientPayable = 0);
results.ForEach(x => x.initialMargin = 0);
}
}
}
}
if (PS.Config.Is润和)
{
var TotalMarginDic = results.GroupBy(l => l.CalcId.Split('-')[0]).ToDictionary(l => l.Key, l => new { totalMargin = l.Sum(i => i.initialMargin), totalTradePrice = l.Sum(i => i.AccurateTradePrice * (i.BuySell == "卖出" ? 1 : -1)) });
foreach (var item in TotalMarginDic)
{
if (item.Value.totalMargin > 0 && item.Value.totalTradePrice + item.Value.totalMargin > 0)
{
var modifyHasNeedOffsetModel = results.Where(l => l.CalcId.Split('-')[0] == item.Key);
modifyHasNeedOffsetModel.ToList().ForEach(l => l.hasInitialMargin = true);
}
else
{
var modifyHasNeedOffsetModel = results.Where(l => l.CalcId.Split('-')[0] == item.Key);
modifyHasNeedOffsetModel.ToList().ForEach(l => l.hasInitialMargin = false);
}
}
}
return results;
}
///
/// 分组取最大
///
///
public static void GroupSpansCalc(List tradeSpans)
{
double? spv1 = tradeSpans.Sum(O => O.Spv1 ?? 0);
double? spv2 = tradeSpans.Sum(O => O.Spv2 ?? 0);
double? spv3 = tradeSpans.Sum(O => O.Spv3 ?? 0);
double? spv4 = tradeSpans.Sum(O => O.Spv4 ?? 0);
double? spv5 = tradeSpans.Sum(O => O.Spv5 ?? 0);
double? spv6 = tradeSpans.Sum(O => O.Spv6 ?? 0);
double? spv7 = tradeSpans.Sum(O => O.Spv7 ?? 0);
double? spv8 = tradeSpans.Sum(O => O.Spv8 ?? 0);
double? deltaMargin = tradeSpans.Sum(O => O.DeltaMargin ?? 0);
//默认使用了某个spv不会赋 null
if (tradeSpans.Any(x => x.Spv1 == null)) spv1 = null;
if (tradeSpans.Any(x => x.Spv2 == null)) spv2 = null;
if (tradeSpans.Any(x => x.Spv3 == null)) spv3 = null;
if (tradeSpans.Any(x => x.Spv4 == null)) spv4 = null;
if (tradeSpans.Any(x => x.Spv5 == null)) spv5 = null;
if (tradeSpans.Any(x => x.Spv6 == null)) spv6 = null;
if (tradeSpans.Any(x => x.Spv7 == null)) spv7 = null;
if (tradeSpans.Any(x => x.Spv8 == null)) spv8 = null;
if (tradeSpans.Any(x => x.DeltaMargin == null)) deltaMargin = null;
var spvArr = new[] { spv1, spv2, spv3, spv4, spv5, spv6, spv7, spv8, deltaMargin };
var maxSpv = spvArr.Max();
var index = Array.IndexOf(spvArr, maxSpv);
tradeSpans.ForEach(x =>
{
var clientRatio = 1.0;
if (PS.Config.Company == CompanyEnum.招证 && x.ClientId != 0)
{
var client = DataCacheProvider.GetClientDataSource().GetData(x?.ClientId.Value ?? 0);
var clientLevel = DataCacheProvider.GetClientLevelDataSource().GetData(client.LevelId ?? 0);
clientRatio = clientLevel?.Ratio1 ?? 1.0;
}
x.WorstCastClientPayable = new[] { x.Spv1, x.Spv2, x.Spv3, x.Spv4, x.Spv5, x.Spv6, x.Spv7, x.Spv8, x.DeltaMargin }[index] * clientRatio;
});
}
///
/// 计算期权权利金
///
public CalcOptionPriceResult CalcOptionPrice(OtcOptionTradeFull trade, bool calcMargin, CalcScenarioEnum calcScenario
, bool calcGreeks = true, string calcVersion = "V1", string fixings = null)
{
if (!trade.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (!trade.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
if (trade.TradeType == "现金流交易")
{
underlying = new underlying_manager();
}
if (underlying == null)
{
throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}");
}
var tdConv = TradeConverter.ConvertOptionTrade(trade);
tdConv.EnableSetFieldsByTradeType = false;
//由于新增交易时,页面交易数量还未根据沽购数量进行观察日倍数乘积处理,所以,如果后台处理了,得到的PV页面处理成权利金时会除以数量,就会有问题,需要和前台同步
if (tdConv.TradeType == "累计期权" && tdConv.id > 0)
{
var customObservDates = QdpHelper.ParseObservationDate(tdConv.trade_accumulator_option.KOObservationDates);
customObservDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(tdConv.TradeDate.Value, tdConv.ExerciseDate.Value).ToArray();
tdConv.TradeAmount = tdConv.trade_accumulator_option.AccumuTradeAmount * customObservDates.Length;
tdConv.Notional = tdConv.TradeAmount * underlying.CountRatio;
tdConv.StockEqvNotionalReal = tdConv.Notional * (tdConv.SpotPrice ?? 0);
tdConv.StockEqvNotional = TradeHelper.GetStockEqvNotional(tdConv.StockEqvNotionalReal, tdConv.ParticipationRate, tdConv.AnnualizeFactor);
}
var buySell = tdConv.BuySell;
if (PS.Config.Is润和)
{
tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(DateTime.Today, tdConv.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
else if (!tdConv.TTMDays.HasValue)
{
tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
var initialMargin = 0d;
if (calcMargin)
{
var tdClone = tdConv.Clone();
tdClone.OptId = UserId;
tdClone.BuySell = "买入";
tdClone.VolType = "报价Bid";
tdClone.TradeCloseVolatility = null;
initialMargin = MarginDefault.GetInitialMargin(tdClone, tdConv.id, true);
}
var QRList = GetOptionCalculatorV2(tdConv, underlying, new OptionCalcParams
{
UserId = UserId.ToString(),
IsCalcGreeks = calcGreeks,
EngineName = ConsTrade.GetEngineName(trade.EngineName),
UnderlyingPrice = trade.UnderlyingPrice,
ValueDate = trade.ValueDate,
CalcVersion = calcVersion ?? "V1",
Fixings = fixings
}, calcScenario);
var calcResult = buySell == "卖出" ? QRList[1] : QRList[0];
calcResult.BuySell = buySell;
if (PS.Config.Is润和)
{
var tdmid = tdConv.Clone();
tdmid.Vol = tdmid.MidVol;
tdmid.TradeOpenVolatility = tdmid.MidVol;
//这个地方会把交易方向改变
var QRList2 = GetOptionCalculatorV2(tdmid, underlying, new OptionCalcParams
{
UserId = UserId.ToString(),
IsCalcGreeks = calcGreeks,
EngineName = ConsTrade.GetEngineName(trade.EngineName),
UnderlyingPrice = trade.UnderlyingPrice,
ValueDate = trade.ValueDate,
CalcVersion = calcVersion ?? "V1"
}, calcScenario);
var calcResult2 = buySell == "卖出" ? QRList2[1] : QRList2[0];
trade.Day1Pnl = calcResult2.Pv - calcResult.Pv;
}
return new CalcOptionPriceResult
{
CalcId = trade.CalcId,
calcResult = calcResult,
initialMargin = initialMargin,
countRatio = underlying.CountRatio,
contractSize = underlying.ContractSize,
Day1Pnl = trade.Day1Pnl ?? 0,
};
}
///
/// 根据标的价格和波动率对交易进行试算
///
///
///
public CalcTradesResult CalcTrades(CalcTradesRequest request)
{
var result = new CalcTradesResult();
var eodTrades = DbContext.eod_trade.Where(x => request.TradeIds.Contains(x.TradeId) && x.ValueDate == request.ValueDate).ToList();
var trades = DbContext.trade.Where(x => request.TradeIds.Contains(x.id)).ToList();
request.TradeIds.ForEach(x =>
{
trade trade = new trade();
var eodTrade = eodTrades.FirstOrDefault(y => y.TradeId == x);
if(eodTrade != null)
{
trade = eodTrade.trade;
}
else
{
trade = trades.FirstOrDefault(y => y.id == x);
new TradeExtendService(OptUser, DbContext).SetTradeExtend(new[] { trade }, tracking: true);
}
if(trade.TradeType != "自定义交易" && trade.TradeType != "现金流交易")
{
double? price = 0;
if (request.TradePricesDic.ContainsKey(x))
{
price = request.TradePricesDic.FirstOrDefault(y => y.Key == x).Value;
}
else if (request.PricesDic.ContainsKey(trade.UnderlyingCode))
{
price = request.PricesDic.FirstOrDefault(y => y.Key == trade.UnderlyingCode).Value;
}
TradeValueResult optionValue = new TradeValueResult();
if (trade.TradeType == "远期")
{
if (price != null)
{
optionValue = ForwardradeCalcService.CalcValue(trade, price.Value);
}
}
else
{
double? vol = 0;
if (request.VolsDic.ContainsKey(x))
{
vol = request.VolsDic.FirstOrDefault(z => z.Key == x).Value;
}
if (price != null && vol != null)
{
double ttm;
var udm = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
if (PS.Config.Is厦门象屿 && trade.SettlementType == SettlementTypeEnum.ReferencePrice)
{
ttm = TradeCalcHelper.CalculateTTMDaysForXiangYu(request.ValueDate, trade.ExerciseDate.Value, udm.UnderlyingTypeId, trade.ExerciseDate.Value == valuedateBLL.ValueDate);
}
else
{
ttm = TradeCalcHelper.CalculateTTMDays(request.ValueDate, trade.ExerciseDate.Value, udm.UnderlyingTypeId, trade.ExerciseDate.Value == valuedateBLL.ValueDate);
}
var calcRequest = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate())
{
vols = new[] { vol.Value },
spotPrices = new[] { price.Value },
engineName = null,
preciseTimeMode = request.ValueDate == valuedateBLL.ValueDate.Date,
pricingRequest = QdpPricingRequest.BASIC_PRICING,
timeToMaturityDays = ttm,
calcScenario = CalcScenarioEnum.Pricing
};
optionValue = OptionCalculatorV2.GetOptionValueResult(request.ValueDate, trade, calcRequest, out _);
}
}
var pnl = EodOperationBase.GetPositionPnl(optionValue.Pv, trade.TradePrice ?? 0.0, trade.Notional, trade.OriginalNotional ?? 0, trade.BuySell);
var singlePV = TradeHelper.GetTradeSinglePriceByTradePrice(optionValue.Pv, trade.Notional, PS.Config.ErpElement.IsPVIncludePrincipal ? trade.PrincipalSum() : 0, trade.BuySell, trade.TradeType, false);
result.TradeOptionValueDic.Add(x, new CalcTradeResult() { Pv = optionValue.Pv, SinglePv = singlePV, Pnl = pnl, Delta = optionValue.Delta });
}
});
return result;
}
public CalcTradesResult CalcSwapTrades(CalcTradesRequest request)
{
var result = new CalcTradesResult();
var eodTrades = DbContext.eod_swap.Where(x => request.TradeIds.Contains(x.SwapTradeId) && x.ValueDate == request.ValueDate).ToList();
var trades = DbContext.trade.Where(x => request.TradeIds.Contains(x.id)).ToList();
request.TradeIds.ForEach(x =>
{
trade trade = new trade();
trade = trades.FirstOrDefault(y => y.id == x);
double? price = request.PricesDic.FirstOrDefault(y => y.Key == trade.UnderlyingCode).Value;
if (price != null)
{
var optionValue = PayoffSwapCalcService.CalcValueSingle(trade, request.ValueDate, null, false, price.Value);
var pnl = EodOperationBase.GetPositionPnl(optionValue.Pv, trade.TradePrice ?? 0.0, trade.Notional, trade.OriginalNotional ?? 0, trade.BuySell);
var singlePV = TradeHelper.GetTradeSinglePriceByTradePrice(optionValue.Pv, trade.Notional, PS.Config.ErpElement.IsPVIncludePrincipal ? trade.PrincipalSum() : 0, trade.BuySell, trade.TradeType, false);
result.TradeOptionValueDic.Add(x, new CalcTradeResult() { Pv = optionValue.Pv, SinglePv = singlePV, Pnl = pnl, Delta = optionValue.Delta });
}
});
return result;
}
//报价参数
private underlying_parameter[] GetRules(int underlyingId)
{
underlying_parameter[] rules = null;
if (underlyingId > 0)
{
rules = DbContext.underlying_parameter.Where(u => u.UnderlyingId == underlyingId).ToArray();
}
//补丁 波动率调整不需要rule
if (rules == null || rules.Length == 0)
{
var arr = new string[] {
underlying_parameter.CallAsk,
underlying_parameter.CallBid,
underlying_parameter.PutAsk,
underlying_parameter.PutBid
};
rules = arr.Select(n => new underlying_parameter()
{
Type = n,
Delta = 0.0,
Gamma = 0.0,
Vega = 0.0,
Theta = 0.0,
Rho = 0.0,
Other = 0.0
}).ToArray();
}
return rules;
}
///
/// 计算保本雪球的年化期权费率
///
public double CalcSnowballAnnualPremium(OtcOptionTradeFull trade)
{
if (!trade.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (!trade.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
if (underlying == null)
{
throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}");
}
var tdConv = TradeConverter.ConvertOptionTrade(trade);
tdConv.EnableSetFieldsByTradeType = false;
if (!tdConv.TTMDays.HasValue)
{
tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
if (tdConv.SpotPrice <= 0)
{
throw new Exception("请输入正确的标的价格");
}
underlying.QuotationDate = trade.TradeDate;
if (underlying.QuotationDate != valuedateBLL.ValueDate.Date)
{
underlying.QuotationDate = AdjustQuotationDate(underlying.QuotationDate.Value, tdConv.TradeType);
}
//交易日等于系统日期,是当天报价,要使用精确时间模式
var isPreciseTimeMode = tdConv.TradeDate.Value.Date == valuedateBLL.ValueDate.Date;
var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0;
return ValueCalculator.CalculateSnowballAnnualPremium(
UserId.ToString(),
underlying,
tdConv,
new double[] { callVol },
new double[] { tdConv.SpotPrice ?? 0 },
preciseTimeMode: isPreciseTimeMode,
engineName: trade.EngineName,
quadratureFastMode: true);
}
///
/// 反算雪球票息
///
public double CalcSnowballKORebate(OtcOptionTradeFull trade)
{
if (!trade.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (!trade.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
if (underlying == null)
{
throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}");
}
var tdConv = TradeConverter.ConvertOptionTrade(trade);
tdConv.EnableSetFieldsByTradeType = false;
if (!tdConv.TTMDays.HasValue)
{
tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
if (tdConv.SpotPrice <= 0)
{
throw new Exception("请输入正确的标的价格");
}
var valueDate = trade.TradeDate.Value;
if (valueDate != valuedateBLL.ValueDate.Date)
{
valueDate = AdjustQuotationDate(valueDate, tdConv.TradeType);
}
//交易日等于系统日期,是当天报价,要使用精确时间模式
var isPreciseTimeMode = tdConv.TradeDate.Value.Date == valuedateBLL.ValueDate.Date;
var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0;
var koRebate = ValueCalculator.CalculateSnowballKORebateV2(
trade: tdConv,
snowball: tdConv.trade_snowball,
valueDate: valueDate,
vols: new double[] { callVol },
spotPrices: new double[] { tdConv.SpotPrice ?? 0 },
preciseTimeMode: isPreciseTimeMode,
timeToMaturityDays: tdConv.TTMDays.Value,
initialMarginRate: trade.InitialAdvance.HasValue ? trade.InitialAdvance.Value : 0.0); // 预付预付金比例
return koRebate;
}
///
/// 反算专业版雪球票息、波动率等
///
public double CalcSnowballSpecialistTargetValue(OtcOptionTradeFull trade, int calcTarget)
{
if (!trade.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (!trade.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
if (underlying == null)
{
throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}");
}
var snowballSpecialistOptionCalculator = new SnowballSpecialistOptionCalculator();
var tdConv = TradeConverter.ConvertOptionTrade(trade);
var breakevenSnowballTrade = snowballSpecialistOptionCalculator.GetBreakevenTrade(tdConv);
var QRList = GetOptionCalculatorV2(breakevenSnowballTrade, underlying, new OptionCalcParams
{
UserId = UserId.ToString(),
IsCalcGreeks = false,
EngineName = ConsTrade.GetEngineName(trade.EngineName),
UnderlyingPrice = trade.UnderlyingPrice,
ValueDate = trade.ValueDate,
CalcVersion = "V1",
Fixings = null
}, CalcScenarioEnum.Pricing);
var breakevenSnowballResult = QRList[0];
tdConv.TradePrice -= breakevenSnowballResult.Pv;
tdConv.trade_snowball.PrepaymentRatio = 0;
if (tdConv.SpotPrice <= 0)
{
throw new Exception("请输入正确的标的价格");
}
var valueDate = trade.TradeDate.Value;
if (valueDate != valuedateBLL.ValueDate.Date)
{
valueDate = AdjustQuotationDate(valueDate, tdConv.TradeType);
}
//交易日等于系统日期,是当天报价,要使用精确时间模式
var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0;
var targetValue = snowballSpecialistOptionCalculator.CalcTargets(valueDate, trade.UnderlyingPrice ?? 0, callVol, calcTarget, tdConv);
//var koRebate = ValueCalculator.CalculateSnowballKORebateV2(
// trade: tdConv,
// snowball: tdConv.trade_snowball,
// valueDate: valueDate,
// vols: new double[] { callVol },
// spotPrices: new double[] { tdConv.SpotPrice ?? 0 },
// preciseTimeMode: isPreciseTimeMode,
// timeToMaturityDays: tdConv.TTMDays.Value,
// initialMarginRate: trade.InitialAdvance.HasValue ? trade.InitialAdvance.Value : 0.0); // 预付预付金比例
return targetValue;
}
///
/// 反算凤凰票息
///
///
///
public double CalcPhoenixCouponRate(OtcOptionTradeFull trade)
{
if (!trade.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (!trade.ExerciseDate.HasValue)
{
throw new ServiceException("到期日期 必须填写");
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
if (underlying == null)
{
throw new ServiceException($"没有找到标的信息,标的代码:{trade.UnderlyingCode}");
}
var tdConv = TradeConverter.ConvertOptionTrade(trade);
tdConv.EnableSetFieldsByTradeType = false;
if (!tdConv.TTMDays.HasValue)
{
tdConv.TTMDays = TradeCalcHelper.CalculateTTMDays(trade.TradeDate.Value, trade.ExerciseDate.Value, underlying.UnderlyingTypeId, PS.Config.ErpElement.PrecisionOfMinuteInQuote);
}
if (tdConv.SpotPrice <= 0)
{
throw new Exception("请输入正确的标的价格");
}
underlying.QuotationDate = trade.TradeDate;
if (underlying.QuotationDate != valuedateBLL.ValueDate.Date)
{
underlying.QuotationDate = AdjustQuotationDate(underlying.QuotationDate.Value, tdConv.TradeType);
}
//交易日等于系统日期,是当天报价,要使用精确时间模式
var isPreciseTimeMode = tdConv.TradeDate.Value.Date == valuedateBLL.ValueDate.Date;
var callVol = PS.Config.IsTradeVol ? trade.TradeOpenVolatility ?? 0 : trade.Vol ?? 0;
return ValueCalculator.CalculatePhoenixCouponRate(
underlying,
tdConv,
new double[] { callVol },
new double[] { tdConv.SpotPrice ?? 0 },
preciseTimeMode: isPreciseTimeMode,
engineName: trade.EngineName);
}
///
/// 在期权定价时调整定价日
///
private DateTime AdjustQuotationDate(DateTime quotationDate, string tradeType)
{
// 对凤凰雪球不做报价日调整
if (tradeType == "雪球期权" || tradeType == "凤凰期权")
{
return quotationDate;
}
//解决定价、新增、修改定价不一致的问题
if (tradeType == "区间累积期权" || tradeType == "累计期权")
{
return quotationDate;
}
//为了暂时修复一个QDP计算方式与客户需求不匹配的情况
//在非精确模式下,QDP不包括交易日当天的时间价值,但根据报价需求,需要将交易日当天的时间价值计算在内,
//所以要将交易日向前移动一天。未来QDP支持传递TTM来计算时,可以直接在TTM上加1,而不用移动交易日
//注意:当前这个临时修改必须在波动率插值之前调用,这样才能正确设置波动率日期
var calendar = CalendarImpl.Get("chn");
return calendar.PrevBizDay(quotationDate).DateTime;
}
///
/// 获取根据rule的补充数据
///
private static void OptionQuote(ref TradeValueResult valueResult, List quoteRules, double notional, string tradeType, ref QuotationResult qr)
{
foreach (var rule in quoteRules)
{
OptionQuote(ref valueResult, rule, notional, tradeType, ref qr);
}
}
///
/// 根据规则 调整报价数据
///
private static double OptionQuote(ref TradeValueResult valueResult, underlying_parameter quoteRule, double notional, string tradeType, ref QuotationResult qr)
{
var result = double.NaN;
if (valueResult != null && quoteRule != null)
{
result = valueResult.Pv +
((quoteRule.Delta ?? 0.0) * NumberHelper.Normalize(valueResult.Delta)) +
((quoteRule.Gamma ?? 0.0) * NumberHelper.Normalize(valueResult.Gamma)) +
((quoteRule.Vega ?? 0.0) * NumberHelper.Normalize(valueResult.Vega)) +
(quoteRule.Other ?? 0.0);
}
else if (valueResult != null && quoteRule == null)
{
result = valueResult.Pv;
}
var roundResult = notional == 0 ? OtcFormatHelper.FormatValue(result, 2) : (OtcFormatHelper.FormatValue(result / notional, 2) * notional);
//valueResult.TradePrice = result;
var tempRet = Math.Abs(result);
var tempRoundRet = Math.Abs(roundResult);
if (tradeType == "凤凰期权" || tradeType == "雪球期权")
{
tempRet = result;
tempRoundRet = roundResult;
}
//设置 报价结果
switch (quoteRule.Type)
{
case underlying_parameter.CallAsk:
qr.CallAsk = tempRet;
valueResult.TradePriceAsk = tempRet;
valueResult.RoundedTradePriceAsk = Math.Abs(tempRoundRet);
break;
case underlying_parameter.CallBid:
qr.CallBid = tempRet;
valueResult.TradePriceBid = tempRet;
valueResult.RoundedTradePriceBid = Math.Abs(tempRoundRet);
break;
case underlying_parameter.PutAsk:
qr.PutAsk = tempRet;
valueResult.TradePriceAsk = tempRet;
valueResult.RoundedTradePriceAsk = Math.Abs(tempRoundRet);
break;
case underlying_parameter.PutBid:
qr.PutBid = tempRet;
valueResult.TradePriceBid = tempRet;
valueResult.RoundedTradePriceBid = Math.Abs(tempRoundRet);
break;
}
return result;
}
}
}