Merge branch 'feat/short' into lev-exchange
This commit is contained in:
commit
dc83e04f9b
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -2,14 +2,16 @@ Thank you for sending your pull request. But first, have you included
|
|||||||
unit tests, and is your code PEP8 conformant? [More details](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
|
unit tests, and is your code PEP8 conformant? [More details](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Explain in one sentence the goal of this PR
|
Explain in one sentence the goal of this PR
|
||||||
|
|
||||||
Solve the issue: #___
|
Solve the issue: #___
|
||||||
|
|
||||||
## Quick changelog
|
## Quick changelog
|
||||||
|
|
||||||
- <change log #1>
|
- <change log 1>
|
||||||
- <change log #2>
|
- <change log 1>
|
||||||
|
|
||||||
## What's new?
|
## What's new?
|
||||||
|
|
||||||
*Explain in details what this PR solve or improve. You can include visuals.*
|
*Explain in details what this PR solve or improve. You can include visuals.*
|
||||||
|
@ -13,7 +13,7 @@ RUN mkdir /freqtrade \
|
|||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get -y install sudo libatlas3-base curl sqlite3 libhdf5-serial-dev \
|
&& apt-get -y install sudo libatlas3-base curl sqlite3 libhdf5-serial-dev \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& useradd -u 1000 -G sudo -U -m ftuser \
|
&& useradd -u 1000 -G sudo -U -m -s /bin/bash ftuser \
|
||||||
&& chown ftuser:ftuser /freqtrade \
|
&& chown ftuser:ftuser /freqtrade \
|
||||||
# Allow sudoers
|
# Allow sudoers
|
||||||
&& echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers
|
&& echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers
|
||||||
|
@ -12,9 +12,12 @@ if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then
|
|||||||
&& curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \
|
&& curl 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' -o config.sub \
|
||||||
&& ./configure --prefix=${INSTALL_LOC}/ \
|
&& ./configure --prefix=${INSTALL_LOC}/ \
|
||||||
&& make -j$(nproc) \
|
&& make -j$(nproc) \
|
||||||
&& which sudo && sudo make install || make install \
|
&& which sudo && sudo make install || make install
|
||||||
&& cd ..
|
if [ -x "$(command -v apt-get)" ]; then
|
||||||
|
echo "Updating library path using ldconfig"
|
||||||
|
sudo ldconfig
|
||||||
|
fi
|
||||||
|
cd .. && rm -rf ./ta-lib/
|
||||||
else
|
else
|
||||||
echo "TA-lib already installed, skipping installation"
|
echo "TA-lib already installed, skipping installation"
|
||||||
fi
|
fi
|
||||||
# && sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \
|
|
||||||
|
@ -80,7 +80,7 @@ To override a pre-defined space (`roi_space`, `generate_roi_table`, `stoploss_sp
|
|||||||
class MyAwesomeStrategy(IStrategy):
|
class MyAwesomeStrategy(IStrategy):
|
||||||
class HyperOpt:
|
class HyperOpt:
|
||||||
# Define a custom stoploss space.
|
# Define a custom stoploss space.
|
||||||
def stoploss_space(self):
|
def stoploss_space():
|
||||||
return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')]
|
return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -444,8 +444,8 @@ The possible values are: `gtc` (default), `fok` or `ioc`.
|
|||||||
```
|
```
|
||||||
|
|
||||||
!!! Warning
|
!!! Warning
|
||||||
This is ongoing work. For now, it is supported only for binance.
|
This is ongoing work. For now, it is supported only for binance and kucoin.
|
||||||
Please don't change the default value unless you know what you are doing and have researched the impact of using different values.
|
Please don't change the default value unless you know what you are doing and have researched the impact of using different values for your particular exchange.
|
||||||
|
|
||||||
### Exchange configuration
|
### Exchange configuration
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
The `Edge Positioning` module uses probability to calculate your win rate and risk reward ratio. It will use these statistics to control your strategy trade entry points, position size and, stoploss.
|
The `Edge Positioning` module uses probability to calculate your win rate and risk reward ratio. It will use these statistics to control your strategy trade entry points, position size and, stoploss.
|
||||||
|
|
||||||
!!! Warning
|
!!! Warning
|
||||||
WHen using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data.
|
When using `Edge positioning` with a dynamic whitelist (VolumePairList), make sure to also use `AgeFilter` and set it to at least `calculate_since_number_of_days` to avoid problems with missing data.
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
`Edge Positioning` only considers *its own* buy/sell/stoploss signals. It ignores the stoploss, trailing stoploss, and ROI settings in the strategy configuration file.
|
`Edge Positioning` only considers *its own* buy/sell/stoploss signals. It ignores the stoploss, trailing stoploss, and ROI settings in the strategy configuration file.
|
||||||
|
@ -4,6 +4,8 @@ This page combines common gotchas and informations which are exchange-specific a
|
|||||||
|
|
||||||
## Binance
|
## Binance
|
||||||
|
|
||||||
|
Binance supports [time_in_force](configuration.md#understand-order_time_in_force).
|
||||||
|
|
||||||
!!! Tip "Stoploss on Exchange"
|
!!! Tip "Stoploss on Exchange"
|
||||||
Binance supports `stoploss_on_exchange` and uses stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it.
|
Binance supports `stoploss_on_exchange` and uses stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it.
|
||||||
|
|
||||||
@ -113,8 +115,12 @@ Kucoin requires a passphrase for each api key, you will therefore need to add th
|
|||||||
"key": "your_exchange_key",
|
"key": "your_exchange_key",
|
||||||
"secret": "your_exchange_secret",
|
"secret": "your_exchange_secret",
|
||||||
"password": "your_exchange_api_key_password",
|
"password": "your_exchange_api_key_password",
|
||||||
|
// ...
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force).
|
||||||
|
|
||||||
### Kucoin Blacklists
|
### Kucoin Blacklists
|
||||||
|
|
||||||
For Kucoin, please add `"KCS/<STAKE>"` to your blacklist to avoid issues.
|
For Kucoin, please add `"KCS/<STAKE>"` to your blacklist to avoid issues.
|
||||||
@ -158,6 +164,8 @@ For example, to test the order type `FOK` with Kraken, and modify candle limit t
|
|||||||
"order_time_in_force": ["gtc", "fok"],
|
"order_time_in_force": ["gtc", "fok"],
|
||||||
"ohlcv_candle_limit": 200
|
"ohlcv_candle_limit": 200
|
||||||
}
|
}
|
||||||
|
//...
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! Warning
|
!!! Warning
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
mkdocs==1.2.2
|
mkdocs==1.2.2
|
||||||
mkdocs-material==7.2.5
|
mkdocs-material==7.2.6
|
||||||
mdx_truly_sane_lists==1.2
|
mdx_truly_sane_lists==1.2
|
||||||
pymdown-extensions==8.2
|
pymdown-extensions==8.2
|
||||||
|
@ -22,7 +22,7 @@ if __version__ == 'develop':
|
|||||||
# subprocess.check_output(
|
# subprocess.check_output(
|
||||||
# ['git', 'log', '--format="%h"', '-n 1'],
|
# ['git', 'log', '--format="%h"', '-n 1'],
|
||||||
# stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"')
|
# stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"')
|
||||||
except Exception:
|
except Exception: # pragma: no cover
|
||||||
# git not available, ignore
|
# git not available, ignore
|
||||||
try:
|
try:
|
||||||
# Try Fallback to freqtrade_commit file (created by CI while building docker image)
|
# Try Fallback to freqtrade_commit file (created by CI while building docker image)
|
||||||
|
@ -61,13 +61,13 @@ def ask_user_config() -> Dict[str, Any]:
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"name": "stake_currency",
|
"name": "stake_currency",
|
||||||
"message": "Please insert your stake currency:",
|
"message": "Please insert your stake currency:",
|
||||||
"default": 'BTC',
|
"default": 'USDT',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"name": "stake_amount",
|
"name": "stake_amount",
|
||||||
"message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):",
|
"message": f"Please insert your stake amount (Number or '{UNLIMITED_STAKE_AMOUNT}'):",
|
||||||
"default": "0.01",
|
"default": "100",
|
||||||
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val),
|
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_float(val),
|
||||||
"filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"'
|
"filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"'
|
||||||
if val == UNLIMITED_STAKE_AMOUNT
|
if val == UNLIMITED_STAKE_AMOUNT
|
||||||
@ -105,6 +105,8 @@ def ask_user_config() -> Dict[str, Any]:
|
|||||||
"bittrex",
|
"bittrex",
|
||||||
"kraken",
|
"kraken",
|
||||||
"ftx",
|
"ftx",
|
||||||
|
"kucoin",
|
||||||
|
"gateio",
|
||||||
Separator(),
|
Separator(),
|
||||||
"other",
|
"other",
|
||||||
],
|
],
|
||||||
@ -128,6 +130,12 @@ def ask_user_config() -> Dict[str, Any]:
|
|||||||
"message": "Insert Exchange Secret",
|
"message": "Insert Exchange Secret",
|
||||||
"when": lambda x: not x['dry_run']
|
"when": lambda x: not x['dry_run']
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "password",
|
||||||
|
"name": "exchange_key_password",
|
||||||
|
"message": "Insert Exchange API Key password",
|
||||||
|
"when": lambda x: not x['dry_run'] and x['exchange_name'] == 'kucoin'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "confirm",
|
"type": "confirm",
|
||||||
"name": "telegram",
|
"name": "telegram",
|
||||||
|
@ -19,6 +19,7 @@ class Binance(Exchange):
|
|||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
"order_time_in_force": ['gtc', 'fok', 'ioc'],
|
"order_time_in_force": ['gtc', 'fok', 'ioc'],
|
||||||
|
"time_in_force_parameter": "timeInForce",
|
||||||
"ohlcv_candle_limit": 1000,
|
"ohlcv_candle_limit": 1000,
|
||||||
"trades_pagination": "id",
|
"trades_pagination": "id",
|
||||||
"trades_pagination_arg": "fromId",
|
"trades_pagination_arg": "fromId",
|
||||||
|
@ -55,12 +55,16 @@ class Exchange:
|
|||||||
# Parameters to add directly to buy/sell calls (like agreeing to trading agreement)
|
# Parameters to add directly to buy/sell calls (like agreeing to trading agreement)
|
||||||
_params: Dict = {}
|
_params: Dict = {}
|
||||||
|
|
||||||
|
# Additional headers - added to the ccxt object
|
||||||
|
_headers: Dict = {}
|
||||||
|
|
||||||
# Dict to specify which options each exchange implements
|
# Dict to specify which options each exchange implements
|
||||||
# This defines defaults, which can be selectively overridden by subclasses using _ft_has
|
# This defines defaults, which can be selectively overridden by subclasses using _ft_has
|
||||||
# or by specifying them in the configuration.
|
# or by specifying them in the configuration.
|
||||||
_ft_has_default: Dict = {
|
_ft_has_default: Dict = {
|
||||||
"stoploss_on_exchange": False,
|
"stoploss_on_exchange": False,
|
||||||
"order_time_in_force": ["gtc"],
|
"order_time_in_force": ["gtc"],
|
||||||
|
"time_in_force_parameter": "timeInForce",
|
||||||
"ohlcv_params": {},
|
"ohlcv_params": {},
|
||||||
"ohlcv_candle_limit": 500,
|
"ohlcv_candle_limit": 500,
|
||||||
"ohlcv_partial_candle": True,
|
"ohlcv_partial_candle": True,
|
||||||
@ -190,7 +194,7 @@ class Exchange:
|
|||||||
asyncio.get_event_loop().run_until_complete(self._api_async.close())
|
asyncio.get_event_loop().run_until_complete(self._api_async.close())
|
||||||
|
|
||||||
def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt,
|
def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt,
|
||||||
ccxt_kwargs: dict = None) -> ccxt.Exchange:
|
ccxt_kwargs: Dict = {}) -> ccxt.Exchange:
|
||||||
"""
|
"""
|
||||||
Initialize ccxt with given config and return valid
|
Initialize ccxt with given config and return valid
|
||||||
ccxt instance.
|
ccxt instance.
|
||||||
@ -210,6 +214,10 @@ class Exchange:
|
|||||||
}
|
}
|
||||||
if ccxt_kwargs:
|
if ccxt_kwargs:
|
||||||
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
|
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
|
||||||
|
if self._headers:
|
||||||
|
# Inject static headers after the above output to not confuse users.
|
||||||
|
ccxt_kwargs = deep_merge_dicts({'headers': self._headers}, ccxt_kwargs)
|
||||||
|
if ccxt_kwargs:
|
||||||
ex_config.update(ccxt_kwargs)
|
ex_config.update(ccxt_kwargs)
|
||||||
try:
|
try:
|
||||||
|
|
||||||
@ -771,7 +779,8 @@ class Exchange:
|
|||||||
|
|
||||||
params = self._params.copy()
|
params = self._params.copy()
|
||||||
if time_in_force != 'gtc' and ordertype != 'market':
|
if time_in_force != 'gtc' and ordertype != 'market':
|
||||||
params.update({'timeInForce': time_in_force})
|
param = self._ft_has.get('time_in_force_parameter', '')
|
||||||
|
params.update({param: time_in_force})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Set the precision for amount and price(rate) as accepted by the exchange
|
# Set the precision for amount and price(rate) as accepted by the exchange
|
||||||
|
@ -21,4 +21,6 @@ class Kucoin(Exchange):
|
|||||||
_ft_has: Dict = {
|
_ft_has: Dict = {
|
||||||
"l2_limit_range": [20, 100],
|
"l2_limit_range": [20, 100],
|
||||||
"l2_limit_range_required": False,
|
"l2_limit_range_required": False,
|
||||||
|
"order_time_in_force": ['gtc', 'fok', 'ioc'],
|
||||||
|
"time_in_force_parameter": "timeInForce",
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ def setup_logging(config: Dict[str, Any]) -> None:
|
|||||||
# syslog config. The messages should be equal for this.
|
# syslog config. The messages should be equal for this.
|
||||||
handler_sl.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s'))
|
handler_sl.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s'))
|
||||||
logging.root.addHandler(handler_sl)
|
logging.root.addHandler(handler_sl)
|
||||||
elif s[0] == 'journald':
|
elif s[0] == 'journald': # pragma: no cover
|
||||||
try:
|
try:
|
||||||
from systemd.journal import JournaldLogHandler
|
from systemd.journal import JournaldLogHandler
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -9,7 +9,7 @@ from typing import Any, List
|
|||||||
|
|
||||||
|
|
||||||
# check min. python version
|
# check min. python version
|
||||||
if sys.version_info < (3, 7):
|
if sys.version_info < (3, 7): # pragma: no cover
|
||||||
sys.exit("Freqtrade requires Python version >= 3.7")
|
sys.exit("Freqtrade requires Python version >= 3.7")
|
||||||
|
|
||||||
from freqtrade.commands import Arguments
|
from freqtrade.commands import Arguments
|
||||||
@ -46,7 +46,7 @@ def main(sysargv: List[str] = None) -> None:
|
|||||||
"`freqtrade --help` or `freqtrade <command> --help`."
|
"`freqtrade --help` or `freqtrade <command> --help`."
|
||||||
)
|
)
|
||||||
|
|
||||||
except SystemExit as e:
|
except SystemExit as e: # pragma: no cover
|
||||||
return_code = e
|
return_code = e
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info('SIGINT received, aborting ...')
|
logger.info('SIGINT received, aborting ...')
|
||||||
@ -60,5 +60,5 @@ def main(sysargv: List[str] = None) -> None:
|
|||||||
sys.exit(return_code)
|
sys.exit(return_code)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__': # pragma: no cover
|
||||||
main()
|
main()
|
||||||
|
@ -17,7 +17,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
|||||||
if keep_invalid:
|
if keep_invalid:
|
||||||
for pair_wc in wildcardpl:
|
for pair_wc in wildcardpl:
|
||||||
try:
|
try:
|
||||||
comp = re.compile(pair_wc)
|
comp = re.compile(pair_wc, re.IGNORECASE)
|
||||||
result_partial = [
|
result_partial = [
|
||||||
pair for pair in available_pairs if re.fullmatch(comp, pair)
|
pair for pair in available_pairs if re.fullmatch(comp, pair)
|
||||||
]
|
]
|
||||||
@ -33,7 +33,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
|||||||
else:
|
else:
|
||||||
for pair_wc in wildcardpl:
|
for pair_wc in wildcardpl:
|
||||||
try:
|
try:
|
||||||
comp = re.compile(pair_wc)
|
comp = re.compile(pair_wc, re.IGNORECASE)
|
||||||
result += [
|
result += [
|
||||||
pair for pair in available_pairs if re.fullmatch(comp, pair)
|
pair for pair in available_pairs if re.fullmatch(comp, pair)
|
||||||
]
|
]
|
||||||
|
@ -5,6 +5,20 @@ import time
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
|
|
||||||
|
def asyncio_setup() -> None: # pragma: no cover
|
||||||
|
# Set eventloop for win32 setups
|
||||||
|
# Reverts a change done in uvicorn 0.15.0 - which now sets the eventloop
|
||||||
|
# via policy.
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 8) and sys.platform == "win32":
|
||||||
|
import asyncio
|
||||||
|
import selectors
|
||||||
|
selector = selectors.SelectSelector()
|
||||||
|
loop = asyncio.SelectorEventLoop(selector)
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
|
||||||
class UvicornServer(uvicorn.Server):
|
class UvicornServer(uvicorn.Server):
|
||||||
"""
|
"""
|
||||||
Multithreaded server - as found in https://github.com/encode/uvicorn/issues/742
|
Multithreaded server - as found in https://github.com/encode/uvicorn/issues/742
|
||||||
@ -28,7 +42,7 @@ class UvicornServer(uvicorn.Server):
|
|||||||
try:
|
try:
|
||||||
import uvloop # noqa
|
import uvloop # noqa
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
from uvicorn.loops.asyncio import asyncio_setup
|
|
||||||
asyncio_setup()
|
asyncio_setup()
|
||||||
else:
|
else:
|
||||||
asyncio.set_event_loop(uvloop.new_event_loop())
|
asyncio.set_event_loop(uvloop.new_event_loop())
|
||||||
|
@ -403,6 +403,9 @@ class RPC:
|
|||||||
# Doing the sum is not right - overall profit needs to be based on initial capital
|
# Doing the sum is not right - overall profit needs to be based on initial capital
|
||||||
profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0
|
profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0
|
||||||
starting_balance = self._freqtrade.wallets.get_starting_balance()
|
starting_balance = self._freqtrade.wallets.get_starting_balance()
|
||||||
|
profit_closed_ratio_fromstart = 0
|
||||||
|
profit_all_ratio_fromstart = 0
|
||||||
|
if starting_balance:
|
||||||
profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance
|
profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance
|
||||||
profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance
|
profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance
|
||||||
|
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
{%set volume_pairlist = '{
|
||||||
|
"method": "VolumePairList",
|
||||||
|
"number_assets": 20,
|
||||||
|
"sort_key": "quoteVolume",
|
||||||
|
"min_value": 0,
|
||||||
|
"refresh_period": 1800
|
||||||
|
}' %}
|
||||||
{
|
{
|
||||||
"max_open_trades": {{ max_open_trades }},
|
"max_open_trades": {{ max_open_trades }},
|
||||||
"stake_currency": "{{ stake_currency }}",
|
"stake_currency": "{{ stake_currency }}",
|
||||||
@ -29,7 +36,7 @@
|
|||||||
},
|
},
|
||||||
{{ exchange | indent(4) }},
|
{{ exchange | indent(4) }},
|
||||||
"pairlists": [
|
"pairlists": [
|
||||||
{"method": "StaticPairList"}
|
{{ '{"method": "StaticPairList"}' if exchange_name == 'bittrex' else volume_pairlist }}
|
||||||
],
|
],
|
||||||
"edge": {
|
"edge": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
|
@ -8,34 +8,8 @@
|
|||||||
"rateLimit": 200
|
"rateLimit": 200
|
||||||
},
|
},
|
||||||
"pair_whitelist": [
|
"pair_whitelist": [
|
||||||
"ALGO/BTC",
|
|
||||||
"ATOM/BTC",
|
|
||||||
"BAT/BTC",
|
|
||||||
"BCH/BTC",
|
|
||||||
"BRD/BTC",
|
|
||||||
"EOS/BTC",
|
|
||||||
"ETH/BTC",
|
|
||||||
"IOTA/BTC",
|
|
||||||
"LINK/BTC",
|
|
||||||
"LTC/BTC",
|
|
||||||
"NEO/BTC",
|
|
||||||
"NXS/BTC",
|
|
||||||
"XMR/BTC",
|
|
||||||
"XRP/BTC",
|
|
||||||
"XTZ/BTC"
|
|
||||||
],
|
],
|
||||||
"pair_blacklist": [
|
"pair_blacklist": [
|
||||||
"BNB/BTC",
|
"BNB/.*"
|
||||||
"BNB/BUSD",
|
|
||||||
"BNB/ETH",
|
|
||||||
"BNB/EUR",
|
|
||||||
"BNB/NGN",
|
|
||||||
"BNB/PAX",
|
|
||||||
"BNB/RUB",
|
|
||||||
"BNB/TRY",
|
|
||||||
"BNB/TUSD",
|
|
||||||
"BNB/USDC",
|
|
||||||
"BNB/USDS",
|
|
||||||
"BNB/USDT"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,6 @@
|
|||||||
"rateLimit": 500
|
"rateLimit": 500
|
||||||
},
|
},
|
||||||
"pair_whitelist": [
|
"pair_whitelist": [
|
||||||
"ETH/BTC",
|
|
||||||
"LTC/BTC",
|
|
||||||
"ETC/BTC",
|
|
||||||
"DASH/BTC",
|
|
||||||
"ZEC/BTC",
|
|
||||||
"XLM/BTC",
|
|
||||||
"XRP/BTC",
|
|
||||||
"TRX/BTC",
|
|
||||||
"ADA/BTC",
|
|
||||||
"XMR/BTC"
|
|
||||||
],
|
],
|
||||||
"pair_blacklist": [
|
"pair_blacklist": [
|
||||||
]
|
]
|
||||||
|
@ -7,28 +7,10 @@
|
|||||||
"ccxt_async_config": {
|
"ccxt_async_config": {
|
||||||
"enableRateLimit": true,
|
"enableRateLimit": true,
|
||||||
"rateLimit": 1000
|
"rateLimit": 1000
|
||||||
|
// Enable the below for downoading data.
|
||||||
|
//"rateLimit": 3100
|
||||||
},
|
},
|
||||||
"pair_whitelist": [
|
"pair_whitelist": [
|
||||||
"ADA/EUR",
|
|
||||||
"ATOM/EUR",
|
|
||||||
"BAT/EUR",
|
|
||||||
"BCH/EUR",
|
|
||||||
"BTC/EUR",
|
|
||||||
"DAI/EUR",
|
|
||||||
"DASH/EUR",
|
|
||||||
"EOS/EUR",
|
|
||||||
"ETC/EUR",
|
|
||||||
"ETH/EUR",
|
|
||||||
"LINK/EUR",
|
|
||||||
"LTC/EUR",
|
|
||||||
"QTUM/EUR",
|
|
||||||
"REP/EUR",
|
|
||||||
"WAVES/EUR",
|
|
||||||
"XLM/EUR",
|
|
||||||
"XMR/EUR",
|
|
||||||
"XRP/EUR",
|
|
||||||
"XTZ/EUR",
|
|
||||||
"ZEC/EUR"
|
|
||||||
],
|
],
|
||||||
"pair_blacklist": [
|
"pair_blacklist": [
|
||||||
|
|
||||||
|
18
freqtrade/templates/subtemplates/exchange_kucoin.j2
Normal file
18
freqtrade/templates/subtemplates/exchange_kucoin.j2
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
"exchange": {
|
||||||
|
"name": "{{ exchange_name | lower }}",
|
||||||
|
"key": "{{ exchange_key }}",
|
||||||
|
"secret": "{{ exchange_secret }}",
|
||||||
|
"password": "{{ exchange_key_password }}",
|
||||||
|
"ccxt_config": {
|
||||||
|
"enableRateLimit": true
|
||||||
|
"rateLimit": 200
|
||||||
|
},
|
||||||
|
"ccxt_async_config": {
|
||||||
|
"enableRateLimit": true,
|
||||||
|
"rateLimit": 200
|
||||||
|
},
|
||||||
|
"pair_whitelist": [
|
||||||
|
],
|
||||||
|
"pair_blacklist": [
|
||||||
|
]
|
||||||
|
}
|
@ -8,7 +8,7 @@ flake8==3.9.2
|
|||||||
flake8-type-annotations==0.1.0
|
flake8-type-annotations==0.1.0
|
||||||
flake8-tidy-imports==4.4.1
|
flake8-tidy-imports==4.4.1
|
||||||
mypy==0.910
|
mypy==0.910
|
||||||
pytest==6.2.4
|
pytest==6.2.5
|
||||||
pytest-asyncio==0.15.1
|
pytest-asyncio==0.15.1
|
||||||
pytest-cov==2.12.1
|
pytest-cov==2.12.1
|
||||||
pytest-mock==3.6.1
|
pytest-mock==3.6.1
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Include all requirements to run the bot.
|
# Include all requirements to run the bot.
|
||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
|
|
||||||
plotly==5.3.0
|
plotly==5.3.1
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
numpy==1.21.2
|
numpy==1.21.2
|
||||||
pandas==1.3.2
|
pandas==1.3.2
|
||||||
|
|
||||||
ccxt==1.55.56
|
ccxt==1.55.83
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==3.4.8
|
cryptography==3.4.8
|
||||||
aiohttp==3.7.4.post0
|
aiohttp==3.7.4.post0
|
||||||
|
14
setup.sh
14
setup.sh
@ -95,19 +95,7 @@ function install_talib() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd build_helpers
|
cd build_helpers && ./install_ta-lib.sh && cd ..
|
||||||
tar zxvf ta-lib-0.4.0-src.tar.gz
|
|
||||||
cd ta-lib
|
|
||||||
sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h
|
|
||||||
./configure --prefix=/usr/local
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
if [ -x "$(command -v apt-get)" ]; then
|
|
||||||
echo "Updating library path using ldconfig"
|
|
||||||
sudo ldconfig
|
|
||||||
fi
|
|
||||||
cd .. && rm -rf ./ta-lib/
|
|
||||||
cd ..
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_mac_newer_python_dependencies() {
|
function install_mac_newer_python_dependencies() {
|
||||||
|
@ -109,6 +109,13 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog):
|
|||||||
assert hasattr(ex._api_async, 'TestKWARG')
|
assert hasattr(ex._api_async, 'TestKWARG')
|
||||||
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
||||||
assert log_has(asynclogmsg, caplog)
|
assert log_has(asynclogmsg, caplog)
|
||||||
|
# Test additional headers case
|
||||||
|
Exchange._headers = {'hello': 'world'}
|
||||||
|
ex = Exchange(conf)
|
||||||
|
|
||||||
|
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
||||||
|
assert ex._api.headers == {'hello': 'world'}
|
||||||
|
Exchange._headers = {}
|
||||||
|
|
||||||
# TODO-lev: Test with options
|
# TODO-lev: Test with options
|
||||||
|
|
||||||
|
@ -739,11 +739,16 @@ def test_auto_hyperopt_interface(default_conf):
|
|||||||
PairLocks.timeframe = default_conf['timeframe']
|
PairLocks.timeframe = default_conf['timeframe']
|
||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
strategy = StrategyResolver.load_strategy(default_conf)
|
||||||
|
|
||||||
|
with pytest.raises(OperationalException):
|
||||||
|
next(strategy.enumerate_parameters('deadBeef'))
|
||||||
|
|
||||||
assert strategy.buy_rsi.value == strategy.buy_params['buy_rsi']
|
assert strategy.buy_rsi.value == strategy.buy_params['buy_rsi']
|
||||||
# PlusDI is NOT in the buy-params, so default should be used
|
# PlusDI is NOT in the buy-params, so default should be used
|
||||||
assert strategy.buy_plusdi.value == 0.5
|
assert strategy.buy_plusdi.value == 0.5
|
||||||
assert strategy.sell_rsi.value == strategy.sell_params['sell_rsi']
|
assert strategy.sell_rsi.value == strategy.sell_params['sell_rsi']
|
||||||
|
|
||||||
|
assert repr(strategy.sell_rsi) == 'IntParameter(74)'
|
||||||
|
|
||||||
# Parameter is disabled - so value from sell_param dict will NOT be used.
|
# Parameter is disabled - so value from sell_param dict will NOT be used.
|
||||||
assert strategy.sell_minusdi.value == 0.5
|
assert strategy.sell_minusdi.value == 0.5
|
||||||
all_params = strategy.detect_all_parameters()
|
all_params = strategy.detect_all_parameters()
|
||||||
|
@ -107,6 +107,7 @@ def test_order_dict_dry_run(default_conf, mocker, caplog) -> None:
|
|||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
|
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
|
||||||
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
|
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_order_dict_live(default_conf, mocker, caplog) -> None:
|
def test_order_dict_live(default_conf, mocker, caplog) -> None:
|
||||||
@ -140,6 +141,7 @@ def test_order_dict_live(default_conf, mocker, caplog) -> None:
|
|||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
|
assert not freqtrade.strategy.order_types['stoploss_on_exchange']
|
||||||
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
|
assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
|
def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
|
||||||
@ -415,6 +417,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord
|
|||||||
|
|
||||||
assert freqtrade.create_trade('ETH/BTC')
|
assert freqtrade.create_trade('ETH/BTC')
|
||||||
assert log_has_re(r"Stake amount for pair .* is too small.*", caplog)
|
assert log_has_re(r"Stake amount for pair .* is too small.*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open,
|
def test_create_trade_zero_stake_amount(default_conf, ticker, limit_buy_order_open,
|
||||||
@ -478,6 +481,7 @@ def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order_ope
|
|||||||
n = freqtrade.enter_positions()
|
n = freqtrade.enter_positions()
|
||||||
assert n == 0
|
assert n == 0
|
||||||
assert log_has_re(r"No currency pair in active pair whitelist.*", caplog)
|
assert log_has_re(r"No currency pair in active pair whitelist.*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee,
|
def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee,
|
||||||
@ -518,11 +522,13 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order,
|
|||||||
# 0 trades, but it's not because of pairlock.
|
# 0 trades, but it's not because of pairlock.
|
||||||
assert n == 0
|
assert n == 0
|
||||||
assert not log_has_re(message, caplog)
|
assert not log_has_re(message, caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because')
|
PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because')
|
||||||
n = freqtrade.enter_positions()
|
n = freqtrade.enter_positions()
|
||||||
assert n == 0
|
assert n == 0
|
||||||
assert log_has_re(message, caplog)
|
assert log_has_re(message, caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
||||||
@ -1086,6 +1092,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog)
|
assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog)
|
||||||
assert trade.stoploss_order_id is None
|
assert trade.stoploss_order_id is None
|
||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Binance.stoploss',
|
'freqtrade.exchange.Binance.stoploss',
|
||||||
@ -1115,6 +1122,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
assert stoploss.call_count == 0
|
assert stoploss.call_count == 0
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
||||||
@ -1154,6 +1162,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
|||||||
assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog)
|
assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog)
|
||||||
assert trade.stoploss_order_id is None
|
assert trade.stoploss_order_id is None
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
||||||
@ -1202,6 +1211,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
|||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value
|
assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == SellType.EMERGENCY_SELL.value
|
||||||
assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market'
|
assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market'
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee,
|
def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, fee,
|
||||||
@ -1431,6 +1441,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
|
|||||||
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging, side="sell")
|
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging, side="sell")
|
||||||
assert cancel_mock.call_count == 1
|
assert cancel_mock.call_count == 1
|
||||||
assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog)
|
assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@ -1671,6 +1682,7 @@ def test_enter_positions(mocker, default_conf, caplog) -> None:
|
|||||||
assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog)
|
assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog)
|
||||||
# create_trade should be called once for every pair in the whitelist.
|
# create_trade should be called once for every pair in the whitelist.
|
||||||
assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist'])
|
assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist'])
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_enter_positions_exception(mocker, default_conf, caplog) -> None:
|
def test_enter_positions_exception(mocker, default_conf, caplog) -> None:
|
||||||
@ -1710,6 +1722,7 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None:
|
|||||||
# test amount modified by fee-logic
|
# test amount modified by fee-logic
|
||||||
n = freqtrade.exit_positions(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
assert n == 0
|
assert n == 0
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None:
|
def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None:
|
||||||
@ -1730,6 +1743,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog)
|
|||||||
n = freqtrade.exit_positions(trades)
|
n = freqtrade.exit_positions(trades)
|
||||||
assert n == 0
|
assert n == 0
|
||||||
assert log_has('Unable to sell trade ETH/BTC: ', caplog)
|
assert log_has('Unable to sell trade ETH/BTC: ', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None:
|
def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None:
|
||||||
@ -1752,10 +1766,12 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
|
|||||||
)
|
)
|
||||||
assert not freqtrade.update_trade_state(trade, None)
|
assert not freqtrade.update_trade_state(trade, None)
|
||||||
assert log_has_re(r'Orderid for trade .* is empty.', caplog)
|
assert log_has_re(r'Orderid for trade .* is empty.', caplog)
|
||||||
|
caplog.clear()
|
||||||
# Add datetime explicitly since sqlalchemy defaults apply only once written to database
|
# Add datetime explicitly since sqlalchemy defaults apply only once written to database
|
||||||
freqtrade.update_trade_state(trade, '123')
|
freqtrade.update_trade_state(trade, '123')
|
||||||
# Test amount not modified by fee-logic
|
# Test amount not modified by fee-logic
|
||||||
assert not log_has_re(r'Applying fee to .*', caplog)
|
assert not log_has_re(r'Applying fee to .*', caplog)
|
||||||
|
caplog.clear()
|
||||||
assert trade.open_order_id is None
|
assert trade.open_order_id is None
|
||||||
assert trade.amount == limit_buy_order['amount']
|
assert trade.amount == limit_buy_order['amount']
|
||||||
|
|
||||||
@ -1773,6 +1789,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
|
|||||||
freqtrade.update_trade_state(trade, '123')
|
freqtrade.update_trade_state(trade, '123')
|
||||||
|
|
||||||
assert log_has_re('Found open order for.*', caplog)
|
assert log_has_re('Found open order for.*', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee,
|
def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee,
|
||||||
@ -1823,6 +1840,7 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_
|
|||||||
assert trade.amount != amount
|
assert trade.amount != amount
|
||||||
assert trade.amount == limit_buy_order['amount']
|
assert trade.amount == limit_buy_order['amount']
|
||||||
assert log_has_re(r'Applying fee on amount for .*', caplog)
|
assert log_has_re(r'Applying fee on amount for .*', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_update_trade_state_exception(mocker, default_conf,
|
def test_update_trade_state_exception(mocker, default_conf,
|
||||||
@ -1841,6 +1859,7 @@ def test_update_trade_state_exception(mocker, default_conf,
|
|||||||
)
|
)
|
||||||
freqtrade.update_trade_state(trade, trade.open_order_id)
|
freqtrade.update_trade_state(trade, trade.open_order_id)
|
||||||
assert log_has('Could not update trade amount: ', caplog)
|
assert log_has('Could not update trade amount: ', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None:
|
def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None:
|
||||||
@ -1857,6 +1876,7 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None
|
|||||||
freqtrade.update_trade_state(trade, trade.open_order_id)
|
freqtrade.update_trade_state(trade, trade.open_order_id)
|
||||||
assert grm_mock.call_count == 0
|
assert grm_mock.call_count == 0
|
||||||
assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog)
|
assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_open,
|
def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order_open,
|
||||||
@ -2025,6 +2045,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open,
|
|||||||
assert freqtrade.handle_trade(trade)
|
assert freqtrade.handle_trade(trade)
|
||||||
assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI",
|
assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI",
|
||||||
caplog)
|
caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open,
|
def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open,
|
||||||
@ -2057,6 +2078,7 @@ def test_handle_trade_use_sell_signal(default_conf, ticker, limit_buy_order_open
|
|||||||
assert freqtrade.handle_trade(trade)
|
assert freqtrade.handle_trade(trade)
|
||||||
assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL",
|
assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL",
|
||||||
caplog)
|
caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order,
|
def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open, limit_sell_order,
|
||||||
@ -2097,6 +2119,7 @@ def test_bot_loop_start_called_once(mocker, default_conf, caplog):
|
|||||||
assert log_has_re(r'Strategy caused the following exception.*', caplog)
|
assert log_has_re(r'Strategy caused the following exception.*', caplog)
|
||||||
assert ftbot.strategy.bot_loop_start.call_count == 1
|
assert ftbot.strategy.bot_loop_start.call_count == 1
|
||||||
assert ftbot.strategy.analyze.call_count == 1
|
assert ftbot.strategy.analyze.call_count == 1
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade,
|
def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_order_old, open_trade,
|
||||||
@ -2211,6 +2234,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o
|
|||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog)
|
assert log_has_re("Buy order cancelled on exchange for Trade.*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade,
|
def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_order_old, open_trade,
|
||||||
@ -2345,6 +2369,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
|
|||||||
assert rpc_mock.call_count == 1
|
assert rpc_mock.call_count == 1
|
||||||
assert open_trade.is_open is True
|
assert open_trade.is_open is True
|
||||||
assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog)
|
assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
|
def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old_partial,
|
||||||
@ -2413,6 +2438,7 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap
|
|||||||
assert trades[0].open_order_id is None
|
assert trades[0].open_order_id is None
|
||||||
assert trades[0].fee_updated('buy')
|
assert trades[0].fee_updated('buy')
|
||||||
assert pytest.approx(trades[0].fee_open) == 0.001
|
assert pytest.approx(trades[0].fee_open) == 0.001
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee,
|
def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade, caplog, fee,
|
||||||
@ -2453,6 +2479,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade,
|
|||||||
limit_buy_order_old_partial['remaining'])
|
limit_buy_order_old_partial['remaining'])
|
||||||
assert trades[0].open_order_id is None
|
assert trades[0].open_order_id is None
|
||||||
assert trades[0].fee_open == fee()
|
assert trades[0].fee_open == fee()
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None:
|
def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocker, caplog) -> None:
|
||||||
@ -2482,6 +2509,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke
|
|||||||
f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}"
|
f"{open_trade.open_date.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||||
r"\) due to Traceback \(most recent call last\):\n*",
|
r"\) due to Traceback \(most recent call last\):\n*",
|
||||||
caplog)
|
caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None:
|
def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> None:
|
||||||
@ -2525,6 +2553,7 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
|
mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
|
||||||
assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
|
assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason)
|
||||||
assert log_has_re(r"Order .* for .* not cancelled.", caplog)
|
assert log_has_re(r"Order .* for .* not cancelled.", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
|
@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
|
||||||
@ -2546,6 +2575,7 @@ def test_handle_cancel_buy_exchanges(mocker, caplog, default_conf,
|
|||||||
assert cancel_order_mock.call_count == 0
|
assert cancel_order_mock.call_count == 0
|
||||||
assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog)
|
assert log_has_re(r'Buy order fully cancelled. Removing .* from database\.', caplog)
|
||||||
assert nofiy_mock.call_count == 1
|
assert nofiy_mock.call_count == 1
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('cancelorder', [
|
@pytest.mark.parametrize('cancelorder', [
|
||||||
@ -2915,6 +2945,7 @@ def test_execute_trade_exit_sloe_cancel_exception(
|
|||||||
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS))
|
||||||
assert create_order_mock.call_count == 2
|
assert create_order_mock.call_count == 2
|
||||||
assert log_has('Could not cancel stoploss order abcd', caplog)
|
assert log_has('Could not cancel stoploss order abcd', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up,
|
def test_execute_trade_exit_with_stoploss_on_exchange(default_conf, ticker, fee, ticker_sell_up,
|
||||||
@ -3310,6 +3341,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_
|
|||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||||
assert trade.amount != amnt
|
assert trade.amount != amnt
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test__safe_sell_amount(default_conf, fee, caplog, mocker):
|
def test__safe_sell_amount(default_conf, fee, caplog, mocker):
|
||||||
@ -3340,6 +3372,7 @@ def test__safe_sell_amount(default_conf, fee, caplog, mocker):
|
|||||||
assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet
|
assert freqtrade._safe_sell_amount(trade.pair, amount_wallet) == amount_wallet
|
||||||
assert not log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
assert not log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||||
assert wallet_update.call_count == 1
|
assert wallet_update.call_count == 1
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test__safe_sell_amount_error(default_conf, fee, caplog, mocker):
|
def test__safe_sell_amount_error(default_conf, fee, caplog, mocker):
|
||||||
@ -3396,6 +3429,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo
|
|||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
|
|
||||||
assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog)
|
assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open,
|
def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open,
|
||||||
@ -3487,6 +3521,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order_open, limit_buy_order,
|
|||||||
assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, "
|
assert log_has("ETH/BTC - HIT STOP: current price at 0.000012, stoploss is 0.000015, "
|
||||||
"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
|
"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
|
||||||
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee,
|
def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_order_open, fee,
|
||||||
@ -3534,6 +3569,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or
|
|||||||
assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog)
|
assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0 profit: 0.2666%", caplog)
|
||||||
assert log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
assert log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
||||||
assert trade.stop_loss == 0.0000138501
|
assert trade.stop_loss == 0.0000138501
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
MagicMock(return_value={
|
MagicMock(return_value={
|
||||||
@ -3547,6 +3583,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, limit_buy_or
|
|||||||
f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
|
f"ETH/BTC - HIT STOP: current price at {buy_price + 0.000002:.6f}, "
|
||||||
f"stoploss is {trade.stop_loss:.6f}, "
|
f"stoploss is {trade.stop_loss:.6f}, "
|
||||||
f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
|
f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee,
|
def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_order_open, fee,
|
||||||
@ -3594,6 +3631,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde
|
|||||||
assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog)
|
assert log_has("ETH/BTC - Using positive stoploss: 0.01 offset: 0.011 profit: 0.2666%", caplog)
|
||||||
assert log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
assert log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
||||||
assert trade.stop_loss == 0.0000138501
|
assert trade.stop_loss == 0.0000138501
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
MagicMock(return_value={
|
MagicMock(return_value={
|
||||||
@ -3608,6 +3646,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, limit_buy_orde
|
|||||||
f"stoploss is {trade.stop_loss:.6f}, "
|
f"stoploss is {trade.stop_loss:.6f}, "
|
||||||
f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
|
f"initial stoploss was at 0.000010, trade opened at 0.000011", caplog)
|
||||||
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee,
|
def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_open, fee,
|
||||||
@ -3658,6 +3697,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_
|
|||||||
|
|
||||||
assert not log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
assert not log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
||||||
assert trade.stop_loss == 0.0000098910
|
assert trade.stop_loss == 0.0000098910
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
# price rises above the offset (rises 12% when the offset is 5.5%)
|
# price rises above the offset (rises 12% when the offset is 5.5%)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
@ -3671,6 +3711,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, limit_buy_order_
|
|||||||
assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog)
|
assert log_has("ETH/BTC - Using positive stoploss: 0.05 offset: 0.055 profit: 0.1218%", caplog)
|
||||||
assert log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
assert log_has("ETH/BTC - Adjusting stoploss...", caplog)
|
||||||
assert trade.stop_loss == 0.0000117705
|
assert trade.stop_loss == 0.0000117705
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open,
|
def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order_open,
|
||||||
@ -3734,6 +3775,7 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, fe
|
|||||||
' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).',
|
' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).',
|
||||||
caplog
|
caplog
|
||||||
)
|
)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee,
|
def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fee, fee,
|
||||||
@ -3759,6 +3801,7 @@ def test_get_real_amount_quote_dust(default_conf, trades_for_order, buy_order_fe
|
|||||||
assert walletmock.call_count == 1
|
assert walletmock.call_count == 1
|
||||||
assert log_has_re(r'Fee amount for Trade.* was in base currency '
|
assert log_has_re(r'Fee amount for Trade.* was in base currency '
|
||||||
'- Eating Fee 0.008 into dust', caplog)
|
'- Eating Fee 0.008 into dust', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee):
|
def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, fee):
|
||||||
@ -3784,6 +3827,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker, f
|
|||||||
'myTrade-Dict empty found',
|
'myTrade-Dict empty found',
|
||||||
caplog
|
caplog
|
||||||
)
|
)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker):
|
def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, fee, mocker):
|
||||||
@ -3879,6 +3923,7 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c
|
|||||||
assert trade.fee_open_currency is not None
|
assert trade.fee_open_currency is not None
|
||||||
assert trade.fee_close_cost is None
|
assert trade.fee_close_cost is None
|
||||||
assert trade.fee_close_currency is None
|
assert trade.fee_close_currency is None
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee,
|
def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee,
|
||||||
@ -3916,6 +3961,7 @@ def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee,
|
|||||||
assert trade.fee_open_currency is not None
|
assert trade.fee_open_currency is not None
|
||||||
assert trade.fee_close_cost is None
|
assert trade.fee_close_cost is None
|
||||||
assert trade.fee_close_currency is None
|
assert trade.fee_close_currency is None
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee,
|
def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee,
|
||||||
@ -3946,6 +3992,7 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
|
|||||||
' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).',
|
' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).',
|
||||||
caplog
|
caplog
|
||||||
)
|
)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker):
|
def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order_fee, fee, mocker):
|
||||||
@ -4189,6 +4236,7 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None
|
|||||||
with pytest.raises(PricingError):
|
with pytest.raises(PricingError):
|
||||||
freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy")
|
freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy")
|
||||||
assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog)
|
assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None:
|
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None:
|
||||||
@ -4259,6 +4307,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o
|
|||||||
freqtrade.handle_trade(trade)
|
freqtrade.handle_trade(trade)
|
||||||
assert log_has_re(r'Sell Price at location 1 from orderbook could not be determined\..*',
|
assert log_has_re(r'Sell Price at location 1 from orderbook could not be determined\..*',
|
||||||
caplog)
|
caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_startup_state(default_conf, mocker):
|
def test_startup_state(default_conf, mocker):
|
||||||
@ -4317,6 +4366,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order_
|
|||||||
assert log_has_re(r"Unable to create trade for XRP/BTC: "
|
assert log_has_re(r"Unable to create trade for XRP/BTC: "
|
||||||
r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)",
|
r"Available balance \(0.0 BTC\) is lower than stake amount \(0.001 BTC\)",
|
||||||
caplog)
|
caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@ -4360,6 +4410,7 @@ def test_update_open_orders(mocker, default_conf, fee, caplog):
|
|||||||
|
|
||||||
freqtrade.update_open_orders()
|
freqtrade.update_open_orders()
|
||||||
assert not log_has_re(r"Error updating Order .*", caplog)
|
assert not log_has_re(r"Error updating Order .*", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
freqtrade.config['dry_run'] = False
|
freqtrade.config['dry_run'] = False
|
||||||
freqtrade.update_open_orders()
|
freqtrade.update_open_orders()
|
||||||
@ -4475,6 +4526,7 @@ def test_reupdate_buy_order_fees(mocker, default_conf, fee, caplog):
|
|||||||
assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
|
assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
|
||||||
assert mock_uts.call_count == 0
|
assert mock_uts.call_count == 0
|
||||||
assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog)
|
assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
@ -4612,6 +4664,7 @@ def test_refind_lost_order(mocker, default_conf, fee, caplog):
|
|||||||
|
|
||||||
freqtrade.refind_lost_order(trades[4])
|
freqtrade.refind_lost_order(trades[4])
|
||||||
assert log_has(f"Error updating {order['id']}.", caplog)
|
assert log_has(f"Error updating {order['id']}.", caplog)
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
|
||||||
def test_get_valid_price(mocker, default_conf) -> None:
|
def test_get_valid_price(mocker, default_conf) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user