Merge branch 'develop' into data_handler
This commit is contained in:
commit
699c0d6bc3
@ -8,6 +8,27 @@ You can analyze the results of backtests and trading history easily using Jupyte
|
|||||||
* Don't forget to start a Jupyter notebook server from within your conda or venv environment or use [nb_conda_kernels](https://github.com/Anaconda-Platform/nb_conda_kernels)*
|
* Don't forget to start a Jupyter notebook server from within your conda or venv environment or use [nb_conda_kernels](https://github.com/Anaconda-Platform/nb_conda_kernels)*
|
||||||
* Copy the example notebook before use so your changes don't get clobbered with the next freqtrade update.
|
* Copy the example notebook before use so your changes don't get clobbered with the next freqtrade update.
|
||||||
|
|
||||||
|
### Using virtual environment with system-wide Jupyter installation
|
||||||
|
|
||||||
|
Sometimes it can be desired to use a system-wide installation of Jupyter notebook, and use a jupyter kernel from the virtual environment.
|
||||||
|
This prevents you from installing the full jupyter suite multiple times per system, and provides an easy way to switch between tasks (freqtrade / other analytics tasks).
|
||||||
|
|
||||||
|
For this to work, first activate your virtual environment and run the following commands:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Activate virtual environment
|
||||||
|
source .env/bin/activate
|
||||||
|
|
||||||
|
pip install ipykernel
|
||||||
|
ipython kernel install --user --name=freqtrade
|
||||||
|
# Restart jupyter (lab / notebook)
|
||||||
|
# select kernel "freqtrade" in the notebook
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
This section is provided for completeness, the Freqtrade Team won't provide full support for problems with this setup and will recommend to install Jupyter in the virtual environment directly, as that is the easiest way to get jupyter notebooks up and running. For help with this setup please refer to the [Project Jupyter](https://jupyter.org/) [documentation](https://jupyter.org/documentation) or [help channels](https://jupyter.org/community).
|
||||||
|
|
||||||
|
|
||||||
## Fine print
|
## Fine print
|
||||||
|
|
||||||
Some tasks don't work especially well in notebooks. For example, anything using asynchronous execution is a problem for Jupyter. Also, freqtrade's primary entry point is the shell cli, so using pure python in a notebook bypasses arguments that provide required objects and parameters to helper functions. You may need to set those values or create expected objects manually.
|
Some tasks don't work especially well in notebooks. For example, anything using asynchronous execution is a problem for Jupyter. Also, freqtrade's primary entry point is the shell cli, so using pure python in a notebook bypasses arguments that provide required objects and parameters to helper functions. You may need to set those values or create expected objects manually.
|
||||||
|
@ -11,34 +11,3 @@ if __version__ == 'develop':
|
|||||||
except Exception:
|
except Exception:
|
||||||
# git not available, ignore
|
# git not available, ignore
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DependencyException(Exception):
|
|
||||||
"""
|
|
||||||
Indicates that an assumed dependency is not met.
|
|
||||||
This could happen when there is currently not enough money on the account.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class OperationalException(Exception):
|
|
||||||
"""
|
|
||||||
Requires manual intervention and will usually stop the bot.
|
|
||||||
This happens when an exchange returns an unexpected error during runtime
|
|
||||||
or given configuration is invalid.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidOrderException(Exception):
|
|
||||||
"""
|
|
||||||
This is returned when the order is not valid. Example:
|
|
||||||
If stoploss on exchange order is hit, then trying to cancel the order
|
|
||||||
should return this exception.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class TemporaryError(Exception):
|
|
||||||
"""
|
|
||||||
Temporary network or exchange related error.
|
|
||||||
This could happen when an exchange is congested, unavailable, or the user
|
|
||||||
has networking problems. Usually resolves itself after a time.
|
|
||||||
"""
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason,
|
from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason,
|
||||||
is_exchange_known_ccxt, is_exchange_bad,
|
is_exchange_bad, is_exchange_known_ccxt,
|
||||||
is_exchange_officially_supported)
|
is_exchange_officially_supported)
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@ from typing import Any, Dict
|
|||||||
from jsonschema import Draft4Validator, validators
|
from jsonschema import Draft4Validator, validators
|
||||||
from jsonschema.exceptions import ValidationError, best_match
|
from jsonschema.exceptions import ValidationError, best_match
|
||||||
|
|
||||||
from freqtrade import constants, OperationalException
|
from freqtrade import constants
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -7,15 +7,16 @@ from copy import deepcopy
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, List, Optional
|
from typing import Any, Callable, Dict, List, Optional
|
||||||
|
|
||||||
from freqtrade import OperationalException, constants
|
from freqtrade import constants
|
||||||
from freqtrade.configuration.check_exchange import check_exchange
|
from freqtrade.configuration.check_exchange import check_exchange
|
||||||
from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings
|
from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings
|
||||||
from freqtrade.configuration.directory_operations import (create_datadir,
|
from freqtrade.configuration.directory_operations import (create_datadir,
|
||||||
create_userdata_dir)
|
create_userdata_dir)
|
||||||
from freqtrade.configuration.load_config import load_config_file
|
from freqtrade.configuration.load_config import load_config_file
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.loggers import setup_logging
|
from freqtrade.loggers import setup_logging
|
||||||
from freqtrade.misc import deep_merge_dicts, json_load
|
from freqtrade.misc import deep_merge_dicts, json_load
|
||||||
from freqtrade.state import RunMode, TRADING_MODES, NON_UTIL_MODES
|
from freqtrade.state import NON_UTIL_MODES, TRADING_MODES, RunMode
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ Functions to handle deprecated settings
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -3,7 +3,7 @@ import shutil
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.constants import USER_DATA_FILES
|
from freqtrade.constants import USER_DATA_FILES
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -6,7 +6,7 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -7,11 +7,11 @@ from typing import Dict, List, Optional, Tuple
|
|||||||
import arrow
|
import arrow
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS
|
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS
|
||||||
from freqtrade.data.converter import parse_ticker_dataframe, trades_to_ohlcv
|
from freqtrade.data.converter import parse_ticker_dataframe, trades_to_ohlcv
|
||||||
from freqtrade.data.history.idatahandler import IDataHandler, get_datahandler
|
from freqtrade.data.history.idatahandler import IDataHandler, get_datahandler
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -8,12 +8,12 @@ import numpy as np
|
|||||||
import utils_find_1st as utf1st
|
import utils_find_1st as utf1st
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import constants, OperationalException
|
from freqtrade import constants
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.strategy.interface import SellType
|
from freqtrade.strategy.interface import SellType
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
37
freqtrade/exceptions.py
Normal file
37
freqtrade/exceptions.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class FreqtradeException(Exception):
|
||||||
|
"""
|
||||||
|
Freqtrade base exception. Handled at the outermost level.
|
||||||
|
All other exception types are subclasses of this exception type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class OperationalException(FreqtradeException):
|
||||||
|
"""
|
||||||
|
Requires manual intervention and will stop the bot.
|
||||||
|
Most of the time, this is caused by an invalid Configuration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class DependencyException(FreqtradeException):
|
||||||
|
"""
|
||||||
|
Indicates that an assumed dependency is not met.
|
||||||
|
This could happen when there is currently not enough money on the account.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidOrderException(FreqtradeException):
|
||||||
|
"""
|
||||||
|
This is returned when the order is not valid. Example:
|
||||||
|
If stoploss on exchange order is hit, then trying to cancel the order
|
||||||
|
should return this exception.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TemporaryError(FreqtradeException):
|
||||||
|
"""
|
||||||
|
Temporary network or exchange related error.
|
||||||
|
This could happen when an exchange is congested, unavailable, or the user
|
||||||
|
has networking problems. Usually resolves itself after a time.
|
||||||
|
"""
|
@ -4,8 +4,8 @@ from typing import Dict
|
|||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
|
|
||||||
from freqtrade import (DependencyException, InvalidOrderException,
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from freqtrade import DependencyException, TemporaryError
|
from freqtrade.exceptions import DependencyException, TemporaryError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ import ccxt.async_support as ccxt_async
|
|||||||
from ccxt.base.decimal_to_precision import ROUND_DOWN, ROUND_UP
|
from ccxt.base.decimal_to_precision import ROUND_DOWN, ROUND_UP
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import (DependencyException, InvalidOrderException,
|
|
||||||
OperationalException, TemporaryError)
|
|
||||||
from freqtrade.data.converter import parse_ticker_dataframe
|
from freqtrade.data.converter import parse_ticker_dataframe
|
||||||
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
|
from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async
|
||||||
from freqtrade.misc import deep_merge_dicts
|
from freqtrade.misc import deep_merge_dicts
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from typing import Dict
|
|||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
|
|
||||||
from freqtrade import OperationalException, TemporaryError
|
from freqtrade.exceptions import OperationalException, TemporaryError
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.exchange import retrier
|
from freqtrade.exchange.exchange import retrier
|
||||||
|
|
||||||
|
@ -12,17 +12,17 @@ from typing import Any, Dict, List, Optional, Tuple
|
|||||||
import arrow
|
import arrow
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
from freqtrade import (DependencyException, InvalidOrderException, __version__,
|
from freqtrade import __version__, constants, persistence
|
||||||
constants, persistence)
|
|
||||||
from freqtrade.configuration import validate_config_consistency
|
from freqtrade.configuration import validate_config_consistency
|
||||||
from freqtrade.data.converter import order_book_to_dataframe
|
from freqtrade.data.converter import order_book_to_dataframe
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.edge import Edge
|
from freqtrade.edge import Edge
|
||||||
|
from freqtrade.exceptions import DependencyException, InvalidOrderException
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
|
||||||
|
from freqtrade.pairlist.pairlistmanager import PairListManager
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||||
from freqtrade.rpc import RPCManager, RPCMessageType
|
from freqtrade.rpc import RPCManager, RPCMessageType
|
||||||
from freqtrade.pairlist.pairlistmanager import PairListManager
|
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from freqtrade.strategy.interface import IStrategy, SellType
|
from freqtrade.strategy.interface import IStrategy, SellType
|
||||||
from freqtrade.wallets import Wallets
|
from freqtrade.wallets import Wallets
|
||||||
@ -136,7 +136,7 @@ class FreqtradeBot:
|
|||||||
self.process_maybe_execute_sells(trades)
|
self.process_maybe_execute_sells(trades)
|
||||||
|
|
||||||
# Then looking for buy opportunities
|
# Then looking for buy opportunities
|
||||||
if len(trades) < self.config['max_open_trades']:
|
if self.get_free_open_trades():
|
||||||
self.process_maybe_execute_buys()
|
self.process_maybe_execute_buys()
|
||||||
|
|
||||||
# Check and handle any timed out open orders
|
# Check and handle any timed out open orders
|
||||||
@ -173,6 +173,14 @@ class FreqtradeBot:
|
|||||||
"""
|
"""
|
||||||
return [(pair, self.config['ticker_interval']) for pair in pairs]
|
return [(pair, self.config['ticker_interval']) for pair in pairs]
|
||||||
|
|
||||||
|
def get_free_open_trades(self):
|
||||||
|
"""
|
||||||
|
Return the number of free open trades slots or 0 if
|
||||||
|
max number of open trades reached
|
||||||
|
"""
|
||||||
|
open_trades = len(Trade.get_open_trades())
|
||||||
|
return max(0, self.config['max_open_trades'] - open_trades)
|
||||||
|
|
||||||
def get_target_bid(self, pair: str, tick: Dict = None) -> float:
|
def get_target_bid(self, pair: str, tick: Dict = None) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates bid target between current ask price and last price
|
Calculates bid target between current ask price and last price
|
||||||
@ -204,14 +212,14 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
return used_rate
|
return used_rate
|
||||||
|
|
||||||
def _get_trade_stake_amount(self, pair) -> Optional[float]:
|
def get_trade_stake_amount(self, pair) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
Check if stake amount can be fulfilled with the available balance
|
Calculate stake amount for the trade
|
||||||
for the stake currency
|
:return: float: Stake amount
|
||||||
:return: float: Stake Amount
|
|
||||||
"""
|
"""
|
||||||
|
stake_amount: Optional[float]
|
||||||
if self.edge:
|
if self.edge:
|
||||||
return self.edge.stake_amount(
|
stake_amount = self.edge.stake_amount(
|
||||||
pair,
|
pair,
|
||||||
self.wallets.get_free(self.config['stake_currency']),
|
self.wallets.get_free(self.config['stake_currency']),
|
||||||
self.wallets.get_total(self.config['stake_currency']),
|
self.wallets.get_total(self.config['stake_currency']),
|
||||||
@ -219,18 +227,31 @@ class FreqtradeBot:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
stake_amount = self.config['stake_amount']
|
stake_amount = self.config['stake_amount']
|
||||||
|
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
|
||||||
|
stake_amount = self._calculate_unlimited_stake_amount()
|
||||||
|
|
||||||
|
return self._check_available_stake_amount(stake_amount)
|
||||||
|
|
||||||
|
def _calculate_unlimited_stake_amount(self) -> Optional[float]:
|
||||||
|
"""
|
||||||
|
Calculate stake amount for "unlimited" stake amount
|
||||||
|
:return: None if max number of trades reached
|
||||||
|
"""
|
||||||
|
free_open_trades = self.get_free_open_trades()
|
||||||
|
if not free_open_trades:
|
||||||
|
return None
|
||||||
|
available_amount = self.wallets.get_free(self.config['stake_currency'])
|
||||||
|
return available_amount / free_open_trades
|
||||||
|
|
||||||
|
def _check_available_stake_amount(self, stake_amount: Optional[float]) -> Optional[float]:
|
||||||
|
"""
|
||||||
|
Check if stake amount can be fulfilled with the available balance
|
||||||
|
for the stake currency
|
||||||
|
:return: float: Stake amount
|
||||||
|
"""
|
||||||
available_amount = self.wallets.get_free(self.config['stake_currency'])
|
available_amount = self.wallets.get_free(self.config['stake_currency'])
|
||||||
|
|
||||||
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
|
if stake_amount is not None and available_amount < stake_amount:
|
||||||
open_trades = len(Trade.get_open_trades())
|
|
||||||
if open_trades >= self.config['max_open_trades']:
|
|
||||||
logger.warning("Can't open a new trade: max number of trades is reached")
|
|
||||||
return None
|
|
||||||
return available_amount / (self.config['max_open_trades'] - open_trades)
|
|
||||||
|
|
||||||
# Check if stake_amount is fulfilled
|
|
||||||
if available_amount < stake_amount:
|
|
||||||
raise DependencyException(
|
raise DependencyException(
|
||||||
f"Available balance ({available_amount} {self.config['stake_currency']}) is "
|
f"Available balance ({available_amount} {self.config['stake_currency']}) is "
|
||||||
f"lower than stake amount ({stake_amount} {self.config['stake_currency']})"
|
f"lower than stake amount ({stake_amount} {self.config['stake_currency']})"
|
||||||
@ -299,18 +320,23 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
buycount = 0
|
buycount = 0
|
||||||
# running get_signal on historical data fetched
|
# running get_signal on historical data fetched
|
||||||
for _pair in whitelist:
|
for pair in whitelist:
|
||||||
if self.strategy.is_pair_locked(_pair):
|
if self.strategy.is_pair_locked(pair):
|
||||||
logger.info(f"Pair {_pair} is currently locked.")
|
logger.info(f"Pair {pair} is currently locked.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
(buy, sell) = self.strategy.get_signal(
|
(buy, sell) = self.strategy.get_signal(
|
||||||
_pair, self.strategy.ticker_interval,
|
pair, self.strategy.ticker_interval,
|
||||||
self.dataprovider.ohlcv(_pair, self.strategy.ticker_interval))
|
self.dataprovider.ohlcv(pair, self.strategy.ticker_interval))
|
||||||
|
|
||||||
if buy and not sell and len(Trade.get_open_trades()) < self.config['max_open_trades']:
|
if buy and not sell:
|
||||||
stake_amount = self._get_trade_stake_amount(_pair)
|
if not self.get_free_open_trades():
|
||||||
|
logger.debug("Can't open a new trade: max number of trades is reached")
|
||||||
|
continue
|
||||||
|
|
||||||
|
stake_amount = self.get_trade_stake_amount(pair)
|
||||||
if not stake_amount:
|
if not stake_amount:
|
||||||
|
logger.debug("Stake amount is 0, ignoring possible trade for {pair}.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"Buy signal found: about create a new trade with stake_amount: "
|
logger.info(f"Buy signal found: about create a new trade with stake_amount: "
|
||||||
@ -320,11 +346,11 @@ class FreqtradeBot:
|
|||||||
get('check_depth_of_market', {})
|
get('check_depth_of_market', {})
|
||||||
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
|
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
|
||||||
(bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
|
(bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
|
||||||
if self._check_depth_of_market_buy(_pair, bidstrat_check_depth_of_market):
|
if self._check_depth_of_market_buy(pair, bidstrat_check_depth_of_market):
|
||||||
buycount += self.execute_buy(_pair, stake_amount)
|
buycount += self.execute_buy(pair, stake_amount)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
buycount += self.execute_buy(_pair, stake_amount)
|
buycount += self.execute_buy(pair, stake_amount)
|
||||||
|
|
||||||
return buycount > 0
|
return buycount > 0
|
||||||
|
|
||||||
@ -351,7 +377,6 @@ class FreqtradeBot:
|
|||||||
:param pair: pair for which we want to create a LIMIT_BUY
|
:param pair: pair for which we want to create a LIMIT_BUY
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
pair_s = pair.replace('_', '/')
|
|
||||||
stake_currency = self.config['stake_currency']
|
stake_currency = self.config['stake_currency']
|
||||||
fiat_currency = self.config.get('fiat_display_currency', None)
|
fiat_currency = self.config.get('fiat_display_currency', None)
|
||||||
time_in_force = self.strategy.order_time_in_force['buy']
|
time_in_force = self.strategy.order_time_in_force['buy']
|
||||||
@ -362,10 +387,10 @@ class FreqtradeBot:
|
|||||||
# Calculate amount
|
# Calculate amount
|
||||||
buy_limit_requested = self.get_target_bid(pair)
|
buy_limit_requested = self.get_target_bid(pair)
|
||||||
|
|
||||||
min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit_requested)
|
min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested)
|
||||||
if min_stake_amount is not None and min_stake_amount > stake_amount:
|
if min_stake_amount is not None and min_stake_amount > stake_amount:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Can't open a new trade for {pair_s}: stake amount "
|
f"Can't open a new trade for {pair}: stake amount "
|
||||||
f"is too small ({stake_amount} < {min_stake_amount})"
|
f"is too small ({stake_amount} < {min_stake_amount})"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
@ -388,7 +413,7 @@ class FreqtradeBot:
|
|||||||
if float(order['filled']) == 0:
|
if float(order['filled']) == 0:
|
||||||
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
|
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
|
||||||
' zero amount is fulfilled.',
|
' zero amount is fulfilled.',
|
||||||
order_tif, order_type, pair_s, order_status, self.exchange.name)
|
order_tif, order_type, pair, order_status, self.exchange.name)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
# the order is partially fulfilled
|
# the order is partially fulfilled
|
||||||
@ -396,7 +421,7 @@ class FreqtradeBot:
|
|||||||
# if the order is fulfilled fully or partially
|
# if the order is fulfilled fully or partially
|
||||||
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
|
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
|
||||||
' %s amount fulfilled out of %s (%s remaining which is canceled).',
|
' %s amount fulfilled out of %s (%s remaining which is canceled).',
|
||||||
order_tif, order_type, pair_s, order_status, self.exchange.name,
|
order_tif, order_type, pair, order_status, self.exchange.name,
|
||||||
order['filled'], order['amount'], order['remaining']
|
order['filled'], order['amount'], order['remaining']
|
||||||
)
|
)
|
||||||
stake_amount = order['cost']
|
stake_amount = order['cost']
|
||||||
@ -413,7 +438,7 @@ class FreqtradeBot:
|
|||||||
self.rpc.send_msg({
|
self.rpc.send_msg({
|
||||||
'type': RPCMessageType.BUY_NOTIFICATION,
|
'type': RPCMessageType.BUY_NOTIFICATION,
|
||||||
'exchange': self.exchange.name.capitalize(),
|
'exchange': self.exchange.name.capitalize(),
|
||||||
'pair': pair_s,
|
'pair': pair,
|
||||||
'limit': buy_limit_filled_price,
|
'limit': buy_limit_filled_price,
|
||||||
'order_type': order_type,
|
'order_type': order_type,
|
||||||
'stake_amount': stake_amount,
|
'stake_amount': stake_amount,
|
||||||
@ -892,6 +917,27 @@ class FreqtradeBot:
|
|||||||
# TODO: figure out how to handle partially complete sell orders
|
# TODO: figure out how to handle partially complete sell orders
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _safe_sell_amount(self, pair: str, amount: float) -> float:
|
||||||
|
"""
|
||||||
|
Get sellable amount.
|
||||||
|
Should be trade.amount - but will fall back to the available amount if necessary.
|
||||||
|
This should cover cases where get_real_amount() was not able to update the amount
|
||||||
|
for whatever reason.
|
||||||
|
:param pair: Pair we're trying to sell
|
||||||
|
:param amount: amount we expect to be available
|
||||||
|
:return: amount to sell
|
||||||
|
:raise: DependencyException: if available balance is not within 2% of the available amount.
|
||||||
|
"""
|
||||||
|
wallet_amount = self.wallets.get_free(pair.split('/')[0])
|
||||||
|
logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}")
|
||||||
|
if wallet_amount > amount:
|
||||||
|
return amount
|
||||||
|
elif wallet_amount > amount * 0.98:
|
||||||
|
logger.info(f"{pair} - Falling back to wallet-amount.")
|
||||||
|
return wallet_amount
|
||||||
|
else:
|
||||||
|
raise DependencyException("Not enough amount to sell.")
|
||||||
|
|
||||||
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellType) -> None:
|
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellType) -> None:
|
||||||
"""
|
"""
|
||||||
Executes a limit sell for the given trade and limit
|
Executes a limit sell for the given trade and limit
|
||||||
@ -922,10 +968,12 @@ class FreqtradeBot:
|
|||||||
# Emergencysells (default to market!)
|
# Emergencysells (default to market!)
|
||||||
ordertype = self.strategy.order_types.get("emergencysell", "market")
|
ordertype = self.strategy.order_types.get("emergencysell", "market")
|
||||||
|
|
||||||
|
amount = self._safe_sell_amount(trade.pair, trade.amount)
|
||||||
|
|
||||||
# Execute sell and update trade record
|
# Execute sell and update trade record
|
||||||
order = self.exchange.sell(pair=str(trade.pair),
|
order = self.exchange.sell(pair=str(trade.pair),
|
||||||
ordertype=ordertype,
|
ordertype=ordertype,
|
||||||
amount=trade.amount, rate=limit,
|
amount=amount, rate=limit,
|
||||||
time_in_force=self.strategy.order_time_in_force['sell']
|
time_in_force=self.strategy.order_time_in_force['sell']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from logging import Formatter
|
|||||||
from logging.handlers import RotatingFileHandler, SysLogHandler
|
from logging.handlers import RotatingFileHandler, SysLogHandler
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -4,6 +4,7 @@ Main Freqtrade bot script.
|
|||||||
Read the documentation to know what cli arguments you need.
|
Read the documentation to know what cli arguments you need.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from freqtrade.exceptions import FreqtradeException, OperationalException
|
||||||
import sys
|
import sys
|
||||||
# check min. python version
|
# check min. python version
|
||||||
if sys.version_info < (3, 6):
|
if sys.version_info < (3, 6):
|
||||||
@ -13,7 +14,6 @@ if sys.version_info < (3, 6):
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import Arguments
|
from freqtrade.configuration import Arguments
|
||||||
|
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ def main(sysargv: List[str] = None) -> None:
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info('SIGINT received, aborting ...')
|
logger.info('SIGINT received, aborting ...')
|
||||||
return_code = 0
|
return_code = 0
|
||||||
except OperationalException as e:
|
except FreqtradeException as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
return_code = 2
|
return_code = 2
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade import DependencyException, constants, OperationalException
|
from freqtrade import constants
|
||||||
|
from freqtrade.exceptions import DependencyException, OperationalException
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from freqtrade.utils import setup_utils_configuration
|
from freqtrade.utils import setup_utils_configuration
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,12 +12,12 @@ from typing import Any, Dict, List, NamedTuple, Optional
|
|||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import (TimeRange, remove_credentials,
|
from freqtrade.configuration import (TimeRange, remove_credentials,
|
||||||
validate_config_consistency)
|
validate_config_consistency)
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.converter import trim_dataframe
|
from freqtrade.data.converter import trim_dataframe
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||||
from freqtrade.misc import file_dump_json
|
from freqtrade.misc import file_dump_json
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
@ -12,8 +12,7 @@ from freqtrade import constants
|
|||||||
from freqtrade.configuration import (TimeRange, remove_credentials,
|
from freqtrade.configuration import (TimeRange, remove_credentials,
|
||||||
validate_config_consistency)
|
validate_config_consistency)
|
||||||
from freqtrade.edge import Edge
|
from freqtrade.edge import Edge
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.resolvers import StrategyResolver, ExchangeResolver
|
||||||
from freqtrade.resolvers import StrategyResolver
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ class EdgeCli:
|
|||||||
# Reset keys for edge
|
# Reset keys for edge
|
||||||
remove_credentials(self.config)
|
remove_credentials(self.config)
|
||||||
self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||||
self.exchange = Exchange(self.config)
|
self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config)
|
||||||
self.strategy = StrategyResolver.load_strategy(self.config)
|
self.strategy = StrategyResolver.load_strategy(self.config)
|
||||||
|
|
||||||
validate_config_consistency(self.config)
|
validate_config_consistency(self.config)
|
||||||
|
@ -22,14 +22,14 @@ from joblib import (Parallel, cpu_count, delayed, dump, load,
|
|||||||
wrap_non_picklable_objects)
|
wrap_non_picklable_objects)
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.data.history import get_timerange
|
|
||||||
from freqtrade.data.converter import trim_dataframe
|
from freqtrade.data.converter import trim_dataframe
|
||||||
|
from freqtrade.data.history import get_timerange
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import plural, round_dict
|
from freqtrade.misc import plural, round_dict
|
||||||
from freqtrade.optimize.backtesting import Backtesting
|
from freqtrade.optimize.backtesting import Backtesting
|
||||||
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
||||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F4
|
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
|
||||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F4
|
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
|
||||||
from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver,
|
from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver,
|
||||||
HyperOptResolver)
|
HyperOptResolver)
|
||||||
|
|
||||||
|
@ -4,17 +4,15 @@ This module defines the interface to apply for hyperopt
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from typing import Dict, Any, Callable, List
|
from typing import Any, Callable, Dict, List
|
||||||
|
|
||||||
from skopt.space import Categorical, Dimension, Integer, Real
|
from skopt.space import Categorical, Dimension, Integer, Real
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_minutes
|
from freqtrade.exchange import timeframe_to_minutes
|
||||||
from freqtrade.misc import round_dict
|
from freqtrade.misc import round_dict
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import logging
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.pairlist.IPairList import IPairList
|
from freqtrade.pairlist.IPairList import IPairList
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -4,11 +4,12 @@ Static List provider
|
|||||||
Provides lists as configured in config.json
|
Provides lists as configured in config.json
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from cachetools import TTLCache, cached
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from cachetools import TTLCache, cached
|
||||||
|
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.pairlist.IPairList import IPairList
|
from freqtrade.pairlist.IPairList import IPairList
|
||||||
from freqtrade.resolvers import PairListResolver
|
from freqtrade.resolvers import PairListResolver
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ from sqlalchemy.orm.scoping import scoped_session
|
|||||||
from sqlalchemy.orm.session import sessionmaker
|
from sqlalchemy.orm.session import sessionmaker
|
||||||
from sqlalchemy.pool import StaticPool
|
from sqlalchemy.pool import StaticPool
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from freqtrade.utils import setup_utils_configuration
|
from freqtrade.utils import setup_utils_configuration
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.constants import DEFAULT_HYPEROPT_LOSS, USERPATH_HYPEROPTS
|
from freqtrade.constants import DEFAULT_HYPEROPT_LOSS, USERPATH_HYPEROPTS
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
@ -9,7 +9,7 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
|
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -11,9 +11,9 @@ from inspect import getfullargspec
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.constants import (REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES,
|
from freqtrade.constants import (REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES,
|
||||||
USERPATH_STRATEGY)
|
USERPATH_STRATEGY)
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
|||||||
import arrow
|
import arrow
|
||||||
from numpy import NAN, mean
|
from numpy import NAN, mean
|
||||||
|
|
||||||
from freqtrade import DependencyException, TemporaryError
|
from freqtrade.exceptions import DependencyException, TemporaryError
|
||||||
from freqtrade.misc import shorten_date
|
from freqtrade.misc import shorten_date
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||||
@ -142,7 +142,7 @@ class RPC:
|
|||||||
def _rpc_status_table(self, stake_currency, fiat_display_currency: str) -> Tuple[List, List]:
|
def _rpc_status_table(self, stake_currency, fiat_display_currency: str) -> Tuple[List, List]:
|
||||||
trades = Trade.get_open_trades()
|
trades = Trade.get_open_trades()
|
||||||
if not trades:
|
if not trades:
|
||||||
raise RPCException('no active order')
|
raise RPCException('no active trade')
|
||||||
else:
|
else:
|
||||||
trades_list = []
|
trades_list = []
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
@ -462,7 +462,7 @@ class RPC:
|
|||||||
raise RPCException(f'position for {pair} already open - id: {trade.id}')
|
raise RPCException(f'position for {pair} already open - id: {trade.id}')
|
||||||
|
|
||||||
# gen stake amount
|
# gen stake amount
|
||||||
stakeamount = self._freqtrade._get_trade_stake_amount(pair)
|
stakeamount = self._freqtrade.get_trade_stake_amount(pair)
|
||||||
|
|
||||||
# execute buy
|
# execute buy
|
||||||
if self._freqtrade.execute_buy(pair, stakeamount, price):
|
if self._freqtrade.execute_buy(pair, stakeamount, price):
|
||||||
|
@ -47,6 +47,7 @@ class {{ strategy }}(IStrategy):
|
|||||||
|
|
||||||
# Trailing stoploss
|
# Trailing stoploss
|
||||||
trailing_stop = False
|
trailing_stop = False
|
||||||
|
# trailing_only_offset_is_reached = False
|
||||||
# trailing_stop_positive = 0.01
|
# trailing_stop_positive = 0.01
|
||||||
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ class SampleStrategy(IStrategy):
|
|||||||
|
|
||||||
# Trailing stoploss
|
# Trailing stoploss
|
||||||
trailing_stop = False
|
trailing_stop = False
|
||||||
|
# trailing_only_offset_is_reached = False
|
||||||
# trailing_stop_positive = 0.01
|
# trailing_stop_positive = 0.01
|
||||||
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import rapidjson
|
|||||||
from colorama import init as colorama_init
|
from colorama import init as colorama_init
|
||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import (Configuration, TimeRange,
|
from freqtrade.configuration import (Configuration, TimeRange,
|
||||||
remove_credentials)
|
remove_credentials)
|
||||||
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
||||||
@ -22,6 +21,7 @@ from freqtrade.data.converter import (convert_ohlcv_format,
|
|||||||
from freqtrade.data.history import (convert_trades_to_ohlcv,
|
from freqtrade.data.history import (convert_trades_to_ohlcv,
|
||||||
refresh_backtest_ohlcv_data,
|
refresh_backtest_ohlcv_data,
|
||||||
refresh_backtest_trades_data)
|
refresh_backtest_trades_data)
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import (available_exchanges, ccxt_exchanges,
|
from freqtrade.exchange import (available_exchanges, ccxt_exchanges,
|
||||||
market_is_active, symbol_is_pair)
|
market_is_active, symbol_is_pair)
|
||||||
from freqtrade.misc import plural, render_template
|
from freqtrade.misc import plural, render_template
|
||||||
|
@ -8,9 +8,9 @@ from typing import Any, Callable, Dict, Optional
|
|||||||
|
|
||||||
import sdnotify
|
import sdnotify
|
||||||
|
|
||||||
from freqtrade import (OperationalException, TemporaryError, __version__,
|
from freqtrade import __version__, constants
|
||||||
constants)
|
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
|
from freqtrade.exceptions import OperationalException, TemporaryError
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.rpc import RPCMessageType
|
from freqtrade.rpc import RPCMessageType
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# requirements without requirements installable via conda
|
# requirements without requirements installable via conda
|
||||||
# mainly used for Raspberry pi installs
|
# mainly used for Raspberry pi installs
|
||||||
ccxt==1.21.12
|
ccxt==1.21.23
|
||||||
SQLAlchemy==1.3.12
|
SQLAlchemy==1.3.12
|
||||||
python-telegram-bot==12.2.0
|
python-telegram-bot==12.2.0
|
||||||
arrow==0.15.4
|
arrow==0.15.4
|
||||||
|
@ -10,7 +10,7 @@ import numpy as np
|
|||||||
import pytest
|
import pytest
|
||||||
from pandas import DataFrame, to_datetime
|
from pandas import DataFrame, to_datetime
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.data.converter import parse_ticker_dataframe
|
from freqtrade.data.converter import parse_ticker_dataframe
|
||||||
from freqtrade.edge import Edge, PairInfo
|
from freqtrade.edge import Edge, PairInfo
|
||||||
from freqtrade.strategy.interface import SellType
|
from freqtrade.strategy.interface import SellType
|
||||||
|
@ -4,8 +4,8 @@ from unittest.mock import MagicMock
|
|||||||
import ccxt
|
import ccxt
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade import (DependencyException, InvalidOrderException,
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from tests.conftest import get_patched_exchange
|
from tests.conftest import get_patched_exchange
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ import ccxt
|
|||||||
import pytest
|
import pytest
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import (DependencyException, InvalidOrderException,
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Binance, Exchange, Kraken
|
from freqtrade.exchange import Binance, Exchange, Kraken
|
||||||
from freqtrade.exchange.common import API_RETRY_COUNT
|
from freqtrade.exchange.common import API_RETRY_COUNT
|
||||||
from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair,
|
from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair,
|
||||||
|
@ -9,13 +9,14 @@ import pandas as pd
|
|||||||
import pytest
|
import pytest
|
||||||
from arrow import Arrow
|
from arrow import Arrow
|
||||||
|
|
||||||
from freqtrade import DependencyException, OperationalException, constants
|
from freqtrade import constants
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.btanalysis import evaluate_result_multi
|
from freqtrade.data.btanalysis import evaluate_result_multi
|
||||||
from freqtrade.data.converter import clean_ohlcv_dataframe
|
from freqtrade.data.converter import clean_ohlcv_dataframe
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.data.history import get_timerange
|
from freqtrade.data.history import get_timerange
|
||||||
|
from freqtrade.exceptions import DependencyException, OperationalException
|
||||||
from freqtrade.optimize import setup_configuration, start_backtesting
|
from freqtrade.optimize import setup_configuration, start_backtesting
|
||||||
from freqtrade.optimize.backtesting import Backtesting
|
from freqtrade.optimize.backtesting import Backtesting
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
@ -24,7 +25,6 @@ from freqtrade.strategy.interface import SellType
|
|||||||
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
|
||||||
patched_configuration_load_config_file)
|
patched_configuration_load_config_file)
|
||||||
|
|
||||||
|
|
||||||
ORDER_TYPES = [
|
ORDER_TYPES = [
|
||||||
{
|
{
|
||||||
'buy': 'limit',
|
'buy': 'limit',
|
||||||
|
@ -9,8 +9,8 @@ import pytest
|
|||||||
from arrow import Arrow
|
from arrow import Arrow
|
||||||
from filelock import Timeout
|
from filelock import Timeout
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.data.history import load_data
|
from freqtrade.data.history import load_data
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.optimize import setup_configuration, start_hyperopt
|
from freqtrade.optimize import setup_configuration, start_hyperopt
|
||||||
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
|
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
|
||||||
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
|
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
|
||||||
|
@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.constants import AVAILABLE_PAIRLISTS
|
from freqtrade.constants import AVAILABLE_PAIRLISTS
|
||||||
from freqtrade.resolvers import PairListResolver
|
from freqtrade.resolvers import PairListResolver
|
||||||
from freqtrade.pairlist.pairlistmanager import PairListManager
|
from freqtrade.pairlist.pairlistmanager import PairListManager
|
||||||
|
@ -7,13 +7,13 @@ from unittest.mock import ANY, MagicMock, PropertyMock
|
|||||||
import pytest
|
import pytest
|
||||||
from numpy import isnan
|
from numpy import isnan
|
||||||
|
|
||||||
from freqtrade import DependencyException, TemporaryError
|
|
||||||
from freqtrade.edge import PairInfo
|
from freqtrade.edge import PairInfo
|
||||||
|
from freqtrade.exceptions import DependencyException, TemporaryError
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import RPC, RPCException
|
from freqtrade.rpc import RPC, RPCException
|
||||||
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
from tests.conftest import patch_get_signal, get_patched_freqtradebot
|
from tests.conftest import get_patched_freqtradebot, patch_get_signal
|
||||||
|
|
||||||
|
|
||||||
# Functions for recurrent object patching
|
# Functions for recurrent object patching
|
||||||
@ -113,7 +113,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
|||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
with pytest.raises(RPCException, match=r'.*no active order*'):
|
with pytest.raises(RPCException, match=r'.*no active trade*'):
|
||||||
rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
|
||||||
|
|
||||||
freqtradebot.create_trades()
|
freqtradebot.create_trades()
|
||||||
|
@ -275,13 +275,13 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
# Status table is also enabled when stopped
|
# Status table is also enabled when stopped
|
||||||
telegram._status_table(update=update, context=MagicMock())
|
telegram._status_table(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert 'no active order' in msg_mock.call_args_list[0][0][0]
|
assert 'no active trade' in msg_mock.call_args_list[0][0][0]
|
||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
telegram._status_table(update=update, context=MagicMock())
|
telegram._status_table(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert 'no active order' in msg_mock.call_args_list[0][0][0]
|
assert 'no active trade' in msg_mock.call_args_list[0][0][0]
|
||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
|
@ -8,7 +8,7 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.resolvers import StrategyResolver
|
from freqtrade.resolvers import StrategyResolver
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
from tests.conftest import log_has, log_has_re
|
from tests.conftest import log_has, log_has_re
|
||||||
|
@ -10,7 +10,6 @@ from unittest.mock import MagicMock
|
|||||||
import pytest
|
import pytest
|
||||||
from jsonschema import ValidationError
|
from jsonschema import ValidationError
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import (Arguments, Configuration, check_exchange,
|
from freqtrade.configuration import (Arguments, Configuration, check_exchange,
|
||||||
remove_credentials,
|
remove_credentials,
|
||||||
validate_config_consistency)
|
validate_config_consistency)
|
||||||
@ -20,6 +19,7 @@ from freqtrade.configuration.deprecated_settings import (
|
|||||||
process_temporary_deprecated_settings)
|
process_temporary_deprecated_settings)
|
||||||
from freqtrade.configuration.load_config import load_config_file
|
from freqtrade.configuration.load_config import load_config_file
|
||||||
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
|
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.loggers import _set_loggers, setup_logging
|
from freqtrade.loggers import _set_loggers, setup_logging
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from tests.conftest import (log_has, log_has_re,
|
from tests.conftest import (log_has, log_has_re,
|
||||||
|
@ -4,10 +4,10 @@ from unittest.mock import MagicMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
from freqtrade.configuration.directory_operations import (copy_sample_files,
|
||||||
create_datadir,
|
create_datadir,
|
||||||
create_userdata_dir)
|
create_userdata_dir)
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from tests.conftest import log_has, log_has_re
|
from tests.conftest import log_has, log_has_re
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,9 +11,9 @@ import arrow
|
|||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from freqtrade import (DependencyException, InvalidOrderException,
|
from freqtrade.constants import MATH_CLOSE_PREC, UNLIMITED_STAKE_AMOUNT
|
||||||
OperationalException, TemporaryError, constants)
|
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
|
||||||
from freqtrade.constants import MATH_CLOSE_PREC
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import RPCMessageType
|
from freqtrade.rpc import RPCMessageType
|
||||||
@ -136,7 +136,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
|
|||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
result = freqtrade._get_trade_stake_amount('ETH/BTC')
|
result = freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||||
assert result == default_conf['stake_amount']
|
assert result == default_conf['stake_amount']
|
||||||
|
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
|
||||||
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
||||||
freqtrade._get_trade_stake_amount('ETH/BTC')
|
freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||||
|
|
||||||
|
|
||||||
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker,
|
def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker,
|
||||||
@ -163,32 +163,32 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker,
|
|||||||
)
|
)
|
||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT
|
||||||
conf['max_open_trades'] = 2
|
conf['max_open_trades'] = 2
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# no open trades, order amount should be 'balance / max_open_trades'
|
# no open trades, order amount should be 'balance / max_open_trades'
|
||||||
result = freqtrade._get_trade_stake_amount('ETH/BTC')
|
result = freqtrade.get_trade_stake_amount('ETH/BTC')
|
||||||
assert result == default_conf['stake_amount'] / conf['max_open_trades']
|
assert result == default_conf['stake_amount'] / conf['max_open_trades']
|
||||||
|
|
||||||
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
|
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
|
||||||
freqtrade.execute_buy('ETH/BTC', result)
|
freqtrade.execute_buy('ETH/BTC', result)
|
||||||
|
|
||||||
result = freqtrade._get_trade_stake_amount('LTC/BTC')
|
result = freqtrade.get_trade_stake_amount('LTC/BTC')
|
||||||
assert result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)
|
assert result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1)
|
||||||
|
|
||||||
# create 2 trades, order amount should be None
|
# create 2 trades, order amount should be None
|
||||||
freqtrade.execute_buy('LTC/BTC', result)
|
freqtrade.execute_buy('LTC/BTC', result)
|
||||||
|
|
||||||
result = freqtrade._get_trade_stake_amount('XRP/BTC')
|
result = freqtrade.get_trade_stake_amount('XRP/BTC')
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
# set max_open_trades = None, so do not trade
|
# set max_open_trades = None, so do not trade
|
||||||
conf['max_open_trades'] = 0
|
conf['max_open_trades'] = 0
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
result = freqtrade._get_trade_stake_amount('NEO/BTC')
|
result = freqtrade.get_trade_stake_amount('NEO/BTC')
|
||||||
assert result is None
|
assert result is None
|
||||||
|
|
||||||
|
|
||||||
@ -214,8 +214,8 @@ def test_edge_overrides_stake_amount(mocker, edge_conf) -> None:
|
|||||||
edge_conf['dry_run_wallet'] = 999.9
|
edge_conf['dry_run_wallet'] = 999.9
|
||||||
freqtrade = FreqtradeBot(edge_conf)
|
freqtrade = FreqtradeBot(edge_conf)
|
||||||
|
|
||||||
assert freqtrade._get_trade_stake_amount('NEO/BTC') == (999.9 * 0.5 * 0.01) / 0.20
|
assert freqtrade.get_trade_stake_amount('NEO/BTC') == (999.9 * 0.5 * 0.01) / 0.20
|
||||||
assert freqtrade._get_trade_stake_amount('LTC/BTC') == (999.9 * 0.5 * 0.01) / 0.21
|
assert freqtrade.get_trade_stake_amount('LTC/BTC') == (999.9 * 0.5 * 0.01) / 0.21
|
||||||
|
|
||||||
|
|
||||||
def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf) -> None:
|
def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf) -> None:
|
||||||
@ -564,13 +564,13 @@ def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order,
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
default_conf['max_open_trades'] = 0
|
default_conf['max_open_trades'] = 0
|
||||||
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
default_conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert not freqtrade.create_trades()
|
assert not freqtrade.create_trades()
|
||||||
assert freqtrade._get_trade_stake_amount('ETH/BTC') is None
|
assert freqtrade.get_trade_stake_amount('ETH/BTC') is None
|
||||||
|
|
||||||
|
|
||||||
def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee,
|
def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee,
|
||||||
@ -887,7 +887,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
|
|||||||
'freqtrade.freqtradebot.FreqtradeBot',
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
get_target_bid=get_bid,
|
get_target_bid=get_bid,
|
||||||
_get_min_pair_stake_amount=MagicMock(return_value=1)
|
_get_min_pair_stake_amount=MagicMock(return_value=1)
|
||||||
)
|
)
|
||||||
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
|
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
@ -1682,6 +1682,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock
|
|||||||
time.sleep(0.01) # Race condition fix
|
time.sleep(0.01) # Race condition fix
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
|
freqtrade.wallets.update()
|
||||||
|
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
@ -2326,6 +2327,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
|||||||
def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None:
|
def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None:
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
|
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
|
||||||
|
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300))
|
||||||
sellmock = MagicMock()
|
sellmock = MagicMock()
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -2548,6 +2550,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
|||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||||
@ -2578,6 +2581,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
|||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||||
@ -2603,7 +2607,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker
|
|||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
|
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
|
||||||
sell_flag=False, sell_type=SellType.NONE))
|
sell_flag=False, sell_type=SellType.NONE))
|
||||||
freqtrade.create_trades()
|
freqtrade.create_trades()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
@ -2638,11 +2642,87 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke
|
|||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||||
|
|
||||||
|
|
||||||
|
def test_sell_not_enough_balance(default_conf, limit_buy_order,
|
||||||
|
fee, mocker, caplog) -> None:
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
fetch_ticker=MagicMock(return_value={
|
||||||
|
'bid': 0.00002172,
|
||||||
|
'ask': 0.00002173,
|
||||||
|
'last': 0.00002172
|
||||||
|
}),
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
)
|
||||||
|
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
|
freqtrade.create_trades()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
amnt = trade.amount
|
||||||
|
trade.update(limit_buy_order)
|
||||||
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
|
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985))
|
||||||
|
|
||||||
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||||
|
assert trade.amount != amnt
|
||||||
|
|
||||||
|
|
||||||
|
def test__safe_sell_amount(default_conf, fee, caplog, mocker):
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
amount = 95.33
|
||||||
|
amount_wallet = 95.29
|
||||||
|
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet))
|
||||||
|
trade = Trade(
|
||||||
|
pair='LTC/ETH',
|
||||||
|
amount=amount,
|
||||||
|
exchange='binance',
|
||||||
|
open_rate=0.245441,
|
||||||
|
open_order_id="123456",
|
||||||
|
fee_open=fee.return_value,
|
||||||
|
fee_close=fee.return_value,
|
||||||
|
)
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
|
assert freqtrade._safe_sell_amount(trade.pair, trade.amount) == amount_wallet
|
||||||
|
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
|
||||||
|
|
||||||
|
|
||||||
|
def test__safe_sell_amount_error(default_conf, fee, caplog, mocker):
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
amount = 95.33
|
||||||
|
amount_wallet = 91.29
|
||||||
|
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet))
|
||||||
|
trade = Trade(
|
||||||
|
pair='LTC/ETH',
|
||||||
|
amount=amount,
|
||||||
|
exchange='binance',
|
||||||
|
open_rate=0.245441,
|
||||||
|
open_order_id="123456",
|
||||||
|
fee_open=fee.return_value,
|
||||||
|
fee_close=fee.return_value,
|
||||||
|
)
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
with pytest.raises(DependencyException, match=r"Not enough amount to sell."):
|
||||||
|
assert freqtrade._safe_sell_amount(trade.pair, trade.amount)
|
||||||
|
|
||||||
|
|
||||||
def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None:
|
def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None:
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -2703,6 +2783,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) ->
|
|||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(True, True))
|
patch_get_signal(freqtrade, value=(True, True))
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
@ -3440,6 +3521,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order
|
|||||||
|
|
||||||
time.sleep(0.01) # Race condition fix
|
time.sleep(0.01) # Race condition fix
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
|
freqtrade.wallets.update()
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
|
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True))
|
||||||
|
@ -71,7 +71,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
)
|
)
|
||||||
mocker.patch("freqtrade.strategy.interface.IStrategy.should_sell", should_sell_mock)
|
mocker.patch("freqtrade.strategy.interface.IStrategy.should_sell", should_sell_mock)
|
||||||
wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
|
||||||
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1))
|
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1000))
|
||||||
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
|
@ -5,8 +5,8 @@ from unittest.mock import MagicMock, PropertyMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import Arguments
|
from freqtrade.configuration import Arguments
|
||||||
|
from freqtrade.exceptions import OperationalException, FreqtradeException
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.main import main
|
from freqtrade.main import main
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
@ -96,7 +96,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None:
|
|||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock())
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.worker.Worker._worker',
|
'freqtrade.worker.Worker._worker',
|
||||||
MagicMock(side_effect=OperationalException('Oh snap!'))
|
MagicMock(side_effect=FreqtradeException('Oh snap!'))
|
||||||
)
|
)
|
||||||
patched_configuration_load_config_file(mocker, default_conf)
|
patched_configuration_load_config_file(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
|
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())
|
||||||
|
@ -6,7 +6,8 @@ import arrow
|
|||||||
import pytest
|
import pytest
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
|
|
||||||
from freqtrade import OperationalException, constants
|
from freqtrade import constants
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.persistence import Trade, clean_dry_run_db, init
|
from freqtrade.persistence import Trade, clean_dry_run_db, init
|
||||||
from tests.conftest import log_has
|
from tests.conftest import log_has
|
||||||
|
|
||||||
|
@ -7,17 +7,17 @@ import plotly.graph_objects as go
|
|||||||
import pytest
|
import pytest
|
||||||
from plotly.subplots import make_subplots
|
from plotly.subplots import make_subplots
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
|
||||||
from freqtrade.configuration import TimeRange
|
from freqtrade.configuration import TimeRange
|
||||||
from freqtrade.data import history
|
from freqtrade.data import history
|
||||||
from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
|
from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit
|
from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit
|
||||||
from freqtrade.plot.plotting import (add_indicators, add_profit,
|
from freqtrade.plot.plotting import (add_indicators, add_profit,
|
||||||
load_and_plot_trades,
|
|
||||||
generate_candlestick_graph,
|
generate_candlestick_graph,
|
||||||
generate_plot_filename,
|
generate_plot_filename,
|
||||||
generate_profit_graph, init_plotscript,
|
generate_profit_graph, init_plotscript,
|
||||||
plot_profit, plot_trades, store_plot_file)
|
load_and_plot_trades, plot_profit,
|
||||||
|
plot_trades, store_plot_file)
|
||||||
from freqtrade.strategy.default_strategy import DefaultStrategy
|
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||||
from tests.conftest import get_args, log_has, log_has_re
|
from tests.conftest import get_args, log_has, log_has_re
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from freqtrade.utils import (setup_utils_configuration, start_convert_data,
|
from freqtrade.utils import (setup_utils_configuration, start_convert_data,
|
||||||
start_create_userdir, start_download_data,
|
start_create_userdir, start_download_data,
|
||||||
@ -448,6 +448,9 @@ def test_create_datadir(caplog, mocker):
|
|||||||
# Ensure that caplog is empty before starting ...
|
# Ensure that caplog is empty before starting ...
|
||||||
# Should prevent random failures.
|
# Should prevent random failures.
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
# Added assert here to analyze random test-failures ...
|
||||||
|
assert len(caplog.record_tuples) == 0
|
||||||
|
|
||||||
cud = mocker.patch("freqtrade.utils.create_userdata_dir", MagicMock())
|
cud = mocker.patch("freqtrade.utils.create_userdata_dir", MagicMock())
|
||||||
csf = mocker.patch("freqtrade.utils.copy_sample_files", MagicMock())
|
csf = mocker.patch("freqtrade.utils.copy_sample_files", MagicMock())
|
||||||
args = [
|
args = [
|
||||||
|
Loading…
Reference in New Issue
Block a user