686 lines
31 KiB
C#
686 lines
31 KiB
C#
using Qdp.Foundation.Implementations;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using Qdp.Pricing.Base.Interfaces;
|
|
using Qdp.Pricing.Base.Utilities;
|
|
using Qdp.Pricing.Library.Common.Base;
|
|
using Qdp.Pricing.Library.Options.Products.Accumulator;
|
|
using Qdp.Pricing.Library.Options.Products.Airbag;
|
|
using Qdp.Pricing.Library.Options.Products.Asian;
|
|
using Qdp.Pricing.Library.Options.Products.AsianSyntheticSpread;
|
|
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
|
|
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
|
|
using Qdp.Pricing.Library.Options.Products.Barrier;
|
|
using Qdp.Pricing.Library.Options.Products.Binary;
|
|
using Qdp.Pricing.Library.Options.Products.DoubleSharkFin;
|
|
using Qdp.Pricing.Library.Options.Products.PayoffEnhance;
|
|
using Qdp.Pricing.Library.Options.Products.Rainbow;
|
|
using Qdp.Pricing.Library.Options.Products.RangeAccrual;
|
|
using Qdp.Pricing.Library.Options.Products.Spread;
|
|
using Qdp.Pricing.Library.Options.Products.SyntheticSpread;
|
|
using Qdp.Pricing.Library.Options.Products.Vanilla;
|
|
using YLErp.BLL;
|
|
using YLErp.BLL.Calculation.V2;
|
|
using YLErp.Modules.TradeModule;
|
|
using YLErp.Modules.VolatilityModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.CalculationModule
|
|
{
|
|
public class OptionTradeAnalysisService : YLBaseService
|
|
{
|
|
public OptionTradeAnalysisService(OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 为一组期权交易计算到期时在不同价格条件下的总体payoff情况
|
|
/// </summary>
|
|
/// <param name="optionTrades"></param>
|
|
/// <returns></returns>
|
|
public List<CurvePoint> GetTradesPayoffLine(IEnumerable<OtcOptionTradeFull> optionTrades)
|
|
{
|
|
if (optionTrades == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var curvePoints = new List<CurvePoint>();
|
|
var keyPricePoints = new List<double>();
|
|
|
|
if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0)
|
|
{
|
|
throw new Exception($"暂时不支持Autocall的分析");
|
|
}
|
|
|
|
var underlyingIds = optionTrades.Select(n => n.UnderlyingId).ToHashSet();
|
|
if (underlyingIds.Count > 1)
|
|
{
|
|
throw new Exception($"待分析的组合交易需要有相同的标的资产");
|
|
}
|
|
|
|
var underlying = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(n => underlyingIds.Contains(n.id));
|
|
if (underlying == null)
|
|
{
|
|
throw new Exception($"找不到标的资产{optionTrades.First().UnderlyingCode}的信息,id为{optionTrades.First().UnderlyingId}");
|
|
}
|
|
|
|
var options = new List<OptionBase>();
|
|
foreach (var otcTrade in optionTrades)
|
|
{
|
|
var trade = TradeConverter.ConvertOptionTrade(otcTrade);
|
|
|
|
if (otcTrade.TradeType == "Risky期权")
|
|
{
|
|
var _options = GetToQdpOptionRisk(trade, underlying);
|
|
if (_options != null)
|
|
{
|
|
options.AddRange(_options);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var option = ToQdpOption(trade, underlying);
|
|
if (option != null)
|
|
{
|
|
options.Add(option);
|
|
}
|
|
}
|
|
var points = GetKeyPoints(trade);
|
|
if (points != null)
|
|
{
|
|
keyPricePoints.AddRange(points);
|
|
}
|
|
}
|
|
|
|
keyPricePoints = keyPricePoints.Distinct().ToList();
|
|
keyPricePoints.Sort();
|
|
keyPricePoints.Insert(0, keyPricePoints.First() * 0.8);
|
|
keyPricePoints.Add(keyPricePoints.Last() * 1.2);
|
|
|
|
var prices = new double[] { 0.0 };
|
|
foreach (var price in keyPricePoints)
|
|
{
|
|
prices[0] = price;
|
|
curvePoints.Add(new CurvePoint()
|
|
{
|
|
X = price,
|
|
Y = options.Sum(x =>
|
|
{
|
|
if (x is BarrierOption barrier)
|
|
{
|
|
//障碍期权在GetPayoff方法中会更改BarrierStatus值,所以需要重置
|
|
barrier.BarrierStatus = Qdp.Pricing.Base.Enums.BarrierStatus.Monitoring;
|
|
}
|
|
return x.GetPayoff(prices)[0].PaymentAmount;
|
|
})
|
|
});
|
|
}
|
|
|
|
return curvePoints;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 为一组期权交易计算不同价格条件下的Pv
|
|
/// </summary>
|
|
public List<CurvePoint> GetTradesPvLine(IEnumerable<OtcOptionTradeFull> optionTrades)
|
|
{
|
|
if (optionTrades == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (!optionTrades.Any())
|
|
{
|
|
return new List<CurvePoint>(0);
|
|
}
|
|
|
|
var curvePoints = new List<CurvePoint>();
|
|
var keyPricePoints = new List<double>();
|
|
|
|
if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0)
|
|
{
|
|
throw new Exception($"暂时不支持Autocall的分析");
|
|
}
|
|
|
|
var underlyingCount = optionTrades.Select(n => n.UnderlyingCode?.ToLowerInvariant()).Distinct().Count();
|
|
if (underlyingCount > 1)
|
|
{
|
|
throw new Exception($"待分析的组合交易需要有相同的标的资产");
|
|
}
|
|
|
|
var trades = new List<trade>();
|
|
|
|
foreach (var otcTrade in optionTrades)
|
|
{
|
|
var trade = TradeConverter.ConvertOptionTrade(otcTrade);
|
|
if (trade != null)
|
|
{
|
|
trades.Add(trade);
|
|
}
|
|
|
|
var points = GetKeyPoints(trade);
|
|
if (points != null)
|
|
{
|
|
keyPricePoints.AddRange(points);
|
|
}
|
|
}
|
|
|
|
return GetTradesPvLineForKeyPoints(optionTrades.First().TradeDate.Value, trades, keyPricePoints);
|
|
}
|
|
|
|
private List<CurvePoint> GetTradesPvLineForKeyPoints(DateTime valueDate, IEnumerable<trade> trades, List<double> keyPoints)
|
|
{
|
|
var curvePoints = new List<CurvePoint>();
|
|
|
|
if (trades == null || trades.Count() == 0)
|
|
{
|
|
return curvePoints;
|
|
}
|
|
|
|
var startPrice = keyPoints.Min() > 0 ? keyPoints.Min() * 0.8 : keyPoints.Min() * 1.2;
|
|
var endPrice = keyPoints.Min() > 0 ? keyPoints.Max() * 1.2 : keyPoints.Max() * 0.8;
|
|
var step = (endPrice - startPrice) / 20.0;
|
|
|
|
var price = startPrice;
|
|
|
|
var calcReq = new OptionValueCalcRequest(valuedateBLL.SysRiskFreeRate())
|
|
{
|
|
correlations = null,
|
|
engineName = null,
|
|
maturityShift = 0,
|
|
ParamOverride = null,
|
|
preciseTimeMode = false,
|
|
pricingRequest = PricingRequest.Pv
|
|
};
|
|
|
|
while (price < endPrice + step)
|
|
{
|
|
var pv = 0.0;
|
|
|
|
foreach (var trade in trades)
|
|
{
|
|
//场内期权交易不会有开仓波动率,因此根据其交易价格计算出隐含波动率
|
|
if (trade.TradeType == "场内期权" && !trade.TradeOpenVolatility.HasValue)
|
|
{
|
|
trade.VolType = "交易";
|
|
trade.TradeOpenVolatility = VolatilityHelper.GetImpliedVol(trade.TradeDate ?? valuedateBLL.ValueDate, trade, trade.TTMDays, trade.SpotPrice ?? 0, true);
|
|
}
|
|
|
|
if (!PS.Config.IsTradeVol && !trade.TradeOpenVolatility.HasValue)
|
|
{
|
|
trade.TradeOpenVolatility = trade.Vol;
|
|
}
|
|
|
|
if (!trade.TradeOpenVolatility.HasValue || double.IsNaN(trade.TradeOpenVolatility.Value))
|
|
{
|
|
throw new Exception("无法获取开仓波动率");
|
|
}
|
|
|
|
calcReq.spotPrices = new[] { price };
|
|
calcReq.vols = new[] { trade.TradeOpenVolatility.Value };
|
|
|
|
var result = OptionCalculatorV2.GetOptionValueResult(valueDate, trade, calcReq, out _);
|
|
|
|
if (double.IsNaN(result.Pv))
|
|
{
|
|
throw new Exception($"计算此交易失败:{trade.UnderlyingCode},{trade.TradeType}");
|
|
}
|
|
|
|
pv += result.Pv;
|
|
}
|
|
|
|
curvePoints.Add(new CurvePoint()
|
|
{
|
|
X = price,
|
|
Y = pv
|
|
});
|
|
|
|
price += step;
|
|
}
|
|
|
|
return curvePoints;
|
|
}
|
|
|
|
public List<CurvePoint> GetTradesPvLine2(int tradeId)
|
|
{
|
|
var trade = DbContext.trade.FirstOrDefault(t => t.id == tradeId);
|
|
if (trade == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
List<trade> trades;
|
|
if (trade.TradeType == "结构化交易")
|
|
{
|
|
trades = DbContext.trade.Where(t => t.ParentTradeId == trade.id).ToList();
|
|
}
|
|
else
|
|
{
|
|
trades = new List<trade> { trade };
|
|
}
|
|
|
|
var tradebll = new tradeBLL();
|
|
trades.ForEach(t => tradeBLL.SetFieldsByTradeType(t));
|
|
|
|
return GetTradesPvLine2(valuedateBLL.ValueDate, trades);
|
|
}
|
|
|
|
private List<CurvePoint> GetTradesPvLine2(DateTime valueDate, List<trade> optionTrades)
|
|
{
|
|
if (optionTrades == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0)
|
|
{
|
|
throw new Exception($"暂时不支持Autocall的分析");
|
|
}
|
|
|
|
var underlyingCount = optionTrades.Select(n => n.UnderlyingCode.ToLowerInvariant()).Distinct().Count();
|
|
if (underlyingCount > 1)
|
|
{
|
|
throw new Exception($"待分析的组合交易需要有相同的标的资产");
|
|
}
|
|
|
|
var keyPricePoints = new List<double>();
|
|
|
|
optionTrades.ForEach(t =>
|
|
{
|
|
keyPricePoints.AddRange(GetKeyPoints(t));
|
|
});
|
|
|
|
return GetTradesPvLineForKeyPoints(valueDate, optionTrades, keyPricePoints);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算交易在一半ttm时候的Pv曲线
|
|
/// </summary>
|
|
public List<CurvePoint> GetTradesHalflifePvLine(IEnumerable<OtcOptionTradeFull> optionTrades)
|
|
{
|
|
var dayCount = valuedateBLL.TradeDayCount.ToDayCountImpl();
|
|
foreach (var trade in optionTrades)
|
|
{
|
|
trade.ExerciseDate = AdjustToHalfLifeMaturity(dayCount, trade.TradeDate, trade.ExerciseDate);
|
|
trade.TTMDays = double.NaN;
|
|
}
|
|
|
|
return GetTradesPvLine(optionTrades);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 返回当前、一半ttm、以及在到期时的Pv曲线
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public List<List<CurvePoint>> GetTradePvLifeLine(IEnumerable<OtcOptionTradeFull> optionTrades)
|
|
{
|
|
var results = new List<List<CurvePoint>>();
|
|
|
|
var pvLine = GetTradesPvLine(optionTrades);
|
|
results.Add(pvLine);
|
|
|
|
var payoffPoints = GetTradesPayoffLine(optionTrades);
|
|
//按Pv曲线的X点对齐
|
|
//int start = -1, end = 0;
|
|
//var payoffLine = new List<CurvePoint>();
|
|
//for (var i = 0; i < pvLine.Count; ++i)
|
|
//{
|
|
// if (pvLine[i].X == payoffPoints[end].X)
|
|
// {
|
|
// payoffLine.Add(payoffPoints[end]);
|
|
// ++start;
|
|
// ++end;
|
|
// }
|
|
// else
|
|
// {
|
|
// payoffLine.Add(new CurvePoint() { X = pvLine[i].X, Y = simpleInterpolate(payoffPoints[start].X, payoffPoints[end].X, payoffPoints[start].Y, payoffPoints[end].Y, pvLine[i].X) });
|
|
// }
|
|
//}
|
|
results.Add(payoffPoints);
|
|
results.Add(GetTradesHalflifePvLine(optionTrades));
|
|
|
|
return results;
|
|
}
|
|
|
|
private DateTime? AdjustToHalfLifeMaturity(IDayCount dayCount, DateTime? tradeDate, DateTime? maturityDate)
|
|
{
|
|
var qdpStart = new Date(tradeDate.Value);
|
|
var qdpEnd = new Date(maturityDate.Value);
|
|
var half = dayCount.CalcDayCountFraction(qdpStart, qdpEnd) / 2.0;
|
|
qdpEnd = dayCount.CalcEndDateFromDayCountFraction(qdpStart, half, null, null);
|
|
return qdpEnd.DateTime;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 为一组期权交易计算随时间变化的希腊字母变化
|
|
/// </summary>
|
|
/// <param name="optionTrades"></param>
|
|
/// <returns></returns>
|
|
public Dictionary<string, List<CurvePoint>> GetTradesGreeksForLifetime(IEnumerable<OtcOptionTradeFull> optionTrades)
|
|
{
|
|
if (optionTrades == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var curvePoints = new List<CurvePoint>();
|
|
|
|
if (optionTrades.Count(t => t.TradeType == "雪球期权" || t.TradeType == "凤凰期权") > 0)
|
|
{
|
|
throw new Exception($"暂时不支持Autocall的分析");
|
|
}
|
|
|
|
var underlyingIds = optionTrades.Select(n => n.UnderlyingId).ToHashSet();
|
|
if (underlyingIds.Count > 1)
|
|
{
|
|
throw new Exception($"待分析的组合交易需要有相同的标的资产");
|
|
}
|
|
|
|
var underlying = DbContext.underlying_manager.AsNoTracking().FirstOrDefault(n => underlyingIds.Contains(n.id));
|
|
if (underlying == null)
|
|
{
|
|
throw new Exception($"找不到标的资产{optionTrades.First().UnderlyingCode}的信息,id为{optionTrades.First().UnderlyingId}");
|
|
}
|
|
var options = new List<OptionBase>();
|
|
var trades = new List<trade>();
|
|
foreach (var otcTrade in optionTrades)
|
|
{
|
|
var trade = TradeConverter.ConvertOptionTrade(otcTrade);
|
|
if (trade != null)
|
|
{
|
|
trades.Add(trade);
|
|
}
|
|
}
|
|
|
|
var minStart = trades.Min(t => t.TradeDate.Value);
|
|
//避开最后一天的计算,在到期日当天会出现一些跟时间相关的结果,会混淆曲线的整体趋势
|
|
var maxMaturity = CalendarImpl.Get("chn").PrevBizDay(new Date(trades.Max(t => t.ExerciseDate.Value)));
|
|
|
|
var valueDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(new Date(minStart), new Date(maxMaturity));
|
|
var userId = Guid.NewGuid().ToString();
|
|
var results = new Dictionary<string, List<CurvePoint>>();
|
|
results["Pv"] = new List<CurvePoint>();
|
|
results["Delta"] = new List<CurvePoint>();
|
|
results["Gamma"] = new List<CurvePoint>();
|
|
results["Vega"] = new List<CurvePoint>();
|
|
results["Theta"] = new List<CurvePoint>();
|
|
for (var i = 0; i < valueDates.Count; ++i)
|
|
{
|
|
double pv = 0.0, delta = 0.0, gamma = 0.0, vega = 0.0, theta = 0.0;
|
|
foreach (var trade in trades)
|
|
{
|
|
trade.TradeDate = valueDates[i].DateTime;
|
|
trade.TTMDays = double.NaN;
|
|
underlying.QuotationDate = trade.TradeDate;
|
|
|
|
if (!PS.Config.IsTradeVol && !trade.TradeOpenVolatility.HasValue)
|
|
{
|
|
trade.TradeOpenVolatility = trade.Vol;
|
|
}
|
|
if (!trade.TradeOpenVolatility.HasValue || double.IsNaN(trade.TradeOpenVolatility.Value))
|
|
{
|
|
throw new Exception("无法获取开仓波动率");
|
|
}
|
|
|
|
var result = ValueCalculator.GetOptionValueResultV2(
|
|
userId,
|
|
underlying,
|
|
trade,
|
|
new double[] { trade.TradeOpenVolatility.Value },
|
|
new double[] { trade.SpotPrice.Value },
|
|
request: QdpPricingRequest.BASIC_GREEKS);
|
|
|
|
if (double.IsNaN(result.Pv))
|
|
{
|
|
throw new Exception($"计算此交易失败:{trade.UnderlyingCode},{trade.TradeType}");
|
|
}
|
|
pv += result.Pv;
|
|
delta += result.Delta;
|
|
gamma += result.Gamma;
|
|
vega += result.Vega;
|
|
theta += result.Theta;
|
|
}
|
|
|
|
results["Pv"].Add(new CurvePoint() { X = i, Y = pv });
|
|
results["Delta"].Add(new CurvePoint() { X = i, Y = delta });
|
|
results["Gamma"].Add(new CurvePoint() { X = i, Y = gamma });
|
|
results["Vega"].Add(new CurvePoint() { X = i, Y = vega });
|
|
results["Theta"].Add(new CurvePoint() { X = i, Y = theta });
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获得一笔期权影响payoff的价格点,如行权价、障碍价格等
|
|
/// </summary>
|
|
/// <param name="trade"></param>
|
|
/// <returns></returns>
|
|
private IEnumerable<double> GetKeyPoints(trade trade)
|
|
{
|
|
switch (trade.TradeType)
|
|
{
|
|
case "香草期权":
|
|
case "场内期权":
|
|
case "亚式期权":
|
|
case "彩虹期权":
|
|
case "合成价差期权":
|
|
case "亚式合成价差期权":
|
|
case "收益增强结构":
|
|
case "区间累积期权":
|
|
case "价差期权":
|
|
case "凤凰期权":
|
|
case "雪球期权":
|
|
case "气囊结构":
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value
|
|
};
|
|
case "累计期权":
|
|
if (trade.trade_accumulator_option.AccumulatorStructureType == AccumulatorStructureTypeEnum.Segmented)
|
|
{
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_accumulator_option.Strike2.Value * trade.SpotPrice.Value : trade.trade_accumulator_option.Strike2.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_accumulator_option.Strike3.Value * trade.SpotPrice.Value : trade.trade_accumulator_option.Strike3.Value,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value
|
|
};
|
|
}
|
|
case "障碍期权":
|
|
if (trade.trade_barrier_option.BarrierTypeEn.StartsWith("Double"))
|
|
{
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value* trade.SpotPrice.Value : trade.trade_barrier_option.BarrierPrice.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value* trade.SpotPrice.Value + 0.01 : trade.trade_barrier_option.BarrierPrice.Value + 0.01,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.UpperBarrierPrice.Value * trade.SpotPrice.Value : trade.trade_barrier_option.UpperBarrierPrice.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.UpperBarrierPrice.Value * trade.SpotPrice.Value - 0.01 : trade.trade_barrier_option.UpperBarrierPrice.Value - 0.01
|
|
};
|
|
}
|
|
else
|
|
{
|
|
if (trade.trade_barrier_option.BarrierTypeEn.StartsWith("Up"))
|
|
{
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value : trade.trade_barrier_option.BarrierPrice.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value - 0.01 : trade.trade_barrier_option.BarrierPrice.Value - 0.01
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value : trade.trade_barrier_option.BarrierPrice.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_barrier_option.BarrierPrice.Value * trade.SpotPrice.Value + 0.01 : trade.trade_barrier_option.BarrierPrice.Value + 0.01
|
|
};
|
|
}
|
|
}
|
|
case "二元期权":
|
|
if (trade.ExerciseMode == "American" && trade.trade_binary_option.PayoffType.StartsWith("Double"))
|
|
{
|
|
var low = trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value;
|
|
var high = trade.IsMoneynessOptionData ? trade.trade_binary_option.UpperBarrier.Value * trade.SpotPrice.Value : trade.trade_binary_option.UpperBarrier.Value;
|
|
return new double[] {
|
|
low - 0.01,
|
|
low,
|
|
low + 0.01,
|
|
high - 0.01,
|
|
high,
|
|
high + 0.01
|
|
};
|
|
}
|
|
else
|
|
{
|
|
var low = trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value;
|
|
return new double[] {
|
|
low - 0.01,
|
|
low,
|
|
low + 0.01
|
|
};
|
|
}
|
|
case "双鲨期权":
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.Strike.Value * trade.SpotPrice.Value : trade.Strike.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.StrikeHigh.Value * trade.SpotPrice.Value : trade.trade_double_sharkfin_option.StrikeHigh.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierHigh * trade.SpotPrice.Value : trade.trade_double_sharkfin_option.BarrierHigh,
|
|
trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierHigh * trade.SpotPrice.Value - 0.01 : trade.trade_double_sharkfin_option.BarrierHigh - 0.01,
|
|
trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierLow * trade.SpotPrice.Value : trade.trade_double_sharkfin_option.BarrierLow,
|
|
trade.IsMoneynessOptionData ? trade.trade_double_sharkfin_option.BarrierLow * trade.SpotPrice.Value + 0.01 : trade.trade_double_sharkfin_option.BarrierLow + 0.01,
|
|
};
|
|
case "Risky期权":
|
|
return new double[] {
|
|
trade.IsMoneynessOptionData ? trade.trade_risky_option.Strike1.Value * trade.SpotPrice.Value : trade.trade_risky_option.Strike1.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_risky_option.Strike2.Value * trade.SpotPrice.Value : trade.trade_risky_option.Strike2.Value,
|
|
trade.IsMoneynessOptionData ? trade.trade_risky_option.Strike3.Value * trade.SpotPrice.Value : trade.trade_risky_option.Strike3.Value,
|
|
};
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将trade对象转换为Qdp对应的期权类型
|
|
/// </summary>
|
|
private OptionBase ToQdpOption(trade trade, underlying_manager underlying)
|
|
{
|
|
underlying = underlying.Clone();
|
|
|
|
underlying.UnderlyingInstrumentType = ConsGlobal.InstrumentType.ConvertCalcType(underlying.UnderlyingInstrumentType);
|
|
|
|
switch (trade.TradeType)
|
|
{
|
|
case "香草期权":
|
|
return QdpTradeBuilder.GetVanillaOptionTrade(trade, null, false)?.Instrument as VanillaOption;
|
|
case "场内期权":
|
|
return QdpTradeBuilder.GetVanillaOptionTrade(trade, null, true)?.Instrument as VanillaOption;
|
|
case "障碍期权":
|
|
return QdpTradeBuilder.GetBarrierOptionTrade(trade, trade.trade_barrier_option, null)?.Instrument as BarrierOption;
|
|
case "亚式期权":
|
|
return QdpTradeBuilder.GetAsianOptionTrade(trade, trade.trade_asian_option, null)?.Instrument as AsianOption;
|
|
case "二元期权":
|
|
return QdpTradeBuilder.GetBinaryOptionTrade(trade, trade.trade_binary_option, null)?.Instrument as BinaryOption;
|
|
case "彩虹期权":
|
|
return QdpTradeBuilder.GetRainbowOptionTrade(trade, trade.trade_rainbow_option, null)?.Instrument as RainbowOption;
|
|
case "价差期权":
|
|
return QdpTradeBuilder.GetSpreadOptionTrade(trade, trade.trade_spread_option, null, null)?.Instrument as SpreadOption;
|
|
case "合成价差期权":
|
|
return QdpTradeBuilder.GetSSpreadOptionTrade(trade)?.Instrument as SyntheticNormalSpreadOption;
|
|
case "亚式合成价差期权":
|
|
return QdpTradeBuilder.GetAsianSSpreadOptionTrade(trade, trade.trade_asian_option, null)?.Instrument as AsianSyntheticNormalSpreadOption;
|
|
case "双鲨期权":
|
|
return QdpTradeBuilder.GetDoubleSharkFinOptionTrade(trade, trade.trade_double_sharkfin_option, null)?.Instrument as DoubleSharkFinOption;
|
|
case "凤凰期权":
|
|
return QdpTradeBuilder.GetAutocallOptionTrade(trade, trade.trade_autocall)?.Instrument as AutoCall;
|
|
case "雪球期权":
|
|
return QdpTradeBuilder.GetSnowballOptionTrade(trade, trade.trade_snowball)?.Instrument as SimpleSnowball;
|
|
case "区间累积期权":
|
|
return QdpTradeBuilder.GetRangeAccrualTrade(trade, trade.trade_rangeaccrual, null)?.Instrument as RangeAccrual;
|
|
case "累积期权":
|
|
case "累计期权":
|
|
return QdpTradeBuilder.GetAccumulatorOptionTrade(trade, trade.trade_accumulator_option, null)?.Instrument as AccumulatorOption;
|
|
case "气囊结构":
|
|
return QdpTradeBuilder.GetAirbagOptionTrade(trade, trade.trade_airbag, null)?.Instrument as Airbag;
|
|
case "收益增强结构":
|
|
return QdpTradeBuilder.GetUnderlyingEnhanceTrade(trade, trade.trade_underlying_enhance, null)?.Instrument as UnderlyingPayoffEnhance;
|
|
case "Risky期权":
|
|
return QdpTradeBuilder.GetVanillaOptionTrade(trade, null, false)?.Instrument as VanillaOption;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public List<OptionBase> GetToQdpOptionRisk(trade trade, underlying_manager underlying)
|
|
{
|
|
var options = new List<OptionBase>();
|
|
var tradeclone = trade.Clone();
|
|
tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(trade, trade.TradeAmount, 1);
|
|
tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(trade, trade.Notional, underlying.CountRatio);
|
|
|
|
var td1 = tradeclone.Clone();
|
|
if (trade.trade_risky_option.ParticipationRate1 != 0)
|
|
{
|
|
td1.Strike = trade.trade_risky_option.Strike1;
|
|
td1.ParticipationRate = trade.trade_risky_option.ParticipationRate1;
|
|
td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1);
|
|
td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, underlying.CountRatio);
|
|
td1.OptionType = "看跌";
|
|
td1.BuySell = trade.BuySell == "买入" ? "卖出" : "买入";
|
|
var option1 = ToQdpOption(td1, underlying);
|
|
if (option1 != null)
|
|
{
|
|
options.Add(option1);
|
|
}
|
|
}
|
|
|
|
var td2 = tradeclone.Clone();
|
|
if (trade.trade_risky_option.ParticipationRate2 != 0)
|
|
{
|
|
td2.Strike = trade.trade_risky_option.Strike2;
|
|
td2.ParticipationRate = trade.trade_risky_option.ParticipationRate2;
|
|
td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1);
|
|
td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, underlying.CountRatio);
|
|
|
|
var option2 = ToQdpOption(td2, underlying);
|
|
if (option2 != null)
|
|
{
|
|
options.Add(option2);
|
|
}
|
|
}
|
|
|
|
var td3 = tradeclone.Clone();
|
|
//decimal 为了解决精度问题: 0.2-0.3=0.0999999999
|
|
var participationRate3 = (decimal)trade.trade_risky_option.ParticipationRate3 - (decimal)trade.trade_risky_option.ParticipationRate2;
|
|
if (participationRate3 != 0)
|
|
{
|
|
td3.Strike = trade.trade_risky_option.Strike3;
|
|
td3.ParticipationRate = (double?)Math.Abs(participationRate3);
|
|
td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1);
|
|
td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, underlying.CountRatio);
|
|
if (participationRate3 < 0)
|
|
{
|
|
td3.BuySell = trade.BuySell == "买入" ? "卖出" : "买入";
|
|
}
|
|
|
|
var option3 = ToQdpOption(td3, underlying);
|
|
if (option3 != null)
|
|
{
|
|
options.Add(option3);
|
|
}
|
|
}
|
|
|
|
return options;
|
|
}
|
|
}
|
|
|
|
public class CurvePoint
|
|
{
|
|
public double X { get; set; }
|
|
public double Y { get; set; }
|
|
}
|
|
}
|