Skip to content

Commit

Permalink
Fix #879 - Include toISO in dydx api requests fetch_ohlcv_dydx (#951)
Browse files Browse the repository at this point in the history
* Add TIMEFRAME_TO_DYDX_RESOLUTION_SECONDS into constants

* Update fetch_ohlcv_dydx.py to include _time_delta_from_timeframe function and toISO arg

* Update test_fetch_ohlcv_dydx.py to fix fromISO issue and update tests

* Add test cases for _time_delta_from_timeframe function
  • Loading branch information
trizin authored Apr 29, 2024
1 parent 3482a2d commit 2cd7c1a
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 14 deletions.
1 change: 1 addition & 0 deletions pdr_backend/exchange/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
# all possible resolution values are listed at:
# https://docs.dydx.exchange/developers/indexer/indexer_api#enumerated-values
TIMEFRAME_TO_DYDX_RESOLUTION = {"5m": "5MINS", "1h": "1HOUR"}
TIMEFRAME_TO_DYDX_RESOLUTION_SECONDS = {"5m": 300, "1h": 3600}
14 changes: 13 additions & 1 deletion pdr_backend/exchange/fetch_ohlcv_dydx.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from datetime import timedelta
from typing import List, Optional, Union

from enforce_typing import enforce_types
Expand All @@ -9,6 +10,7 @@
from pdr_backend.exchange.constants import (
BASE_URL_DYDX,
TIMEFRAME_TO_DYDX_RESOLUTION,
TIMEFRAME_TO_DYDX_RESOLUTION_SECONDS,
)
from pdr_backend.util.time_types import UnixTimeMs

Expand Down Expand Up @@ -54,11 +56,13 @@ def fetch_ohlcv_dydx(
ticker = _dydx_ticker(pair_str)
resolution = _dydx_resolution(timeframe)
fromISO = since.to_iso_timestr()
toISO_dt = since.to_dt() + _time_delta_from_timeframe(timeframe, limit)
toISO = UnixTimeMs(toISO_dt.timestamp() * 1e3).to_iso_timestr()
headers = {"Accept": "application/json"}
try:
s = (
f"{baseURL}/candles/perpetualMarkets/{ticker}"
f"?resolution={resolution}&fromISO={fromISO}&limit={limit}"
f"?resolution={resolution}&fromISO={fromISO}&toISO={toISO}&limit={limit}"
)
response = requests.get(s, headers=headers, timeout=20)

Expand Down Expand Up @@ -125,3 +129,11 @@ def _dydx_resolution(timeframe: str):
@enforce_types
def _float_or_none(x: Optional[str]) -> Optional[float]:
return None if x is None else float(x)


def _time_delta_from_timeframe(timeframe: str, limit: int) -> timedelta:
# Convert timeframe and limit to a timedelta.
if timeframe not in TIMEFRAME_TO_DYDX_RESOLUTION_SECONDS:
raise ValueError(f"Don't currently support timeframe={timeframe}")
second_duration = TIMEFRAME_TO_DYDX_RESOLUTION_SECONDS[timeframe]
return timedelta(seconds=second_duration * limit)
63 changes: 50 additions & 13 deletions pdr_backend/exchange/test/test_fetch_ohlcv_dydx.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from datetime import datetime, timedelta
from enforce_typing import enforce_types

import pytest
import requests_mock

Expand All @@ -8,6 +10,7 @@
_dydx_ticker,
_dydx_resolution,
_float_or_none,
_time_delta_from_timeframe,
)
from pdr_backend.util.time_types import UnixTimeMs

Expand Down Expand Up @@ -76,9 +79,8 @@ def test_dydx__real_response__basic():
assert len(tohlcv) == 6


@pytest.mark.skip(reason="Unskip once #879 is fixed")
@enforce_types
def test_dydx__real_response__fromISO_issue_879():
def test_dydx__real_response__fromISO():
# setup problem: 'tsince'
tsince_iso_str = "2024-02-27_00:00:00.000"
tsince_UnixTimeMs = UnixTimeMs.from_timestr(tsince_iso_str)
Expand All @@ -88,22 +90,34 @@ def test_dydx__real_response__fromISO_issue_879():
# setup problem: the rest
symbol = "BTC/USD"
timeframe = "5m"
limit = 1
limit = 10

# get result
raw_tohlcv_data = fetch_ohlcv_dydx(symbol, timeframe, tsince_UnixTimeMs, limit)
tohlcv = raw_tohlcv_data[0]

# dydx api doesn't properly address fromISO. We must fix this, see #879
t = tohlcv[0]
t_UnixTimeMs = UnixTimeMs(t)
t_iso_str = t_UnixTimeMs.to_iso_timestr() # bad eg '2024-04-16T00:25:00.000Z'
assert t_iso_str == tsince_iso_str
assert t_UnixTimeMs == tsince_UnixTimeMs
assert len(raw_tohlcv_data) == 10, "Length must be 10, limit is 10"

# First timestamp is expected to be:
# 2024-02-27T00:00:00.000Z
dt = datetime.fromisoformat("2024-02-27T00:00:00.000")
unix_ms = dt.timestamp() * 1e3
assert (
raw_tohlcv_data[-1][0] == unix_ms
), f"Expected {unix_ms}, got {raw_tohlcv_data[-1][0]}"

# Last timestamp is expected to be:
# 2024-02-27T00:45:00.000Z
dt = datetime.fromisoformat("2024-02-27T00:45:00.000")
unix_ms = dt.timestamp() * 1e3
assert (
raw_tohlcv_data[0][0] == unix_ms
), f"Expected {unix_ms}, got {raw_tohlcv_data[0][0]}"

# when #879 fixed, add proper vals here
# ohlcv = tohlcv[1:]
# assert ohlcv == (fix me val, ..)
# Price checks
assert raw_tohlcv_data[-1][1] == 54541.0
assert raw_tohlcv_data[-1][2] == 54661.0
assert raw_tohlcv_data[0][3] == 54545.0
assert raw_tohlcv_data[9][4] == 54645.0


@enforce_types
Expand Down Expand Up @@ -168,3 +182,26 @@ def test_fetch_ohlcv_float_or_none():
_ = _float_or_none(3)
with pytest.raises(ValueError):
_ = _float_or_none("foo")


def test_time_delta_from_valid_timeframes():
test_cases = [
("5m", 1, timedelta(seconds=300 * 1)),
("5m", 10, timedelta(seconds=300 * 10)),
("5m", 25, timedelta(seconds=300 * 25)),
("5m", 500, timedelta(seconds=300 * 500)),
("1h", 1, timedelta(seconds=3600 * 1)),
("1h", 5, timedelta(seconds=3600 * 5)),
("1h", 42, timedelta(seconds=3600 * 42)),
]

for timeframe, limit, expected in test_cases:
assert (
_time_delta_from_timeframe(timeframe, limit) == expected
), f"Failed for timeframe={timeframe}"


def test_time_delta_from_invalid_timeframe():
with pytest.raises(ValueError) as excinfo:
_time_delta_from_timeframe("1hh", 1)
assert "Don't currently support timeframe=1hh" in str(excinfo.value)

0 comments on commit 2cd7c1a

Please sign in to comment.