Files
zszq-trs/YLErpDAL/Modules/TradeRiskCalcModule/TradeRiskCalcService.cs
T
2024-06-12 10:28:49 +08:00

1191 lines
54 KiB
C#

using BaseOUDAL;
using Microsoft.Extensions.Caching.Memory;
using Qdp.Pricing.Base.Utilities;
using YLErp.Abstract;
using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.Configuration;
using YLErp.DBModels.Enums;
using YLErp.Models;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.CalculationModule.Abstract;
using YLErp.Modules.TradeRiskCalcModule.Abstract;
using YLErp.Modules.SwapModule;
using YLErp.Modules.TradeRiskCalcModule.TaskRunner;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeRiskCalcModule
{
/// <summary>
/// 交易风险计算业务服务
/// </summary>
public class TradeRiskCalcService
{
static readonly IYcLogger _logger;
static readonly MemoryCache _cache;
static TradeRiskCalcService()
{
_cache = new MemoryCache(new MemoryCacheOptions());
_logger = LogFactory.GetLogger(nameof(TradeRiskCalcService));
}
readonly ITradeRiskCalcContext _context;
readonly IOptionCalcDataProvider _dataProvider;
HashSet<string> _underlyingCodeFilterSet;
readonly string _skipTradeTypes;
public TradeRiskCalcService(ITradeRiskCalcContext context, string skipTradeTypes)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
_dataProvider = CalcCheckHelper.CheckOptionCalcDataProvider(context.OptionCalcDataProvider);
_skipTradeTypes = skipTradeTypes;
}
public TradeRiskCalcService SetUnderlyingFilter(params string[] underlyingCodes)
{
if (underlyingCodes != null && underlyingCodes.Any(n => !string.IsNullOrEmpty(n)))
{
var linq = underlyingCodes.Where(n => !string.IsNullOrEmpty(n));
_underlyingCodeFilterSet = new HashSet<string>(linq, StringComparer.OrdinalIgnoreCase);
}
return this;
}
/// <summary>
/// 计算
/// </summary>
public IEnumerable<TradingRiskParameter> Calculate(ITradeDataSource tradeDataSource, IDataSource<ManualRisk> manualRiskProvider)
{
if (tradeDataSource == null)
{
throw new ArgumentNullException(nameof(tradeDataSource));
}
IOtcTradeValueCalcContext optionContext = null;
try
{
_logger?.Debug($"[{_context.VolType}]开始计算当前风险>>>>>>>>>");
//构建计算上下文
var sysRiskFreeRate = valuedateBLL.RiskFreeRate * 0.01;
optionContext = _context.CreateOptionValueCalcContext(sysRiskFreeRate);
//场外交易处理
var tradeRiskList = CalcOtcRisks(tradeDataSource, optionContext, manualRiskProvider);
var swapTradeRiskList = CalcSwapRisks(tradeDataSource, optionContext);
tradeRiskList.AddRange(swapTradeRiskList);
var hedgeOptionContext = PS.Config.Is润和 ? new InnerOptionValueCalcContext(DateTime.Today, optionContext) : optionContext;
//对冲交易处理
var hedgingRisk = CalcHedgeTradeRisks(tradeDataSource, hedgeOptionContext);
tradeRiskList.AddRange(hedgingRisk);
//计算每笔期权的盈亏
TradeRiskHelper.CalculateOptionTradePnlWithHedge(tradeRiskList);
_logger?.Debug($"[{_context.VolType}]当前风险计算完毕<<<<<<<<<<<");
return tradeRiskList;
}
finally
{
optionContext?.Dispose();
}
}
#region----场外交易处理----
//场外交易风险和盈亏计算(除收益互换)
private List<TradingRiskParameter> CalcOtcRisks(ITradeDataSource tradeDataSource
, IOtcTradeValueCalcContext optionContext, IDataSource<ManualRisk> manualRiskProvider)
{
var volType = _context.VolType;
//准备计算数据
var tradeRiskList = new List<TradingRiskParameter>();
var otcTrades = tradeDataSource.GetOtcTrades() ?? Enumerable.Empty<trade>();
otcTrades = otcTrades.Where(x => x.TradeType != "收益互换");
if (_underlyingCodeFilterSet != null)
{
otcTrades = otcTrades.Where(n => _underlyingCodeFilterSet.Contains(n.UnderlyingCode)).ToArray();
}
_logger?.Debug($"[{volType}]参与计算的场外交易数量:" + otcTrades.Count());
var set = new HashSet<int>(otcTrades.Select(t => t.id));
var tradeCashLookup = _context.GetTrade_Cashes(set).ToLookup(n => n.TradeId);
var ydContexts = GetYdOptionCalculateContexts(_context, optionContext);
DateTime valueDate, preValueDate;
//执行风险计算
foreach (var trad in otcTrades)
{
SetTradeLots(trad);
valueDate = _context.ValueDate;
preValueDate = _context.PreValueDate;
void getTradeCashList(out IEnumerable<trade_cash> tradeCashList, out IEnumerable<trade_cash> tdTradeCashUnwindList)
{
tradeCashList = tradeCashLookup.FirstOrDefault(a => a.Key == trad.id)?.AsEnumerable() ?? Enumerable.Empty<trade_cash>();
tdTradeCashUnwindList = tradeCashList.Where(tc => tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted
&& ClientCashInCashOut.PROFIT_ACTION.Contains(tc.Action) && tc.ValueDate <= valueDate && tc.ValueDate > preValueDate);
}
if (!string.IsNullOrEmpty(_skipTradeTypes) && _skipTradeTypes.Contains(trad.TradeType))
{
var resultRisk = UseManualRisk(trad, volType, manualRiskProvider, (calcRisk, lastPv) =>
{
getTradeCashList(out var tradeCashList, out var tdTradeCashUnwindList);
UpdateOTCPnl(trad, calcRisk, lastPv, tradeCashList: tradeCashList, tdTradeCashUnwindList: tdTradeCashUnwindList);
});
if (resultRisk != null)
{
tradeRiskList.Add(resultRisk);
}
}
else
{
TradingRiskParameter firstRisk = null;
IOtcTradeValueCalcContext ydContext = null;
try
{
var tdOtionContext = optionContext;
ydContext = ydContexts[(int)trad.SettlementType];
if (PS.Config.Is润和)
{
tdOtionContext = new InnerOptionValueCalcContext(DateTime.Today, optionContext);
ydContext = new InnerOptionValueCalcContext(DateTime.Today.AddDays(-1), optionContext);
}
ydContext.UserGroup = tdOtionContext.UserGroup = UserBLL.GetUserGroup(trad.TraderId);
if (volType == "交易曲面分红率0")
{
trad.DividendRate = 0;
}
var result = TradeRiskCalcUtil.CalcTradeRisk(trad, tdOtionContext, out var underlyings);
if (result == null)
{
continue;
}
result.TradeId = trad.id;
if (underlyings == null)
{
continue;
}
var index = 0;
foreach (var underlying in underlyings)
{
var trp = TransformOneUnderlying(underlying, trad, new List<TradeValueResult> { result }, index++);
tradeRiskList.Add(trp);
if (index == 1)
{
firstRisk = trp;
}
}
}
catch (Exception ex)
{
var key = "today_otcTrade_calc_" + trad.id;
if (_cache.Get(key) == null)
{
_cache.Set(key, this, DateTimeOffset.Now.AddMinutes(10));
_logger?.Error(ex, $"[{volType}-{trad.id}-{trad.TradeNumber}]当前风险计算出错");
}
}
if (firstRisk != null)
{
double? lastPv = null;
getTradeCashList(out var tradeCashList, out var tdTradeCashUnwindList);
if (firstRisk.TradeType == "远期")
{
firstRisk.TotalPnl = 0;
}
if (trad.TradeDate < valueDate)
{
var yp = _context.YdEodPositionDataProvider.GetOtcTradePositionData(trad.id, _context.VolType);
if (yp != null)
{
lastPv = yp.Position.Pv;
if (firstRisk.TradeType == "远期")
{
firstRisk.TotalPnl = yp.Position.TotalPnL;
}
}
else
{
lastPv = CalcYdOtcValue(trad, ydContext, tdTradeCashUnwindList, volType, out var spotPrice, out var vol);
if (firstRisk.Debug != null)
{
firstRisk.Debug.Add(spotPrice);
firstRisk.Debug.Add(vol);
}
}
}
UpdateOTCPnl(trad, firstRisk, lastPv, tradeCashList: tradeCashList, tdTradeCashUnwindList: tdTradeCashUnwindList);
}
}
}
AppManager.SetSysInfo(string.Concat("实时风险-", volType, "-计算结果"), tradeRiskList.Count + "条");
_logger?.Debug($"[{volType}]场外交易风险计算完毕");
return tradeRiskList;
}
//场外收益互换交易风险和盈亏计算
private List<TradingRiskParameter> CalcSwapRisks(ITradeDataSource tradeDataSource, IOtcTradeValueCalcContext optionContext)
{
var volType = _context.VolType;
//厦门象屿
DateTime? nextDateXmxy = null;
if (PS.Config.Company == Configuration.CompanyEnum.厦门象屿)
{
var date = SpecialModule.XiaMenXiangYuHelper.GetRefernceValueDate(_context.ValueDate);
if (date > _context.ValueDate)
{
nextDateXmxy = date;
}
}
//准备计算数据
var tradeRiskList = new List<TradingRiskParameter>();
var otcTrades = tradeDataSource.GetOtcTrades() ?? Enumerable.Empty<trade>();
otcTrades = otcTrades.Where(x => x.TradeType == "收益互换");
var tradeIds = otcTrades.Select(t => t.id).ToList();
var swapTradeService = new SwapTradeService(OptUserInfo.SystemUser);
var swapEodPositionService = new SwapEodPositionService(OptUserInfo.SystemUser);
var tradePositions = swapTradeService.GetSwapPositions(tradeIds, _underlyingCodeFilterSet);
var tradePositionSwapIds = tradePositions.Select(s => s.SwapTradeId).Distinct().ToList();
otcTrades = otcTrades.Where(x => tradePositionSwapIds.Contains(x.id));
_logger?.Debug($"[{volType}]参与计算的场外互换交易数量:" + otcTrades.Count());
var ydContexts = GetYdOptionCalculateContexts(_context, optionContext);
DateTime valueDate = _context.ValueDate;
DateTime preValueDate = _context.PreValueDate;
var swapEodPositions = swapEodPositionService.GetEodPositions(tradePositionSwapIds, valueDate, preValueDate);
var eodSwaps = swapEodPositionService.GetEodSwaps(tradePositionSwapIds, valueDate, preValueDate);
//风险计算
foreach (var trad in otcTrades)
{
var lastSwapEodPositions = swapEodPositions.Where(x => x.SwapTradeId == trad.id).OrderByDescending(o => o.ValueDate).ToList();//可能没有
var lastPositionIds = lastSwapEodPositions.Select(x => x.PositionId).Distinct();
var eodSwap = eodSwaps.Where(x => x.SwapTradeId == trad.id).OrderByDescending(o => o.ValueDate).FirstOrDefault();
var positions = tradePositions.Where(x=>x.SwapTradeId==trad.id).ToList();
if (lastPositionIds.Any())
{
foreach (var item in lastPositionIds)
{
var todayPosition = lastSwapEodPositions.FirstOrDefault(x => x.PositionId == item && x.ValueDate == valueDate);
var lastPosition = lastSwapEodPositions.FirstOrDefault(x => x.PositionId == item && x.ValueDate == preValueDate);
var currentPosition = todayPosition == null ? lastPosition : todayPosition;
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(currentPosition.UnderlyingCode);
var position = tradePositions.FirstOrDefault(f => f.id == item);
var result = new TradeValueResult();
var spotPrice = swapEodPositionService.UnderlyingCodePrice(currentPosition.UnderlyingCode, valueDate,out decimal vobp);
int shortRatio = currentPosition.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = currentPosition.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
result.Pv =Convert.ToDouble( currentPosition.UnderlyingMarketValue);
result.Delta = directionRatio * shortRatio * Convert.ToDouble(currentPosition.PosiQuantity) * Convert.ToDouble(currentPosition.CountRatio);
result.DeltaCash = result.Delta * Convert.ToDouble(spotPrice);
result.Gamma = 0;
result.GammaCash = 0;
result.Vega = 0;
result.VegaCash = 0;
result.TradingDayTheta = 0;
result.CalendarDayTheta = 0;
result.Rho = 0;
var resultRisk = SwapTransformOneUnderlying(underlying, trad, new List<TradeValueResult> { result }, currentPosition, position);
if (resultRisk != null)
{
tradeRiskList.Add(resultRisk);
}
}
}
}
AppManager.SetSysInfo(string.Concat("实时风险-", volType, "-计算结果"), tradeRiskList.Count + "条");
_logger?.Debug($"[{volType}]互换交易风险计算完毕");
return tradeRiskList;
}
//使用自定义交易风险,注意:应该屏蔽远期
private TradingRiskParameter UseManualRisk(trade trad, string volType, IDataSource<ManualRisk> manualRiskProvider,
Action<TradingRiskParameter, double?> UpdateOTCPnl)
{
var usePrePve = false;
var result = new TradeValueResult();
if (!ConsTrade.TradeCompleteStatus.Contains(trad.TradeStatus))
{
var risk = manualRiskProvider?.GetData(trad.id);
if (risk == null)
{
var yp = _context.YdEodPositionDataProvider.GetOtcTradePositionData(trad.id, _context.VolType);
if (yp != null)
{
var rate = trad.Notional / yp.Position.Amount;
result.Pv = yp.Position.Pv * rate;
var ypRisk = yp.Risk;
if (ypRisk != null)
{
result.Delta = ypRisk.Delta * rate;
result.DeltaCash = ypRisk.DeltaCash * rate;
result.Gamma = ypRisk.Gamma * rate;
result.GammaCash = ypRisk.GammaCash * rate;
result.Vega = ypRisk.Vega * rate;
result.VegaCash = ypRisk.VegaCash * rate;
result.TradingDayTheta = ypRisk.Theta * rate;
result.CalendarDayTheta = ypRisk.Theta * rate;
result.Rho = ypRisk.Rho * rate;
}
}
}
else
{
result.Pv = risk.Pv ?? 0;
result.Delta = risk.Delta ?? 0;
result.Gamma = risk.Gamma ?? 0;
result.Vega = risk.Vega ?? 0;
result.TradingDayTheta = risk.Theta ?? 0;
result.CalendarDayTheta = risk.Theta ?? 0;
result.Rho = risk.Rho ?? 0;
if (_dataProvider.UnderlyingPriceProvider.TryGetPrice(trad.UnderlyingCode, out var price))
{
result.DeltaCash = result.Delta * price;
result.GammaCash = result.Gamma * Math.Pow(price, 2) / 100;
result.VegaCash = result.Vega * price;
}
usePrePve = true;
}
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trad.UnderlyingCode);
var resultRisk = TransformOneUnderlying(underlying, trad, new List<TradeValueResult> { result }, 0);
if (usePrePve)
{
double? lastPv = null;
var yp = _context.YdEodPositionDataProvider.GetOtcTradePositionData(trad.id, _context.VolType);
if (yp != null)
{
lastPv = yp.Position.Pv;
}
UpdateOTCPnl(resultRisk, lastPv);
}
return resultRisk;
}
//计算场外交易上个交易日市值
private double? CalcYdOtcValue(trade trad, IOtcTradeValueCalcContext ydContext, IEnumerable<trade_cash> tdTradeCashUnwindList, string volType
, out double? spotPrice, out double? vol)
{
vol = null;
spotPrice = null;
try
{
if (ConsTrade.TradeCompleteStatus.Contains(trad.TradeStatus))
{
trad.Notional = 0;
}
//计算昨日pv时需要还原昨日的持仓数量
trad.Notional += tdTradeCashUnwindList.Sum(tc => tc.UnwindNotional ?? 0);
var result = TradeRiskCalcUtil.CalcTradeRisk(trad, ydContext, out var underlyings);
if (result != null)
{
vol = result.Vol;
spotPrice = result.SpotPrice;
}
return result?.Pv;
}
catch (Exception ex)
{
var key = "yeday_otcTrade_calc_" + trad.id;
if (_cache.Get(key) == null)
{
_cache.Set(key, this, DateTimeOffset.Now.AddMinutes(10));
_logger?.Error(ex, $"[{volType}-{trad.id}-{trad.TradeNumber}]昨日场外期权交易PV计算出错");
}
return null;
}
}
//获取三种结算方式的昨日交易估值上下文(因为昨日交易有日终价格)
private static YdOptionCalculateContext[] GetYdOptionCalculateContexts(ITradeRiskCalcContext riskContext, IOtcTradeValueCalcContext optionContext)
{
var ydContexts = new YdOptionCalculateContext[3];
ydContexts[(int)SettlementTypeEnum.ClosePrice] =
new YdOptionCalculateContext(riskContext.PreValueDate, optionContext, riskContext.YdEodPriceProvider, riskContext.YdTradeHisDataProvider, SettlementTypeEnum.ClosePrice)
{
CalcScenario = Enums.CalcScenarioEnum.RealtimeRisk
};
ydContexts[(int)SettlementTypeEnum.SettlePrice] =
new YdOptionCalculateContext(riskContext.PreValueDate, optionContext, riskContext.YdEodPriceProvider, riskContext.YdTradeHisDataProvider, SettlementTypeEnum.SettlePrice)
{
CalcScenario = Enums.CalcScenarioEnum.RealtimeRisk
};
ydContexts[(int)SettlementTypeEnum.ReferencePrice] =
new YdOptionCalculateContext(riskContext.PreValueDate, optionContext, riskContext.YdEodPriceProvider, riskContext.YdTradeHisDataProvider, SettlementTypeEnum.ReferencePrice)
{
CalcScenario = Enums.CalcScenarioEnum.RealtimeRisk
};
return ydContexts;
}
/// <summary>
/// 设置交易记录手数值
/// </summary>
private void SetTradeLots(trade trad)
{
//成交手数
trad.Lots = TradeLotsCalc.GetLots(trad.UnderlyingCode, trad.OriginalNotional ?? 0);
trad.LotsNewInfo = TradeLotsCalc.GetLots(trad.UnderlyingCode, trad.Notional);
}
#endregion
#region----对冲交易处理----
/// <summary>
/// 更新对冲交易盈亏数据
/// </summary>
private IEnumerable<TradingRiskParameter> CalcHedgeTradeRisks(ITradeDataSource tradeDataSource, IOtcTradeValueCalcContext optionCalcContext)
{
_logger?.Debug($"[{_context.VolType}]对冲交易PNL处理开始");
//获取所有未结算过的对冲交易
var HedgeTradeList = tradeDataSource.GetExchangeTrades() ?? Enumerable.Empty<ExchangeTrade>();
if (_underlyingCodeFilterSet != null)
{
HedgeTradeList = HedgeTradeList.Where(n => _underlyingCodeFilterSet.Contains(n.UnderlyingCode)).ToArray();
}
//股票 商品期货临时tradeId
var tempTradeId = -1;
var togetherUnOptionList = new List<TradingRiskParameter>();
var hedgePnlContext = _context.CreateHedgePnlCalcContext(optionCalcContext);
//昨日持仓信息
var predicat = PredicateBuilder.True<EodTradePosition>();
if (_underlyingCodeFilterSet != null)
{
predicat = predicat.And(t => _underlyingCodeFilterSet.Contains(t.UnderlyingCode));
}
var lastEodPositions = _context.YdEodPositionDataProvider
.GetExchangeTradePositionList(_context.VolType).Where(predicat.Compile()).ToArray();
//计算昨日持仓+新增加交易合计的PV
var hedgePnlList = new HedgePnlCalc(hedgePnlContext).Calculate(HedgeTradeList, lastEodPositions);
//根据是否展示累积盈亏过滤前一交易日已清仓的持仓为0的持仓信息
if (!PS.Config.ShowAccruedTotalPnL)
{
hedgePnlList = hedgePnlList.Where(t => t.HasNewTrade || Math.Abs(t.Notional) != 0).ToList();
}
var calcDeltaT1 = TradeRiskHelper.IsCalcDeltaT1();
foreach (var pnl in hedgePnlList)
{
var um = _dataProvider.UnderlyingDataProvider.GetUnderlying(pnl.UnderlyingCode);
if (um == null)
{
continue;
}
um.Price = pnl.SettlePrice;
var variety = _dataProvider.UnderlyingDataProvider.GetVariety(um.UnderlyingTypeId);
if (variety == null)
{
optionCalcContext.ErrorHandler?.AddError($"{um.UnderlyingCode}没有找到品种信息");
variety = new Variety();
}
var TradeUnitValue = variety?.TradeUnitValue;
var tempRisk = new TradingRiskParameter
{
VarietyCode = variety.VarietyCode?.ToUpperInvariant(),
IsOption = false,
TradeId = tempTradeId--,
UnderlyingId = um.id,
UnderlyingCode = pnl.UnderlyingCode,
UnderlyingName = um?.UnderlyingName ?? string.Empty,
SpotPriceChangePercent = um.GetPriceChangePercent(),
TradeType = pnl.TradeType,
BookId = pnl.BookId,
BuySell = pnl.BuySell,
Notional = pnl.Notional,
Pv = pnl.Pv,
Tv = pnl.TimeValue,
TradePrice = pnl.Cost,
OriginalPrincipalSum = 0,
DailyPnl = pnl.DailyPnL,
TotalPnl = pnl.TotalPnl,
ExercisePnl = pnl.TotalPnl,
SpotPrice = um.Price,
TradeStatus = ConsTrade.确认成交,
InstrumentType = um.UnderlyingInstrumentType,
IsSynthetic = um.IsSynthetic(),
ExchangeOptionCode = pnl.ExchangeOptionCode,
PositionType = pnl.PositionType,
CallPut = pnl.CallPut,
Strike = pnl.Strike,
//风险参数
Delta = pnl.Delta,
SA_Delta = pnl.SA_Delta,
DeltaCash = pnl.DeltaCash,
Gamma = pnl.Gamma,
Vega = pnl.Vega,
GammaCash = pnl.GammaCash,
DeltaInLots = pnl.DeltaInLots,
GammaInLots = pnl.GammaInLots,
Theta = pnl.Theta,
Rho = pnl.Rho,
DdeltaDt = pnl.DdeltaDt,
DdeltaDvol = pnl.DdeltaDvol,
DvegaDt = pnl.DvegaDt,
DvegaDvol = pnl.DvegaDvol,
Lots = pnl.Lots,
LotsNewInfo = pnl.Lots,
Cost = pnl.Cost,
AccruedTotalPnl = pnl.TotalPnl,
ExerciseDate = pnl.ExerciseDate,
Vol = pnl.Vol,
TradeSavedVol = pnl.Vol,
ExOptionPrice = pnl.ExOptionPrice
};
if (calcDeltaT1)
{
tempRisk.DeltaT1 = pnl.TradeType == "场内期权" ? pnl.DeltaT1 : pnl.Delta;
}
if (PS.Config.Is渤海)
{
tempRisk.ExercisePnl = 0.0;
}
tempRisk.OriginalNotionalV = tempRisk.OriginalNotional = tempRisk.Notional;
tempRisk.TradeFlag = (int)EnumTradeTypeUtil.GetTradeFlag(tempRisk.TradeType);
if (Math.Abs(tempRisk.Notional) > 1e-7)
{
tempRisk.Premium = tempRisk.TradePrice / tempRisk.Notional;
tempRisk.StockEqvNotional = Math.Abs(tempRisk.Pv);
tempRisk.BuySell = tempRisk.Notional > 0 ? "买入" : "卖出";
}
else
{
tempRisk.BuySell = string.Empty;
}
if (PS.Config.Company == CompanyEnum.红塔众鑫)
{
var day = QdpCalendarHelper.GetNonHolidayDaysBetween(valuedateBLL.ValueDate
,pnl.ExerciseDate.GetValueOrDefault()); //剩余天数
tempRisk.ThetaNet = Math.Round(pnl.Theta,2) * day;
}
togetherUnOptionList.Add(tempRisk);
}
if (togetherUnOptionList.Any())
{
//更新行权盈亏
foreach (var t in togetherUnOptionList)
{
if (t.TradeType == "场内期权")
{
var pnl = (t.CallPut == "Call" ? 1 : -1) * ((t.SpotPrice ?? 0.0) - (t.Strike ?? 0.0)) * Math.Abs(t.Notional);
pnl = pnl < 0 ? 0 : pnl * EodOperationBase.GetSign(t.BuySell);
t.ExercisePnl = pnl - t.Cost;
}
else
{
t.ExercisePnl = t.Pv - t.Cost;
}
}
}
_logger?.Debug($"[{_context.VolType}]对冲交易PNL计算完毕");
return togetherUnOptionList;
}
#endregion
#region-----tradingRiskParameter-----
//衍生品交易计算结果转换(未考虑多标的期权--待明确业务后重写)
private TradingRiskParameter TransformOneUnderlying(underlying_manager um, trade tradeObj, List<TradeValueResult> optionValueResult, int index)
{
var contractSize = 0d;
var variety = um == null ? null : _dataProvider.UnderlyingDataProvider.GetVariety(um.UnderlyingCode, out contractSize) ?? new Variety();
var sign = EodOperationBase.GetSign(tradeObj.BuySell);
var trp = new TradingRiskParameter
{
IsOption = true,
UnderlyingCode = um?.UnderlyingCode,
UnderlyingId = um?.id,
UnderlyingName = um?.UnderlyingName,
InstrumentType = um?.UnderlyingInstrumentType,
SpotPrice = um?.Price,
SpotPriceChangePercent = um?.GetPriceChangePercent(),
IsGroup = tradeObj.IsGroup,
TradeId = tradeObj.id,
TradeNumber = tradeObj.TradeNumber,
TradeType = tradeObj.TradeType,
StructureType = tradeObj.StructureType,
TradeStatus = tradeObj.TradeStatus,
TradeDate = tradeObj.TradeDate,
TradePrice = (tradeObj.TradePrice ?? 0) * sign,
OriginalPrincipalSum = (tradeObj.OriginalPrincipalSum ?? 0) * sign,
ClientId = tradeObj.ClientId,
ClientName = tradeObj.ClientName,
CallPut = tradeObj.CallPut,
BuySell = tradeObj.BuySell,
BookId = tradeObj.AssetId,
ExerciseMode = tradeObj.ExerciseMode,
ExerciseDate = tradeObj.ExerciseDate,
InitSpotPrice = tradeObj.SpotPrice,
Notional = tradeObj.Notional,
OriginalNotional = tradeObj.OriginalNotional ?? tradeObj.Notional,
StockEqvNotional = tradeObj.StockEqvNotional,
IsMoneynessOption = tradeObj.IsMoneynessOption,
IsPremiumRate = tradeObj.IsUsePremiumRate ?? false,
DividendRate = tradeObj.DividendRate,
NoRiskRate = tradeObj.NoRiskRate,
OpenVol = tradeObj.TradeOpenVolatility ?? 0,
Premium = tradeObj.IsUsePremiumRate == true ? (tradeObj.PremiumRate ?? 0) : (tradeObj.TradeSinglePrice ?? 0),
Lots = tradeObj.Lots,
LotsNewInfo = tradeObj.LotsNewInfo,
TradeSavedVol = tradeObj.TradeSavedVol,
Pv = optionValueResult.Sum(t => t.Pv),
NPv = optionValueResult.Sum(t => t.NPv),
Tv = optionValueResult.Sum(t => t.TimeValue),
Delta = optionValueResult.Sum(t => t.GetDelta(index)),
DeltaT1 = optionValueResult.FirstOrDefault()?.DeltaT1,
SA_Delta = optionValueResult.Sum(t => t.SA_Delta),
Gamma = optionValueResult.Sum(t => t.GetGamma(index)),
Vega = optionValueResult.Sum(t => t.GetVega(index)),
DeltaCash = optionValueResult.Sum(t => t.GetDeltaCash(index)),
GammaCash = optionValueResult.Sum(t => t.GetGammaCash(index)),
TradeFlag = (int)EnumTradeTypeUtil.GetTradeFlag(tradeObj.TradeType, tradeObj.Comments),
AccruedTotalPnl = 0,
CompanyObj = null,
Cost = 0,
DailyPnl = 0,
GammaInLots = 0,
Strike = tradeObj.Strike,
Debug = new List<object> { optionValueResult[0].SpotPrice, optionValueResult[0].Vol }
};
if (tradeObj.TradeType == "远期")
{
trp.TradePrice = tradeObj.TradePrice ?? 0;
}
var list = DbContextFactory.GetYLDbContext().dividendrate_record.Where(x => x.TradeType.Contains(tradeObj.TradeType) && valuedateBLL.ValueDate >= x.ValueDate && (x.OptionType == tradeObj.OptionType || x.OptionType == "全部")).ToList();
var record = list.Where(x => x.UnderlyingCode.Split(',').Any(code => code == tradeObj.UnderlyingCode)).OrderByDescending(x => x.OptDate).OrderByDescending(x => x.ValueDate);
if (record.Any())
{
trp.DividendRate = record.FirstOrDefault()?.DividendRate;
}
if (!trp.DividendRate.HasValue)
{
trp.DividendRate = trp.NoRiskRate;
}
//成交份额(countRatio传入1算出来的是成交份额)
trp.OriginalNotionalV = TradeCalcHelper.GetTradeAmountV(tradeObj, tradeObj.OriginalNotional ?? tradeObj.Notional, 1);
//组合标的设置DeltaInLots为0
if (um != null)
{
if (um.IsSynthetic())
{
trp.IsSynthetic = true;
trp.VarietyCode = "组合标的";
trp.DeltaInLots = optionValueResult.Sum(t => t.GetDelta(index));
if (um.ContractSize > 0)
{
trp.DeltaInLots /= um.ContractSize;
}
}
else
{
trp.VarietyCode = variety?.VarietyCode?.ToUpperInvariant();
trp.DeltaInLots = optionValueResult.Sum(t => t.GetDelta(index)) / contractSize;
}
}
else if (string.IsNullOrEmpty(trp.UnderlyingCode))
{
if (trp.TradeType == ConsGlobal.TradeType.CashFlow)
{
trp.VarietyCode = trp.UnderlyingCode = "现金流";
}
else
{
trp.VarietyCode = trp.UnderlyingCode = "未知";
}
}
trp.GammaInLots = optionValueResult.Sum(t => t.GetGamma(index)) / contractSize;
trp.Theta = optionValueResult.Sum(t => t.Theta);
if (PS.Config.Company == CompanyEnum.红塔众鑫)
{
trp.ThetaNet = optionValueResult.Sum(t =>
{
var day = QdpCalendarHelper.GetNonHolidayDaysBetween(
valuedateBLL.ValueDate,tradeObj.ExerciseDate.GetValueOrDefault()); //剩余天数
return Math.Round(t.Theta, 2) * day;
});
}
trp.Rho = optionValueResult.Sum(t => t.Rho) * 100;
trp.DdeltaDt = optionValueResult.Sum(t => t.DDeltaDt);
trp.DdeltaDvol = optionValueResult.Sum(t => t.DDeltaDVol);
trp.DvegaDt = optionValueResult.Sum(t => t.DVegaDt);
trp.DvegaDvol = optionValueResult.Sum(t => t.DVegaDVol);
if (tradeObj.TradeType == "场内期权")
{
trp.PositionType = sign > 0 ? "long" : "short";
trp.ExchangeOptionCode = tradeObj.ExchangeOptionCode;
}
else if (tradeObj.TradeType == "结构化交易")
{
trp.TradeType = tradeObj.StructureType ?? string.Empty;
trp.StructureType = "结构化交易";
}
//目前optionValueResult中只有一个标的的Vol值
//对于彩虹期权、价差期权等多标的的期权交易,并不能获取各标的的Vol,因此暂时使用第一个Vol
//TODO: 当optionValueResult支持多个Vol之后,需要做相应修改
trp.Vol = optionValueResult[0].Vol;
trp.PricingT = optionValueResult[0].PricingT;
//场外期权行权盈亏
if (trp.TradeFlag > (int)EnumTradeType.ExchangeOption)
{
trp.ExercisePnl = GetExercisePnl(tradeObj, um?.Price ?? 0.0, out var actualStrike);
trp.Strike = actualStrike;
}
if (PS.Config.Is润和)
{
var unVol = VolatilityHelper.GetVol(DateTime.Today, "交易", tradeObj.UnderlyingCode, DataCacheProvider.GetAssetUnitDataSource().GetData(tradeObj.AssetId)?.UserGroup);
trp.TradeSavedVol = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: unVol,
valueDate: DateTime.Today,
underlyingCode: tradeObj.UnderlyingCode,
exerciseDate: tradeObj.ExerciseDate.Value,
strike: tradeObj.Strike ?? 0,
isBuy: tradeObj.BuySell == "买入",
isCall: tradeObj.CallPut == "Call",
spotPrice: tradeObj.SpotPrice ?? 0,
isMoneynessOption: tradeObj.IsMoneynessOption == "是");
}
trp.QuoteUnit = variety?.QuoteUnit;
if (optionValueResult != null && optionValueResult.Count() > 0 && optionValueResult[0] != null && optionValueResult[0].IsKnockOut)
{
trp.IsKnockOut = true;
trp.KnockOutPayoff = optionValueResult.Sum(t => t.KnockOutPayoff);
}
return trp;
}
//衍生品互换交易计算结果转换(未考虑多标的期权--待明确业务后重写)
private TradingRiskParameter SwapTransformOneUnderlying(underlying_manager um, trade tradeObj, List<TradeValueResult> optionValueResult, eod_swap_position eodPosition, swap_position position)
{
var contractSize = 0d;
var variety = um == null ? null : _dataProvider.UnderlyingDataProvider.GetVariety(um.UnderlyingCode, out contractSize) ?? new Variety();
int shortRatio = position.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
int directionRatio = position.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
var trp = new TradingRiskParameter
{
IsOption = true,
UnderlyingCode = um?.UnderlyingCode,
UnderlyingId = um?.id,
UnderlyingName = um?.UnderlyingName,
InstrumentType = um?.UnderlyingInstrumentType,
SpotPrice = um?.Price,
SpotPriceChangePercent = um?.GetPriceChangePercent(),
IsGroup = tradeObj.IsGroup,
TradeId = tradeObj.id,
TradeNumber = tradeObj.TradeNumber,
TradeType = tradeObj.TradeType,
StructureType = tradeObj.StructureType,
TradeStatus = tradeObj.TradeStatus,
TradeDate = tradeObj.TradeDate,
OriginalPrincipalSum = 0,
ClientId = tradeObj.ClientId,
ClientName = tradeObj.ClientName,
CallPut = shortRatio == 1 ? "Call" : "Put",
BuySell = directionRatio == 1 ? "卖出" : "买入",
BookId = tradeObj.AssetId,
ExerciseMode = tradeObj.ExerciseMode,
ExerciseDate = tradeObj.ExerciseDate,
InitSpotPrice = Convert.ToDouble(position.PosiNetPrice),
OriginalNotional = Convert.ToDouble(position.PosiQuantity * position.CountRatio),
IsMoneynessOption = tradeObj.IsMoneynessOption,
IsPremiumRate = false,
DividendRate = 0,
NoRiskRate = 0,
OpenVol = 0,
Premium = Convert.ToDouble(position.PosiNetPrice),
Lots = Convert.ToDouble(position.PosiQuantity * position.CountRatio / position.ContractSize),
TradeSavedVol = tradeObj.TradeSavedVol,
Pv = optionValueResult.Sum(t => t.Pv),
Tv = optionValueResult.Sum(t => t.TimeValue),
Delta = optionValueResult.Sum(t => t.GetDelta(0)),
DeltaT1 = optionValueResult.FirstOrDefault()?.DeltaT1,
SA_Delta = optionValueResult.Sum(t => t.SA_Delta),
Gamma = optionValueResult.Sum(t => t.GetGamma(0)),
Vega = optionValueResult.Sum(t => t.GetVega(0)),
DeltaCash = optionValueResult.Sum(t => t.GetDeltaCash(0)),
GammaCash = optionValueResult.Sum(t => t.GetGammaCash(0)),
TradeFlag = (int)EnumTradeType.SwapOption,
CompanyObj = null,
Cost = 0,
DailyPnl = 0,
GammaInLots = 0,
Strike = tradeObj.Strike,
Debug = new List<object> { optionValueResult[0].SpotPrice, optionValueResult[0].Vol }
};
if (eodPosition!=null)
{
trp.AccruedTotalPnl = Convert.ToDouble(eodPosition.RealizedMtmPnL);
trp.LotsNewInfo = Convert.ToDouble(eodPosition.PosiQuantity * eodPosition.CountRatio / eodPosition.ContractSize);
trp.TradePrice = Convert.ToDouble(eodPosition.PosiNetPrice * shortRatio);
trp.Notional = Convert.ToDouble(eodPosition.PosiQuantity * eodPosition.CountRatio);
trp.StockEqvNotional = Convert.ToDouble(eodPosition.PosiNotionalValue);
}
else
{
trp.LotsNewInfo = trp.Lots;
trp.TradePrice = Convert.ToDouble(position.PosiNetPrice * shortRatio);
trp.Notional = Convert.ToDouble(position.PosiQuantity * position.CountRatio);
trp.StockEqvNotional = Convert.ToDouble(position.PosiNotionalValue);
}
//成交份额(countRatio传入1算出来的是成交份额)
trp.OriginalNotionalV = trp.OriginalNotional;
trp.GammaInLots = optionValueResult.Sum(t => t.GetGamma(0)) / contractSize;
trp.Theta = optionValueResult.Sum(t => t.Theta);
trp.Rho = optionValueResult.Sum(t => t.Rho) * 100;
trp.DdeltaDt = optionValueResult.Sum(t => t.DDeltaDt);
trp.DdeltaDvol = optionValueResult.Sum(t => t.DDeltaDVol);
trp.DvegaDt = optionValueResult.Sum(t => t.DVegaDt);
trp.DvegaDvol = optionValueResult.Sum(t => t.DVegaDVol);
trp.Vol = optionValueResult[0].Vol;
trp.PricingT = optionValueResult[0].PricingT;
if (PS.Config.Is润和)
{
var unVol = VolatilityHelper.GetVol(DateTime.Today, "交易", um.UnderlyingCode, DataCacheProvider.GetAssetUnitDataSource().GetData(tradeObj.AssetId)?.UserGroup);
trp.TradeSavedVol = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: unVol,
valueDate: DateTime.Today,
underlyingCode: um.UnderlyingCode,
exerciseDate: tradeObj.ExerciseDate.Value,
strike: Convert.ToDouble(eodPosition.PosiNetPrice),
isBuy: trp.BuySell == "买入",
isCall: trp.CallPut == "Call",
spotPrice: tradeObj.SpotPrice ?? 0,
isMoneynessOption: tradeObj.IsMoneynessOption == "是");
}
trp.QuoteUnit = variety?.QuoteUnit;
return trp;
}
//更新实时盈亏
private static void UpdateOTCPnl(trade td, TradingRiskParameter para, double? lastPv
, IEnumerable<trade_cash> tradeCashList, IEnumerable<trade_cash> tdTradeCashUnwindList)
{
var hasLastPv = lastPv.HasValue;
var hasNotional = Math.Abs(para.Notional) > 1e-6;
var completed = ConsTrade.TradeCompleteStatus.Contains(para.TradeStatus);
var pendingForAudit = !completed && para.TradeStatus == ConsTrade.平仓待复核 || para.TradeStatus == ConsTrade.行权待复核;
//当日实现收益
double RealizedProfit = 0;
if (hasLastPv)
{
RealizedProfit = tdTradeCashUnwindList.Sum(tc => tc.Amount);
}
else
{
var lastTc = tradeCashList.Where(tc => tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && tc.Action == ClientCashInCashOut.系统操作_期权费);
if (lastTc.Any())
{
lastPv = -lastTc.Sum(tc => tc.Amount - (tc.ExtraAmount ?? 0));
}
else if (para.TradeType == "远期")
{
lastPv = -para.TradePrice; //远期交易金额没有买卖方向区分,转换为pv直接取符号即可
}
else
{
var sign = TradeCalcHelper.GetBuySellSign(para.BuySell);
lastPv = para.TradePrice - para.OriginalPrincipalSum * sign;
}
if (td.TradeType == "雪球期权" && td.trade_snowball.PrepaymentUsed)
{
lastPv += (td.trade_snowball.PrepaymentRatio ?? 0) * (td.OriginalStockEqvNotional ?? 0) * (td.BuySell == "卖出" ? -1 : 1);
}
RealizedProfit = tradeCashList.Where(tc => tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted && ClientCashInCashOut.PROFIT_ACTION.Contains(tc.Action)).Sum(tc => tc.Amount);
}
//已平仓 已到期 已行权
if (completed)
{
para.DailyPnl = RealizedProfit - lastPv.Value;
para.TotalPnl = tradeCashList.Where(tc => tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted).Sum(tc => tc.Amount);
para.AccruedTotalPnl = para.TotalPnl;
para.ExercisePnl = 0;
para.Notional = 0;
para.TradePrice = 0;
para.OriginalPrincipalSum = 0;
para.Pv = 0;
para.Delta = 0;
para.DeltaT1 = 0;
para.DeltaT1Lots = 0;
para.SA_Delta = 0;
para.DeltaCash = 0;
para.Gamma = 0;
para.Vega = 0;
para.GammaCash = 0;
para.DeltaInLots = 0;
para.GammaInLots = 0;
para.Theta = 0;
para.Rho = 0;
para.DdeltaDt = 0;
para.DdeltaDvol = 0;
para.DvegaDt = 0;
para.DvegaDvol = 0;
para.StockEqvNotional = 0;
para.KnockOutPayoff = 0;
}
else if (pendingForAudit)
{
//平仓待复核 行权待复核
var tradeCashTemp = tradeCashList.Where(tc => tc.ValidState == ConsGlobal.InValid && !tc.IsDeleted && tc.Action != ClientCashInCashOut.系统操作_期权费).OrderByDescending(a => a.id).FirstOrDefault();
//剩余持仓
var holdNotional = para.Notional - (tradeCashTemp == null ? 0 : ConsTrade.平仓待复核.Equals(para.TradeStatus) ? (tradeCashTemp.UnwindNotional ?? 0) : para.Notional);
//剩余持仓百分比
var holdRatio = hasNotional ? holdNotional / para.Notional : 0;
var newPv = para.Pv * holdRatio + (tradeCashTemp?.Amount ?? 0);
para.Notional = holdNotional;
para.DailyPnl = newPv + RealizedProfit - lastPv.Value;
para.AccruedTotalPnl = para.TotalPnl = tradeCashList.Where(tc => tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted).Sum(t => t.Amount) + newPv;
if (Math.Abs(1 - holdRatio) > 1e-5)
{
para.ExercisePnl *= holdRatio;
para.Pv *= holdRatio;
para.Tv *= holdRatio;
para.TradePrice *= holdRatio;
para.OriginalPrincipalSum *= holdRatio;
para.Delta *= holdRatio;
para.SA_Delta *= holdRatio;
para.DeltaCash *= holdRatio;
para.Gamma *= holdRatio;
para.Vega *= holdRatio;
para.GammaCash *= holdRatio;
para.DeltaInLots *= holdRatio;
para.GammaInLots *= holdRatio;
para.Theta *= holdRatio;
para.Rho *= holdRatio;
para.DdeltaDt *= holdRatio;
para.DdeltaDvol *= holdRatio;
para.DvegaDt *= holdRatio;
para.DvegaDvol *= holdRatio;
if (para.IsKnockOut)
{
para.KnockOutPayoff *= holdRatio;
}
}
}
else if (para.TradeType == "远期")
{
para.DailyPnl = para.Pv + RealizedProfit - lastPv.Value;
if (hasLastPv)
{
para.TotalPnl += para.DailyPnl;
}
else
{
para.TotalPnl = para.DailyPnl;
}
para.AccruedTotalPnl = para.TotalPnl;
}
else if (ConsTrade.TradeStatusBeforConfirmed.Contains(para.TradeStatus))
{
para.DailyPnl = para.Pv - para.TradePrice;
if (td.TradeType == "雪球期权" && td.trade_snowball.PrepaymentUsed)
{
para.DailyPnl -= (td.trade_snowball.PrepaymentRatio ?? 0) * td.StockEqvNotional * (td.BuySell == "卖出" ? -1 : 1);
}
para.TotalPnl = para.DailyPnl;
para.AccruedTotalPnl = para.DailyPnl;
}
else
{
para.DailyPnl = para.Pv + RealizedProfit - lastPv.Value;
para.TotalPnl = para.Pv + tradeCashList.Where(tc => tc.ValidState != ConsGlobal.InValid && !tc.IsDeleted).Sum(t => t.Amount);
if (td.TradeType == "雪球期权" && td.trade_snowball.PrepaymentUsed)
{
//只关注持仓的部分
para.TotalPnl -= (td.trade_snowball.PrepaymentRatio ?? 0) * para.Notional * (td.SpotPrice ?? 0) * (td.BuySell == "卖出" ? -1 : 1);
}
para.AccruedTotalPnl = para.TotalPnl;
}
//用于计算错误时查看
para.LastPv = lastPv;
}
//获取行权盈亏
private double GetExercisePnl(OtcTradeBase tradeObj, double spotPrice, out double? actualStrike)
{
actualStrike = null;
if (tradeObj == null)
{
return 0;
}
if ("亚式期权".Equals(tradeObj.TradeType))
{
double? avgPrice = null;
var trade_asian_option = _dataProvider.TradeExtendDataProvider.GetTrade_Asian_Option(tradeObj.id);
if (trade_asian_option != null)
{
var fixings = AsiaOptionProvider.Default.GetFixingString(_context.ValueDate, tradeObj, trade_asian_option);
avgPrice = GetAsianAveragePrice(fixings, trade_asian_option.PayoffType);
if (avgPrice != null && "Floating".Equals(trade_asian_option.StrikeType))
{
actualStrike = avgPrice;
}
}
if (actualStrike == null)
{
actualStrike = tradeObj.IsMoneynessOptionData ? (tradeObj.SpotPrice * tradeObj.Strike) : tradeObj.Strike;
}
if (trade_asian_option == null)
{
return 0.0;
}
if ("Fixed".Equals(trade_asian_option?.StrikeType))
{
return (Math.Max((tradeObj.CallPut == "Call" ? 1 : -1) * ((avgPrice ?? spotPrice) - (actualStrike ?? 0.0)) * tradeObj.Notional, 0.0) - (tradeObj.TradePrice ?? 0.0)) * EodOperationBase.GetSign(tradeObj.BuySell);
}
if (avgPrice == null)
{
return 0.0;
}
return (Math.Max((tradeObj.CallPut == "Call" ? 1 : -1) * (spotPrice - avgPrice.Value) * tradeObj.Notional, 0.0) - (tradeObj.TradePrice ?? 0.0)) * EodOperationBase.GetSign(tradeObj.BuySell);
}
else if ("结构化交易".Equals(tradeObj.TradeType))
{
throw new ServiceException($"不支持'结构化交易'主交易的计算,tradeId:{tradeObj.id},tradeNumber:{tradeObj.TradeNumber}");
}
else
{
actualStrike = tradeObj.IsMoneynessOptionData ? (tradeObj.SpotPrice * tradeObj.Strike) : tradeObj.Strike;
return (Math.Max((tradeObj.CallPut == "Call" ? 1 : -1) * (spotPrice - (actualStrike ?? 0.0)) * tradeObj.Notional, 0.0) - (tradeObj.TradePrice ?? 0.0)) * EodOperationBase.GetSign(tradeObj.BuySell);
}
}
//获取亚式期权均价起算日之后的均价,未到均价起算日则返回Null
private static double? GetAsianAveragePrice(string fixings, string PayoffType)
{
if (string.IsNullOrWhiteSpace(fixings))
{
return null;
}
var arr = fixings.Split(QdpConsts.Semilicon);
var fixingValues = arr.Select(x => DataConvert.ConvertCommaValueToDouble(x, 1) ?? 0).ToArray();
if ("GeometricAverage".Equals(PayoffType))
{
var n = fixingValues.Length;
return Math.Pow(fixingValues.Aggregate(func: (result, item) => result * item), 1.0 / n);
}
if ("ArithmeticAverage".Equals(PayoffType) || "DiscreteArithmeticAverage".Equals(PayoffType))
{
return fixingValues.Average();
}
return null;
}
#endregion
}
}