|
From: Luigi B. <lui...@gm...> - 2022-05-18 14:30:57
|
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
>
|