diff --git a/README.md b/README.md index d0c488b..7f4b293 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ In finmarketpy/examples you will find several examples, including some simple tr # Release Notes +* 0.11.11 - finmarketpy (07 Oct 2021) * 0.11.10 - finmarketpy (06 Oct 2021) * 0.11.9 - finmarketpy (01 Jun 2021) * 0.11.8 - finmarketpy (25 Jan 2021) @@ -172,6 +173,8 @@ In finmarketpy/examples you will find several examples, including some simple tr # finmarketpy log +* 07 Oct 2021 + * Set FinancePy version required to 0.193 * 23 Sep 2021 * Fixed bug in YoY plot * 23 Jul 2021 diff --git a/finmarketpy_examples/fx_options_indices_examples.py b/finmarketpy_examples/fx_options_indices_examples.py index a470fda..0667b70 100644 --- a/finmarketpy_examples/fx_options_indices_examples.py +++ b/finmarketpy_examples/fx_options_indices_examples.py @@ -68,185 +68,187 @@ def prepare_indices(cross, df_option_tot=None, df_option_tc=None, df_spot_tot=No return calculations.create_mult_index_from_prices(df) -###### Fetch market data for pricing AUDUSD options in 2007 (ie. FX spot, FX forwards, FX deposits and FX vol quotes) -###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation -###### Enters a long 1M call, and MTM every day, and at expiry rolls into another long 1M call -if run_example == 1 or run_example == 0: +if __name__ == '__main__': - # Warning make sure you choose dates, where there is full vol surface! If points are missing interpolation - # will fail - start_date = '01 Jan 2007'; finish_date = '31 Dec 2020' # Use smaller window for quicker execution + ###### Fetch market data for pricing AUDUSD options in 2007 (ie. FX spot, FX forwards, FX deposits and FX vol quotes) + ###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation + ###### Enters a long 1M call, and MTM every day, and at expiry rolls into another long 1M call + if run_example == 1 or run_example == 0: - cross = 'AUDUSD' - fx_options_trading_tenor = '1M' + # Warning make sure you choose dates, where there is full vol surface! If points are missing interpolation + # will fail + start_date = '01 Jan 2007'; finish_date = '31 Dec 2020' # Use smaller window for quicker execution - # Download the whole all market data for AUDUSD for pricing options (FX vol surface + spot + FX forwards + depos) - md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date, - data_source='bloomberg', cut='BGN', category='fx-vol-market', - tickers=cross, fx_vol_tenor=['1W', '1M', '3M'], - cache_algo='cache_algo_return', base_depos_currencies=[cross[0:3], cross[3:6]]) + cross = 'AUDUSD' + fx_options_trading_tenor = '1M' - df_vol_market = market.fetch_market(md_request) - df_vol_market = df_vol_market.fillna(method='ffill') + # Download the whole all market data for AUDUSD for pricing options (FX vol surface + spot + FX forwards + depos) + md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date, + data_source='bloomberg', cut='BGN', category='fx-vol-market', + tickers=cross, fx_vol_tenor=['1W', '1M', '3M'], + cache_algo='cache_algo_return', base_depos_currencies=[cross[0:3], cross[3:6]]) - # Remove New Year's Day and Christmas - df_vol_market = Filter().filter_time_series_by_holidays(df_vol_market, cal='FX') + df_vol_market = market.fetch_market(md_request) + df_vol_market = df_vol_market.fillna(method='ffill') - # Get a total return index for trading spot - # This way we can take into account carry when calculating delta hedging P&L - md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date, - data_source='bloomberg', cut='NYC', category='fx-tot', - tickers=cross, - cache_algo='cache_algo_return') + # Remove New Year's Day and Christmas + df_vol_market = Filter().filter_time_series_by_holidays(df_vol_market, cal='FX') - df_tot = market.fetch_market(md_request) + # Get a total return index for trading spot + # This way we can take into account carry when calculating delta hedging P&L + md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date, + data_source='bloomberg', cut='NYC', category='fx-tot', + tickers=cross, + cache_algo='cache_algo_return') - df_vol_market = df_vol_market.join(df_tot, how='left') - df_vol_market = df_vol_market.fillna(method='ffill') + df_tot = market.fetch_market(md_request) - # We want to roll long 1M ATM call at expiry - # We'll mark to market the option price through the month by interpolating between 1W and 1M (and using whole vol curve - # at each tenor) - fx_options_curve = FXOptionsCurve(fx_options_trading_tenor=fx_options_trading_tenor, - roll_days_before=0, - roll_event='expiry-date', - roll_months=1, - fx_options_tenor_for_interpolation=['1W', '1M'], - strike='atm', - contract_type='european-call', - depo_tenor_for_option='1M', - position_multiplier=1.0, - tot_label='tot', - output_calculation_fields=True) - - # Let's trade a long 1M call, and we roll at expiry - df_cuemacro_option_call_tot = fx_options_curve.construct_total_return_index(cross, df_vol_market) - - # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX) - df_cuemacro_option_call_tc = fx_options_curve.apply_tc_signals_to_total_return_index(cross, df_cuemacro_option_call_tot, - option_tc_bp=5, spot_tc_bp=2) - - # Let's trade a long 1M OTM put, and we roll at expiry - df_cuemacro_option_put_tot = fx_options_curve.construct_total_return_index( - cross, df_vol_market, contract_type='european-put', strike='10d-otm', position_multiplier=1.0) - - # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX) - df_cuemacro_option_put_tc = fx_options_curve.apply_tc_signals_to_total_return_index(cross, df_cuemacro_option_put_tot, - option_tc_bp=5, spot_tc_bp=2) - - - # Get total returns for spot - df_bbg_tot = df_tot # from earlier! - df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns] - - # Calculate a hedged portfolio of spot + 2*options (can we reduce drawdowns?) - calculations = Calculations() - - ret_stats = RetStats() - - df_hedged = calculations.join([df_bbg_tot[cross + '-tot.close-bbg'].to_frame(), df_cuemacro_option_put_tc[cross + '-option-tot-with-tc.close'].to_frame()], how='outer') - df_hedged = df_hedged.fillna(method='ffill') - df_hedged = df_hedged.pct_change() - - df_hedged['Spot + 2*option put hedge'] = df_hedged[cross + '-tot.close-bbg'] + df_hedged[cross + '-option-tot-with-tc.close'] - - df_hedged.columns = RetStats(returns_df=df_hedged, ann_factor=252).summary() - - # Plot everything - - # P&L from call - chart.plot(calculations.create_mult_index_from_prices( - prepare_indices(cross=cross, df_option_tot=df_cuemacro_option_call_tot, - df_option_tc=df_cuemacro_option_call_tc, df_spot_tot=df_bbg_tot))) - - # P&L from put option, put option + TC and total returns from spot - chart.plot(calculations.create_mult_index_from_prices( - prepare_indices(cross=cross,df_option_tot=df_cuemacro_option_put_tot, - df_option_tc=df_cuemacro_option_put_tc, df_spot_tot=df_bbg_tot))) - - # P&L from put option + TC and total returns from spot - chart.plot(calculations.create_mult_index_from_prices( - prepare_indices(cross=cross, df_option_tc=df_cuemacro_option_put_tc, df_spot_tot=df_bbg_tot))) - - # P&L for total returns from spot and total returns from + 2*put option + TC (ie. hedged portfolio) - chart.plot(calculations.create_mult_index(df_hedged)) - - # Plot delta from put option - chart.plot(df_cuemacro_option_put_tot[cross + '-delta.close']) - - -###### Fetch market data for pricing EURUSD options from 2006-2020 (ie. FX spot, FX forwards, FX deposits and FX vol quotes) -###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation -###### Enters a short 1W straddle, and MTM every day, and at expiry rolls into another 1W straddle -if run_example == 2 or run_example == 0: + df_vol_market = df_vol_market.join(df_tot, how='left') + df_vol_market = df_vol_market.fillna(method='ffill') - # Warning make sure you choose dates, where there is full vol surface! If vol points in the tenors you are looking at - # are missing then interpolation will fail (or if eg. spot data is missing etc.) - start_date = '08 Mar 2007'; finish_date = '31 Dec 2020' # Monday - # start_date = '09 Mar 2007'; finish_date = '31 Dec 2014' - # start_date = '04 Jan 2006'; finish_date = '31 Dec 2008' - # start_date = '01 Jan 2007'; finish_date = '31 Dec 2007' # Use smaller window for quicker execution - - cross = 'USDJPY' - fx_options_trading_tenor = '1W' # Try changing between 1W and 1M! - - # Download the whole all market data for USDJPY for pricing options (FX vol surface + spot + FX forwards + depos) - md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date, - data_source='bloomberg', cut='10AM', category='fx-vol-market', - tickers=cross, fx_vol_tenor=['1W', '1M'], base_depos_tenor=['1W', '1M'], - cache_algo='cache_algo_return', base_depos_currencies=[cross[0:3], cross[3:6]]) + # We want to roll long 1M ATM call at expiry + # We'll mark to market the option price through the month by interpolating between 1W and 1M (and using whole vol curve + # at each tenor) + fx_options_curve = FXOptionsCurve(fx_options_trading_tenor=fx_options_trading_tenor, + roll_days_before=0, + roll_event='expiry-date', + roll_months=1, + fx_options_tenor_for_interpolation=['1W', '1M'], + strike='atm', + contract_type='european-call', + depo_tenor_for_option='1M', + position_multiplier=1.0, + tot_label='tot', + output_calculation_fields=True) + + # Let's trade a long 1M call, and we roll at expiry + df_cuemacro_option_call_tot = fx_options_curve.construct_total_return_index(cross, df_vol_market) + + # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX) + df_cuemacro_option_call_tc = fx_options_curve.apply_tc_signals_to_total_return_index(cross, df_cuemacro_option_call_tot, + option_tc_bp=5, spot_tc_bp=2) + + # Let's trade a long 1M OTM put, and we roll at expiry + df_cuemacro_option_put_tot = fx_options_curve.construct_total_return_index( + cross, df_vol_market, contract_type='european-put', strike='10d-otm', position_multiplier=1.0) + + # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX) + df_cuemacro_option_put_tc = fx_options_curve.apply_tc_signals_to_total_return_index(cross, df_cuemacro_option_put_tot, + option_tc_bp=5, spot_tc_bp=2) + + + # Get total returns for spot + df_bbg_tot = df_tot # from earlier! + df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns] + + # Calculate a hedged portfolio of spot + 2*options (can we reduce drawdowns?) + calculations = Calculations() + + ret_stats = RetStats() + + df_hedged = calculations.join([df_bbg_tot[cross + '-tot.close-bbg'].to_frame(), df_cuemacro_option_put_tc[cross + '-option-tot-with-tc.close'].to_frame()], how='outer') + df_hedged = df_hedged.fillna(method='ffill') + df_hedged = df_hedged.pct_change() + + df_hedged['Spot + 2*option put hedge'] = df_hedged[cross + '-tot.close-bbg'] + df_hedged[cross + '-option-tot-with-tc.close'] + + df_hedged.columns = RetStats(returns_df=df_hedged, ann_factor=252).summary() + + # Plot everything + + # P&L from call + chart.plot(calculations.create_mult_index_from_prices( + prepare_indices(cross=cross, df_option_tot=df_cuemacro_option_call_tot, + df_option_tc=df_cuemacro_option_call_tc, df_spot_tot=df_bbg_tot))) + + # P&L from put option, put option + TC and total returns from spot + chart.plot(calculations.create_mult_index_from_prices( + prepare_indices(cross=cross,df_option_tot=df_cuemacro_option_put_tot, + df_option_tc=df_cuemacro_option_put_tc, df_spot_tot=df_bbg_tot))) + + # P&L from put option + TC and total returns from spot + chart.plot(calculations.create_mult_index_from_prices( + prepare_indices(cross=cross, df_option_tc=df_cuemacro_option_put_tc, df_spot_tot=df_bbg_tot))) - df = market.fetch_market(md_request) + # P&L for total returns from spot and total returns from + 2*put option + TC (ie. hedged portfolio) + chart.plot(calculations.create_mult_index(df_hedged)) + + # Plot delta from put option + chart.plot(df_cuemacro_option_put_tot[cross + '-delta.close']) + + + ###### Fetch market data for pricing EURUSD options from 2006-2020 (ie. FX spot, FX forwards, FX deposits and FX vol quotes) + ###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation + ###### Enters a short 1W straddle, and MTM every day, and at expiry rolls into another 1W straddle + if run_example == 2 or run_example == 0: - # Fill data for every workday and use weekend calendar (note: this is a bit of a fudge, filling down) - # CHECK DATA isn't missing at start of series - df = df.resample('B').last().fillna(method='ffill') - df = df[df.index >= '09 Mar 2007'] # Try starting on a different day of the week & see how it impact P&L? - cal = 'WKD' + # Warning make sure you choose dates, where there is full vol surface! If vol points in the tenors you are looking at + # are missing then interpolation will fail (or if eg. spot data is missing etc.) + start_date = '08 Mar 2007'; finish_date = '31 Dec 2020' # Monday + # start_date = '09 Mar 2007'; finish_date = '31 Dec 2014' + # start_date = '04 Jan 2006'; finish_date = '31 Dec 2008' + # start_date = '01 Jan 2007'; finish_date = '31 Dec 2007' # Use smaller window for quicker execution + + cross = 'USDJPY' + fx_options_trading_tenor = '1W' # Try changing between 1W and 1M! + + # Download the whole all market data for USDJPY for pricing options (FX vol surface + spot + FX forwards + depos) + md_request = MarketDataRequest(start_date=start_date, finish_date=finish_date, + data_source='bloomberg', cut='10AM', category='fx-vol-market', + tickers=cross, fx_vol_tenor=['1W', '1M'], base_depos_tenor=['1W', '1M'], + cache_algo='cache_algo_return', base_depos_currencies=[cross[0:3], cross[3:6]]) - # Remove New Year's Day and Christmas - # df = Filter().filter_time_series_by_holidays(df, cal='FX') - - # We want to roll a short 1W option at expiry - # If we select longer dates, it will mark to market the price through the month by interpolating between eg. 1W and 1M - # (and using whole vol curve at each tenor) - fx_options_curve = FXOptionsCurve(fx_options_trading_tenor=fx_options_trading_tenor, - roll_days_before=0, - roll_event='expiry-date', - roll_months=1, # This is ignored if we roll on expiry date - fx_options_tenor_for_interpolation=['1W', '1M'], - strike='atm', - contract_type='european-straddle', - position_multiplier=-1.0, # +1.0 for long options, -1.0 for short options - output_calculation_fields=True, - freeze_implied_vol=True, - cal=cal, - cum_index='mult') + df = market.fetch_market(md_request) - # Let's trade a short straddle, and we roll at expiry - df_cuemacro_option_straddle_tot = fx_options_curve.construct_total_return_index(cross, df, - depo_tenor_for_option=fx_options_trading_tenor) + # Fill data for every workday and use weekend calendar (note: this is a bit of a fudge, filling down) + # CHECK DATA isn't missing at start of series + df = df.resample('B').last().fillna(method='ffill') + df = df[df.index >= '09 Mar 2007'] # Try starting on a different day of the week & see how it impact P&L? + cal = 'WKD' - # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX) - # Have wider spread for straddle (because adding call + put) - df_cuemacro_option_straddle_tc = fx_options_curve.apply_tc_signals_to_total_return_index(cross, df_cuemacro_option_straddle_tot, - option_tc_bp=10, spot_tc_bp=2) + # Remove New Year's Day and Christmas + # df = Filter().filter_time_series_by_holidays(df, cal='FX') + + # We want to roll a short 1W option at expiry + # If we select longer dates, it will mark to market the price through the month by interpolating between eg. 1W and 1M + # (and using whole vol curve at each tenor) + fx_options_curve = FXOptionsCurve(fx_options_trading_tenor=fx_options_trading_tenor, + roll_days_before=0, + roll_event='expiry-date', + roll_months=1, # This is ignored if we roll on expiry date + fx_options_tenor_for_interpolation=['1W', '1M'], + strike='atm', + contract_type='european-straddle', + position_multiplier=-1.0, # +1.0 for long options, -1.0 for short options + output_calculation_fields=True, + freeze_implied_vol=True, + cal=cal, + cum_index='mult') - # Get total returns for spot - md_request.abstract_curve = None + # Let's trade a short straddle, and we roll at expiry + df_cuemacro_option_straddle_tot = fx_options_curve.construct_total_return_index(cross, df, + depo_tenor_for_option=fx_options_trading_tenor) - # Get Bloomberg calculated total return indices (for spot) - md_request.category = 'fx-tot' - md_request.cut = 'NYC' + # Add transaction costs to the option index (bid/ask bp for the option premium and spot FX) + # Have wider spread for straddle (because adding call + put) + df_cuemacro_option_straddle_tc = fx_options_curve.apply_tc_signals_to_total_return_index(cross, df_cuemacro_option_straddle_tot, + option_tc_bp=10, spot_tc_bp=2) - df_bbg_tot = market.fetch_market(md_request) - df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns] + # Get total returns for spot + md_request.abstract_curve = None - calculations = Calculations() + # Get Bloomberg calculated total return indices (for spot) + md_request.category = 'fx-tot' + md_request.cut = 'NYC' - df_index = calculations.create_mult_index_from_prices( - prepare_indices(cross=cross, df_option_tc=df_cuemacro_option_straddle_tc, df_spot_tot=df_bbg_tot)) + df_bbg_tot = market.fetch_market(md_request) + df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns] - from finmarketpy.economics.quickchart import QuickChart + calculations = Calculations() - QuickChart(engine='plotly').plot_chart_with_ret_stats(df=df_index, plotly_plot_mode='offline_html', scale_factor=-1.5) \ No newline at end of file + df_index = calculations.create_mult_index_from_prices( + prepare_indices(cross=cross, df_option_tc=df_cuemacro_option_straddle_tc, df_spot_tot=df_bbg_tot)) + + from finmarketpy.economics.quickchart import QuickChart + + QuickChart(engine='plotly').plot_chart_with_ret_stats(df=df_index, plotly_plot_mode='offline_html', scale_factor=-1.5) \ No newline at end of file diff --git a/finmarketpy_examples/fx_options_pricing_examples.py b/finmarketpy_examples/fx_options_pricing_examples.py index e2a2cb4..882c2ad 100644 --- a/finmarketpy_examples/fx_options_pricing_examples.py +++ b/finmarketpy_examples/fx_options_pricing_examples.py @@ -48,254 +48,256 @@ # run_example = 6 - another USDJPY option # run_example = 7 - price USDBRL options -run_example = 7 +run_example = 1 -###### 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: +if __name__ == '__main__': - horizon_date = '23 Jun 2016' - cross = 'GBPUSD' + ###### 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: - # 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') + horizon_date = '23 Jun 2016' + cross = 'GBPUSD' - df = market.fetch_market(md_request) + # 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') - fx_vol_surface = FXVolSurface(market_df=df, asset=cross) + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + fx_vol_surface = FXVolSurface(market_df=df, asset=cross) - # Price several different options + fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) - print("atm 1M european call") - print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-call', tenor='1M').to_string()) + # Price several different options - 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()) + print("atm 1M european call") + print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-call', tenor='1M').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()) + 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') + ###### 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: - cross = 'USDJPY' + start_date = '02 Nov 2020'; finish_date = '05 Nov 2020' + horizon_date = pd.bdate_range(start_date, finish_date, freq='B') - # 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]]) + cross = 'USDJPY' - df = market.fetch_market(md_request) + # 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]]) - # 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) + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + # 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) - 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()) + fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) - 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("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("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()) + 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()) -###### 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: + 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()) - horizon_date = '18 Apr 2007' - cross = 'AUDUSD' + ###### 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: - # 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') + horizon_date = '18 Apr 2007' + cross = 'AUDUSD' - df = market.fetch_market(md_request) + # 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') - fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['ON', '1W', '1M']) + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['ON', '1W', '1M']) - # 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()) + fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) -###### Fetch market data for pricing AUDUSD options during start of 2008 Credit Crisis -if run_example == 4 or run_example == 0: + # 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()) - horizon_date = '17 Aug 2007' - cross = 'AUDUSD' + ###### Fetch market data for pricing AUDUSD options during start of 2008 Credit Crisis + if run_example == 4 or run_example == 0: - # 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') + horizon_date = '17 Aug 2007' + cross = 'AUDUSD' - df = market.fetch_market(md_request) + # 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') - fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W', '1M', '3M']) - fx_vol_surface.build_vol_surface(pd.Timestamp(horizon_date)) + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W', '1M', '3M']) + fx_vol_surface.build_vol_surface(pd.Timestamp(horizon_date)) - # Price several different options + 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") + # Price several different options - 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()) + # 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") -###### Fetch market data for pricing EURUSD options during start of 2006 -if run_example == 5 or run_example == 0: + 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()) - horizon_date = '04 Jan 2006' - cross = 'EURUSD' + ###### Fetch market data for pricing EURUSD options during start of 2006 + if run_example == 5 or run_example == 0: - # 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') + horizon_date = '04 Jan 2006' + cross = 'EURUSD' - df = market.fetch_market(md_request) + # 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') - fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W', '1M', '3M']) + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W', '1M', '3M']) - # Price several different options + 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 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()) + # Price several different options -###### Fetch market data for pricing USDJPY ATM 1W -if run_example == 6 or run_example == 0: + # 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()) - horizon_date = '30 March 2007' - cross = 'USDJPY' + ###### Fetch market data for pricing USDJPY ATM 1W + if run_example == 6 or run_example == 0: - # 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') + horizon_date = '30 March 2007' + cross = 'USDJPY' - df = market.fetch_market(md_request) + # 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') - fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W'], solver='nelmer-mead-numba') + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + fx_vol_surface = FXVolSurface(market_df=df, asset=cross, tenors=['1W'], solver='nelmer-mead-numba') - market_df = fx_vol_surface.get_all_market_data() + fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) - # Print 1W data - print(market_df[[x for x in market_df.columns if '1W' in x]][market_df.index == horizon_date].to_string()) + market_df = fx_vol_surface.get_all_market_data() - # Print ATM vol - fx_vol_surface.build_vol_surface(horizon_date) - fx_vol_surface.extract_vol_surface(num_strike_intervals=None) + # 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 " + str(fx_vol_surface.get_atm_vol(tenor='1W'))) + # Print ATM vol + fx_vol_surface.build_vol_surface(horizon_date) + fx_vol_surface.extract_vol_surface(num_strike_intervals=None) - # 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()) + 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' + ###### Price USDBRL option around 2018 2nd round of presidential election + if run_example == 7 or run_example == 0: - # 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') + horizon_date = '26 Oct 2018' + cross = 'USDBRL' + non_usd = 'BRL' - df = market.fetch_market(md_request) + # 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') - # Compute implied deposit BRL 1M from USDBRL forwards (and USD 1M depo) - fx_forwards_price = FXForwardsPricer() + df = market.fetch_market(md_request) - implied_depo_df = fx_forwards_price.calculate_implied_depo(cross, non_usd, market_df=df, - fx_forwards_tenor=['1W', '1M'], - depo_tenor=['1W', '1M']) + # Compute implied deposit BRL 1M from USDBRL forwards (and USD 1M depo) + fx_forwards_price = FXForwardsPricer() - implied_depo_df.columns = [x.replace('-implied-depo', '') for x in implied_depo_df.columns] - df = df.join(implied_depo_df, how='left') + implied_depo_df = fx_forwards_price.calculate_implied_depo(cross, non_usd, market_df=df, + fx_forwards_tenor=['1W', '1M'], + depo_tenor=['1W', '1M']) - # 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')) + implied_depo_df.columns = [x.replace('-implied-depo', '') for x in implied_depo_df.columns] + df = df.join(implied_depo_df, how='left') - # Price several different options - print(df) + # 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')) - print("atm 1M european put") - print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-put', tenor='1M').to_string()) + # Price several different options + print(df) - # 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()) + print("atm 1M european put") + print(fx_op.price_instrument(cross, pd.Timestamp(horizon_date), 'atm', contract_type='european-put', tenor='1M').to_string()) -###### Price GBPUSD option around Brexit with unquoted deltas -if run_example == 8 or run_example == 0: + # 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()) - horizon_date = '23 Jun 2016' - cross = 'GBPUSD' + ###### Price GBPUSD option around Brexit with unquoted deltas + if run_example == 8 or run_example == 0: - # 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') + horizon_date = '23 Jun 2016' + cross = 'GBPUSD' - df = market.fetch_market(md_request) + # 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') - fx_vol_surface = FXVolSurface(market_df=df, asset=cross) + df = market.fetch_market(md_request) - fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) + fx_vol_surface = FXVolSurface(market_df=df, asset=cross) - # 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()) + fx_op = FXOptionsPricer(fx_vol_surface=fx_vol_surface) - 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()) + # 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()) diff --git a/setup.py b/setup.py index 571d5a5..7221989 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ trading strategies using a simple to use API, which has prebuilt templates for you to define backtest.""" setup(name='finmarketpy', - version='0.11.10', + version='0.11.11', description='finmarketpy is a Python based library for backtesting trading strategies', author='Saeed Amen', author_email='saeed@cuemacro.com', @@ -24,5 +24,5 @@ 'sklearn', 'matplotlib', 'numba', - 'financepy'], + 'financepy==0.193'], zip_safe=False)