Skip to content

Commit

Permalink
Added intraday FX total returns
Browse files Browse the repository at this point in the history
  • Loading branch information
saeedamen committed Jan 22, 2021
1 parent eb70d8b commit a31a745
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 17 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ In finmarketpy/examples you will find several examples, including some simple tr

# finmarketpy log

* 22 Jan 2021
* FX spot total returns now supports intraday data and added example
* Fixed problem with Numba implementation of FX spot total returns
* 17 Jan 2021
* Fix vol surface examples to work with new FXVolSurface
* 16 Jan 2021
Expand Down
28 changes: 14 additions & 14 deletions finmarketpy/curve/fxspotcurve.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

market_constants = MarketConstants()

@guvectorize(['void(f8[:], f8[:], f8[:], f8[:], intp, intp, f8[:])'],
@guvectorize(['void(f8[:], f8[:], f8[:], f8[:], f8, f8, f8[:])'],
'(n),(n),(n),(n),(),()->(n)', cache=True, target="cpu", nopython=True)
def _spot_index_numba(spot, time_diff, base_deposit, terms_deposit, base_daycount, terms_daycount, out):

Expand All @@ -39,7 +39,7 @@ def _spot_index_numba(spot, time_diff, base_deposit, terms_deposit, base_daycoun
def _spot_index(spot, time_diff, base_deposit, terms_deposit, base_daycount, terms_daycount):
import numpy as np

out = np.zero((len(spot)))
out = np.zeros((len(spot)))
out[0] = 100

for i in range(1, len(out)):
Expand All @@ -51,10 +51,6 @@ def _spot_index(spot, time_diff, base_deposit, terms_deposit, base_daycount, ter

return out


def _spot_index():
pass

class FXSpotCurve(object):
"""Construct total return (spot) indices for FX. In future will also convert assets from local currency to foreign currency
denomination and construct indices from forwards series.
Expand Down Expand Up @@ -110,7 +106,7 @@ def fetch_continuous_time_series(self, md_request, market_data_generator, depo_t
spot_df = market.fetch_market(md_request_download)

return self.construct_total_return_index(md_request.tickers,
self._calculations.pandas_outer_join([spot_df, depo_df]), tenor=depo_tenor,
self._calculations.pandas_outer_join([spot_df, depo_df]), depo_tenor=depo_tenor,
output_calculation_fields=output_calculation_fields)
else:
# eg. we calculate via your domestic currency such as USD, so returns will be in your domestic currency
Expand Down Expand Up @@ -217,26 +213,30 @@ def construct_total_return_index(self, cross_fx, market_df, depo_tenor=None, out
carry = carry.fillna(method='bfill')

spot = spot[cross + ".close"].to_frame()
base_deposit = carry[base_deposit.columns]
terms_deposit = carry[terms_deposit.columns]

# Calculate the time difference between each data point
spot['index_col'] = spot.index
spot_vals = spot[cross + ".close"].values
base_deposit_vals = carry[cross[0:3] + depo_tenor + ".close"].values
terms_deposit_vals = carry[cross[3:6] + depo_tenor + ".close"].values

# Calculate the time difference between each data point (flooring it to whole days, because carry
# is accured when there's a new day)
spot['index_col'] = spot.index.floor('D')
time = spot['index_col'].diff()
spot = spot.drop('index_col', 1)

time_diff = time.values.astype(float) / 86400000000000.0 # get time difference in days
time_diff[0] = 0.0

# Use Numba to do total return index calculation given has many loops
total_return_index_df = pd.DataFrame(index=spot.index, columns=[cross + "-tot.close"],
data=_spot_index_numba(spot.values, time_diff, base_deposit.values, terms_deposit.values,
data=_spot_index_numba(spot_vals, time_diff, base_deposit_vals, terms_deposit_vals,
base_daycount, terms_daycount))

if output_calculation_fields:
total_return_index_df[cross + '-carry.close'] = carry
total_return_index_df[cross + '-tot-return.close'] = total_return_index_df / total_return_index_df.shift(1) - 1.0
total_return_index_df[cross + '-spot-return.close'] = spot / spot.shift(1) - 1.0
total_return_index_df[cross + '-spot-return.close'] = spot / spot.shift(1) - 1.0

# Use Numba to do total return index calculation given has many loops
total_return_index_df_agg.append(total_return_index_df)

return self._calculations.pandas_outer_join(total_return_index_df_agg)
3 changes: 1 addition & 2 deletions finmarketpy_examples/fx_options_indices_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@
# Choose run_example = 0 for everything
# run_example = 1 - create total return index AUDUSD 1M long calls (and separately long puts) over 2008 financial crisis and further
# run_example = 2 - create total return index USDJPY 1W short straddles over a long sample
# run_example = 3 - create total return index USDJPY 1W short straddles (only selling on the last day of every month)

run_example = 2
run_example = 0

def prepare_indices(cross, df_option_tot=None, df_option_tc=None, df_spot_tot=None):
df_list = []
Expand Down
65 changes: 64 additions & 1 deletion finmarketpy_examples/fx_spot_indices_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
Shows how to use finmarketpy to create total return indices for FX spot (ie. calculates spot returns + carry returns)
"""

import pandas as pd

# For plotting
from chartpy import Chart, Style

Expand All @@ -32,7 +34,8 @@
calculations = Calculations()

# Choose run_example = 0 for everything
# run_example = 1 - create total return indices from FX spot data + deposit for AUDJPY, and compare
# run_example = 1 - create daily total return indices from FX spot data + deposit for AUDJPY, and compare
# run_example = 2 - create intraday total return indices from FX spot data + deposit for GBPUSD, and compare with daily

run_example = 0

Expand Down Expand Up @@ -76,4 +79,64 @@
df = calculations.pandas_outer_join([df_tot, df_bbg_tot, df_spot, df_bbg_tot_forwards]).fillna(method='ffill')
df = calculations.create_mult_index_from_prices(df)

chart.plot(df)

###### Create total return indices plot for GBPUSD with intraday and daily data (from perspective of a USD investor)
###### Compare intraday and daily total return indices
if run_example == 2 or run_example == 0:

import pytz

# Get GBPUSD total returns from perspective of USD investor (via GBP and USD rates)
md_request = MarketDataRequest(start_date='01 Jan 2019', finish_date='01 Jul 2019',
data_source='bloomberg', cut='NYC', category='fx',
tickers=['GBPUSD'],
cache_algo='cache_algo_return',
abstract_curve=FXSpotCurve(construct_via_currency='USD', depo_tenor='ON'))

df_tot = market.fetch_market(md_request=md_request)
df_tot.columns = [x + '-tot-cuemacro' for x in df_tot.columns]
df_tot = df_tot.tz_localize(pytz.utc)
df_tot.index = df_tot.index + pd.Timedelta(hours=22) # Roughly NY close 2200 GMT

md_request.abstract_curve = None

# Get intraday spot data
md_request.freq = 'tick'
md_request.data_source = 'dukascopy'

df_intraday_spot = market.fetch_market(md_request=md_request)
df_intraday_spot = pd.DataFrame(df_intraday_spot.resample('1min').last().dropna())

# Get Bloomberg calculated total return indices (for spot)
md_request.category = 'fx-tot'
md_request.freq = 'daily'
md_request.data_source = 'bloomberg'

df_bbg_tot = market.fetch_market(md_request)
df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns]
df_bbg_tot = df_bbg_tot.tz_localize(pytz.utc)
df_bbg_tot.index = df_bbg_tot.index + pd.Timedelta(hours=22) # Roughly NY close 2200 GMT

md_request = MarketDataRequest(start_date='01 Jan 2019', finish_date='01 Jul 2019',
data_source='bloomberg', cut='NYC', category='base-depos',
tickers=['GBPON', 'USDON'],
cache_algo='cache_algo_return')

# Join daily deposit data with intraday spot data
# OK to fill down, because deposit data isn't very volatile
df_deposit_rates = market.fetch_market(md_request).tz_localize(pytz.utc)

df_intraday_market = df_intraday_spot.join(df_deposit_rates, how='left')
df_intraday_market = df_intraday_market.fillna(method='ffill').fillna(method='bfill')

df_intraday_tot = FXSpotCurve().construct_total_return_index('GBPUSD', df_intraday_market, depo_tenor='ON')

# df_intraday_spot.columns = [x + '-intraday-spot' for x in df_intraday_spot.columns]
df_intraday_tot.columns = [x + '-intraday-tot' for x in df_intraday_spot.columns]

# Combine into a single data frame and plot
df = calculations.pandas_outer_join([df_bbg_tot, df_tot, df_intraday_tot]).fillna(method='ffill')
df = calculations.create_mult_index_from_prices(df)

chart.plot(df)

0 comments on commit a31a745

Please sign in to comment.