Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions alphapy/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@

logger = logging.getLogger(__name__)

FXMACRODATA_API_ROOT = 'https://fxmacrodata.com/api/v1'


#
# Function get_data
Expand Down Expand Up @@ -724,11 +726,69 @@ def get_yahoo_data(schema, subschema, symbol, intraday_data, data_fractal,
return df


#
# Function get_fxmacrodata_data
#

def get_fxmacrodata_data(schema, subschema, symbol, intraday_data, data_fractal,
from_date, to_date, lookback_period):
r"""Get daily FX reference rates from FXMacroData.

FXMacroData returns one official reference value per currency pair and
date. The value is copied into open, high, low, and close with zero volume
so MarketFlow can consume it through the normal OHLCV path.

"""

df = pd.DataFrame()
if intraday_data:
logger.info("FXMacroData supports daily reference rates, not intraday bars")
return df

pair = symbol.upper().replace('/', '').replace('-', '').replace('_', '')
if len(pair) != 6:
logger.error("FXMacroData symbol must be formatted like EURUSD or EUR/USD")
return df

base = pair[:3]
quote = pair[3:]
url = SSEP.join([FXMACRODATA_API_ROOT.rstrip('/'), 'forex', base, quote])
params = {
'start_date': from_date,
'end_date': to_date,
'limit': 5000,
}
api_key = os.environ.get('FXMACRODATA_API_KEY')
if api_key:
params['api_key'] = api_key

try:
response = requests.get(url, params=params)
response.raise_for_status()
rows = response.json().get('data', [])
except Exception:
logger.info("Could not retrieve %s data with FXMacroData", symbol.upper())
return df

records = []
for row in rows:
value = float(row['val'])
records.append((row['date'], value, value, value, value, 0.0))

if records:
df = pd.DataFrame.from_records(
records,
columns=['date', 'open', 'high', 'low', 'close', 'volume'])

return df


#
# Data Dispatch Tables
#

data_dispatch_table = {'google' : get_google_data,
'fxmacrodata' : get_fxmacrodata_data,
'iex' : get_iex_data,
'pandas' : get_pandas_data,
'quandl' : get_quandl_data,
Expand Down
51 changes: 51 additions & 0 deletions tests/test_fxmacrodata_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest

from alphapy import data


class TestFXMacroDataData(unittest.TestCase):

def test_get_fxmacrodata_data(self):
class MockResponse:
def raise_for_status(self):
pass

def json(self):
return {
'data': [
{'date': '2026-01-02', 'val': 1.2},
{'date': '2026-01-01', 'val': 1.1},
]
}

calls = {}

def mock_get(url, params):
calls['url'] = url
calls['params'] = params
return MockResponse()

original_get = data.requests.get
try:
data.requests.get = mock_get
df = data.get_fxmacrodata_data(
'fxmacrodata',
'',
'EUR/USD',
False,
'1D',
'2026-01-01',
'2026-01-02',
2,
)
finally:
data.requests.get = original_get

self.assertEqual(calls['url'], 'https://fxmacrodata.com/api/v1/forex/EUR/USD')
self.assertEqual(calls['params']['start_date'], '2026-01-01')
self.assertEqual(list(df.columns), ['date', 'open', 'high', 'low', 'close', 'volume'])
self.assertEqual(list(df['close']), [1.2, 1.1])


if __name__ == '__main__':
unittest.main()