Merge branch 'develop' into data_handler

This commit is contained in:
Matthias 2019-12-30 19:40:43 +01:00
commit 699c0d6bc3
53 changed files with 319 additions and 156 deletions

View File

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

View File

@ -11,34 +11,3 @@ if __version__ == 'develop':
except Exception:
# git not available, ignore
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.
"""

View File

@ -1,9 +1,9 @@
import logging
from typing import Any, Dict
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
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)
from freqtrade.state import RunMode

View File

@ -4,7 +4,8 @@ from typing import Any, Dict
from jsonschema import Draft4Validator, validators
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
logger = logging.getLogger(__name__)

View File

@ -7,15 +7,16 @@ from copy import deepcopy
from pathlib import Path
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.deprecated_settings import process_temporary_deprecated_settings
from freqtrade.configuration.directory_operations import (create_datadir,
create_userdata_dir)
from freqtrade.configuration.load_config import load_config_file
from freqtrade.exceptions import OperationalException
from freqtrade.loggers import setup_logging
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__)

View File

@ -5,7 +5,7 @@ Functions to handle deprecated settings
import logging
from typing import Any, Dict
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ import shutil
from pathlib import Path
from typing import Any, Dict, Optional
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.constants import USER_DATA_FILES
logger = logging.getLogger(__name__)

View File

@ -6,7 +6,7 @@ import logging
import sys
from typing import Any, Dict
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
logger = logging.getLogger(__name__)

View File

@ -7,11 +7,11 @@ from typing import Dict, List, Optional, Tuple
import arrow
from pandas import DataFrame
from freqtrade import OperationalException
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS
from freqtrade.data.converter import parse_ticker_dataframe, trades_to_ohlcv
from freqtrade.data.history.idatahandler import IDataHandler, get_datahandler
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import Exchange
logger = logging.getLogger(__name__)

View File

@ -8,12 +8,12 @@ import numpy as np
import utils_find_1st as utf1st
from pandas import DataFrame
from freqtrade import constants, OperationalException
from freqtrade import constants
from freqtrade.configuration import TimeRange
from freqtrade.data import history
from freqtrade.exceptions import OperationalException
from freqtrade.strategy.interface import SellType
logger = logging.getLogger(__name__)

37
freqtrade/exceptions.py Normal file
View 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.
"""

View File

@ -4,8 +4,8 @@ from typing import Dict
import ccxt
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange import Exchange
logger = logging.getLogger(__name__)

View File

@ -1,6 +1,6 @@
import logging
from freqtrade import DependencyException, TemporaryError
from freqtrade.exceptions import DependencyException, TemporaryError
logger = logging.getLogger(__name__)

View File

@ -17,9 +17,9 @@ import ccxt.async_support as ccxt_async
from ccxt.base.decimal_to_precision import ROUND_DOWN, ROUND_UP
from pandas import DataFrame
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
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.misc import deep_merge_dicts

View File

@ -4,7 +4,7 @@ from typing import Dict
import ccxt
from freqtrade import OperationalException, TemporaryError
from freqtrade.exceptions import OperationalException, TemporaryError
from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange import retrier

View File

@ -12,17 +12,17 @@ from typing import Any, Dict, List, Optional, Tuple
import arrow
from requests.exceptions import RequestException
from freqtrade import (DependencyException, InvalidOrderException, __version__,
constants, persistence)
from freqtrade import __version__, constants, persistence
from freqtrade.configuration import validate_config_consistency
from freqtrade.data.converter import order_book_to_dataframe
from freqtrade.data.dataprovider import DataProvider
from freqtrade.edge import Edge
from freqtrade.exceptions import DependencyException, InvalidOrderException
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
from freqtrade.pairlist.pairlistmanager import PairListManager
from freqtrade.persistence import Trade
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.pairlist.pairlistmanager import PairListManager
from freqtrade.state import State
from freqtrade.strategy.interface import IStrategy, SellType
from freqtrade.wallets import Wallets
@ -136,7 +136,7 @@ class FreqtradeBot:
self.process_maybe_execute_sells(trades)
# Then looking for buy opportunities
if len(trades) < self.config['max_open_trades']:
if self.get_free_open_trades():
self.process_maybe_execute_buys()
# Check and handle any timed out open orders
@ -173,6 +173,14 @@ class FreqtradeBot:
"""
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:
"""
Calculates bid target between current ask price and last price
@ -204,14 +212,14 @@ class FreqtradeBot:
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
for the stake currency
:return: float: Stake Amount
Calculate stake amount for the trade
:return: float: Stake amount
"""
stake_amount: Optional[float]
if self.edge:
return self.edge.stake_amount(
stake_amount = self.edge.stake_amount(
pair,
self.wallets.get_free(self.config['stake_currency']),
self.wallets.get_total(self.config['stake_currency']),
@ -219,18 +227,31 @@ class FreqtradeBot:
)
else:
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'])
if stake_amount == constants.UNLIMITED_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:
if stake_amount is not None and available_amount < stake_amount:
raise DependencyException(
f"Available balance ({available_amount} {self.config['stake_currency']}) is "
f"lower than stake amount ({stake_amount} {self.config['stake_currency']})"
@ -299,18 +320,23 @@ class FreqtradeBot:
buycount = 0
# running get_signal on historical data fetched
for _pair in whitelist:
if self.strategy.is_pair_locked(_pair):
logger.info(f"Pair {_pair} is currently locked.")
for pair in whitelist:
if self.strategy.is_pair_locked(pair):
logger.info(f"Pair {pair} is currently locked.")
continue
(buy, sell) = self.strategy.get_signal(
_pair, self.strategy.ticker_interval,
self.dataprovider.ohlcv(_pair, self.strategy.ticker_interval))
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']:
stake_amount = self._get_trade_stake_amount(_pair)
if buy and not sell:
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:
logger.debug("Stake amount is 0, ignoring possible trade for {pair}.")
continue
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', {})
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
(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):
buycount += self.execute_buy(_pair, stake_amount)
if self._check_depth_of_market_buy(pair, bidstrat_check_depth_of_market):
buycount += self.execute_buy(pair, stake_amount)
continue
buycount += self.execute_buy(_pair, stake_amount)
buycount += self.execute_buy(pair, stake_amount)
return buycount > 0
@ -351,7 +377,6 @@ class FreqtradeBot:
:param pair: pair for which we want to create a LIMIT_BUY
:return: None
"""
pair_s = pair.replace('_', '/')
stake_currency = self.config['stake_currency']
fiat_currency = self.config.get('fiat_display_currency', None)
time_in_force = self.strategy.order_time_in_force['buy']
@ -362,10 +387,10 @@ class FreqtradeBot:
# Calculate amount
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:
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})"
)
return False
@ -388,7 +413,7 @@ class FreqtradeBot:
if float(order['filled']) == 0:
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
' 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
else:
# the order is partially fulfilled
@ -396,7 +421,7 @@ class FreqtradeBot:
# if the order is fulfilled fully or partially
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).',
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']
)
stake_amount = order['cost']
@ -413,7 +438,7 @@ class FreqtradeBot:
self.rpc.send_msg({
'type': RPCMessageType.BUY_NOTIFICATION,
'exchange': self.exchange.name.capitalize(),
'pair': pair_s,
'pair': pair,
'limit': buy_limit_filled_price,
'order_type': order_type,
'stake_amount': stake_amount,
@ -892,6 +917,27 @@ class FreqtradeBot:
# TODO: figure out how to handle partially complete sell orders
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:
"""
Executes a limit sell for the given trade and limit
@ -922,10 +968,12 @@ class FreqtradeBot:
# Emergencysells (default to market!)
ordertype = self.strategy.order_types.get("emergencysell", "market")
amount = self._safe_sell_amount(trade.pair, trade.amount)
# Execute sell and update trade record
order = self.exchange.sell(pair=str(trade.pair),
ordertype=ordertype,
amount=trade.amount, rate=limit,
amount=amount, rate=limit,
time_in_force=self.strategy.order_time_in_force['sell']
)

View File

@ -5,7 +5,7 @@ from logging import Formatter
from logging.handlers import RotatingFileHandler, SysLogHandler
from typing import Any, Dict, List
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
logger = logging.getLogger(__name__)

View File

@ -4,6 +4,7 @@ Main Freqtrade bot script.
Read the documentation to know what cli arguments you need.
"""
from freqtrade.exceptions import FreqtradeException, OperationalException
import sys
# check min. python version
if sys.version_info < (3, 6):
@ -13,7 +14,6 @@ if sys.version_info < (3, 6):
import logging
from typing import Any, List
from freqtrade import OperationalException
from freqtrade.configuration import Arguments
@ -50,7 +50,7 @@ def main(sysargv: List[str] = None) -> None:
except KeyboardInterrupt:
logger.info('SIGINT received, aborting ...')
return_code = 0
except OperationalException as e:
except FreqtradeException as e:
logger.error(str(e))
return_code = 2
except Exception:

View File

@ -1,11 +1,11 @@
import logging
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.utils import setup_utils_configuration
logger = logging.getLogger(__name__)

View File

@ -12,12 +12,12 @@ from typing import Any, Dict, List, NamedTuple, Optional
from pandas import DataFrame
from tabulate import tabulate
from freqtrade import OperationalException
from freqtrade.configuration import (TimeRange, remove_credentials,
validate_config_consistency)
from freqtrade.data import history
from freqtrade.data.converter import trim_dataframe
from freqtrade.data.dataprovider import DataProvider
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.misc import file_dump_json
from freqtrade.persistence import Trade

View File

@ -12,8 +12,7 @@ from freqtrade import constants
from freqtrade.configuration import (TimeRange, remove_credentials,
validate_config_consistency)
from freqtrade.edge import Edge
from freqtrade.exchange import Exchange
from freqtrade.resolvers import StrategyResolver
from freqtrade.resolvers import StrategyResolver, ExchangeResolver
logger = logging.getLogger(__name__)
@ -33,7 +32,7 @@ class EdgeCli:
# Reset keys for edge
remove_credentials(self.config)
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)
validate_config_consistency(self.config)

View File

@ -22,14 +22,14 @@ from joblib import (Parallel, cpu_count, delayed, dump, load,
wrap_non_picklable_objects)
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.history import get_timerange
from freqtrade.exceptions import OperationalException
from freqtrade.misc import plural, round_dict
from freqtrade.optimize.backtesting import Backtesting
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F4
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F4
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver,
HyperOptResolver)

View File

@ -4,17 +4,15 @@ This module defines the interface to apply for hyperopt
"""
import logging
import math
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 freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.misc import round_dict
logger = logging.getLogger(__name__)

View File

@ -8,7 +8,7 @@ import logging
from datetime import datetime
from typing import Dict, List
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.pairlist.IPairList import IPairList
logger = logging.getLogger(__name__)

View File

@ -4,11 +4,12 @@ Static List provider
Provides lists as configured in config.json
"""
from cachetools import TTLCache, cached
import logging
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.resolvers import PairListResolver

View File

@ -16,7 +16,7 @@ from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.pool import StaticPool
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
logger = logging.getLogger(__name__)

View File

@ -1,6 +1,6 @@
from typing import Any, Dict
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.state import RunMode
from freqtrade.utils import setup_utils_configuration

View File

@ -7,8 +7,8 @@ import logging
from pathlib import Path
from typing import Dict
from freqtrade import OperationalException
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_loss_interface import IHyperOptLoss
from freqtrade.resolvers import IResolver

View File

@ -9,7 +9,7 @@ import logging
from pathlib import Path
from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
logger = logging.getLogger(__name__)

View File

@ -11,9 +11,9 @@ from inspect import getfullargspec
from pathlib import Path
from typing import Dict, Optional
from freqtrade import OperationalException
from freqtrade.constants import (REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES,
USERPATH_STRATEGY)
from freqtrade.exceptions import OperationalException
from freqtrade.resolvers import IResolver
from freqtrade.strategy.interface import IStrategy

View File

@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple
import arrow
from numpy import NAN, mean
from freqtrade import DependencyException, TemporaryError
from freqtrade.exceptions import DependencyException, TemporaryError
from freqtrade.misc import shorten_date
from freqtrade.persistence import Trade
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]:
trades = Trade.get_open_trades()
if not trades:
raise RPCException('no active order')
raise RPCException('no active trade')
else:
trades_list = []
for trade in trades:
@ -462,7 +462,7 @@ class RPC:
raise RPCException(f'position for {pair} already open - id: {trade.id}')
# gen stake amount
stakeamount = self._freqtrade._get_trade_stake_amount(pair)
stakeamount = self._freqtrade.get_trade_stake_amount(pair)
# execute buy
if self._freqtrade.execute_buy(pair, stakeamount, price):

View File

@ -47,6 +47,7 @@ class {{ strategy }}(IStrategy):
# Trailing stoploss
trailing_stop = False
# trailing_only_offset_is_reached = False
# trailing_stop_positive = 0.01
# trailing_stop_positive_offset = 0.0 # Disabled / not configured

View File

@ -48,6 +48,7 @@ class SampleStrategy(IStrategy):
# Trailing stoploss
trailing_stop = False
# trailing_only_offset_is_reached = False
# trailing_stop_positive = 0.01
# trailing_stop_positive_offset = 0.0 # Disabled / not configured

View File

@ -11,7 +11,6 @@ import rapidjson
from colorama import init as colorama_init
from tabulate import tabulate
from freqtrade import OperationalException
from freqtrade.configuration import (Configuration, TimeRange,
remove_credentials)
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,
refresh_backtest_ohlcv_data,
refresh_backtest_trades_data)
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import (available_exchanges, ccxt_exchanges,
market_is_active, symbol_is_pair)
from freqtrade.misc import plural, render_template

View File

@ -8,9 +8,9 @@ from typing import Any, Callable, Dict, Optional
import sdnotify
from freqtrade import (OperationalException, TemporaryError, __version__,
constants)
from freqtrade import __version__, constants
from freqtrade.configuration import Configuration
from freqtrade.exceptions import OperationalException, TemporaryError
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.rpc import RPCMessageType
from freqtrade.state import State

View File

@ -1,6 +1,6 @@
# requirements without requirements installable via conda
# mainly used for Raspberry pi installs
ccxt==1.21.12
ccxt==1.21.23
SQLAlchemy==1.3.12
python-telegram-bot==12.2.0
arrow==0.15.4

View File

@ -10,7 +10,7 @@ import numpy as np
import pytest
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.edge import Edge, PairInfo
from freqtrade.strategy.interface import SellType

View File

@ -4,8 +4,8 @@ from unittest.mock import MagicMock
import ccxt
import pytest
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from tests.conftest import get_patched_exchange

View File

@ -11,8 +11,8 @@ import ccxt
import pytest
from pandas import DataFrame
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exchange import Binance, Exchange, Kraken
from freqtrade.exchange.common import API_RETRY_COUNT
from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair,

View File

@ -9,13 +9,14 @@ import pandas as pd
import pytest
from arrow import Arrow
from freqtrade import DependencyException, OperationalException, constants
from freqtrade import constants
from freqtrade.configuration import TimeRange
from freqtrade.data import history
from freqtrade.data.btanalysis import evaluate_result_multi
from freqtrade.data.converter import clean_ohlcv_dataframe
from freqtrade.data.dataprovider import DataProvider
from freqtrade.data.history import get_timerange
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.optimize import setup_configuration, start_backtesting
from freqtrade.optimize.backtesting import Backtesting
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,
patched_configuration_load_config_file)
ORDER_TYPES = [
{
'buy': 'limit',

View File

@ -9,8 +9,8 @@ import pytest
from arrow import Arrow
from filelock import Timeout
from freqtrade import OperationalException
from freqtrade.data.history import load_data
from freqtrade.exceptions import OperationalException
from freqtrade.optimize import setup_configuration, start_hyperopt
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss

View File

@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
import pytest
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.constants import AVAILABLE_PAIRLISTS
from freqtrade.resolvers import PairListResolver
from freqtrade.pairlist.pairlistmanager import PairListManager

View File

@ -7,13 +7,13 @@ from unittest.mock import ANY, MagicMock, PropertyMock
import pytest
from numpy import isnan
from freqtrade import DependencyException, TemporaryError
from freqtrade.edge import PairInfo
from freqtrade.exceptions import DependencyException, TemporaryError
from freqtrade.persistence import Trade
from freqtrade.rpc import RPC, RPCException
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
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
@ -113,7 +113,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
rpc = RPC(freqtradebot)
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')
freqtradebot.create_trades()

View File

@ -275,13 +275,13 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
# Status table is also enabled when stopped
telegram._status_table(update=update, context=MagicMock())
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()
freqtradebot.state = State.RUNNING
telegram._status_table(update=update, context=MagicMock())
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()
# Create some test data

View File

@ -8,7 +8,7 @@ from pathlib import Path
import pytest
from pandas import DataFrame
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy.interface import IStrategy
from tests.conftest import log_has, log_has_re

View File

@ -10,7 +10,6 @@ from unittest.mock import MagicMock
import pytest
from jsonschema import ValidationError
from freqtrade import OperationalException
from freqtrade.configuration import (Arguments, Configuration, check_exchange,
remove_credentials,
validate_config_consistency)
@ -20,6 +19,7 @@ from freqtrade.configuration.deprecated_settings import (
process_temporary_deprecated_settings)
from freqtrade.configuration.load_config import load_config_file
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.state import RunMode
from tests.conftest import (log_has, log_has_re,

View File

@ -4,10 +4,10 @@ from unittest.mock import MagicMock
import pytest
from freqtrade import OperationalException
from freqtrade.configuration.directory_operations import (copy_sample_files,
create_datadir,
create_userdata_dir)
from freqtrade.exceptions import OperationalException
from tests.conftest import log_has, log_has_re

View File

@ -11,9 +11,9 @@ import arrow
import pytest
import requests
from freqtrade import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError, constants)
from freqtrade.constants import MATH_CLOSE_PREC
from freqtrade.constants import MATH_CLOSE_PREC, UNLIMITED_STAKE_AMOUNT
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType
@ -136,7 +136,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
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']
@ -147,7 +147,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
freqtrade = FreqtradeBot(default_conf)
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,
@ -163,32 +163,32 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker,
)
conf = deepcopy(default_conf)
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT
conf['max_open_trades'] = 2
freqtrade = FreqtradeBot(conf)
patch_get_signal(freqtrade)
# 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']
# create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)'
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)
# create 2 trades, order amount should be None
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
# set max_open_trades = None, so do not trade
conf['max_open_trades'] = 0
freqtrade = FreqtradeBot(conf)
result = freqtrade._get_trade_stake_amount('NEO/BTC')
result = freqtrade.get_trade_stake_amount('NEO/BTC')
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
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('LTC/BTC') == (999.9 * 0.5 * 0.01) / 0.21
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
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,
)
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)
patch_get_signal(freqtrade)
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,
@ -887,7 +887,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
'freqtrade.freqtradebot.FreqtradeBot',
get_target_bid=get_bid,
_get_min_pair_stake_amount=MagicMock(return_value=1)
)
)
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
mocker.patch.multiple(
'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
trade.update(limit_buy_order)
assert trade.is_open is True
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, 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:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300))
sellmock = MagicMock()
patch_exchange(mocker)
mocker.patch.multiple(
@ -2548,6 +2550,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
trade = Trade.query.first()
trade.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
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.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
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)
patch_get_signal(freqtrade)
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()
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.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
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:
patch_RPCManager(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.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(True, True))
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
trade.update(limit_buy_order)
freqtrade.wallets.update()
assert trade.is_open is True
patch_get_signal(freqtrade, value=(False, True))

View File

@ -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)
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.strategy.order_types['stoploss_on_exchange'] = True

View File

@ -5,8 +5,8 @@ from unittest.mock import MagicMock, PropertyMock
import pytest
from freqtrade import OperationalException
from freqtrade.configuration import Arguments
from freqtrade.exceptions import OperationalException, FreqtradeException
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.main import main
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.worker.Worker._worker',
MagicMock(side_effect=OperationalException('Oh snap!'))
MagicMock(side_effect=FreqtradeException('Oh snap!'))
)
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())

View File

@ -6,7 +6,8 @@ import arrow
import pytest
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 tests.conftest import log_has

View File

@ -7,17 +7,17 @@ import plotly.graph_objects as go
import pytest
from plotly.subplots import make_subplots
from freqtrade import OperationalException
from freqtrade.configuration import TimeRange
from freqtrade.data import history
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.plotting import (add_indicators, add_profit,
load_and_plot_trades,
generate_candlestick_graph,
generate_plot_filename,
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 tests.conftest import get_args, log_has, log_has_re

View File

@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
import pytest
from freqtrade import OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.state import RunMode
from freqtrade.utils import (setup_utils_configuration, start_convert_data,
start_create_userdir, start_download_data,
@ -448,6 +448,9 @@ def test_create_datadir(caplog, mocker):
# Ensure that caplog is empty before starting ...
# Should prevent random failures.
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())
csf = mocker.patch("freqtrade.utils.copy_sample_files", MagicMock())
args = [