__author__ = 'saeedamen'

#
# Copyright 2020 Cuemacro
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
# See the License for the specific language governing permissions and limitations under the License.
#

"""
Shows how to use finmarketpy to price FX options (uses FinancePy underneath - it is recommended you pull the latest
version of FinancePy from GitHub).

Note, you will need to have a Bloomberg terminal (with blpapi Python library) to download the FX market data in order
to plot these vol surface (FX spot, FX forwards, FX implied_vol volatility quotes and deposits)
"""

import pandas as pd

# For plotting
from chartpy import Chart, Style

# For loading market data
from findatapy.market import Market, MarketDataGenerator, MarketDataRequest

from findatapy.util.loggermanager import LoggerManager

from finmarketpy.curve.rates.fxforwardspricer import FXForwardsPricer
from finmarketpy.curve.volatility.fxvolsurface import FXVolSurface
from finmarketpy.curve.volatility.fxoptionspricer import FXOptionsPricer

logger = LoggerManager().getLogger(__name__)

chart = Chart(engine='plotly')
market = Market(market_data_generator=MarketDataGenerator())

# Choose run_example = 0 for everything
# run_example = 1 - price GBPUSD options
# run_example = 2 - price USDJPY options
# run_example = 3 - price AUDUSD options
# run_example = 4 - more pricing of AUDUSD options
# run_example = 5 - pricing of EURUSD options
# run_example = 6 - another USDJPY option
# run_example = 7 - price USDBRL options

run_example = 7

###### Fetch market data for pricing GBPUSD FX options over Brexit vote (ie. FX spot, FX forwards, FX deposits and FX vol quotes)
###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation and
###### Then price some options over these dates eg. atm, 25d-call etc.
if run_example == 1 or run_example == 0:

    horizon_date = '23 Jun 2016'
    cross = 'GBPUSD'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='NYC', category='fx-vol-market',
                                   tickers=cross, base_depos_currencies=[cross[0:3], cross[3:6]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    fx_vol_surface = FXVolSurface(market_df=df, asset=cross)

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    # Price several different options

    print("atm 1M european call")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-call', tenor='1M').to_string())

    print("25d 1W european put")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), '25d-otm', contract_type='european-put', tenor='1W').to_string())

    # Try a broken date 12D option (note, for broken dates, currently doesn't interpolate key strikes)
    # Specify expiry date instead of the tenor for broken dates
    print("1.50 12D european call")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 1.50,
            expiry_date=pd.Timestamp(horizon_date) + pd.Timedelta(days=12), contract_type='european-call').to_string())


###### Fetch market data for pricing USDJPY FX options over Brexit vote (ie. FX spot, FX forwards, FX deposits and FX vol quotes)
###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation
###### Then price a series of 1W ATM call options
if run_example == 2 or run_example == 0:

    start_date = '02 Nov 2020'; finish_date = '05 Nov 2020'
    horizon_date = pd.bdate_range(start_date, finish_date, freq='B')

    cross = 'USDJPY'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date,
                                   data_source='bloomberg', cut='NYC', category='fx-vol-market',
                                   tickers=cross,
                                   cache_algo='cache_algo_return', base_depos_currencies=[cross[0:3], cross[3:6]])

    df = market.fetch_market(md_request)

    # Skip 3W/4M because this particular close (NYC) doesn't have that in USDJPY market data
    tenors = ["ON", "1W", "2W", "1M", "2M", "3M", "6M", "9M", "1Y", "2Y", "3Y"]
    fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=tenors)

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    print("atm 1W european put")
    print(fx_op.price_instrument(cross, horizon_date, 'atm', contract_type='european-put',
                                 tenor='1W', depo_tenor='1W').to_string())

    print("25d 3M european call")
    print(fx_op.price_instrument(cross, horizon_date, '25d-otm', contract_type='european-call',
                                 tenor='3M', depo_tenor='3M').to_string())

    print("10d 1M european put")
    print(fx_op.price_instrument(cross, horizon_date, '10d-otm', contract_type='european-put',
                                 tenor='1M', depo_tenor='1M').to_string())

###### Fetch market data for pricing AUDUSD options on 18 Apr 2007, just before credit crisis
###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation and
###### Then price some options over these dates eg. atm, 25d-call etc.
if run_example == 3 or run_example == 0:

    horizon_date = '18 Apr 2007'
    cross = 'AUDUSD'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='LDN', category='fx-vol-market',
                                   tickers=cross, base_depos_currencies=[cross[0:3], cross[3:6]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['ON', '1W', '1M'])

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    # Try a broken date 15D option (note, for broken dates, currently doesn't interpolate key strikes)
    # Specify expiry date instead of the tenor for broken dates
    print("atm 15D european call")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 0.8124,
            expiry_date=pd.Timestamp(horizon_date) + pd.Timedelta(days=15), contract_type='european-call').to_string())

###### Fetch market data for pricing AUDUSD options during start of 2008 Credit Crisis
if run_example == 4 or run_example == 0:

    horizon_date = '17 Aug 2007'
    cross = 'AUDUSD'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='BGN', category='fx-vol-market',
                                   tickers=cross, base_depos_currencies=[cross[0:3], cross[3:6]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W', '1M', '3M'])
    fx_vol_surface.build_vol_surface(pd.Timestamp(horizon_date))

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    # Price several different options

    # Try a broken date 15D option (note, for broken dates, currently doesn't interpolate key strikes)
    # Specify expiry date instead of the tenor for broken dates
    print("atm 15D european call")

    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 0.8535,
            expiry_date=pd.Timestamp('05 Sep 2007'), contract_type='european-call').to_string())

###### Fetch market data for pricing EURUSD options during start of 2006
if run_example == 5 or run_example == 0:

    horizon_date = '04 Jan 2006'
    cross = 'EURUSD'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='BGN', category='fx-vol-market',
                                   tickers=cross, base_depos_currencies=[cross[0:3], cross[3:6]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W', '1M', '3M'])

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    # Price several different options

    # Try a broken date 15D option (note, for broken dates, currently doesn't interpolate key strikes)
    # Specify expiry date instead of the tenor for broken dates
    print("atm 1W european call")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm',
            tenor="1W", depo_tenor='1W', contract_type='european-call').to_string())

###### Fetch market data for pricing USDJPY ATM 1W
if run_example == 6 or run_example == 0:

    horizon_date = '30 March 2007'
    cross = 'USDJPY'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='LDN', category='fx-vol-market',
                                   fx_vol_tenor=['1W'],
                                   tickers=cross, base_depos_currencies=[cross[0:3], cross[3:6]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W'], solver='nelmer-mead-numba')

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    market_df = fx_vol_surface.get_all_market_data()

    # Print 1W data
    print(market_df[[x for x in market_df.columns if '1W' in x]][market_df.index == horizon_date].to_string())

    # Print ATM vol
    fx_vol_surface.build_vol_surface(horizon_date)
    fx_vol_surface.extract_vol_surface(num_strike_intervals=None)

    print("ATM vol " + str(fx_vol_surface.get_atm_vol(tenor='1W')))

    # Specify expiry date instead of the tenor for broken dates
    print("atm 1W european straddle")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm',
            tenor="1W", depo_tenor='1W', contract_type='european-straddle').to_string())


###### Price USDBRL option around 2018 2nd round of presidential election
if run_example == 7 or run_example == 0:

    horizon_date = '26 Oct 2018'
    cross = 'USDBRL'
    non_usd = 'BRL'

    # Download the whole all market data for USDBRL for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='NYC', category='fx-vol-market',
                                   tickers=cross, base_depos_currencies=[cross[0:3]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    # Compute implied deposit BRL 1M from USDBRL forwards (and USD 1M depo)
    fx_forwards_price = FXForwardsPricer()

    implied_depo_df = fx_forwards_price.calculate_implied_depo(cross, non_usd, market_df=df,
                               fx_forwards_tenor=['1W', '1M'],
                               depo_tenor=['1W', '1M'])

    implied_depo_df.columns = [x.replace('-implied-depo', '') for x in implied_depo_df.columns]
    df = df.join(implied_depo_df, how='left')

    # USDBRL quoted ATMF implied vol (as opposed to delta neutral) hence 'fwd' parameter
    fx_op = FXOptionsPricer(fx_vol_surface=FXVolSurface(market_df=df, asset=cross, atm_method='fwd', depo_tenor='1M'))

    # Price several different options
    print(df)

    print("atm 1M european put")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-put', tenor='1M').to_string())

    # TODO: calendar around election results in slightly different pricing
    # print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), '25d-otm', contract_type='european-put', tenor='1W').to_string())
    # print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 3.5724, contract_type='european-put', expiry_date=pd.Timestamp('2 Nov 2018')).to_string())

###### Price GBPUSD option around Brexit with unquoted deltas
if run_example == 8 or run_example == 0:

    horizon_date = '23 Jun 2016'
    cross = 'GBPUSD'

    # Download the whole all market data for GBPUSD for pricing options (vol surface)
    md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date,
                                   data_source='bloomberg', cut='NYC', category='fx-vol-market',
                                   tickers=cross, base_depos_currencies=[cross[0:3], cross[3:6]],
                                   cache_algo='cache_algo_return')

    df = market.fetch_market(md_request)

    fx_vol_surface = FXVolSurface(market_df=df, asset=cross)

    fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface)

    # Price several different options
    print("atm 1M european call")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-call', tenor='1M').to_string())

    print("25d 1W european put")
    print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), '25d-otm', contract_type='european-put', tenor='1W').to_string())
