Skip to content

Commit a31a745

Browse files
committed
Added intraday FX total returns
1 parent eb70d8b commit a31a745

File tree

4 files changed

+82
-17
lines changed

4 files changed

+82
-17
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ In finmarketpy/examples you will find several examples, including some simple tr
166166

167167
# finmarketpy log
168168

169+
* 22 Jan 2021
170+
* FX spot total returns now supports intraday data and added example
171+
* Fixed problem with Numba implementation of FX spot total returns
169172
* 17 Jan 2021
170173
* Fix vol surface examples to work with new FXVolSurface
171174
* 16 Jan 2021

finmarketpy/curve/fxspotcurve.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
market_constants = MarketConstants()
2525

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

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

42-
out = np.zero((len(spot)))
42+
out = np.zeros((len(spot)))
4343
out[0] = 100
4444

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

5252
return out
5353

54-
55-
def _spot_index():
56-
pass
57-
5854
class FXSpotCurve(object):
5955
"""Construct total return (spot) indices for FX. In future will also convert assets from local currency to foreign currency
6056
denomination and construct indices from forwards series.
@@ -110,7 +106,7 @@ def fetch_continuous_time_series(self, md_request, market_data_generator, depo_t
110106
spot_df = market.fetch_market(md_request_download)
111107

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

219215
spot = spot[cross + ".close"].to_frame()
220-
base_deposit = carry[base_deposit.columns]
221-
terms_deposit = carry[terms_deposit.columns]
222216

223-
# Calculate the time difference between each data point
224-
spot['index_col'] = spot.index
217+
spot_vals = spot[cross + ".close"].values
218+
base_deposit_vals = carry[cross[0:3] + depo_tenor + ".close"].values
219+
terms_deposit_vals = carry[cross[3:6] + depo_tenor + ".close"].values
220+
221+
# Calculate the time difference between each data point (flooring it to whole days, because carry
222+
# is accured when there's a new day)
223+
spot['index_col'] = spot.index.floor('D')
225224
time = spot['index_col'].diff()
226225
spot = spot.drop('index_col', 1)
227226

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

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

234235
if output_calculation_fields:
235236
total_return_index_df[cross + '-carry.close'] = carry
236237
total_return_index_df[cross + '-tot-return.close'] = total_return_index_df / total_return_index_df.shift(1) - 1.0
237-
total_return_index_df[cross + '-spot-return.close'] = spot / spot.shift(1) - 1.0
238+
total_return_index_df[cross + '-spot-return.close'] = spot / spot.shift(1) - 1.0
238239

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

242242
return self._calculations.pandas_outer_join(total_return_index_df_agg)

finmarketpy_examples/fx_options_indices_examples.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@
4545
# Choose run_example = 0 for everything
4646
# run_example = 1 - create total return index AUDUSD 1M long calls (and separately long puts) over 2008 financial crisis and further
4747
# run_example = 2 - create total return index USDJPY 1W short straddles over a long sample
48-
# run_example = 3 - create total return index USDJPY 1W short straddles (only selling on the last day of every month)
4948

50-
run_example = 2
49+
run_example = 0
5150

5251
def prepare_indices(cross, df_option_tot=None, df_option_tc=None, df_spot_tot=None):
5352
df_list = []

finmarketpy_examples/fx_spot_indices_examples.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
Shows how to use finmarketpy to create total return indices for FX spot (ie. calculates spot returns + carry returns)
1717
"""
1818

19+
import pandas as pd
20+
1921
# For plotting
2022
from chartpy import Chart, Style
2123

@@ -32,7 +34,8 @@
3234
calculations = Calculations()
3335

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

3740
run_example = 0
3841

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

82+
chart.plot(df)
83+
84+
###### Create total return indices plot for GBPUSD with intraday and daily data (from perspective of a USD investor)
85+
###### Compare intraday and daily total return indices
86+
if run_example == 2 or run_example == 0:
87+
88+
import pytz
89+
90+
# Get GBPUSD total returns from perspective of USD investor (via GBP and USD rates)
91+
md_request = MarketDataRequest(start_date='01 Jan 2019', finish_date='01 Jul 2019',
92+
data_source='bloomberg', cut='NYC', category='fx',
93+
tickers=['GBPUSD'],
94+
cache_algo='cache_algo_return',
95+
abstract_curve=FXSpotCurve(construct_via_currency='USD', depo_tenor='ON'))
96+
97+
df_tot = market.fetch_market(md_request=md_request)
98+
df_tot.columns = [x + '-tot-cuemacro' for x in df_tot.columns]
99+
df_tot = df_tot.tz_localize(pytz.utc)
100+
df_tot.index = df_tot.index + pd.Timedelta(hours=22) # Roughly NY close 2200 GMT
101+
102+
md_request.abstract_curve = None
103+
104+
# Get intraday spot data
105+
md_request.freq = 'tick'
106+
md_request.data_source = 'dukascopy'
107+
108+
df_intraday_spot = market.fetch_market(md_request=md_request)
109+
df_intraday_spot = pd.DataFrame(df_intraday_spot.resample('1min').last().dropna())
110+
111+
# Get Bloomberg calculated total return indices (for spot)
112+
md_request.category = 'fx-tot'
113+
md_request.freq = 'daily'
114+
md_request.data_source = 'bloomberg'
115+
116+
df_bbg_tot = market.fetch_market(md_request)
117+
df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns]
118+
df_bbg_tot = df_bbg_tot.tz_localize(pytz.utc)
119+
df_bbg_tot.index = df_bbg_tot.index + pd.Timedelta(hours=22) # Roughly NY close 2200 GMT
120+
121+
md_request = MarketDataRequest(start_date='01 Jan 2019', finish_date='01 Jul 2019',
122+
data_source='bloomberg', cut='NYC', category='base-depos',
123+
tickers=['GBPON', 'USDON'],
124+
cache_algo='cache_algo_return')
125+
126+
# Join daily deposit data with intraday spot data
127+
# OK to fill down, because deposit data isn't very volatile
128+
df_deposit_rates = market.fetch_market(md_request).tz_localize(pytz.utc)
129+
130+
df_intraday_market = df_intraday_spot.join(df_deposit_rates, how='left')
131+
df_intraday_market = df_intraday_market.fillna(method='ffill').fillna(method='bfill')
132+
133+
df_intraday_tot = FXSpotCurve().construct_total_return_index('GBPUSD', df_intraday_market, depo_tenor='ON')
134+
135+
# df_intraday_spot.columns = [x + '-intraday-spot' for x in df_intraday_spot.columns]
136+
df_intraday_tot.columns = [x + '-intraday-tot' for x in df_intraday_spot.columns]
137+
138+
# Combine into a single data frame and plot
139+
df = calculations.pandas_outer_join([df_bbg_tot, df_tot, df_intraday_tot]).fillna(method='ffill')
140+
df = calculations.create_mult_index_from_prices(df)
141+
79142
chart.plot(df)

0 commit comments

Comments
 (0)