Compare commits

...

54 Commits

Author SHA1 Message Date
dependabot[bot]
40e4035a65 Bump ruff from 0.0.251 to 0.0.252
Bumps [ruff](https://github.com/charliermarsh/ruff) from 0.0.251 to 0.0.252.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](https://github.com/charliermarsh/ruff/compare/v0.0.251...v0.0.252)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 05:24:31 +00:00
Matthias
303c628998 Merge pull request #8239 from freqtrade/dependabot/pip/develop/pre-commit-3.1.0
Bump pre-commit from 3.0.4 to 3.1.0
2023-02-27 06:23:26 +01:00
Matthias
8cab2e85be Merge pull request #8236 from freqtrade/dependabot/pip/develop/pydantic-1.10.5
Bump pydantic from 1.10.4 to 1.10.5
2023-02-27 06:23:04 +01:00
dependabot[bot]
7add902bc7 Bump pre-commit from 3.0.4 to 3.1.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.0.4 to 3.1.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.0.4...v3.1.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 03:57:04 +00:00
dependabot[bot]
533f97f080 Bump pydantic from 1.10.4 to 1.10.5
Bumps [pydantic](https://github.com/pydantic/pydantic) from 1.10.4 to 1.10.5.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/v1.10.5/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v1.10.4...v1.10.5)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 03:56:50 +00:00
Matthias
5b0bc5bbc5 Don't "fix" dry-run kucoin orders
closes #8229
2023-02-26 16:17:41 +01:00
Matthias
6f7ab97fc3 Improve bybit test coverage 2023-02-26 16:17:41 +01:00
Matthias
27676f4aa2 Add explicit bybit test 2023-02-26 16:17:41 +01:00
Matthias
79dc972e5a Add explicit test for kucoin 2023-02-26 16:17:41 +01:00
Matthias
66c2e145cb Merge pull request #7975 from freqtrade/improve-freqai-gap-handling
handle data gaps between FreqAI and DP better
2023-02-26 15:26:46 +01:00
Matthias
d3d7cb1b14 Merge pull request #8216 from freqtrade/close-rl-env
Close training and eval environments in ReinforcementLearner_multiproc
2023-02-26 15:20:32 +01:00
Matthias
e88bb4e05c Revert small change - otherwise the data is never updated. 2023-02-26 15:09:25 +01:00
Matthias
305eda74e2 Enable Complexity for ruff 2023-02-25 20:50:26 +01:00
Matthias
c8a4a773ee Fix _pairs_last_refresh_time storing the wrong date
Depending on the drop_incomplete settings, this can lead to implicit bugs
2023-02-25 16:18:46 +01:00
Matthias
ff3aa7c1a9 Bump Version to 2023.3.dev 2023-02-25 16:18:33 +01:00
Matthias
84b8cee004 Merge branch 'stable' into develop 2023-02-25 16:18:25 +01:00
Matthias
6d9e50d60c Merge pull request #8209 from freqtrade/ruff
Ruff
2023-02-25 16:14:26 +01:00
Matthias
be352ae014 Update more enums 2023-02-25 15:49:45 +01:00
Matthias
563742f13c Fix enum behavior for python 3.11
closes #8221
closes #8217
2023-02-25 15:49:45 +01:00
robcaulk
cb80d7c26f close the multi_proc env before creating new ones in an attempt to avoid increasing processes 2023-02-24 11:19:54 +01:00
Matthias
6b829d839b Improve ruff config 2023-02-23 07:12:54 +01:00
Matthias
bf968a9fd8 Use actions as documented 2023-02-23 06:51:03 +01:00
Matthias
b4ea37d598 Remove flake8 in favor of ruff 2023-02-22 21:08:17 +01:00
Matthias
549a0e1c44 Add ruff linting - initial configuration 2023-02-22 21:06:11 +01:00
Matthias
2bc9413be1 Fix minor stylistic errors 2023-02-22 20:58:24 +01:00
Matthias
e6766b9b82 Add bt-error to UI backtest method. 2023-02-22 20:22:59 +01:00
Matthias
75bc5809a9 Better handle backtest errors 2023-02-22 20:02:51 +01:00
robcaulk
986bc63e54 raise OperationalException if latest historical data candle is older than earliest dataprovider candle 2023-02-21 21:23:58 +01:00
Matthias
cd6602882c Merge pull request #8076 from freqtrade/new_release
New release 2023.1
2023-01-30 18:11:08 +01:00
Matthias
786f746958 Version bump to 2023.1 2023-01-30 07:16:16 +01:00
Matthias
c4482d56ab Merge branch 'stable' into new_release 2023-01-30 07:10:32 +01:00
robcaulk
3cbe51c3ca remove duplicated line 2023-01-04 13:58:25 +01:00
robcaulk
dc25668468 handle data gaps between FreqAI and DP better 2023-01-04 11:41:06 +01:00
Matthias
9a46613975 Merge pull request #7954 from freqtrade/new_release
New release 2022.12
2022-12-29 17:45:08 +01:00
Matthias
8e8f71ade5 Version bump 2022.12 2022-12-28 15:42:38 +01:00
Matthias
149539d3f9 Merge branch 'stable' into new_release 2022-12-28 15:42:29 +01:00
Matthias
77826ebf78 Merge pull request #7806 from freqtrade/new_release
New release 2022.11
2022-11-27 17:10:48 +01:00
Matthias
5c571f565f Version bump 2022.11 2022-11-27 15:34:00 +01:00
Matthias
178e5a195a Merge branch 'stable' into new_release 2022-11-27 15:33:45 +01:00
Matthias
9adce8d167 Merge pull request #7657 from freqtrade/new_release
New release 2022.10
2022-10-29 09:04:34 +02:00
Matthias
ec7d663496 Version bump 2022.10 2022-10-28 19:34:30 +02:00
Matthias
a56465e049 Merge branch 'stable' into new_release 2022-10-28 19:34:15 +02:00
Matthias
851d1e9da1 Version bump 2022.9.1 2022-10-02 06:59:10 +02:00
Matthias
59cfde3767 Fix pandas deprecation warnings from freqAI 2022-10-02 06:59:10 +02:00
Matthias
c53ff94b8e Force joblib update via setup.py 2022-10-02 06:54:08 +02:00
Robert Caulk
03256fc776 Merge pull request #7508 from aemr3/fix-pca-errors
Fix feature list match for PCA
2022-10-02 06:53:08 +02:00
Matthias
19b3669d97 Decrease message throughput
fixes memory leak by queue raising indefinitely
2022-10-02 06:50:34 +02:00
Matthias
6841bdaa81 Update test to verify webhook won't log-spam on new messagetypes 2022-10-02 06:50:19 +02:00
Matthias
8e101a9f1c Disable log spam from analyze_df in webhook/discord 2022-10-02 06:50:12 +02:00
Matthias
0680ca2fe8 Merge pull request #7497 from freqtrade/new_release
New release 2022.9
2022-09-29 18:06:57 +02:00
Matthias
d0456b698c Version bump 2022.9 2022-09-29 07:22:41 +02:00
Matthias
f3085443d5 Merge branch 'stable' into new_release 2022-09-29 07:22:29 +02:00
Matthias
958a4565db Merge pull request #7313 from freqtrade/new_release
New release 2022.8
2022-08-30 23:01:19 +02:00
Matthias
2403a03fcb Version bump 2022.8 2022-08-29 06:28:53 +02:00
22 changed files with 191 additions and 59 deletions

View File

@@ -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: Flake8 - name: Run Ruff
run: | run: |
flake8 ruff check --format=github .
- name: Mypy - name: Mypy
run: | run: |

View File

@@ -27,6 +27,12 @@ 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:

View File

@@ -45,16 +45,17 @@ 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 Flake8 #### Run Ruff
```bash ```bash
flake8 freqtrade tests scripts ruff .
``` ```
We receive a lot of code that fails the `flake8` checks. We receive a lot of code that fails the `ruff` 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

View File

@@ -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`, `flake8`, `mypy`, and `coveralls`. This will install all required tools for development, including `pytest`, `ruff`, `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.

View File

@@ -41,7 +41,6 @@ dependencies:
# 2/4 req dev # 2/4 req dev
- coveralls - coveralls
- flake8
- mypy - mypy
- pytest - pytest
- pytest-asyncio - pytest-asyncio
@@ -70,6 +69,6 @@ dependencies:
- tables - tables
- pytest-random-order - pytest-random-order
- ccxt - ccxt
- flake8-tidy-imports - ruff
- -e . - -e .
# - python-rapidjso # - python-rapidjso

View File

@@ -1,5 +1,5 @@
""" Freqtrade bot """ """ Freqtrade bot """
__version__ = '2023.2.dev' __version__ = '2023.3.dev'
if 'dev' in __version__: if 'dev' in __version__:
from pathlib import Path from pathlib import Path

View File

@@ -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 from freqtrade.constants import DATETIME_PRINT_FORMAT, Config
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,15 +20,24 @@ 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)
if 'days' in config and 'timerange' in config: _data_download_sanity(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")
@@ -40,11 +49,6 @@ 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

View File

@@ -13,6 +13,9 @@ 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:

View File

@@ -37,5 +37,8 @@ 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)

View File

@@ -10,6 +10,9 @@ 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):
""" """
@@ -18,7 +21,13 @@ 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()}"

View File

@@ -1961,7 +1961,8 @@ 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:
self._pairs_last_refresh_time[(pair, timeframe, c_type)] = ticks[-1][0] // 1000 idx = -2 if drop_incomplete and len(ticks) > 1 else -1
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)
@@ -2034,7 +2035,9 @@ 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
return plr < arrow.utcnow().int_timestamp # current,active candle open date
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(

View File

@@ -64,6 +64,7 @@ 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)
res['type'] = ordertype if not self._config['dry_run']:
res['status'] = 'open' res['type'] = ordertype
res['status'] = 'open'
return res return res

View File

@@ -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(history_data[pair][tf].iloc[-1]["date"]) == str( if str(hist_df.iloc[-1]["date"]) == str(
df_dp.iloc[-1:]["date"].iloc[-1] df_dp.iloc[-1:]["date"].iloc[-1]
): ):
continue continue
@@ -583,21 +583,30 @@ class FreqaiDataDrawer:
try: try:
index = ( index = (
df_dp.loc[ df_dp.loc[
df_dp["date"] == history_data[pair][tf].iloc[-1]["date"] df_dp["date"] == hist_df.iloc[-1]["date"]
].index[0] ].index[0]
+ 1 + 1
) )
except IndexError: except IndexError:
logger.warning( if hist_df.iloc[-1]['date'] < df_dp['date'].iloc[0]:
f"Unable to update pair history for {pair}. " raise OperationalException("In memory historical data is older than "
"If this does not resolve itself after 1 additional candle, " f"oldest DataProvider candle for {pair} on "
"please report the error to #freqai discord channel" f"timeframe {tf}")
) else:
return index = -1
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(
[ [
history_data[pair][tf], hist_df,
df_dp.iloc[index:], df_dp.iloc[index:],
], ],
ignore_index=True, ignore_index=True,

View File

@@ -34,6 +34,11 @@ 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"

View File

@@ -56,3 +56,24 @@ 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

View File

@@ -7,10 +7,9 @@
-r docs/requirements-docs.txt -r docs/requirements-docs.txt
coveralls==3.3.1 coveralls==3.3.1
flake8==6.0.0 ruff==0.0.252
flake8-tidy-imports==4.8.0
mypy==1.0.1 mypy==1.0.1
pre-commit==3.0.4 pre-commit==3.1.0
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

View File

@@ -35,7 +35,7 @@ sdnotify==0.3.2
# API Server # API Server
fastapi==0.92.0 fastapi==0.92.0
pydantic==1.10.4 pydantic==1.10.5
uvicorn==0.20.0 uvicorn==0.20.0
pyjwt==2.6.0 pyjwt==2.6.0
aiofiles==23.1.0 aiofiles==23.1.0

View File

@@ -32,8 +32,6 @@ hdf5 = [
develop = [ develop = [
'coveralls', 'coveralls',
'flake8',
'flake8-tidy-imports',
'mypy', 'mypy',
'pytest', 'pytest',
'pytest-asyncio', 'pytest-asyncio',

View File

@@ -1,3 +1,4 @@
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
@@ -55,3 +56,19 @@ 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

View File

@@ -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'] EXCHANGES = ['bittrex', 'binance', 'kraken', 'gate', 'kucoin', 'bybit']
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_dry_run_order( order = exchange.create_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_dry_run_order( order = exchange.create_order(
pair='LTC/USDT', pair='LTC/USDT',
ordertype='market', ordertype='market',
side=side, side=side,
@@ -1425,9 +1425,10 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
assert order['amount'] == 0.01 assert order['amount'] == 0.01
def test_buy_dry_run(default_conf, mocker): @pytest.mark.parametrize("exchange_name", EXCHANGES)
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) exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
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,
@@ -2215,7 +2216,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[-1][0] // 1000 assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000
exchange._api_async.fetch_ohlcv.reset_mock() exchange._api_async.fetch_ohlcv.reset_mock()
# Returned from cache # Returned from cache
@@ -2224,7 +2225,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[-1][0] // 1000 assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][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))
@@ -2234,7 +2235,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[-1][0] // 1000 assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][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
@@ -2252,8 +2253,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[-1][0] // 1000 assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000
assert exchange._pairs_last_refresh_time[pair2] == ohlcv[-1][0] // 1000 assert exchange._pairs_last_refresh_time[pair2] == ohlcv[-2][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
@@ -5015,7 +5016,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']) @pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'gate', 'okx', 'bybit'])
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)
@@ -5036,6 +5037,9 @@ 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',

View File

@@ -125,3 +125,45 @@ 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'

View File

@@ -356,6 +356,14 @@ def test_exception_send_msg(default_conf, mocker, caplog):
} }
webhook.send_msg(msg) webhook.send_msg(msg)
# Test no failure for not implemented but known messagetypes
for e in RPCMessageType:
msg = {
'type': e,
'status': 'whatever'
}
webhook.send_msg(msg)
def test__send_msg(default_conf, mocker, caplog): def test__send_msg(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict() default_conf["webhook"] = get_webhook_dict()