|
From: Ashish B. <ash...@gm...> - 2022-05-23 06:32:57
|
Luigi, We ran the asian code, which i sent earlier for a couple of asian options traded on exchange. We got prices very close to traded prices when we used the value of running accumulator as 1 for march option being valued on 17th with past fixing count as 12. But when we used the sum of past fixing prices i.e. 1302.88 we got a very different number like 6.09 against 4.57 earlier and 14.11 against 12.39 earlier. Just want to confirm if i am missing something about running accumulator and should it be really the sum of all past fixings? Thanks for help in advance. Ashish On Fri, 20 May 2022 at 14:58, Ashish Bansal <ash...@gm...> wrote: > 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 >>>>>>> >>>>>> |