|
From: Ashish B. <ash...@gm...> - 2022-05-19 10:37:53
|
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 >>>> >>> |