/// <summary> /// 获取行权收益 /// </summary> /// <param name="price">标的资产价格</param> /// <returns>行权收益</returns> public override Cashflow[] GetPayoff(double[] price) { var payoff = 0.0; var effectiveStrike = IsMoneynessOption? Strike * InitialSpotPrice : Strike; switch (OptionType) { case OptionType.Call: payoff = Math.Max(0.0, price[0] - effectiveStrike); break; case OptionType.Put: payoff = Math.Max(0.0, effectiveStrike - price[0]); break; default: throw new PricingBaseException("Unknow/illegal option type!"); } var actualPaymentDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); return(new[] { new Cashflow(StartDate, UnderlyingMaturityDate, actualPaymentDate, payoff * Notional, PayoffCcy, CashflowType.Net, false, double.NaN, null), //new Cashflow(StartDate, UnderlyingMaturityDate, OptionPremiumPaymentDate ?? actualPaymentDate, OptionPremium, PayoffCcy, CashflowType.Net, true, double.NaN, null) }); }
protected OptionBase(Date startDate, Date maturityDate, OptionExercise exercise, OptionType optionType, double[] strike, InstrumentType underlyingInstrumentType, ICalendar calendar, IDayCount dayCount, CurrencyCode payoffCcy, CurrencyCode settlementCcy, Date[] exerciseDates, Date[] observationDates, double notional = 1.0, DayGap settlementGap = null, Date optionPremiumPaymentDate = null, double optionPremium = 0.0, string[] underlyingTickers = null, bool isMoneynessOption = false, double initialSpotPrice = 0.0, Dictionary <Date, double> dividends = null, bool hasNightMarket = false, bool commodityFuturesPreciseTimeMode = false) { StartDate = startDate; Exercise = exercise; OptionType = optionType; Strike = strike[0]; Strikes = strike; UnderlyingProductType = underlyingInstrumentType; Calendar = calendar; DayCount = dayCount; SettlmentGap = settlementGap ?? new DayGap("+0BD"); UnderlyingMaturityDate = SettlmentGap.Get(Calendar, maturityDate); ExerciseDates = exerciseDates; ObservationDates = observationDates; UnderlyingTickers = underlyingTickers; Notional = notional; PayoffCcy = payoffCcy; SettlementCcy = settlementCcy; OptionPremiumPaymentDate = optionPremiumPaymentDate; OptionPremium = optionPremium; IsMoneynessOption = isMoneynessOption; InitialSpotPrice = initialSpotPrice; Dividends = dividends; HasNightMarket = hasNightMarket; CommodityFuturesPreciseTimeMode = commodityFuturesPreciseTimeMode; }
/// <summary> /// 获取行权收益 /// </summary> /// <param name="pricePath">价格路径</param> /// <returns>行权收益</returns> public override Cashflow[] GetPayoff(Dictionary <Date, double> pricePath) { var prices = new SortedDictionary <Date, double>(pricePath); //merges fixings and monte carlo path foreach (var key in Fixings.Keys) { prices[key] = Fixings[key]; } var simulatedValuesToCheck = ObservationDates.Select(x => prices[x]).ToArray(); var actualPaymentDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); var voidCf = new[] { new Cashflow(StartDate, UnderlyingMaturityDate, actualPaymentDate, Rebate * Notional, PayoffCcy, CashflowType.Net, false, double.NaN, null), new Cashflow(StartDate, UnderlyingMaturityDate, OptionPremiumPaymentDate ?? actualPaymentDate, OptionPremium, PayoffCcy, CashflowType.Net, true, double.NaN, null) }; switch (BarrierType) { case BarrierType.UpAndOut: return(simulatedValuesToCheck.Any(val => val > Barrier) ? voidCf : GetPayoff(new[] { prices.Last().Value })); case BarrierType.UpAndIn: return(simulatedValuesToCheck.Any(val => val > Barrier) ? GetPayoff(new[] { prices.Last().Value }) : voidCf); case BarrierType.DownAndOut: return(simulatedValuesToCheck.Any(val => val < Barrier) ? voidCf : GetPayoff(new[] { prices.Last().Value })); case BarrierType.DownAndIn: return(simulatedValuesToCheck.Any(val => val < Barrier) ? GetPayoff(new[] { prices.Last().Value }) : voidCf); case BarrierType.DoubleTouchOut: return(simulatedValuesToCheck.Any(val => (val > UpperBarrier || val < Barrier)) ? voidCf : GetPayoff(new[] { prices.Last().Value })); case BarrierType.DoubleTouchIn: return(simulatedValuesToCheck.Any(val => (val > UpperBarrier || val < Barrier)) ? GetPayoff(new[] { prices.Last().Value }) : voidCf); default: return(voidCf); } }
public override Cashflow[] GetPayoff(Dictionary <Date, double> pricePath) { var prices = new Dictionary <Date, double>(pricePath); //merges fixings and monte carlo path foreach (var key in Fixings.Keys) { prices[key] = Fixings[key]; } var amount = Ranges.Select( x => 1.0 * x.ObservationDates.Count(date => prices[date] > x.LowerRange && prices[date] < x.UpperRange) * x.BonusRate / x.ObservationDates.Length ).Sum(); var settlementDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); return(new[] { new Cashflow(StartDate, UnderlyingMaturityDate, settlementDate, amount * Notional, SettlementCcy, CashflowType.Net, false, double.NaN, null), new Cashflow(StartDate, UnderlyingMaturityDate, OptionPremiumPaymentDate ?? settlementDate, OptionPremium, SettlementCcy, CashflowType.Net, false, double.NaN, null) }); }
//Todo Fixing payoff public override Cashflow[] GetPayoff(double[] price) { //price:{ minPrice, maxPrice, pricePath[ExerciseDates[0]] } //used in approximation var amount = 0.0; var effectiveStrike = IsMoneynessOption ? Strike * InitialSpotPrice : Strike; if (OptionType == OptionType.Call) { amount = StrikeStyle == StrikeStyle.Fixed ? Math.Max(0.0, price[1] - effectiveStrike) : Math.Max(0.0, price[2] - price[0]); } else if (OptionType == OptionType.Put) { amount = StrikeStyle == StrikeStyle.Fixed ? Math.Max(0.0, effectiveStrike - price[0]) : Math.Max(0.0, price[1] - price[2]); } var settlementDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); return(new[] { new Cashflow(StartDate, UnderlyingMaturityDate, settlementDate, amount * Notional, SettlementCcy, CashflowType.Net, false, double.NaN, null), //new Cashflow(StartDate, UnderlyingMaturityDate, OptionPremiumPaymentDate ?? settlementDate, OptionPremium, SettlementCcy, CashflowType.Net, false, double.NaN, null) }); }
private Cashflow[] GetBondCashflows(IMarketCondition market, bool netted = true, bool calcAi = false) { var accruals = Accruals.ToArray(); var schedulePaymentDates = calcAi ? Accruals.Skip(1).ToArray() : PaymentSchedule.ToArray(); if (schedulePaymentDates.Length == 0) { throw new PricingLibraryException("Number of payments is 0"); } List <CfCalculationDetail[]> cfCalcDetails; var cashflows = new List <Cashflow>(); var prevCfDate = accruals[0]; var coupons = Coupon.GetCoupon( Accruals, market.FixingCurve.HasValue ? market.FixingCurve.Value : null, market.HistoricalIndexRates.HasValue ? market.HistoricalIndexRates.Value : null, out cfCalcDetails, IssueRate, _compensationRate); var refStartDates = new List <Date>(); var refEndDates = new List <Date>(); for (var i = 0; i < coupons.Length; ++i) { var refStartDate = accruals[i]; var refEndDate = accruals[i + 1]; if (i == 0 && !_isRegualDate[i]) { if (PaymentFreq != Frequency.None) { if (Stub == Stub.LongStart || Stub == Stub.ShortStart) { refStartDate = PaymentFreq.GetTerm().Prev(refEndDate); } else { refEndDate = PaymentFreq.GetTerm().Next(refStartDate); } } } refStartDates.Add(refStartDate); refEndDates.Add(refEndDate); } IAmortization amortization; if (AmortizationType == AmortizationType.EqualPrincipal || AmortizationType == AmortizationType.EqualPrincipalAndInterest) { //for these two types, amortization is calculated in cash flow calculation var tArr = coupons.Select((x, i) => AccrualDayCount.CalcDayCountFraction(accruals[i], accruals[i + 1], refStartDates[i], refEndDates[i])).ToArray(); double[] begPrincipal; double[] interest; double[] principalPay; double[] prepayment; double[] defaultPrincipal; _mortgageCalculator.GetPaymentDetails(tArr, Notional, coupons, PaymentFreq, AmortizationType, out begPrincipal, out interest, out principalPay, out prepayment, out defaultPrincipal, 0); amortization = new Amortization(ToAmortizationSchedule(schedulePaymentDates, principalPay.Select((x, i) => Tuple.Create(i + 1, x)).ToDictionary(x => x.Item1, x => x.Item2)), false); } else { amortization = Amoritzation.Adjust(new Schedule(schedulePaymentDates), Calendar, calcAi ? AccrualBizDayRule : PaymentBizDayRule, PaymentFreq); amortization = amortization.ResetAmortization(market.ValuationDate); } for (var i = 0; i < coupons.Length; ++i) { var amortizationRedemption = 0.0; var remainPrincipal = 100.0; var couponPay = 0.0; var prevDate = prevCfDate; if (amortization.AmortizationSchedule != null && amortization.AmortizationSchedule.Any(x => x.Key > prevCfDate && x.Key < schedulePaymentDates[i])) { var intermediatePrePayDate = amortization.AmortizationSchedule.Where(x => x.Key > prevCfDate && x.Key < schedulePaymentDates[i]).Select(x => x.Key).OrderBy(x => x).ToArray(); CfCalculationDetail[] tmpCfCalculationDetails; foreach (var date in intermediatePrePayDate) { amortizationRedemption = amortization.GetRemainingPrincipal(prevDate); remainPrincipal = Notional * amortizationRedemption; var periodCoupon = Coupon.GetCoupon(prevDate, date, market.FixingCurve.Value, market.HistoricalIndexRates, out tmpCfCalculationDetails, _compensationRate[i]); couponPay = remainPrincipal * periodCoupon * AccrualDayCount.CalcDayCountFraction(prevDate, date, refStartDates[i], refEndDates[i]); cashflows.Add(new Cashflow(prevDate, date, date, couponPay, Currency, CashflowType.Coupon, tmpCfCalculationDetails.Aggregate(true, (current, v) => current & v.IsFixed), market.GetDf(date), cfCalcDetails[i], refStartDates[i], refEndDates[i], remainPrincipal, periodCoupon)); prevDate = date; } coupons[i] = Coupon.GetCoupon(prevDate, accruals[i + 1], market.FixingCurve.Value, market.HistoricalIndexRates, out tmpCfCalculationDetails, _compensationRate[i]); cfCalcDetails[i] = tmpCfCalculationDetails; } amortizationRedemption = amortization.GetRemainingPrincipal(prevDate); remainPrincipal = Notional * amortizationRedemption; couponPay = remainPrincipal * coupons[i] * AccrualDayCount.CalcDayCountFraction(prevDate, accruals[i + 1], refStartDates[i], refEndDates[i]); if (i == coupons.Length - 1) { amortizationRedemption = amortization.GetRemainingPrincipal(prevCfDate); remainPrincipal = Notional * amortizationRedemption; //final settlement might be adjusted, coupon [MaturityDate, settlementDate) is added if (SettlmentGap != null) { var settlementDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); if (settlementDate > accruals[i + 1]) { var tmpCoupon = double.IsNaN(_settlementCoupon) ? coupons[i] : _settlementCoupon; var additionalCoupon = remainPrincipal * tmpCoupon * AccrualDayCount.CalcDayCountFraction(UnderlyingMaturityDate, settlementDate, refStartDates[i], refEndDates[i]); couponPay += additionalCoupon; } } couponPay = Redemption.GetRedemptionPayment(couponPay, remainPrincipal) - remainPrincipal; } cashflows.Add(new Cashflow(prevDate, accruals[i + 1], schedulePaymentDates[i], couponPay, Currency, CashflowType.Coupon, cfCalcDetails[i] == null ? true : cfCalcDetails[i].Aggregate(true, (current, v) => current & v.IsFixed), market.GetDf(schedulePaymentDates[i]), cfCalcDetails[i], refStartDates[i], refEndDates[i], remainPrincipal, coupons[i])); prevCfDate = accruals[i + 1]; } cashflows.AddRange(amortization.AmortizationSchedule.Keys.Select(key => new Cashflow(key, key, key, amortization.AmortizationSchedule[key] * Notional, Currency, CashflowType.Principal, true, market.GetDf(key), null))); if (netted) { return(cashflows .GroupBy(cf => cf.PaymentDate) .Select(item => new Cashflow(item.Min(x => x.AccrualStartDate), item.Max(x => x.AccrualEndDate), item.Key, item.Sum(entry => entry.PaymentAmount), Currency, CashflowType.Coupon, item.Aggregate(true, (current, v) => current && v.IsFixed), market.GetDf(item.Key), item.Min(x => x.CalculationDetails), item.Min(x => x.RefStartDate), item.Max(x => x.RefEndDate), item.Max(entry => entry.StartPrincipal), item.Sum(entry => entry.CouponRate))) .OrderBy(cf => cf.PaymentDate) .ToArray()); } else { return(cashflows.ToArray()); } }
/// <summary> /// 获取行权收益 /// </summary> /// <param name="price">标的资产价格</param> /// <returns>行权收益</returns> public override Cashflow[] GetPayoff(double[] price) { //used in approximation var amount = 0.0; var effectiveStrike = IsMoneynessOption ? Strike * InitialSpotPrice : Strike; if (OptionType == OptionType.Call) { if (StrikeStyle == StrikeStyle.Fixed) { if (this.AsianType == AsianType.GeometricAverage) { var n = this.Fixings.Count; var finalPrice = Math.Pow(this.Fixings.Select(x => x.Value).Aggregate(func: (result, item) => result * item), 1.0 / n); amount = Math.Max(finalPrice - effectiveStrike, 0); } else if (AsianType == AsianType.ArithmeticAverage || AsianType == AsianType.DiscreteArithmeticAverage) { var finalPrice = this.Fixings.Select(x => x.Value).Average(); amount = Math.Max(finalPrice - effectiveStrike, 0); } } else { if (this.AsianType == AsianType.GeometricAverage) { var strike = this.Fixings.Select(x => x.Value).Aggregate(func: (result, item) => result * item); amount = Math.Max(price[0] - strike, 0); } else if (AsianType == AsianType.ArithmeticAverage || AsianType == AsianType.DiscreteArithmeticAverage) { var strike = this.Fixings.Select(x => x.Value).Average(); amount = Math.Max(price[0] - strike, 0); } } } else // Put { if (StrikeStyle == StrikeStyle.Fixed) { if (this.AsianType == AsianType.GeometricAverage) { var n = this.Fixings.Count; var finalPrice = Math.Pow(this.Fixings.Select(x => x.Value).Aggregate(func: (result, item) => result * item), 1.0 / n); amount = Math.Max(effectiveStrike - finalPrice, 0); } else if (AsianType == AsianType.ArithmeticAverage || AsianType == AsianType.DiscreteArithmeticAverage) { var finalPrice = this.Fixings.Select(x => x.Value).Average(); amount = Math.Max(effectiveStrike - finalPrice, 0); } } else { if (this.AsianType == AsianType.GeometricAverage) { var strike = this.Fixings.Select(x => x.Value).Aggregate(func: (result, item) => result * item); amount = Math.Max(strike - price[0], 0); } else if (AsianType == AsianType.ArithmeticAverage || AsianType == AsianType.DiscreteArithmeticAverage) { var strike = this.Fixings.Select(x => x.Value).Average(); amount = Math.Max(strike - price[0], 0); } } } var settlementDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); return(new[] { new Cashflow(StartDate, UnderlyingMaturityDate, settlementDate, amount * Notional, SettlementCcy, CashflowType.Net, false, double.NaN, null), //new Cashflow(StartDate, UnderlyingMaturityDate, OptionPremiumPaymentDate ?? settlementDate, OptionPremium, SettlementCcy, CashflowType.Net, false, double.NaN, null) }); }
/// <summary> /// 获取行权收益 /// </summary> /// <param name="price">标的资产价格</param> /// <returns>行权收益</returns> public override Cashflow[] GetPayoff(double[] price) { var actualPaymentDate = SettlmentGap.Get(Calendar, UnderlyingMaturityDate); double payoff; double vanillaPayoff; var effectiveStrike = IsMoneynessOption ? Strike * InitialSpotPrice : Strike; switch (OptionType) { case OptionType.Call: vanillaPayoff = Math.Max(price[0] - effectiveStrike, 0.0) * ParticipationRate + Coupon; break; case OptionType.Put: vanillaPayoff = Math.Max(effectiveStrike - price[0], 0.0) * ParticipationRate + Coupon; break; default: throw new PricingBaseException("Unknow/illegal option type!"); } if (BarrierStatus == BarrierStatus.Monitoring) { switch (BarrierType) { case BarrierType.UpAndIn: case BarrierType.DownAndIn: payoff = Rebate; break; default: payoff = vanillaPayoff; break; } } else if (BarrierStatus == BarrierStatus.KnockedOut) { switch (BarrierType) { case BarrierType.UpAndOut: case BarrierType.DownAndOut: payoff = Rebate; break; default: payoff = 0.0; break; } } else { payoff = vanillaPayoff; //if knocked in, barrier reduce to vanilla } return(new[] { new Cashflow(StartDate, UnderlyingMaturityDate, actualPaymentDate, payoff * Notional, PayoffCcy, CashflowType.Net, false, double.NaN, null), //new Cashflow(StartDate, UnderlyingMaturityDate, OptionPremiumPaymentDate ?? actualPaymentDate, OptionPremium, PayoffCcy, CashflowType.Net, true, double.NaN,null) }); }