Compare commits
3 Commits
dependabot
...
2023.2
Author | SHA1 | Date | |
---|---|---|---|
|
a31045874e | ||
|
25724ef729 | ||
|
46458bf5eb |
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@@ -90,14 +90,14 @@ jobs:
|
|||||||
freqtrade create-userdir --userdir user_data
|
freqtrade create-userdir --userdir user_data
|
||||||
freqtrade hyperopt --datadir tests/testdata -e 6 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
freqtrade hyperopt --datadir tests/testdata -e 6 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
||||||
|
|
||||||
|
- name: Flake8
|
||||||
|
run: |
|
||||||
|
flake8
|
||||||
|
|
||||||
- name: Sort imports (isort)
|
- name: Sort imports (isort)
|
||||||
run: |
|
run: |
|
||||||
isort --check .
|
isort --check .
|
||||||
|
|
||||||
- name: Run Ruff
|
|
||||||
run: |
|
|
||||||
ruff check --format=github .
|
|
||||||
|
|
||||||
- name: Mypy
|
- name: Mypy
|
||||||
run: |
|
run: |
|
||||||
mypy freqtrade scripts tests
|
mypy freqtrade scripts tests
|
||||||
@@ -186,14 +186,14 @@ jobs:
|
|||||||
freqtrade create-userdir --userdir user_data
|
freqtrade create-userdir --userdir user_data
|
||||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
||||||
|
|
||||||
|
- name: Flake8
|
||||||
|
run: |
|
||||||
|
flake8
|
||||||
|
|
||||||
- name: Sort imports (isort)
|
- name: Sort imports (isort)
|
||||||
run: |
|
run: |
|
||||||
isort --check .
|
isort --check .
|
||||||
|
|
||||||
- name: Run Ruff
|
|
||||||
run: |
|
|
||||||
ruff check --format=github .
|
|
||||||
|
|
||||||
- name: Mypy
|
- name: Mypy
|
||||||
run: |
|
run: |
|
||||||
mypy freqtrade scripts
|
mypy freqtrade scripts
|
||||||
@@ -248,9 +248,9 @@ jobs:
|
|||||||
freqtrade create-userdir --userdir user_data
|
freqtrade create-userdir --userdir user_data
|
||||||
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
|
||||||
|
|
||||||
- name: Run Ruff
|
- name: Flake8
|
||||||
run: |
|
run: |
|
||||||
ruff check --format=github .
|
flake8
|
||||||
|
|
||||||
- name: Mypy
|
- name: Mypy
|
||||||
run: |
|
run: |
|
||||||
|
@@ -27,12 +27,6 @@ repos:
|
|||||||
name: isort (python)
|
name: isort (python)
|
||||||
# stages: [push]
|
# stages: [push]
|
||||||
|
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
|
||||||
# Ruff version.
|
|
||||||
rev: 'v0.0.251'
|
|
||||||
hooks:
|
|
||||||
- id: ruff
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
|
@@ -45,17 +45,16 @@ pytest tests/test_<file_name>.py::test_<method_name>
|
|||||||
|
|
||||||
### 2. Test if your code is PEP8 compliant
|
### 2. Test if your code is PEP8 compliant
|
||||||
|
|
||||||
#### Run Ruff
|
#### Run Flake8
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ruff .
|
flake8 freqtrade tests scripts
|
||||||
```
|
```
|
||||||
|
|
||||||
We receive a lot of code that fails the `ruff` checks.
|
We receive a lot of code that fails the `flake8` checks.
|
||||||
To help with that, we encourage you to install the git pre-commit
|
To help with that, we encourage you to install the git pre-commit
|
||||||
hook that will warn you when you try to commit code that fails these checks.
|
hook that will warn you when you try to commit code that fails these checks.
|
||||||
|
Guide for installing them is [here](http://flake8.pycqa.org/en/latest/user/using-hooks.html).
|
||||||
you can manually run pre-commit with `pre-commit run -a`.
|
|
||||||
|
|
||||||
##### Additional styles applied
|
##### Additional styles applied
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ This will spin up a local server (usually on port 8000) so you can see if everyt
|
|||||||
To configure a development environment, you can either use the provided [DevContainer](#devcontainer-setup), or use the `setup.sh` script and answer "y" when asked "Do you want to install dependencies for dev [y/N]? ".
|
To configure a development environment, you can either use the provided [DevContainer](#devcontainer-setup), or use the `setup.sh` script and answer "y" when asked "Do you want to install dependencies for dev [y/N]? ".
|
||||||
Alternatively (e.g. if your system is not supported by the setup.sh script), follow the manual installation process and run `pip3 install -e .[all]`.
|
Alternatively (e.g. if your system is not supported by the setup.sh script), follow the manual installation process and run `pip3 install -e .[all]`.
|
||||||
|
|
||||||
This will install all required tools for development, including `pytest`, `ruff`, `mypy`, and `coveralls`.
|
This will install all required tools for development, including `pytest`, `flake8`, `mypy`, and `coveralls`.
|
||||||
|
|
||||||
Then install the git hook scripts by running `pre-commit install`, so your changes will be verified locally before committing.
|
Then install the git hook scripts by running `pre-commit install`, so your changes will be verified locally before committing.
|
||||||
This avoids a lot of waiting for CI already, as some basic formatting checks are done locally on your machine.
|
This avoids a lot of waiting for CI already, as some basic formatting checks are done locally on your machine.
|
||||||
|
@@ -41,6 +41,7 @@ dependencies:
|
|||||||
# 2/4 req dev
|
# 2/4 req dev
|
||||||
|
|
||||||
- coveralls
|
- coveralls
|
||||||
|
- flake8
|
||||||
- mypy
|
- mypy
|
||||||
- pytest
|
- pytest
|
||||||
- pytest-asyncio
|
- pytest-asyncio
|
||||||
@@ -69,6 +70,6 @@ dependencies:
|
|||||||
- tables
|
- tables
|
||||||
- pytest-random-order
|
- pytest-random-order
|
||||||
- ccxt
|
- ccxt
|
||||||
- ruff
|
- flake8-tidy-imports
|
||||||
- -e .
|
- -e .
|
||||||
# - python-rapidjso
|
# - python-rapidjso
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
""" Freqtrade bot """
|
""" Freqtrade bot """
|
||||||
__version__ = '2023.3.dev'
|
__version__ = '2023.2'
|
||||||
|
|
||||||
if 'dev' in __version__:
|
if 'dev' in __version__:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
|
|||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from freqtrade.configuration import TimeRange, setup_utils_configuration
|
from freqtrade.configuration import TimeRange, setup_utils_configuration
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config
|
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
||||||
from freqtrade.data.converter import convert_ohlcv_format, convert_trades_format
|
from freqtrade.data.converter import convert_ohlcv_format, convert_trades_format
|
||||||
from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_ohlcv_data,
|
from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_ohlcv_data,
|
||||||
refresh_backtest_trades_data)
|
refresh_backtest_trades_data)
|
||||||
@@ -20,24 +20,15 @@ from freqtrade.util.binance_mig import migrate_binance_futures_data
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _data_download_sanity(config: Config) -> None:
|
|
||||||
if 'days' in config and 'timerange' in config:
|
|
||||||
raise OperationalException("--days and --timerange are mutually exclusive. "
|
|
||||||
"You can only specify one or the other.")
|
|
||||||
|
|
||||||
if 'pairs' not in config:
|
|
||||||
raise OperationalException(
|
|
||||||
"Downloading data requires a list of pairs. "
|
|
||||||
"Please check the documentation on how to configure this.")
|
|
||||||
|
|
||||||
|
|
||||||
def start_download_data(args: Dict[str, Any]) -> None:
|
def start_download_data(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Download data (former download_backtest_data.py script)
|
Download data (former download_backtest_data.py script)
|
||||||
"""
|
"""
|
||||||
config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE)
|
config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE)
|
||||||
|
|
||||||
_data_download_sanity(config)
|
if 'days' in config and 'timerange' in config:
|
||||||
|
raise OperationalException("--days and --timerange are mutually exclusive. "
|
||||||
|
"You can only specify one or the other.")
|
||||||
timerange = TimeRange()
|
timerange = TimeRange()
|
||||||
if 'days' in config:
|
if 'days' in config:
|
||||||
time_since = (datetime.now() - timedelta(days=config['days'])).strftime("%Y%m%d")
|
time_since = (datetime.now() - timedelta(days=config['days'])).strftime("%Y%m%d")
|
||||||
@@ -49,6 +40,11 @@ def start_download_data(args: Dict[str, Any]) -> None:
|
|||||||
# Remove stake-currency to skip checks which are not relevant for datadownload
|
# Remove stake-currency to skip checks which are not relevant for datadownload
|
||||||
config['stake_currency'] = ''
|
config['stake_currency'] = ''
|
||||||
|
|
||||||
|
if 'pairs' not in config:
|
||||||
|
raise OperationalException(
|
||||||
|
"Downloading data requires a list of pairs. "
|
||||||
|
"Please check the documentation on how to configure this.")
|
||||||
|
|
||||||
pairs_not_available: List[str] = []
|
pairs_not_available: List[str] = []
|
||||||
|
|
||||||
# Init exchange
|
# Init exchange
|
||||||
|
@@ -13,9 +13,6 @@ class CandleType(str, Enum):
|
|||||||
FUNDING_RATE = "funding_rate"
|
FUNDING_RATE = "funding_rate"
|
||||||
# BORROW_RATE = "borrow_rate" # * unimplemented
|
# BORROW_RATE = "borrow_rate" # * unimplemented
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name.lower()}"
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_string(value: str) -> 'CandleType':
|
def from_string(value: str) -> 'CandleType':
|
||||||
if not value:
|
if not value:
|
||||||
|
@@ -37,8 +37,5 @@ class RPCRequestType(str, Enum):
|
|||||||
WHITELIST = 'whitelist'
|
WHITELIST = 'whitelist'
|
||||||
ANALYZED_DF = 'analyzed_df'
|
ANALYZED_DF = 'analyzed_df'
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.value
|
|
||||||
|
|
||||||
|
|
||||||
NO_ECHO_MESSAGES = (RPCMessageType.ANALYZED_DF, RPCMessageType.WHITELIST, RPCMessageType.NEW_CANDLE)
|
NO_ECHO_MESSAGES = (RPCMessageType.ANALYZED_DF, RPCMessageType.WHITELIST, RPCMessageType.NEW_CANDLE)
|
||||||
|
@@ -10,9 +10,6 @@ class SignalType(Enum):
|
|||||||
ENTER_SHORT = "enter_short"
|
ENTER_SHORT = "enter_short"
|
||||||
EXIT_SHORT = "exit_short"
|
EXIT_SHORT = "exit_short"
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name.lower()}"
|
|
||||||
|
|
||||||
|
|
||||||
class SignalTagType(Enum):
|
class SignalTagType(Enum):
|
||||||
"""
|
"""
|
||||||
@@ -21,13 +18,7 @@ class SignalTagType(Enum):
|
|||||||
ENTER_TAG = "enter_tag"
|
ENTER_TAG = "enter_tag"
|
||||||
EXIT_TAG = "exit_tag"
|
EXIT_TAG = "exit_tag"
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name.lower()}"
|
|
||||||
|
|
||||||
|
|
||||||
class SignalDirection(str, Enum):
|
class SignalDirection(str, Enum):
|
||||||
LONG = 'long'
|
LONG = 'long'
|
||||||
SHORT = 'short'
|
SHORT = 'short'
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name.lower()}"
|
|
||||||
|
@@ -1961,8 +1961,7 @@ class Exchange:
|
|||||||
cache: bool, drop_incomplete: bool) -> DataFrame:
|
cache: bool, drop_incomplete: bool) -> DataFrame:
|
||||||
# keeping last candle time as last refreshed time of the pair
|
# keeping last candle time as last refreshed time of the pair
|
||||||
if ticks and cache:
|
if ticks and cache:
|
||||||
idx = -2 if drop_incomplete and len(ticks) > 1 else -1
|
self._pairs_last_refresh_time[(pair, timeframe, c_type)] = ticks[-1][0] // 1000
|
||||||
self._pairs_last_refresh_time[(pair, timeframe, c_type)] = ticks[idx][0] // 1000
|
|
||||||
# keeping parsed dataframe in cache
|
# keeping parsed dataframe in cache
|
||||||
ohlcv_df = ohlcv_to_dataframe(ticks, timeframe, pair=pair, fill_missing=True,
|
ohlcv_df = ohlcv_to_dataframe(ticks, timeframe, pair=pair, fill_missing=True,
|
||||||
drop_incomplete=drop_incomplete)
|
drop_incomplete=drop_incomplete)
|
||||||
@@ -2035,9 +2034,7 @@ class Exchange:
|
|||||||
# Timeframe in seconds
|
# Timeframe in seconds
|
||||||
interval_in_sec = timeframe_to_seconds(timeframe)
|
interval_in_sec = timeframe_to_seconds(timeframe)
|
||||||
plr = self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0) + interval_in_sec
|
plr = self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0) + interval_in_sec
|
||||||
# current,active candle open date
|
return plr < arrow.utcnow().int_timestamp
|
||||||
now = int(timeframe_to_prev_date(timeframe).timestamp())
|
|
||||||
return plr < now
|
|
||||||
|
|
||||||
@retrier_async
|
@retrier_async
|
||||||
async def _async_get_candle_history(
|
async def _async_get_candle_history(
|
||||||
|
@@ -64,7 +64,6 @@ class Kucoin(Exchange):
|
|||||||
# ccxt returns status = 'closed' at the moment - which is information ccxt invented.
|
# ccxt returns status = 'closed' at the moment - which is information ccxt invented.
|
||||||
# Since we rely on status heavily, we must set it to 'open' here.
|
# Since we rely on status heavily, we must set it to 'open' here.
|
||||||
# ref: https://github.com/ccxt/ccxt/pull/16674, (https://github.com/ccxt/ccxt/pull/16553)
|
# ref: https://github.com/ccxt/ccxt/pull/16674, (https://github.com/ccxt/ccxt/pull/16553)
|
||||||
if not self._config['dry_run']:
|
res['type'] = ordertype
|
||||||
res['type'] = ordertype
|
res['status'] = 'open'
|
||||||
res['status'] = 'open'
|
|
||||||
return res
|
return res
|
||||||
|
@@ -570,12 +570,12 @@ class FreqaiDataDrawer:
|
|||||||
|
|
||||||
for pair in dk.all_pairs:
|
for pair in dk.all_pairs:
|
||||||
for tf in feat_params.get("include_timeframes"):
|
for tf in feat_params.get("include_timeframes"):
|
||||||
hist_df = history_data[pair][tf]
|
|
||||||
# check if newest candle is already appended
|
# check if newest candle is already appended
|
||||||
df_dp = strategy.dp.get_pair_dataframe(pair, tf)
|
df_dp = strategy.dp.get_pair_dataframe(pair, tf)
|
||||||
if len(df_dp.index) == 0:
|
if len(df_dp.index) == 0:
|
||||||
continue
|
continue
|
||||||
if str(hist_df.iloc[-1]["date"]) == str(
|
if str(history_data[pair][tf].iloc[-1]["date"]) == str(
|
||||||
df_dp.iloc[-1:]["date"].iloc[-1]
|
df_dp.iloc[-1:]["date"].iloc[-1]
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
@@ -583,30 +583,21 @@ class FreqaiDataDrawer:
|
|||||||
try:
|
try:
|
||||||
index = (
|
index = (
|
||||||
df_dp.loc[
|
df_dp.loc[
|
||||||
df_dp["date"] == hist_df.iloc[-1]["date"]
|
df_dp["date"] == history_data[pair][tf].iloc[-1]["date"]
|
||||||
].index[0]
|
].index[0]
|
||||||
+ 1
|
+ 1
|
||||||
)
|
)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
if hist_df.iloc[-1]['date'] < df_dp['date'].iloc[0]:
|
logger.warning(
|
||||||
raise OperationalException("In memory historical data is older than "
|
f"Unable to update pair history for {pair}. "
|
||||||
f"oldest DataProvider candle for {pair} on "
|
"If this does not resolve itself after 1 additional candle, "
|
||||||
f"timeframe {tf}")
|
"please report the error to #freqai discord channel"
|
||||||
else:
|
)
|
||||||
index = -1
|
return
|
||||||
logger.warning(
|
|
||||||
f"No common dates in historical data and dataprovider for {pair}. "
|
|
||||||
f"Appending latest dataprovider candle to historical data "
|
|
||||||
"but please be aware that there is likely a gap in the historical "
|
|
||||||
"data. \n"
|
|
||||||
f"Historical data ends at {hist_df.iloc[-1]['date']} "
|
|
||||||
f"while dataprovider starts at {df_dp['date'].iloc[0]} and"
|
|
||||||
f"ends at {df_dp['date'].iloc[0]}."
|
|
||||||
)
|
|
||||||
|
|
||||||
history_data[pair][tf] = pd.concat(
|
history_data[pair][tf] = pd.concat(
|
||||||
[
|
[
|
||||||
hist_df,
|
history_data[pair][tf],
|
||||||
df_dp.iloc[index:],
|
df_dp.iloc[index:],
|
||||||
],
|
],
|
||||||
ignore_index=True,
|
ignore_index=True,
|
||||||
|
@@ -34,11 +34,6 @@ class ReinforcementLearner_multiproc(ReinforcementLearner):
|
|||||||
train_df = data_dictionary["train_features"]
|
train_df = data_dictionary["train_features"]
|
||||||
test_df = data_dictionary["test_features"]
|
test_df = data_dictionary["test_features"]
|
||||||
|
|
||||||
if self.train_env:
|
|
||||||
self.train_env.close()
|
|
||||||
if self.eval_env:
|
|
||||||
self.eval_env.close()
|
|
||||||
|
|
||||||
env_info = self.pack_env_dict(dk.pair)
|
env_info = self.pack_env_dict(dk.pair)
|
||||||
|
|
||||||
env_id = "train_env"
|
env_id = "train_env"
|
||||||
|
@@ -56,24 +56,3 @@ exclude = [
|
|||||||
"build_helpers/*.py",
|
"build_helpers/*.py",
|
||||||
]
|
]
|
||||||
ignore = ["freqtrade/vendor/**"]
|
ignore = ["freqtrade/vendor/**"]
|
||||||
|
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
line-length = 100
|
|
||||||
extend-exclude = [".env"]
|
|
||||||
target-version = "py38"
|
|
||||||
extend-select = [
|
|
||||||
"C90", # mccabe
|
|
||||||
# "N", # pep8-naming
|
|
||||||
# "UP", # pyupgrade
|
|
||||||
"TID", # flake8-tidy-imports
|
|
||||||
# "EXE", # flake8-executable
|
|
||||||
"YTT", # flake8-2020
|
|
||||||
# "DTZ", # flake8-datetimez
|
|
||||||
# "RSE", # flake8-raise
|
|
||||||
# "TCH", # flake8-type-checking
|
|
||||||
# "PTH", # flake8-use-pathlib
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.ruff.mccabe]
|
|
||||||
max-complexity = 12
|
|
||||||
|
@@ -7,9 +7,10 @@
|
|||||||
-r docs/requirements-docs.txt
|
-r docs/requirements-docs.txt
|
||||||
|
|
||||||
coveralls==3.3.1
|
coveralls==3.3.1
|
||||||
ruff==0.0.252
|
flake8==6.0.0
|
||||||
|
flake8-tidy-imports==4.8.0
|
||||||
mypy==1.0.1
|
mypy==1.0.1
|
||||||
pre-commit==3.1.0
|
pre-commit==3.0.4
|
||||||
pytest==7.2.1
|
pytest==7.2.1
|
||||||
pytest-asyncio==0.20.3
|
pytest-asyncio==0.20.3
|
||||||
pytest-cov==4.0.0
|
pytest-cov==4.0.0
|
||||||
|
@@ -35,7 +35,7 @@ sdnotify==0.3.2
|
|||||||
|
|
||||||
# API Server
|
# API Server
|
||||||
fastapi==0.92.0
|
fastapi==0.92.0
|
||||||
pydantic==1.10.5
|
pydantic==1.10.4
|
||||||
uvicorn==0.20.0
|
uvicorn==0.20.0
|
||||||
pyjwt==2.6.0
|
pyjwt==2.6.0
|
||||||
aiofiles==23.1.0
|
aiofiles==23.1.0
|
||||||
|
2
setup.py
2
setup.py
@@ -32,6 +32,8 @@ hdf5 = [
|
|||||||
|
|
||||||
develop = [
|
develop = [
|
||||||
'coveralls',
|
'coveralls',
|
||||||
|
'flake8',
|
||||||
|
'flake8-tidy-imports',
|
||||||
'mypy',
|
'mypy',
|
||||||
'pytest',
|
'pytest',
|
||||||
'pytest-asyncio',
|
'pytest-asyncio',
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
from datetime import datetime, timezone
|
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from freqtrade.enums.marginmode import MarginMode
|
from freqtrade.enums.marginmode import MarginMode
|
||||||
@@ -56,19 +55,3 @@ async def test_bybit_fetch_funding_rate(default_conf, mocker):
|
|||||||
kwargs = api_mock.fetch_funding_rate_history.call_args_list[0][1]
|
kwargs = api_mock.fetch_funding_rate_history.call_args_list[0][1]
|
||||||
assert kwargs['params'] == {'until': since_ms_end}
|
assert kwargs['params'] == {'until': since_ms_end}
|
||||||
assert kwargs['since'] == since_ms
|
assert kwargs['since'] == since_ms
|
||||||
|
|
||||||
|
|
||||||
def test_bybit_get_funding_fees(default_conf, mocker):
|
|
||||||
now = datetime.now(timezone.utc)
|
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id='bybit')
|
|
||||||
exchange._fetch_and_calculate_funding_fees = MagicMock()
|
|
||||||
exchange.get_funding_fees('BTC/USDT:USDT', 1, False, now)
|
|
||||||
assert exchange._fetch_and_calculate_funding_fees.call_count == 0
|
|
||||||
|
|
||||||
default_conf['trading_mode'] = 'futures'
|
|
||||||
default_conf['margin_mode'] = 'isolated'
|
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id='bybit')
|
|
||||||
exchange._fetch_and_calculate_funding_fees = MagicMock()
|
|
||||||
exchange.get_funding_fees('BTC/USDT:USDT', 1, False, now)
|
|
||||||
|
|
||||||
assert exchange._fetch_and_calculate_funding_fees.call_count == 1
|
|
||||||
|
@@ -27,7 +27,7 @@ from tests.conftest import (generate_test_data_raw, get_mock_coro, get_patched_e
|
|||||||
|
|
||||||
|
|
||||||
# Make sure to always keep one exchange here which is NOT subclassed!!
|
# Make sure to always keep one exchange here which is NOT subclassed!!
|
||||||
EXCHANGES = ['bittrex', 'binance', 'kraken', 'gate', 'kucoin', 'bybit']
|
EXCHANGES = ['bittrex', 'binance', 'kraken', 'gate']
|
||||||
|
|
||||||
get_entry_rate_data = [
|
get_entry_rate_data = [
|
||||||
('other', 20, 19, 10, 0.0, 20), # Full ask side
|
('other', 20, 19, 10, 0.0, 20), # Full ask side
|
||||||
@@ -1269,7 +1269,7 @@ def test_create_dry_run_order_limit_fill(default_conf, mocker, side, price, fill
|
|||||||
fetch_l2_order_book=order_book_l2_usd,
|
fetch_l2_order_book=order_book_l2_usd,
|
||||||
)
|
)
|
||||||
|
|
||||||
order = exchange.create_order(
|
order = exchange.create_dry_run_order(
|
||||||
pair='LTC/USDT',
|
pair='LTC/USDT',
|
||||||
ordertype='limit',
|
ordertype='limit',
|
||||||
side=side,
|
side=side,
|
||||||
@@ -1332,7 +1332,7 @@ def test_create_dry_run_order_market_fill(default_conf, mocker, side, rate, amou
|
|||||||
fetch_l2_order_book=order_book_l2_usd,
|
fetch_l2_order_book=order_book_l2_usd,
|
||||||
)
|
)
|
||||||
|
|
||||||
order = exchange.create_order(
|
order = exchange.create_dry_run_order(
|
||||||
pair='LTC/USDT',
|
pair='LTC/USDT',
|
||||||
ordertype='market',
|
ordertype='market',
|
||||||
side=side,
|
side=side,
|
||||||
@@ -1425,10 +1425,9 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
|||||||
assert order['amount'] == 0.01
|
assert order['amount'] == 0.01
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
def test_buy_dry_run(default_conf, mocker):
|
||||||
def test_buy_dry_run(default_conf, mocker, exchange_name):
|
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
|
|
||||||
order = exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy",
|
order = exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy",
|
||||||
amount=1, rate=200, leverage=1.0,
|
amount=1, rate=200, leverage=1.0,
|
||||||
@@ -2216,7 +2215,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach
|
|||||||
assert len(res[pair1]) == 99
|
assert len(res[pair1]) == 99
|
||||||
assert len(res[pair2]) == 99
|
assert len(res[pair2]) == 99
|
||||||
assert exchange._klines
|
assert exchange._klines
|
||||||
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000
|
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-1][0] // 1000
|
||||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||||
|
|
||||||
# Returned from cache
|
# Returned from cache
|
||||||
@@ -2225,7 +2224,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach
|
|||||||
assert len(res) == 2
|
assert len(res) == 2
|
||||||
assert len(res[pair1]) == 99
|
assert len(res[pair1]) == 99
|
||||||
assert len(res[pair2]) == 99
|
assert len(res[pair2]) == 99
|
||||||
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000
|
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-1][0] // 1000
|
||||||
|
|
||||||
# Move time 1 candle further but result didn't change yet
|
# Move time 1 candle further but result didn't change yet
|
||||||
time_machine.move_to(start + timedelta(hours=101))
|
time_machine.move_to(start + timedelta(hours=101))
|
||||||
@@ -2235,7 +2234,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach
|
|||||||
assert len(res[pair1]) == 99
|
assert len(res[pair1]) == 99
|
||||||
assert len(res[pair2]) == 99
|
assert len(res[pair2]) == 99
|
||||||
assert res[pair2].at[0, 'open']
|
assert res[pair2].at[0, 'open']
|
||||||
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000
|
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-1][0] // 1000
|
||||||
refresh_pior = exchange._pairs_last_refresh_time[pair1]
|
refresh_pior = exchange._pairs_last_refresh_time[pair1]
|
||||||
|
|
||||||
# New candle on exchange - return 100 candles - but skip one candle so we actually get 2 candles
|
# New candle on exchange - return 100 candles - but skip one candle so we actually get 2 candles
|
||||||
@@ -2253,8 +2252,8 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach
|
|||||||
assert res[pair2].at[0, 'open']
|
assert res[pair2].at[0, 'open']
|
||||||
assert refresh_pior != exchange._pairs_last_refresh_time[pair1]
|
assert refresh_pior != exchange._pairs_last_refresh_time[pair1]
|
||||||
|
|
||||||
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000
|
assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-1][0] // 1000
|
||||||
assert exchange._pairs_last_refresh_time[pair2] == ohlcv[-2][0] // 1000
|
assert exchange._pairs_last_refresh_time[pair2] == ohlcv[-1][0] // 1000
|
||||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||||
|
|
||||||
# Retry same call - from cache
|
# Retry same call - from cache
|
||||||
@@ -5016,7 +5015,7 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
|
|||||||
exchange.get_max_leverage("BTC/USDT:USDT", 1000000000.01)
|
exchange.get_max_leverage("BTC/USDT:USDT", 1000000000.01)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'gate', 'okx', 'bybit'])
|
@pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'gate', 'okx'])
|
||||||
def test__get_params(mocker, default_conf, exchange_name):
|
def test__get_params(mocker, default_conf, exchange_name):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True)
|
||||||
@@ -5037,9 +5036,6 @@ def test__get_params(mocker, default_conf, exchange_name):
|
|||||||
params2['tdMode'] = 'isolated'
|
params2['tdMode'] = 'isolated'
|
||||||
params2['posSide'] = 'net'
|
params2['posSide'] = 'net'
|
||||||
|
|
||||||
if exchange_name == 'bybit':
|
|
||||||
params2['position_idx'] = 0
|
|
||||||
|
|
||||||
assert exchange._get_params(
|
assert exchange._get_params(
|
||||||
side="buy",
|
side="buy",
|
||||||
ordertype='market',
|
ordertype='market',
|
||||||
|
@@ -125,45 +125,3 @@ def test_stoploss_adjust_kucoin(mocker, default_conf):
|
|||||||
# Test with invalid order case
|
# Test with invalid order case
|
||||||
order['stopPrice'] = None
|
order['stopPrice'] = None
|
||||||
assert exchange.stoploss_adjust(1501, order, 'sell')
|
assert exchange.stoploss_adjust(1501, order, 'sell')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("side", ["buy", "sell"])
|
|
||||||
@pytest.mark.parametrize("ordertype,rate", [
|
|
||||||
("market", None),
|
|
||||||
("market", 200),
|
|
||||||
("limit", 200),
|
|
||||||
("stop_loss_limit", 200)
|
|
||||||
])
|
|
||||||
def test_kucoin_create_order(default_conf, mocker, side, ordertype, rate):
|
|
||||||
api_mock = MagicMock()
|
|
||||||
order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6))
|
|
||||||
api_mock.create_order = MagicMock(return_value={
|
|
||||||
'id': order_id,
|
|
||||||
'info': {
|
|
||||||
'foo': 'bar'
|
|
||||||
},
|
|
||||||
'symbol': 'XRP/USDT',
|
|
||||||
'amount': 1
|
|
||||||
})
|
|
||||||
default_conf['dry_run'] = False
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y)
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id='kucoin')
|
|
||||||
exchange._set_leverage = MagicMock()
|
|
||||||
exchange.set_margin_mode = MagicMock()
|
|
||||||
|
|
||||||
order = exchange.create_order(
|
|
||||||
pair='XRP/USDT',
|
|
||||||
ordertype=ordertype,
|
|
||||||
side=side,
|
|
||||||
amount=1,
|
|
||||||
rate=rate,
|
|
||||||
leverage=1.0
|
|
||||||
)
|
|
||||||
|
|
||||||
assert 'id' in order
|
|
||||||
assert 'info' in order
|
|
||||||
assert order['id'] == order_id
|
|
||||||
assert order['amount'] == 1
|
|
||||||
# Status must be faked to open for kucoin.
|
|
||||||
assert order['status'] == 'open'
|
|
||||||
|
Reference in New Issue
Block a user