Merge branch 'feat/short' into lev-exchange

This commit is contained in:
Sam Germain 2021-09-07 22:13:20 -06:00
commit dc83e04f9b
30 changed files with 173 additions and 99 deletions

View File

@ -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.*

View File

@ -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

View File

@ -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 \

View File

@ -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')]
``` ```

View File

@ -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

View File

@ -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.

View 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

View File

@ -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

View File

@ -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)

View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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",
} }

View File

@ -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:

View File

@ -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()

View File

@ -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)
] ]

View File

@ -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())

View File

@ -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

View File

@ -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,

View File

@ -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"
] ]
} }

View File

@ -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": [
] ]

View File

@ -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": [

View 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": [
]
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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

View File

@ -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()

View File

@ -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: