|
From: Jack G <jac...@gm...> - 2022-05-18 13:44:23
|
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
>>>
>>
|