using Qdp.Pricing.Base.Implementations;
using System.Linq.Expressions;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.Configuration;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Enums;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.EodModule.SettlementModule;
using YLErp.Modules.ExchangeOptionTradeModule;
using YLErp.QdpModule;
using YLErp.QdpModule.Constants;
namespace YLErp.Modules.ScenarioModule
{
///
/// 情景分析计算服务
///
public class ScenarioCalcService : YLBaseService
{
const string SpotPriceFormat = "0.00##";
const string RatePercentFormat = "0.00##%";
public ScenarioCalcService(OptUserInfo userInfo) : base(userInfo)
{
}
//获取日终持仓
private IEnumerable GetEodPosition(TradeScenarioSearchModel searchModel)
{
var predicate = PredicateBuilder.Create(
t => t.ValueDate == searchModel.PositionDate.Value && !ScenarioHelper.ExcludeTradeTypes.Contains(t.TradeType));
if (searchModel.UserAssets != null || searchModel.UserClients != null)
{
predicate = predicate.And(n => searchModel.UserAssets.Contains(n.BookId) || searchModel.UserClients.Contains(n.ClientId));
}
if (searchModel.IsSelAll)
{
if (searchModel.ClientIds != null && searchModel.ClientIds.Any(n => n > 0))
{
predicate = predicate.And(x => searchModel.ClientIds.Contains(x.ClientId));
}
if (searchModel.UnderlyingIds != null && searchModel.UnderlyingIds.Any(n => n > 0))
{
predicate = predicate.And(x => searchModel.UnderlyingIds.Contains(x.UnderlyingId));
}
if (searchModel.AssetIds != null && searchModel.AssetIds.Any(n => n > 0))
{
predicate = predicate.And(x => searchModel.AssetIds.Contains(x.BookId));
}
if (searchModel.VarietyIds != null && searchModel.VarietyIds.Any(n => n > 0))
{
predicate = predicate.And(
x => DbContext.underlying_manager.Any(n => n.UnderlyingCode == x.UnderlyingCode && searchModel.VarietyIds.Contains(n.UnderlyingTypeId)));
}
if (searchModel.TradeTypes != null && searchModel.TradeTypes.Any(n => !string.IsNullOrEmpty(n)))
{
if (searchModel.TradeTypes.Contains("场外期权"))
{
predicate = predicate.And(x => x.TradeId > 0 || searchModel.TradeTypes.Contains(x.TradeType));
}
else
{
predicate = predicate.And(x => searchModel.TradeTypes.Contains(x.TradeType));
}
}
}
else if (searchModel.EodTradeIds == null || !searchModel.EodTradeIds.Any(n => n > 0))
{
return Enumerable.Empty();
}
else
{
predicate = predicate.And(x => searchModel.EodTradeIds.Contains(x.id));
}
var query = from et in DbContext.eod_trade_position.Where(predicate)
join td in DbContext.trade on et.TradeId equals td.id into temptd
from td in temptd.DefaultIfEmpty()
select new tradeDto
{
id = et.TradeId,
ClientId = et.ClientId,
AssetId = et.BookId,
TradeType = et.TradeType,
UnderlyingId = et.UnderlyingId,
UnderlyingCode = et.UnderlyingCode,
BuySell = td != null ? td.BuySell : et.PositionType == "long" ? "买入" : "卖出",
ExerciseDate = td != null ? td.ExerciseDate : null,
ExerciseMode = td != null ? td.ExerciseMode : null,
OptionType = td != null ? td.OptionType : null,
Strike = td != null ? td.Strike : null,
IsMoneynessOption = td != null ? td.IsMoneynessOption : "否",
Notional = td == null ? (et.PositionType == "long" ? et.Amount : -et.Amount) : et.Amount,
TradeDate = td != null ? td.TradeDate : null,
StartDate = td != null ? td.StartDate : null,
SpotPrice = td != null ? td.SpotPrice : null,
MaturityDate = td != null ? td.MaturityDate : null,
ExchangeOptionCode = et.ExchangeOptionCode
};
return query.ToArray();
}
//获取日间持仓
private IEnumerable GetIntradayPosition(TradeScenarioSearchModel searchModel)
{
var blQuery = true;
//场外交易持仓
var tdPredicate = PredicateBuilder.Create(
t => t.ExerciseDate >= valuedateBLL.ValueDate && ConsTrade.NeedMarginTradeStatusList.Contains(t.TradeStatus)
&& !ScenarioHelper.ExcludeTradeTypes.Contains(t.TradeType) && t.ClientId > 0 && t.ValidState != "InValid");
if (searchModel.UserAssets != null || searchModel.UserClients != null)
{
tdPredicate = tdPredicate.And(n => searchModel.UserAssets.Contains(n.AssetId) || searchModel.UserClients.Contains(n.ClientId));
}
if (searchModel.IsSelAll)
{
if (searchModel.TradeTypes != null && searchModel.TradeTypes.Any(n => !string.IsNullOrEmpty(n)))
{
if (blQuery = searchModel.TradeTypes.Contains("场外期权"))
{
tdPredicate = tdPredicate.And(x => searchModel.TradeTypes.Contains(x.TradeType));
}
else
{
tdPredicate = null;
}
}
if (blQuery)
{
if (searchModel.ClientIds != null && searchModel.ClientIds.Any(n => n > 0))
{
tdPredicate = tdPredicate.And(x => searchModel.ClientIds.Contains(x.ClientId));
}
if (searchModel.UnderlyingIds != null && searchModel.UnderlyingIds.Any(n => n > 0))
{
tdPredicate = tdPredicate.And(x => searchModel.UnderlyingIds.Contains(x.UnderlyingId));
}
if (searchModel.AssetIds != null && searchModel.AssetIds.Any(n => n > 0))
{
tdPredicate = tdPredicate.And(x => searchModel.AssetIds.Contains(x.AssetId));
}
if (searchModel.VarietyIds != null && searchModel.VarietyIds.Any(n => n > 0))
{
tdPredicate = tdPredicate.And(
x => DbContext.underlying_manager.Any(n => n.UnderlyingCode == x.UnderlyingCode && searchModel.VarietyIds.Contains(n.UnderlyingTypeId)));
}
}
}
else if (searchModel.OtcTradeIds != null && searchModel.OtcTradeIds.Any(n => n > 0))
{
tdPredicate = tdPredicate.And(x => searchModel.OtcTradeIds.Contains(x.id));
}
else
{
tdPredicate = null;
}
var tdList = tdPredicate == null ? Array.Empty() : DbContext.trade.Where(tdPredicate).ToArray();
//场内交易持仓
blQuery = true;
var exPredicate = PredicateBuilder.True();
if (searchModel.IsSelAll)
{
if (searchModel.ClientIds != null && searchModel.ClientIds.Any(n => n > 0))
{
blQuery = false;
}
if (blQuery && searchModel.TradeTypes != null && searchModel.TradeTypes.Any(n => !string.IsNullOrEmpty(n)))
{
var tradeTypes = searchModel.TradeTypes.Union(ConsTrade.TradeTypesForHedge).ToArray();
if (blQuery = tradeTypes.Any())
{
exPredicate = exPredicate.And(x => tradeTypes.Contains(x.TradeType));
}
}
if (blQuery)
{
if (searchModel.UnderlyingIds != null && searchModel.UnderlyingIds.Any(n => n > 0))
{
exPredicate = exPredicate.And(x => searchModel.UnderlyingIds.Contains(x.UnderlyingId));
}
if (searchModel.AssetIds != null && searchModel.AssetIds.Any(n => n > 0))
{
exPredicate = exPredicate.And(x => searchModel.AssetIds.Contains(x.BookId));
}
if (searchModel.VarietyIds != null && searchModel.VarietyIds.Any(n => n > 0))
{
exPredicate = exPredicate.And(
x => DbContext.underlying_manager.Any(n => n.UnderlyingCode == x.UnderlyingCode && searchModel.VarietyIds.Contains(n.UnderlyingTypeId)));
}
}
}
else if (searchModel.ExchangeTradeIds != null && searchModel.ExchangeTradeIds.Any(n => n > 0))
{
exPredicate = exPredicate.And(x => searchModel.ExchangeTradeIds.Contains(x.id));
}
else
{
blQuery = false;
}
IEnumerable exTdList = null;
if (blQuery)
{
exTdList = DbContext.TradePosition.Where(exPredicate).Select(n => new tradeDto
{
AssetId = n.BookId,
BuySell = n.PositionType == PositionTypeFlag.Long ? "买入" : "卖出",
ExerciseDate = n.MaturityDate,
//OptionType,
//Strike,
IsMoneynessOption = "否",
Notional = n.Position,
TradeAmount = n.Position,
TradeDate = n.CreateTime,
StartDate = n.CreateTime,
TradeType = n.TradeType,
UnderlyingId = n.UnderlyingId,
UnderlyingCode = n.UnderlyingCode,
//UnderlyingAssetClass,
//UnderlyingInstrumentType,
MaturityDate = n.MaturityDate,
ExchangeOptionCode = n.InstrumentCode
}).ToArray();
}
return exTdList != null ? tdList.Concat(exTdList).ToArray() : tdList;
}
///
/// 计算
///
public ScenarioCalcResult Calculate(ScenarioRequest req)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
var (xActions, yActions) = GetCalcActions(req.ConfigId, out var config);
return Calculate(req, config, xActions, yActions);
}
///
/// 计算
///
public ScenarioCalcResult Calculate(ScenarioCalcApiRequest req)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
var config = req.ScenarioConfig;
if (config == null)
{
throw new ServiceException("缺少情景分析配置");
}
if (config.xType == config.yType)
{
throw new ServiceException("横纵坐标类型 不能一致");
}
var req2 = new ScenarioRequest
{
SearchModel = new TradeScenarioSearchModel
{
IsSelAll = true,
PositionDate = req?.ValueDate
},
ValueDate = req.ValueDate,
RiskFreeRate = req.RiskFreeRate,
UseTradeDivendRate = req.UseTradeDivendRate,
VolType = req.VolType,
Underlyings = req.UnderlyingParams
};
if (req.DataFilter != null)
{
var clientNames = req.DataFilter.ClientNames;
var clientNumbers = req.DataFilter.ClientNumbers;
if (clientNames != null && clientNames.All(n => string.IsNullOrEmpty(n)))
{
clientNames = null;
}
if (clientNumbers != null && clientNumbers.All(n => string.IsNullOrEmpty(n)))
{
clientNumbers = null;
}
if (clientNames != null || clientNumbers != null)
{
req2.SearchModel.ClientIds = DataCacheProvider.GetClientDataSource().AsQueryable()
.Where(n => (clientNames == null || clientNames.Contains(n.Name))
&& clientNumbers == null || clientNumbers.Contains(n.Number))
.Select(n => n.id).ToList();
}
IEnumerable assetGroupIds = null;
if (req.DataFilter.AssetGroupNames != null && req.DataFilter.AssetGroupNames.Any(n => !string.IsNullOrEmpty(n)))
{
assetGroupIds = DataCacheProvider.GetAssetUnitGroupDataSource().AsQueryable()
.Where(n => req.DataFilter.AssetGroupNames.Contains(n.Name)).Select(n => n.id).ToList();
}
var assetNames = req.DataFilter.AssetBookNames;
if (assetNames != null && assetNames.All(n => string.IsNullOrEmpty(n)))
{
assetNames = null;
}
if (assetGroupIds != null || assetNames != null)
{
req2.SearchModel.AssetIds = DataCacheProvider.GetAssetUnitDataSource().AsQueryable()
.Where(n => (assetGroupIds == null || assetGroupIds.Contains(n.GroupId)) && (assetNames == null || assetNames.Contains(n.Name)))
.Select(n => n.id).ToList();
}
if (req.DataFilter.UnderlyingCodes != null && req.DataFilter.UnderlyingCodes.Any(n => !string.IsNullOrEmpty(n)))
{
req2.SearchModel.UnderlyingIds = DataCacheProvider.GetUnderlyingDataSource().AsQueryable()
.Where(n => req.DataFilter.UnderlyingCodes.Contains(n.UnderlyingCode)).Select(n => n.id).ToList();
}
if (req.DataFilter.VarietyCodes != null && req.DataFilter.VarietyCodes.Any(n => !string.IsNullOrEmpty(n)))
{
req2.SearchModel.VarietyIds = DataCacheProvider.GetVarietyDataSource().AsQueryable()
.Where(n => req.DataFilter.VarietyCodes.Contains(n.VarietyCode)).Select(n => n.id).ToList();
}
req2.SearchModel.TradeTypes = req.DataFilter.TradeTypes;
}
var xActions = CalcDataActionHelper.ConvertToActions(config.xType, config.xRates);
var yActions = CalcDataActionHelper.ConvertToActions(config.yType, config.yRates);
return Calculate(req2, config, xActions, yActions);
}
///
/// 计算
///
public ScenarioCalcResult Calculate(ScenarioRequest req, ScenarioConfigV2 config, List xActions = null, List yActions = null)
{
if (req is null)
{
throw new ArgumentNullException(nameof(req));
}
if (config is null)
{
throw new ArgumentNullException(nameof(config));
}
if (xActions is null)
{
xActions = CalcDataActionHelper.ConvertToActions(config.xType, config.xRates);
}
if (yActions is null)
{
yActions = CalcDataActionHelper.ConvertToActions(config.yType, config.yRates);
}
var valueDate = req.ValueDate >= valuedateBLL.ValueDate ? valuedateBLL.ValueDate : req.ValueDate;
//获取用于计算的交易列表
var tdList = req.SearchModel.PositionDate != null && req.SearchModel.PositionDate < valuedateBLL.ValueDate
? GetEodPosition(req.SearchModel) : GetIntradayPosition(req.SearchModel);
var volProvider = EodVolProviderFactory.GetEodVolProvider(valueDate, req.VolType);
var priceProvider = valueDate == valuedateBLL.ValueDate
? (IEodPriceProvider)new EodPriceProviderAs(DataCacheProvider.GetUnderlyingDataSource())
: new EodPriceProvider(valueDate);
//合并计算结果和标的
var resultSumDic = new Dictionary(StringComparer.OrdinalIgnoreCase);
//合并计算结果和交易
var tradeSumDic = new Dictionary(StringComparer.OrdinalIgnoreCase);
var underlyingSumDic = req.Underlyings == null
? new Dictionary(StringComparer.OrdinalIgnoreCase)
: req.Underlyings.ToDictionary(t => t.UnderlyingCode, v =>
{
v.OriVol = v.ConstVol; v.ConstVol = null;
v.OriDividenRate = v.DividenRate; v.DividenRate = null;
return v;
}, StringComparer.OrdinalIgnoreCase);
var zeroResultSumDic = new Dictionary(StringComparer.OrdinalIgnoreCase);
void setSumDic(trade td, CalcData calcData, underlying_manager underlying, TradeValueResult zeroResult, Dictionary sumDic, int x, int y, string key)
{
if (!sumDic.TryGetValue(key, out var sumResult))
{
sumDic[key] = sumResult = new ScenarioCalcResultItem
{
TradeId = td.id,
xIndex = x,
yIndex = y,
UnderlyingCode = td.UnderlyingCode,
UnderlyingPrice = calcData.SpotPrice.ToString(SpotPriceFormat)
};
}
var clone = calcData.Clone();
xActions[x].UpdateCalcData(clone);
yActions[y].UpdateCalcData(clone);
var callResult = GetCallResult(td, clone);
sumResult.AddValueResult(callResult, underlying.ContractSize, zeroResult.Pv, zeroResult.Delta);
}
// 矩阵计算
var matrixCalc = new Action((trade td, CalcData calcData, underlying_manager underlying, TradeValueResult zeroResult) =>
{
for (var x = 0; x < xActions.Count; x++)
{
for (var y = 0; y < yActions.Count; y++)
{
var key = $"{td.UnderlyingCode}#{x}_{y}";
setSumDic(td, calcData, underlying, zeroResult, resultSumDic, x, y, key);
if (td.id > 0)
{
key = $"{td.id}#{x}_{y}";
setSumDic(td, calcData, underlying, zeroResult, tradeSumDic, x, y, key);
}
}
}
});
var exchangeOptionPriceProvider = new Lazy(() => new EodExchangeOptionPriceProvider(valueDate, true));
foreach (var td in tdList)
{
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
if (underlying == null)
{
continue;
}
td.TradeAmount = td.Notional / underlying.CountRatio;
td.UnderlyingAssetClass = underlying.UnderlyingType;
td.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
if (td.MaturityDate == null)
{
td.MaturityDate = underlying.MaturityDate;
}
double? vol = null;
double dividenRate;
if (underlyingSumDic.TryGetValue(td.UnderlyingCode, out var scenarioUnderlying))
{
vol = scenarioUnderlying.OriVol;
if (scenarioUnderlying.OriDividenRate.HasValue)
{
dividenRate = scenarioUnderlying.OriDividenRate.Value;
}
else
{
dividenRate = req.UseTradeDivendRate && td.TradeType != "场内期权" ? (td.DividendRate ?? 0) : (underlying.DividendRate ?? 0);
}
if (!scenarioUnderlying.UnderlyingPrice.HasValue)
{
scenarioUnderlying.UnderlyingPrice = priceProvider.GetPrice(td.UnderlyingCode, td.SettlementType);
}
}
else
{
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
underlyingSumDic[td.UnderlyingCode] = scenarioUnderlying = new ScenarioUnderlyingModel
{
UnderlyingCode = td.UnderlyingCode,
//DividenRate = underlying.DividendRate ?? 0,
UnderlyingPrice = priceProvider.GetPrice(td.UnderlyingCode, td.SettlementType),
VarietyCode = variety?.VarietyCode ?? underlying.CommodityCode
};
dividenRate = req.UseTradeDivendRate && td.TradeType != "场内期权" ? (td.DividendRate ?? 0) : (underlying.DividendRate ?? 0);
}
if (ConsTrade.TradeTypesForHedge.Contains(td.TradeType))
{
if (td.TradeType == "场内期权")
{
var exchangeOption = DataCacheProvider.GetExchangeListOptionDataSource().GetData(td.ExchangeOptionCode);
if (exchangeOption == null)
{
continue;
}
td.ExerciseDate = exchangeOption.MaturityDate;
td.ExerciseMode = exchangeOption.ExerciseMode;
td.OptionType = exchangeOption.OptionType;
td.Strike = exchangeOption.Strike;
td.TradeDate = td.StartDate = exchangeOption.OpenDate;
if (!vol.HasValue)
{
string userGroup = null;
if (ConsUserGroup.HasGroup)
{
userGroup = DataCacheProvider.GetAssetUnitDataSource().GetData(td.AssetId)?.UserGroup;
}
if (req.VolType == "对冲" || req.VolType == "持仓")
{
var request = new ExchangeOptionVolRequest
{
ValueDate = valueDate,
OptionCode = td.ExchangeOptionCode,
UnderlyingPrice = scenarioUnderlying.UnderlyingPrice.Value,
ExchangeOptionPriceProvider = exchangeOptionPriceProvider,
UserGroup = userGroup,
VolType = req.VolType
};
vol = new ExchangeOptionVolQueryService(OptUser).GetVolValue(request).volValue ?? ConsVolInfos.defVol;
}
else
{
vol = volProvider.GetVol(td, scenarioUnderlying.UnderlyingPrice.Value);
}
}
}
}
else
{
tradeBLL.SetFieldsByTradeType(td);
if (!vol.HasValue)
{
vol = volProvider.GetVol(td, scenarioUnderlying.UnderlyingPrice.Value);
}
}
var calcData = new CalcData
{
ValueDate = req.ValueDate,
SpotPrice = scenarioUnderlying.UnderlyingPrice ?? 0,
DividenRate = dividenRate,
RiskFreeRate = req.RiskFreeRate,
Vol = vol ?? ConsGlobal.DefaultVol,
PricingRequest = PricingRequest.Pv | PricingRequest.Delta | PricingRequest.Gamma | PricingRequest.Theta | PricingRequest.Vega
};
scenarioUnderlying.SetVol(calcData.Vol);
scenarioUnderlying.SetDividenRate(calcData.DividenRate);
var clone = calcData.Clone();
clone.PricingRequest = PricingRequest.Pv | PricingRequest.Delta;
var zeroResult = GetCallResult(td, clone) ?? new TradeValueResult(false) { ErrorMessage = "未计算出结果" };
// 合计zeroResult
if (!zeroResultSumDic.TryGetValue(td.UnderlyingCode, out var zeroSumResult))
{
zeroResultSumDic[td.UnderlyingCode] = zeroSumResult = new ScenarioCalcResultItem()
{
UnderlyingCode = td.UnderlyingCode,
UnderlyingPrice = calcData.SpotPrice.ToString(SpotPriceFormat)
};
}
zeroSumResult.AddValueResult(zeroResult, underlying.ContractSize, zeroResult.Pv, zeroResult.Delta);
// 矩阵计算
matrixCalc(td, calcData, underlying, zeroResult);
//for (var x = 0; x < xActions.Count; x++)
//{
// for (var y = 0; y < yActions.Count; y++)
// {
// var key = $"{td.UnderlyingCode}#{x}_{y}";
// if (!resultSumDic.TryGetValue(key, out var sumResult))
// {
// resultSumDic[key] = sumResult = new ScenarioCalcResultItem
// {
// xIndex = x,
// yIndex = y,
// UnderlyingCode = td.UnderlyingCode,
// UnderlyingPrice = calcData.SpotPrice.ToString(SpotPriceFormat)
// };
// }
// clone = calcData.Clone();
// xActions[x].UpdateCalcData(clone);
// yActions[y].UpdateCalcData(clone);
// var callResult = GetCallResult(td, clone);
// sumResult.AddValueResult(callResult, underlying.ContractSize, zeroResult.Pv, zeroResult.Delta);
// }
//}
}
#region 宏源-完全对冲假设
var actionType = "标的价格";
var hedgeResuts = zeroResultSumDic.Values.Where(b => b.Delta != 0).Select(g => new { g.UnderlyingCode, g.Delta }).Distinct();
if (req.IsFullyHedged
&& PS.Config.Company == CompanyEnum.宏源
&& hedgeResuts != null && hedgeResuts.Count() > 0
&& (xActions.Exists(x => x.Type == actionType) || xActions.Exists(x => x.Type == actionType)))
{
foreach (var resultSum in hedgeResuts.ToArray())
{
#region 添加对冲场内交易,并计算矩阵风险参数
string underlyingCode = resultSum.UnderlyingCode;
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
var td = new trade()
{
TradeType = "股票", // 股票、商品期货、商品现货 都可以作为对冲类型
Notional = resultSum.Delta * (-1),
UnderlyingCode = underlyingCode,
};
underlyingSumDic.TryGetValue(underlyingCode, out var scenarioUnderlying);
var calcData = new CalcData
{
SpotPrice = scenarioUnderlying?.UnderlyingPrice ?? 0,
};
var clone = calcData.Clone();
var zeroResult = GetCallResult(td, clone) ?? new TradeValueResult(false) { ErrorMessage = "未计算出结果" };
matrixCalc(td, calcData, underlying, zeroResult);
#endregion
}
}
#endregion
return new ScenarioCalcResult
{
ConfigName = config.ConfigName,
ResultItems = resultSumDic.Values,
Underlyings = underlyingSumDic.Values,
Trades = tradeSumDic.Values,
xType = config.xType,
yType = config.yType,
xRates = xActions.Select(x => x.GetRateShow()).ToArray(),
yRates = yActions.Select(y => y.GetRateShow()).ToArray()
};
}
//情景分析配置转换为可执行的action
private (List xActions, List yActions) GetCalcActions(int configId, out ScenarioConfigV2 config)
{
config = DbContext.ScenarioConfig.Find(configId);
if (config == null)
{
throw new ServiceException("没有找到情景分析配置");
}
var xActions = CalcDataActionHelper.ConvertToActions(config.xType, config.xRates);
var yActions = CalcDataActionHelper.ConvertToActions(config.yType, config.yRates);
return (xActions, yActions);
}
//获取计算结果
private TradeValueResult GetCallResult(trade td, CalcData calcData)
{
if (td.TradeType == "商品期货" || td.TradeType == "股票" || td.TradeType == "商品现货" || td.TradeType == "信用债")
{
var pv = td.Notional * calcData.SpotPrice;
return new TradeValueResult { Pv = pv, Delta = td.Notional, DeltaCash = pv };
}
if (td.TradeType == "彩虹期权")
{
throw new NotSupportedException("情景分析 暂不支持彩虹期权");
}
if (td.TradeType == "自定义交易")
{
//这个错误不会报出因为数据选取中已经过滤掉此种类型的交易
throw new NotSupportedException("情景分析 暂不支持自定义交易");
}
using (var mp = new MarketProxy(calcData.ValueDate, calcData.RiskFreeRate))
{
var calcReq = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate())
{
pricingRequest = calcData.PricingRequest,
spotPrices = new[] { calcData.SpotPrice },
vols = new[] { calcData.Vol },
calcScenario = CalcScenarioEnum.ScenarioCalc,
ParamOverride = p =>
{
p.riskFreeRate = calcData.RiskFreeRate;
p.dividendRate = calcData.DividenRate;
}
};
var result = OptionCalculatorV2.GetOptionValueResult(mp, td, calcReq, out _);
return result;
}
}
///
/// 计算用的数据
///
public class CalcData
{
public DateTime ValueDate;
public double Vol;
public double SpotPrice;
public double RiskFreeRate;
public double DividenRate;
public PricingRequest PricingRequest;
public CalcData Clone()
{
return (CalcData)MemberwiseClone();
}
public override string ToString()
{
return $"{ValueDate}--vol:{Vol}--spot:{SpotPrice}--riskfreerate:{RiskFreeRate}--dividen:{DividenRate}";
}
}
///
/// 情景分析配置转换为可执行的action
///
public class CalcDataAction
{
string _value;
readonly Action _action;
public CalcDataAction(string type, string rate, Action action)
{
Type = type;
Rate = rate;
_action = action;
}
public string Type { get; }
public string Rate { get; }
public CalcData UpdateCalcData(CalcData data)
{
_action?.Invoke(data);
switch (Type)
{
case "估值日期":
if (_value == null)
{
_value = data.ValueDate.ToString("yyyy-MM-dd");
}
else if (_value != "##" && _value != data.ValueDate.ToString("yyyy-MM-dd"))
{
_value = "##";
}
break;
case "标的价格":
if (_value == null)
{
_value = data.SpotPrice.ToString(SpotPriceFormat);
}
else if (_value != "##" && _value != data.SpotPrice.ToString(SpotPriceFormat))
{
_value = "##";
}
break;
case "波动率":
if (_value == null)
{
_value = data.Vol.ToString(RatePercentFormat);
}
else if (_value != "##" && _value != data.Vol.ToString(RatePercentFormat))
{
_value = "##";
}
break;
case "分红率":
if (_value == null)
{
_value = data.DividenRate.ToString(RatePercentFormat);
}
else if (_value != "##" && _value != data.DividenRate.ToString(RatePercentFormat))
{
_value = "##";
}
break;
case "无风险利率":
if (_value == null)
{
_value = data.RiskFreeRate.ToString(RatePercentFormat);
}
else if (_value != "##" && _value != data.RiskFreeRate.ToString(RatePercentFormat))
{
_value = "##";
}
break;
}
return data;
}
public string GetRateShow()
{
return _value == "##" ? Rate : $"{_value}({Rate})";
}
public override string ToString()
{
return GetRateShow();
}
}
///
/// 将情景分析配置转换为可执行的action
///
class CalcDataActionHelper
{
public static List ConvertToActions(string type, string rates)
{
switch (type)
{
case "估值日期":
return GetDateActions(type, rates);
case "标的价格":
return GetRateActions(type, rates, x => x.SpotPrice, false);
case "波动率":
return GetRateActions(type, rates, x => x.Vol, true);
case "分红率":
return GetRateActions(type, rates, x => x.DividenRate, true);
case "无风险利率":
return GetRateActions(type, rates, x => x.RiskFreeRate, true);
default: return null;
}
}
private static List GetDateActions(string type, string rates)
{
var strArr = (rates ?? string.Empty).Split(new[] { ',', ',' });
var list = new List(strArr.Length);
foreach (var s in strArr)
{
if (int.TryParse(s, out var days))
{
var strDays = (days < 0 ? string.Empty : "+") + days.ToString();
if (!list.Any(n => n.Rate == strDays))
{
list.Add(new CalcDataAction(type, strDays, x => x.ValueDate = QdpCalendarHelper.BizDayShift(x.ValueDate, days)));
}
}
}
if (list.Count < 1)
{
list.Add(new CalcDataAction(type, "0", null));
}
return list;
}
private static Action GetRateAction(double rate, Expression> propExp, bool rateAdd)
{
var assign = rateAdd
? Expression.AddAssign(propExp.Body, Expression.Constant(rate, typeof(double)))
: Expression.MultiplyAssign(propExp.Body, Expression.Constant(1 + rate, typeof(double)));
return Expression.Lambda>(assign, propExp.Parameters).Compile();
}
private static List GetRateActions(string type, string rates
, Expression> propExp, bool rateAdd)
{
var strArr = (rates ?? string.Empty).Split(new[] { ',', ',' });
var list = new List(strArr.Length);
foreach (var s in strArr)
{
if (NumberHelper.TryParse(s, out var rate, out _))
{
var strRate = (rate < 0 ? string.Empty : "+") + rate.ToString("0.####%");
if (!list.Any(n => n.Rate == strRate))
{
var action = GetRateAction(rate, propExp, rateAdd);
list.Add(new CalcDataAction(type, strRate, action));
}
}
}
if (list.Count < 1)
{
list.Add(new CalcDataAction(type, "0", null));
}
return list;
}
}
}
}