|
From: Ashish B. <ash...@gm...> - 2022-05-20 09:28:31
|
Ok understood. thanks. On Fri, 20 May 2022 at 14:46, Luigi Ballabio <lui...@gm...> wrote: > It would be the sum of past fixings; for three past fixings of 100.2, > 101.3 and 94.6 it would be 296.1. > > Luigi > > > On Thu, May 19, 2022 at 12:37 PM Ashish Bansal <ash...@gm...> > wrote: > >> Thanks, Luigi for the suggestion. After taking the running accumulator, >> we are able to generate the numbers. What exactly should be passed here? >> The average price of past fixings or just 1 when the trade is priced during >> averaging period? >> >> Thanks for help. >> >> Ashish >> >> On Wed, 18 May 2022 at 21:17, Luigi Ballabio <lui...@gm...> >> wrote: >> >>> I don't have a 1.7 version to try it, but the check is the one at < >>> https://github.com/lballabio/QuantLib/blob/QuantLib-v1.7/ql/pricingengines/asian/fdblackscholesasianengine.cpp#L51-L53>, >>> suggesting that your running accumulator should be 0 (which makes sense, >>> because you're using arithmetic average, i.e., a sum, and if you don't have >>> any fixings yet the sum should be 0.) >>> Your default seems to be 1 instead. >>> >>> Luigi >>> >>> >>> On Wed, May 18, 2022 at 5:17 PM Ashish Bansal <ash...@gm...> >>> wrote: >>> >>>> Luigi, >>>> >>>> Our code is not too much but we are using deprecated functions like >>>> FDEuropeanEngine and FDAmericanEngine. Not sure if using new function >>>> called FdBlackScholesVanillaEngine has any change in the calculation >>>> that our code will have an impact. >>>> >>>> Can we use the DiscreteAveragingAsianOption without the overloaded >>>> parameter of pastfixings under 1.7? >>>> >>>> Ashish >>>> >>>> On Wed, 18 May 2022 at 20:00, Luigi Ballabio <lui...@gm...> >>>> wrote: >>>> >>>>> Hi — it could be. Any particular reason you're still using a version >>>>> from 4 years ago? If you have a substantial amount of code that depends on >>>>> QuantLib, it might require some work to update because some methods were >>>>> deprecated and then removed in the meantime; and of course you'll probably >>>>> have some validation process in place when updating, so that's work too. >>>>> But on the other hand, you're missing 4 years worth of bug fixings... >>>>> >>>>> Luigi >>>>> >>>>> >>>>> On Wed, May 18, 2022 at 4:19 PM Ashish Bansal <ash...@gm...> >>>>> wrote: >>>>> >>>>>> We tried again and got the same error. >>>>>> >>>>>> Could it be due to the old version (1.7) we are using? I see in the >>>>>> release notes of Apr-2021 that the overload for past fixing was added: >>>>>> >>>>>> - Added an overloaded constructor for Asian options that takes >>>>>> all past fixings and thus allows to reprice them correctly when the >>>>>> evaluation date changes (thanks to Jack Gillett). >>>>>> >>>>>> Ashish >>>>>> >>>>>> On Wed, 18 May 2022 at 19:14, Jack G <jac...@gm...> >>>>>> wrote: >>>>>> >>>>>>> Hi Ashish, >>>>>>> >>>>>>> I have a better answer for you with a screenshot awaiting moderation >>>>>>> as it's quite large, but in the meantime, I was able to run your code >>>>>>> without problems using the python at the bottom of this email (although I >>>>>>> didn't check the numbers). >>>>>>> >>>>>>> If there is something you're doing specifically to generate the >>>>>>> error, could you please attach a minimal reproduction of it? >>>>>>> >>>>>>> Best, >>>>>>> Jack >>>>>>> >>>>>>> >>>>>>> >>>>>>> >>>>>>> import QuantLib as ql >>>>>>> from copy import deepcopy >>>>>>> from datetime import datetime >>>>>>> from datetime import timedelta >>>>>>> >>>>>>> >>>>>>> # DEFAULT_PERIODS = [ql.Period(f"{i}d") for i in range(0,31)] >>>>>>> DEFAULT_PERIODS = [ql.Period("6M"), ql.Period("12M"), >>>>>>> ql.Period("18M"), ql.Period("24M")] >>>>>>> DEFAULT_tGRID = 100 >>>>>>> DEFAULT_xGRID = 100 >>>>>>> DEFAULT_aGRID = 50 >>>>>>> DEFAULT_aRA = 1 # arithmeticRunningAccumulator >>>>>>> DEFAULT_PASTFIXINGS = 1 >>>>>>> >>>>>>> >>>>>>> class AsianOptionPrice: >>>>>>> def __init__(self, **x): >>>>>>> for k, v in x.items(): >>>>>>> setattr(self, k, v) >>>>>>> self.tGrid = x.get("tGrid", DEFAULT_tGRID) >>>>>>> self.xGrid = x.get("xGrid", DEFAULT_xGRID) >>>>>>> self.aGrid = x.get("aGrid", DEFAULT_aGRID) >>>>>>> self.periods = x.get("periods", DEFAULT_PERIODS) >>>>>>> self.pastFixings = x.get("pastFixings", DEFAULT_PASTFIXINGS) >>>>>>> self.aRA = x.get("arithmeticRunningAccumulator", DEFAULT_aRA) >>>>>>> self.keyList = [k for k, v in x.items()] >>>>>>> self.dist = 10**(-2) >>>>>>> self.date_format = "%d-%m-%Y" >>>>>>> self.value_date = ql.DateParser.parseFormatted( >>>>>>> self.valueDate, '%d-%m-%Y') >>>>>>> self.expiry_date = ql.DateParser.parseFormatted( >>>>>>> self.expiryDate, '%d-%m-%Y') >>>>>>> ql.Settings.instance().evaluationDate = self.value_date >>>>>>> self.exDay = ql.DateParser.parseFormatted(self.expiryDate, >>>>>>> '%d-%m-%Y') >>>>>>> # Volatility input is required for running the computing >>>>>>> implied volatility function. This volatility is NOT used in the implied >>>>>>> volatility calculation. >>>>>>> self.vol_for_iv_calc = 0.10 >>>>>>> self.IV = None >>>>>>> >>>>>>> def generate_fixture_dates(self,start_date,end_date,period): >>>>>>> schedule = ql.MakeSchedule(start_date, end_date, period) >>>>>>> fixture_dates = [_date for _date in schedule if >>>>>>> _date.weekday() not in [1,7]] >>>>>>> return fixture_dates >>>>>>> def asian(self): >>>>>>> """ >>>>>>> arithmeticRunningAccumulator(aRA) >>>>>>> """ >>>>>>> arithmeticRunningAccumulator = self.aRA >>>>>>> pastFixings = self.pastFixings >>>>>>> print(pastFixings) >>>>>>> option_type = self.optionType.lower() >>>>>>> if option_type not in ["call", "put"]: >>>>>>> raise Exception( >>>>>>> f"Option Type is Neither Call nor Put, Option type >>>>>>> is {option_type}") >>>>>>> option_type = ql.Option.Call if option_type == "call" else >>>>>>> ql.Option.Put >>>>>>> strike = self.strikePrice >>>>>>> today = self.value_date >>>>>>> print(today) >>>>>>> fixingStartDate >>>>>>> =ql.DateParser.parseFormatted(self.fixingStartDate, '%d-%m-%Y') >>>>>>> print("#"*40,fixingStartDate) >>>>>>> periods = self.periods >>>>>>> asianFutureFixingDates = >>>>>>> self.generate_fixture_dates(fixingStartDate,self.expiry_date,ql.Period("1d")) >>>>>>> print("#"*40,asianFutureFixingDates) >>>>>>> asianExpiryDate = self.expiry_date >>>>>>> vanillaPayoff = ql.PlainVanillaPayoff(option_type, strike) >>>>>>> europeanExercise = ql.EuropeanExercise(asianExpiryDate) >>>>>>> arithmeticAverage = ql.Average().Arithmetic >>>>>>> discreteArithmeticAsianOption = >>>>>>> ql.DiscreteAveragingAsianOption( >>>>>>> arithmeticAverage, arithmeticRunningAccumulator, >>>>>>> pastFixings, asianFutureFixingDates, vanillaPayoff, europeanExercise) >>>>>>> return discreteArithmeticAsianOption >>>>>>> >>>>>>> def create_arg_list(self, Option=None, compute_implied_vol=None, >>>>>>> implied_vol_am_put=None): >>>>>>> ''' >>>>>>> The arguments with disturbances added. Will be used for >>>>>>> calculation of Greeks >>>>>>> using numerical differentiation. >>>>>>> Creating a dictionary of arguments that will be used for >>>>>>> option price and greeks calculation. >>>>>>> ''' >>>>>>> self.riskFreeRate = self.riskFreeRate/100 >>>>>>> originalDict_ = {k: getattr(self, k) for k in self.keyList} >>>>>>> ListDict = [] >>>>>>> for i in range(4): >>>>>>> ListDict.append(deepcopy(originalDict_)) >>>>>>> ListDict[1]['riskFreeRate'] = self.riskFreeRate + self.dist >>>>>>> ListDict[2]['volatility'] = self.volatility + self.dist >>>>>>> ListDict[3]['expiryDate'] = (datetime.strptime( >>>>>>> self.expiryDate, self.date_format) - >>>>>>> timedelta(days=1)).strftime(self.date_format) >>>>>>> print(ListDict) >>>>>>> return(ListDict) >>>>>>> >>>>>>> def bs_process(self, create_process_for_iv=None, use_IV=None): >>>>>>> """ >>>>>>> Creating the Black-Scholes process for option valuation. >>>>>>> """ >>>>>>> riskFreeTS = ql.YieldTermStructureHandle( >>>>>>> ql.FlatForward(self.value_date, self.riskFreeRate, >>>>>>> ql.Actual365Fixed())) >>>>>>> dividendTS = ql.YieldTermStructureHandle( >>>>>>> ql.FlatForward(self.value_date, 0, ql.Actual365Fixed())) >>>>>>> volatility = >>>>>>> ql.BlackVolTermStructureHandle(ql.BlackConstantVol( >>>>>>> self.value_date, ql.NullCalendar(), self.volatility, >>>>>>> ql.Actual365Fixed())) >>>>>>> initialValue = >>>>>>> ql.QuoteHandle(ql.SimpleQuote(self.underlyingPrice)) >>>>>>> process = ql.BlackScholesMertonProcess( >>>>>>> initialValue, dividendTS, riskFreeTS, volatility) >>>>>>> return(process) >>>>>>> >>>>>>> def evaluate(self, Option=None, compute_implied_vol=None): >>>>>>> """ >>>>>>> Call the relevant option based on the input. >>>>>>> """ >>>>>>> self.riskFreeRate = self.riskFreeRate/100 >>>>>>> res = None >>>>>>> option = AsianOptionPrice.asian(self) >>>>>>> if compute_implied_vol == True: >>>>>>> # create Black Scholes process using IV. >>>>>>> process = AsianOptionPrice.bs_process( >>>>>>> self, create_process_for_iv=False, use_IV=True) >>>>>>> engine = ql.FdBlackScholesAsianEngine( >>>>>>> process, tGrid=self.tGrid, xGrid=self.xGrid, >>>>>>> aGrid=self.aGrid) >>>>>>> option.setPricingEngine(engine) >>>>>>> res = {'value': option.NPV(), 'impliedVolatility': >>>>>>> self.IV} >>>>>>> else: >>>>>>> # Create Black Scholes process using HV. >>>>>>> process = AsianOptionPrice.bs_process( >>>>>>> self, create_process_for_iv=False, use_IV=False) >>>>>>> engine = ql.FdBlackScholesAsianEngine( >>>>>>> process, tGrid=self.tGrid, xGrid=self.xGrid, >>>>>>> aGrid=self.aGrid) >>>>>>> option.setPricingEngine(engine) >>>>>>> res = {'value': option.NPV()} >>>>>>> return res >>>>>>> >>>>>>> def evaluate_asian_npv(self): >>>>>>> Option = AsianOptionPrice.asian(self) >>>>>>> process = AsianOptionPrice.bs_process( >>>>>>> self, create_process_for_iv=False, use_IV=False) >>>>>>> engine = ql.FdBlackScholesAsianEngine( >>>>>>> process, tGrid=self.tGrid, xGrid=self.xGrid, >>>>>>> aGrid=self.aGrid) >>>>>>> Option.setPricingEngine(engine) >>>>>>> res = Option.NPV() >>>>>>> return(res) >>>>>>> >>>>>>> def calculate_greeks(self, Option=None, >>>>>>> compute_implied_vol=None): >>>>>>> today = self.value_date >>>>>>> Option = AsianOptionPrice.asian(self) >>>>>>> if compute_implied_vol == True: >>>>>>> process = AsianOptionPrice.bs_process( >>>>>>> self, create_process_for_iv=False, use_IV=True) >>>>>>> else: >>>>>>> process = AsianOptionPrice.bs_process( >>>>>>> self, create_process_for_iv=False, use_IV=False) >>>>>>> engine = ql.FdBlackScholesAsianEngine( >>>>>>> process, tGrid=self.tGrid, xGrid=self.xGrid, >>>>>>> aGrid=self.aGrid) >>>>>>> Option.setPricingEngine(engine) >>>>>>> option_greeks_ = {"delta": Option.delta(), "gamma": >>>>>>> Option.gamma( >>>>>>> )} >>>>>>> ArgmntList = AsianOptionPrice.create_arg_list( >>>>>>> self, Option, compute_implied_vol=True, >>>>>>> implied_vol_am_put=self.volatility) >>>>>>> DisturbedPrices = [] >>>>>>> arg_keys = ['Price', 'PRup', 'PVup', 'PTup'] >>>>>>> for i in range(4): >>>>>>> DisturbedPrices.append(eval( >>>>>>> >>>>>>> "AsianOptionPrice(**ArgmntList[i]).evaluate_asian_npv()")) >>>>>>> print(DisturbedPrices) >>>>>>> PricesDistrbd = dict(zip(arg_keys, DisturbedPrices)) >>>>>>> Rho = (PricesDistrbd['PRup'] - >>>>>>> PricesDistrbd['Price'])/(self.dist*100) >>>>>>> vega = (PricesDistrbd['PVup'] - >>>>>>> PricesDistrbd['Price'])/(self.dist*100) >>>>>>> thetaDay = (PricesDistrbd['PTup'] - PricesDistrbd['Price']) >>>>>>> option_greeks_.update({'rho': Rho, 'vega': vega, 'theta': >>>>>>> thetaDay}) >>>>>>> return option_greeks_ >>>>>>> >>>>>>> >>>>>>> settings = { >>>>>>> "computeHistoricalVolatiltiy": False, >>>>>>> "tenantId": "380", >>>>>>> "gridPoints": 149, >>>>>>> "computeImpliedVolatiltiy": False, >>>>>>> "runId": 3735, >>>>>>> "timeSteps": 150, >>>>>>> "optionTrades": [ >>>>>>> { >>>>>>> "underlyingPrice": 98.69, >>>>>>> "refNo": "OPT-1135-GVA", >>>>>>> "underlyingPriceUnit": "US cents/LB", >>>>>>> "valueDate": "17-03-2022", >>>>>>> "fixingStartDate":"01-05-2022", >>>>>>> "pastFixings":0, >>>>>>> "volatility": 0.6015, >>>>>>> "openQuantity": 1, >>>>>>> "strikePriceUnit": "US cents/LB", >>>>>>> "optionType": "Call", >>>>>>> "expiryDate": "31-05-2022", >>>>>>> "exType": "asian", >>>>>>> "riskFreeRate": 0.8080374999999999, >>>>>>> "premium": 1, >>>>>>> "portfolioId": 18209, >>>>>>> "marketPremium": 11.42, >>>>>>> "longShort": "Long", >>>>>>> "optionInstrument": "EURO USD 1.105 Call", >>>>>>> "asset": "EURO USD 1.105 Call", >>>>>>> "portfolioName": "Option Trades", >>>>>>> "scenarioId": 8356, >>>>>>> "scenarioName": "Option Valuation - Greeks", >>>>>>> "strikePrice": 95, >>>>>>> "maturityInYears": 0.205479452 >>>>>>> } >>>>>>> ], >>>>>>> "userId": "5263", >>>>>>> "americanOptionsEngine": "CrankNicolson" >>>>>>> } >>>>>>> >>>>>>> opt = AsianOptionPrice(**settings["optionTrades"][0]) >>>>>>> opt.evaluate_asian_npv() >>>>>>> >>>>>>> On Wed, May 18, 2022 at 9:25 PM Ashish Bansal < >>>>>>> ash...@gm...> wrote: >>>>>>> >>>>>>>> Hi Jack, >>>>>>>> >>>>>>>> Please find attached the Asian option python code and the option >>>>>>>> trade payload. >>>>>>>> >>>>>>>> Thanks for your help, >>>>>>>> Regards, >>>>>>>> Ashish >>>>>>>> >>>>>>>> On Wed, 18 May 2022 at 12:12, Jack G <jac...@gm...> >>>>>>>> wrote: >>>>>>>> >>>>>>>>> Hi Ashish, >>>>>>>>> >>>>>>>>> I've just lifted the examples from your references directly for a >>>>>>>>> discrete arithmetic Asian using the FD Asian engine and it is working for >>>>>>>>> me - can you share the code you are using that breaks please? >>>>>>>>> >>>>>>>>> Best, >>>>>>>>> Jack >>>>>>>>> >>>>>>>>> On Wed, 18 May 2022, 13:55 Ashish Bansal, <ash...@gm...> >>>>>>>>> wrote: >>>>>>>>> >>>>>>>>>> Hi all, >>>>>>>>>> >>>>>>>>>> Could somebody help with the asian options? We are using >>>>>>>>>> FdBlackScholesAsianengine with DiscreteAveragingAsianOption. Online >>>>>>>>>> references are given below. In DiscreteAveragingAsianOption, we need to >>>>>>>>>> pass the past fixing which is suggested to be 0 for new trade. Whereas when >>>>>>>>>> we are passing it as 0, we are getting the following error: >>>>>>>>>> "QuantLib Error - Running average requires at least one past >>>>>>>>>> fixing" >>>>>>>>>> ref: >>>>>>>>>> >>>>>>>>>> https://quantlib-python-docs.readthedocs.io/en/latest/pricing_engines.html#fdblackscholesasianengine >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> https://quantlib-python-docs.readthedocs.io/en/latest/instruments/options.html?highlight=DiscreteAveragingAsianOption#ql.DiscreteAveragingAsianOption >>>>>>>>>> >>>>>>>>>> Queries: >>>>>>>>>> 1. What is to be passed here for new trade if not 0? >>>>>>>>>> 2. For trade which is mid of averaging and has few past fixings, >>>>>>>>>> do we need to pass the count of past fixing or the actual rates of the >>>>>>>>>> pastfixing as an array? >>>>>>>>>> >>>>>>>>>> Any help will be appreciated. >>>>>>>>>> >>>>>>>>>> Thanks in advance >>>>>>>>>> Ashish >>>>>>>>>> _______________________________________________ >>>>>>>>>> QuantLib-users mailing list >>>>>>>>>> Qua...@li... >>>>>>>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users >>>>>>>>>> >>>>>>>>> _______________________________________________ >>>>>> QuantLib-users mailing list >>>>>> Qua...@li... >>>>>> https://lists.sourceforge.net/lists/listinfo/quantlib-users >>>>>> >>>>> |