mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-12-16 12:51:14 +00:00
chore: improve comments
This commit is contained in:
@@ -5,12 +5,13 @@ from datetime import datetime, timezone
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
from pandas import DataFrame, concat
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS
|
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS
|
||||||
from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode
|
from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode
|
||||||
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
||||||
from freqtrade.exchange import Exchange, binance_public_data
|
from freqtrade.exchange import Exchange, binance_public_data
|
||||||
|
from freqtrade.exchange.binance_public_data import concat
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
from freqtrade.exchange.exchange_types import FtHas, Tickers
|
from freqtrade.exchange.exchange_types import FtHas, Tickers
|
||||||
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_msecs
|
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_msecs
|
||||||
@@ -162,10 +163,11 @@ class Binance(Exchange):
|
|||||||
candle_type: CandleType,
|
candle_type: CandleType,
|
||||||
is_new_pair: bool = False,
|
is_new_pair: bool = False,
|
||||||
until_ms: int | None = None,
|
until_ms: int | None = None,
|
||||||
):
|
) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Fetch ohlcv fast by utilizing https://data.binance.vision
|
Fastly fetch OHLCV data by leveraging https://data.binance.vision.
|
||||||
"""
|
"""
|
||||||
|
# only download timeframes with significant improvements, otherwise fall back to rest API
|
||||||
if (candle_type == CandleType.SPOT and timeframe in ["1s", "1m", "3m", "5m"]) or (
|
if (candle_type == CandleType.SPOT and timeframe in ["1s", "1m", "3m", "5m"]) or (
|
||||||
candle_type == CandleType.FUTURES and timeframe in ["1m", "3m", "5m", "15m", "30m"]
|
candle_type == CandleType.FUTURES and timeframe in ["1m", "3m", "5m", "15m", "30m"]
|
||||||
):
|
):
|
||||||
@@ -179,10 +181,14 @@ class Binance(Exchange):
|
|||||||
markets=self.markets,
|
markets=self.markets,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# download the remaining data from rest API
|
||||||
if df.empty:
|
if df.empty:
|
||||||
rest_since_ms = since_ms
|
rest_since_ms = since_ms
|
||||||
else:
|
else:
|
||||||
rest_since_ms = dt_ts(df.iloc[-1].date) + timeframe_to_msecs(timeframe)
|
rest_since_ms = dt_ts(df.iloc[-1].date) + timeframe_to_msecs(timeframe)
|
||||||
|
|
||||||
|
# make sure since <= until
|
||||||
if until_ms and rest_since_ms > until_ms:
|
if until_ms and rest_since_ms > until_ms:
|
||||||
rest_df = DataFrame()
|
rest_df = DataFrame()
|
||||||
else:
|
else:
|
||||||
@@ -195,6 +201,7 @@ class Binance(Exchange):
|
|||||||
until_ms=until_ms,
|
until_ms=until_ms,
|
||||||
)
|
)
|
||||||
all_df = concat([df, rest_df])
|
all_df = concat([df, rest_df])
|
||||||
|
return all_df
|
||||||
else:
|
else:
|
||||||
return super().get_historic_ohlcv(
|
return super().get_historic_ohlcv(
|
||||||
pair=pair,
|
pair=pair,
|
||||||
@@ -204,7 +211,6 @@ class Binance(Exchange):
|
|||||||
is_new_pair=is_new_pair,
|
is_new_pair=is_new_pair,
|
||||||
until_ms=until_ms,
|
until_ms=until_ms,
|
||||||
)
|
)
|
||||||
return all_df
|
|
||||||
|
|
||||||
def funding_fee_cutoff(self, open_date: datetime):
|
def funding_fee_cutoff(self, open_date: datetime):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -42,12 +42,22 @@ async def fetch_ohlcv(
|
|||||||
stop_on_404: bool = True,
|
stop_on_404: bool = True,
|
||||||
) -> DataFrame:
|
) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Fetch OHLCV data from https://data.binance.vision/
|
Fetch OHLCV data from https://data.binance.vision
|
||||||
|
The function makes its best effort to download data within the time range
|
||||||
|
[`since_ms`, `until_ms`) -- including `since_ms`, but excluding `until_ms`.
|
||||||
|
If `stop_one_404` is True, this returned DataFrame is guaranteed to start from `since_ms`
|
||||||
|
with no gaps in the data.
|
||||||
|
|
||||||
:candle_type: Currently only spot and futures are supported
|
:candle_type: Currently only spot and futures are supported
|
||||||
|
:pair: symbol name in CCXT convention
|
||||||
|
:since_ms: the start timestamp of data, including itself
|
||||||
|
:until_ms: the end timestamp of data, excluding itself
|
||||||
:param until_ms: `None` indicates the timestamp of the latest available data
|
:param until_ms: `None` indicates the timestamp of the latest available data
|
||||||
|
:markets: the CCXT markets dict, when it's None, the function will load the markets data
|
||||||
|
from a new `ccxt.binance` instance
|
||||||
:param stop_on_404: Stop to download the following data when a 404 returned
|
:param stop_on_404: Stop to download the following data when a 404 returned
|
||||||
:return: the date range is between [since_ms, until_ms),
|
:return: the date range is between [since_ms, until_ms), return an empty DataFrame if no data
|
||||||
return and empty DataFrame if no data available in the time range
|
available in the time range
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if candle_type == CandleType.SPOT:
|
if candle_type == CandleType.SPOT:
|
||||||
@@ -82,6 +92,7 @@ async def fetch_ohlcv(
|
|||||||
df = DataFrame()
|
df = DataFrame()
|
||||||
|
|
||||||
if not df.empty:
|
if not df.empty:
|
||||||
|
# only return the data within the requested time range
|
||||||
return df.loc[(df["date"] >= start) & (df["date"] < end)]
|
return df.loc[(df["date"] >= start) & (df["date"] < end)]
|
||||||
else:
|
else:
|
||||||
return df
|
return df
|
||||||
@@ -102,7 +113,9 @@ async def _fetch_ohlcv(
|
|||||||
end: datetime.date,
|
end: datetime.date,
|
||||||
stop_on_404: bool,
|
stop_on_404: bool,
|
||||||
) -> DataFrame:
|
) -> DataFrame:
|
||||||
|
# daily dataframes
|
||||||
dfs: list[DataFrame | None] = []
|
dfs: list[DataFrame | None] = []
|
||||||
|
# the current day being processing, starting at 1.
|
||||||
current_day = 0
|
current_day = 0
|
||||||
|
|
||||||
connector = aiohttp.TCPConnector(limit=100)
|
connector = aiohttp.TCPConnector(limit=100)
|
||||||
@@ -116,8 +129,10 @@ async def _fetch_ohlcv(
|
|||||||
current_day += 1
|
current_day += 1
|
||||||
if isinstance(result, Http404):
|
if isinstance(result, Http404):
|
||||||
if stop_on_404:
|
if stop_on_404:
|
||||||
if current_day == 1:
|
# A 404 error on the first day indicates missing data
|
||||||
|
# on https://data.binance.vision, we provide the warning and the advice.
|
||||||
# https://github.com/freqtrade/freqtrade/blob/acc53065e5fa7ab5197073276306dc9dc3adbfa3/tests/exchange_online/test_binance_compare_ohlcv.py#L7
|
# https://github.com/freqtrade/freqtrade/blob/acc53065e5fa7ab5197073276306dc9dc3adbfa3/tests/exchange_online/test_binance_compare_ohlcv.py#L7
|
||||||
|
if current_day == 1:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Failed to use fast download, fall back to rest API download, this "
|
"Failed to use fast download, fall back to rest API download, this "
|
||||||
"can take more time. If you're downloading BTC/USDT:USDT, "
|
"can take more time. If you're downloading BTC/USDT:USDT, "
|
||||||
@@ -131,8 +146,7 @@ async def _fetch_ohlcv(
|
|||||||
dfs.append(None)
|
dfs.append(None)
|
||||||
elif isinstance(result, BaseException):
|
elif isinstance(result, BaseException):
|
||||||
logger.warning(f"An exception raised: : {result}")
|
logger.warning(f"An exception raised: : {result}")
|
||||||
# Directly return the existing data, do not allow the gap
|
# Directly return the existing data, do not allow the gap within the data
|
||||||
# between the data
|
|
||||||
return concat(dfs)
|
return concat(dfs)
|
||||||
else:
|
else:
|
||||||
dfs.append(result)
|
dfs.append(result)
|
||||||
@@ -175,7 +189,7 @@ async def get_daily_ohlcv(
|
|||||||
session: aiohttp.ClientSession,
|
session: aiohttp.ClientSession,
|
||||||
retry_count: int = 3,
|
retry_count: int = 3,
|
||||||
retry_delay: float = 0.0,
|
retry_delay: float = 0.0,
|
||||||
) -> DataFrame | None | Exception:
|
) -> DataFrame | Exception:
|
||||||
"""
|
"""
|
||||||
Get daily OHLCV from https://data.binance.vision
|
Get daily OHLCV from https://data.binance.vision
|
||||||
See https://github.com/binance/binance-public-data
|
See https://github.com/binance/binance-public-data
|
||||||
@@ -225,7 +239,10 @@ async def get_daily_ohlcv(
|
|||||||
else:
|
else:
|
||||||
raise BadHttpStatus(f"{resp.status} - {resp.reason}")
|
raise BadHttpStatus(f"{resp.status} - {resp.reason}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
retry += 1
|
if isinstance(e, Http404):
|
||||||
|
return e
|
||||||
|
else:
|
||||||
if retry >= retry_count:
|
if retry >= retry_count:
|
||||||
logger.debug(f"Failed to get data from {url}: {e}")
|
logger.debug(f"Failed to get data from {url}: {e}")
|
||||||
return e
|
return e
|
||||||
|
retry += 1
|
||||||
|
|||||||
@@ -254,43 +254,48 @@ async def test_get_daily_ohlcv(mocker, testdatadir):
|
|||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
path = testdatadir / "binance/binance_public_data/spot-klines-BTCUSDT-1h-2024-10-28.zip"
|
path = testdatadir / "binance/binance_public_data/spot-klines-BTCUSDT-1h-2024-10-28.zip"
|
||||||
mocker.patch(
|
get = mocker.patch(
|
||||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||||
return_value=MockResponse(path.read_bytes(), 200),
|
return_value=MockResponse(path.read_bytes(), 200),
|
||||||
)
|
)
|
||||||
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session)
|
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session)
|
||||||
|
assert get.call_count == 1
|
||||||
assert df["date"].iloc[0] == first_date
|
assert df["date"].iloc[0] == first_date
|
||||||
assert df["date"].iloc[-1] == last_date
|
assert df["date"].iloc[-1] == last_date
|
||||||
|
|
||||||
path = (
|
path = (
|
||||||
testdatadir / "binance/binance_public_data/futures-um-klines-BTCUSDT-1h-2024-10-28.zip"
|
testdatadir / "binance/binance_public_data/futures-um-klines-BTCUSDT-1h-2024-10-28.zip"
|
||||||
)
|
)
|
||||||
mocker.patch(
|
get = mocker.patch(
|
||||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||||
return_value=MockResponse(path.read_bytes(), 200),
|
return_value=MockResponse(path.read_bytes(), 200),
|
||||||
)
|
)
|
||||||
df = await get_daily_ohlcv("futures/um", symbol, timeframe, date, session)
|
df = await get_daily_ohlcv("futures/um", symbol, timeframe, date, session)
|
||||||
|
assert get.call_count == 1
|
||||||
assert df["date"].iloc[0] == first_date
|
assert df["date"].iloc[0] == first_date
|
||||||
assert df["date"].iloc[-1] == last_date
|
assert df["date"].iloc[-1] == last_date
|
||||||
|
|
||||||
mocker.patch(
|
get = mocker.patch(
|
||||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||||
return_value=MockResponse(b"", 404),
|
return_value=MockResponse(b"", 404),
|
||||||
)
|
)
|
||||||
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session, retry_delay=0)
|
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session, retry_delay=0)
|
||||||
|
assert get.call_count == 1
|
||||||
assert isinstance(df, Http404)
|
assert isinstance(df, Http404)
|
||||||
|
|
||||||
mocker.patch(
|
get = mocker.patch(
|
||||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||||
return_value=MockResponse(b"", 500),
|
return_value=MockResponse(b"", 500),
|
||||||
)
|
)
|
||||||
mocker.patch("asyncio.sleep")
|
mocker.patch("asyncio.sleep")
|
||||||
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session)
|
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session)
|
||||||
|
assert get.call_count == 4 # 1 + 3 default retries
|
||||||
assert isinstance(df, BadHttpStatus)
|
assert isinstance(df, BadHttpStatus)
|
||||||
|
|
||||||
mocker.patch(
|
get = mocker.patch(
|
||||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||||
return_value=MockResponse(b"nop", 200),
|
return_value=MockResponse(b"nop", 200),
|
||||||
)
|
)
|
||||||
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session)
|
df = await get_daily_ohlcv("spot", symbol, timeframe, date, session)
|
||||||
|
assert get.call_count == 4 # 1 + 3 default retries
|
||||||
assert isinstance(df, zipfile.BadZipFile)
|
assert isinstance(df, zipfile.BadZipFile)
|
||||||
|
|||||||
Reference in New Issue
Block a user