|
From: Luigi B. <lui...@gm...> - 2022-05-18 15:47:13
|
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 >>> >> |