def evaluate_american_option(input_dict):

    tparams = input_dict["tradeParameters"]

    # Option Construction
    todaysDate = construct_date(tparams["evaluationDate"])
    ql.Settings.instance().evaluationDate = todaysDate

    exercise = ql.AmericanExercise(todaysDate, construct_date(tparams["exerciseDate"]))
    payoff = ql.PlainVanillaPayoff(ql.Option.Put, tparams["payoff"])

    option = ql.VanillaOption(payoff, exercise)

    # Market Data
    underlying = ql.SimpleQuote(tparams["underlying"])
    dividendYield = ql.FlatForward(todaysDate, tparams["dividendYield"], ql.Actual365Fixed())
    volatility = ql.BlackConstantVol(todaysDate, ql.TARGET(), tparams["volatility"], ql.Actual365Fixed())
    riskFreeRate = ql.FlatForward(todaysDate, tparams["riskFreeRate"], ql.Actual365Fixed())

    process = ql.BlackScholesMertonProcess(
        ql.QuoteHandle(underlying),
        ql.YieldTermStructureHandle(dividendYield),
        ql.YieldTermStructureHandle(riskFreeRate),
        ql.BlackVolTermStructureHandle(volatility),
    )

    if input_dict["engineName"] == "BaroneAdesiWhaleyApproximationEngine":
        option.setPricingEngine(ql.BaroneAdesiWhaleyApproximationEngine(process))

    elif input_dict["engineName"] == "BjerksundStenslandApproximationEngine":
        option.setPricingEngine(ql.BjerksundStenslandApproximationEngine(process))

    elif input_dict["engineName"] == "FdBlackScholesVanillaEngine":
        timeSteps = input_dict["engineParameters"]["timeSteps"]
        gridPoints = input_dict["engineParameters"]["gridPoints"]
        option.setPricingEngine(ql.FdBlackScholesVanillaEngine(process, timeSteps, gridPoints))

    elif input_dict["engineName"] == "BinomialVanillaEngine":
        timeSteps = input_dict["engineParameters"]["timeSteps"]
        # possible tree settings: ["JR", "CRR", "EQP", "Trigeorgis", "Tian", "LR", "Joshi4"]
        tree = input_dict["engineParameters"]["tree"]
        option.setPricingEngine(ql.BinomialVanillaEngine(process, tree, timeSteps))
    else:
        raise Exception("Unimplemented engineName [{}]".format(input_dict["engineName"]))

    value = option.NPV()
    return value
# semi-analytic Bates for European
method = 'Bates semi-analytic'
batesProcess = ql.BatesProcess(
    flatTermStructure, flatDividendTS, underlyingH,
    volatility * volatility, 1.0, volatility * volatility,
    0.001, 0.0, 1e-14, 1e-14, 1e-14)
batesModel = ql.BatesModel(batesProcess)
europeanOption.setPricingEngine(
    ql.BatesEngine(batesModel))
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# Barone-Adesi and Whaley approximation for American
method = 'Barone-Adesi/Whaley'
americanOption.setPricingEngine(
    ql.BaroneAdesiWhaleyApproximationEngine(bsmProcess))
tab.add_row([method, 'N/A', 'N/A', americanOption.NPV()])

# Bjerksund and Stensland approximation for American
method = 'Bjerksund/Stensland'
americanOption.setPricingEngine(
    ql.BjerksundStenslandApproximationEngine(bsmProcess))
tab.add_row([method, 'N/A', 'N/A', americanOption.NPV()])

# Integral
method = 'Integral'
europeanOption.setPricingEngine(
    ql.IntegralEngine(bsmProcess))
tab.add_row([method, europeanOption.NPV(), 'N/A', 'N/A'])

# Finite differences
    ql.BlackVolTermStructureHandle(volatility),
)

# %% [markdown]
# ### Pricing
#
# We'll collect tuples of method name, option value, and estimated error from the analytic formula.

# %%
results = []

# %% [markdown]
# #### Analytic approximations

# %%
option.setPricingEngine(ql.BaroneAdesiWhaleyApproximationEngine(process))
results.append(("Barone-Adesi-Whaley", option.NPV()))

# %%
option.setPricingEngine(ql.BjerksundStenslandApproximationEngine(process))
results.append(("Bjerksund-Stensland", option.NPV()))

# %% [markdown]
# #### Finite-difference method

# %%
timeSteps = 801
gridPoints = 800

# %%
option.setPricingEngine(