conflict resolved

This commit is contained in:
misagh 2018-11-26 19:08:58 +01:00
commit 86354ed258
24 changed files with 247 additions and 340 deletions

View File

@ -156,6 +156,9 @@ The below is the default which is used if this is not configured in either Strat
}, },
``` ```
**NOTE**: Not all exchanges support "market" orders.
The following message will be shown if your exchange does not support market orders: `"Exchange <yourexchange> does not support market orders."`
### What values for exchange.name? ### What values for exchange.name?
Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports 115 cryptocurrency Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports 115 cryptocurrency

View File

@ -537,6 +537,8 @@ class Exchange(object):
# Because some exchange sort Tickers ASC and other DESC. # Because some exchange sort Tickers ASC and other DESC.
# Ex: Bittrex returns a list of tickers ASC (oldest first, newest last) # Ex: Bittrex returns a list of tickers ASC (oldest first, newest last)
# when GDAX returns a list of tickers DESC (newest first, oldest last) # when GDAX returns a list of tickers DESC (newest first, oldest last)
# Only sort if necessary to save computing time
if data and data[0][0] > data[-1][0]:
data = sorted(data, key=lambda x: x[0]) data = sorted(data, key=lambda x: x[0])
# keeping last candle time as last refreshed time of the pair # keeping last candle time as last refreshed time of the pair
@ -559,51 +561,6 @@ class Exchange(object):
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(f'Could not fetch ticker data. Msg: {e}') raise OperationalException(f'Could not fetch ticker data. Msg: {e}')
@retrier
def get_candle_history(self, pair: str, tick_interval: str,
since_ms: Optional[int] = None) -> List[Dict]:
try:
# last item should be in the time interval [now - tick_interval, now]
till_time_ms = arrow.utcnow().shift(
minutes=-constants.TICKER_INTERVAL_MINUTES[tick_interval]
).timestamp * 1000
# it looks as if some exchanges return cached data
# and they update it one in several minute, so 10 mins interval
# is necessary to skeep downloading of an empty array when all
# chached data was already downloaded
till_time_ms = min(till_time_ms, arrow.utcnow().shift(minutes=-10).timestamp * 1000)
data: List[Dict[Any, Any]] = []
while not since_ms or since_ms < till_time_ms:
data_part = self._api.fetch_ohlcv(pair, timeframe=tick_interval, since=since_ms)
# Because some exchange sort Tickers ASC and other DESC.
# Ex: Bittrex returns a list of tickers ASC (oldest first, newest last)
# when GDAX returns a list of tickers DESC (newest first, oldest last)
data_part = sorted(data_part, key=lambda x: x[0])
if not data_part:
break
logger.debug('Downloaded data for %s time range [%s, %s]',
pair,
arrow.get(data_part[0][0] / 1000).format(),
arrow.get(data_part[-1][0] / 1000).format())
data.extend(data_part)
since_ms = data[-1][0] + 1
return data
except ccxt.NotSupported as e:
raise OperationalException(
f'Exchange {self._api.name} does not support fetching historical candlestick data.'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(f'Could not fetch ticker data. Msg: {e}')
@retrier @retrier
def cancel_order(self, order_id: str, pair: str) -> None: def cancel_order(self, order_id: str, pair: str) -> None:
if self._conf['dry_run']: if self._conf['dry_run']:

View File

@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
def parse_ticker_dataframe(ticker: list) -> DataFrame: def parse_ticker_dataframe(ticker: list) -> DataFrame:
""" """
Analyses the trend for the given ticker history Analyses the trend for the given ticker history
:param ticker: See exchange.get_candle_history :param ticker: ticker list, as returned by exchange.async_get_candle_history
:return: DataFrame :return: DataFrame
""" """
cols = ['date', 'open', 'high', 'low', 'close', 'volume'] cols = ['date', 'open', 'high', 'low', 'close', 'volume']

View File

@ -21,9 +21,9 @@ from freqtrade.wallets import Wallets
from freqtrade.edge import Edge from freqtrade.edge import Edge
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.resolvers import StrategyResolver
from freqtrade.state import State from freqtrade.state import State
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType, IStrategy
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
from freqtrade.exchange.exchange_helpers import order_book_to_dataframe from freqtrade.exchange.exchange_helpers import order_book_to_dataframe
@ -338,9 +338,7 @@ class FreqtradeBot(object):
else: else:
stake_amount = self.config['stake_amount'] stake_amount = self.config['stake_amount']
# TODO: should come from the wallet avaliable_amount = self.wallets.get_free(self.config['stake_currency'])
avaliable_amount = self.exchange.get_balance(self.config['stake_currency'])
# avaliable_amount = self.wallets.wallets[self.config['stake_currency']].free
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all()) open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())

View File

@ -45,7 +45,7 @@ def main(sysargv: List[str]) -> None:
freqtrade = FreqtradeBot(config) freqtrade = FreqtradeBot(config)
state = None state = None
while 1: while True:
state = freqtrade.worker(old_state=state) state = freqtrade.worker(old_state=state)
if state == State.RELOAD_CONF: if state == State.RELOAD_CONF:
freqtrade = reconfigure(freqtrade, args) freqtrade = reconfigure(freqtrade, args)

View File

@ -20,8 +20,8 @@ from freqtrade.configuration import Configuration
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.misc import file_dump_json from freqtrade.misc import file_dump_json
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.strategy.interface import SellType from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy.resolver import IStrategy, StrategyResolver from freqtrade.strategy.interface import SellType, IStrategy
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -12,7 +12,7 @@ from freqtrade.edge import Edge
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.strategy.resolver import StrategyResolver from freqtrade.resolvers import StrategyResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -22,7 +22,7 @@ from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade.optimize import load_data from freqtrade.optimize import load_data
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
from freqtrade.optimize.hyperopt_resolver import HyperOptResolver from freqtrade.resolvers import HyperOptResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,104 +0,0 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom hyperopts
"""
import importlib.util
import inspect
import logging
import os
from typing import Optional, Dict, Type
from freqtrade.constants import DEFAULT_HYPEROPT
from freqtrade.optimize.hyperopt_interface import IHyperOpt
logger = logging.getLogger(__name__)
class HyperOptResolver(object):
"""
This class contains all the logic to load custom hyperopt class
"""
__slots__ = ['hyperopt']
def __init__(self, config: Optional[Dict] = None) -> None:
"""
Load the custom class from config parameter
:param config: configuration dictionary or None
"""
config = config or {}
# Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt
hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT
self.hyperopt = self._load_hyperopt(hyperopt_name, extra_dir=config.get('hyperopt_path'))
def _load_hyperopt(
self, hyperopt_name: str, extra_dir: Optional[str] = None) -> IHyperOpt:
"""
Search and loads the specified hyperopt.
:param hyperopt_name: name of the module to import
:param extra_dir: additional directory to search for the given hyperopt
:return: HyperOpt instance or None
"""
current_path = os.path.dirname(os.path.realpath(__file__))
abs_paths = [
os.path.join(current_path, '..', '..', 'user_data', 'hyperopts'),
current_path,
]
if extra_dir:
# Add extra hyperopt directory on top of search paths
abs_paths.insert(0, extra_dir)
for path in abs_paths:
hyperopt = self._search_hyperopt(path, hyperopt_name)
if hyperopt:
logger.info('Using resolved hyperopt %s from \'%s\'', hyperopt_name, path)
return hyperopt
raise ImportError(
"Impossible to load Hyperopt '{}'. This class does not exist"
" or contains Python code errors".format(hyperopt_name)
)
@staticmethod
def _get_valid_hyperopts(module_path: str, hyperopt_name: str) -> Optional[Type[IHyperOpt]]:
"""
Returns a list of all possible hyperopts for the given module_path
:param module_path: absolute path to the module
:param hyperopt_name: Class name of the hyperopt
:return: Tuple with (name, class) or None
"""
# Generate spec based on absolute path
spec = importlib.util.spec_from_file_location('user_data.hyperopts', module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
valid_hyperopts_gen = (
obj for name, obj in inspect.getmembers(module, inspect.isclass)
if hyperopt_name == name and IHyperOpt in obj.__bases__
)
return next(valid_hyperopts_gen, None)
@staticmethod
def _search_hyperopt(directory: str, hyperopt_name: str) -> Optional[IHyperOpt]:
"""
Search for the hyperopt_name in the given directory
:param directory: relative or absolute directory path
:return: name of the hyperopt class
"""
logger.debug('Searching for hyperopt %s in \'%s\'', hyperopt_name, directory)
for entry in os.listdir(directory):
# Only consider python files
if not entry.endswith('.py'):
logger.debug('Ignoring %s', entry)
continue
hyperopt = HyperOptResolver._get_valid_hyperopts(
os.path.abspath(os.path.join(directory, entry)), hyperopt_name
)
if hyperopt:
return hyperopt()
return None

View File

@ -0,0 +1,3 @@
from freqtrade.resolvers.iresolver import IResolver # noqa: F401
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver # noqa: F401
from freqtrade.resolvers.strategy_resolver import StrategyResolver # noqa: F401

View File

@ -0,0 +1,64 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom hyperopts
"""
import logging
from pathlib import Path
from typing import Optional, Dict
from freqtrade.constants import DEFAULT_HYPEROPT
from freqtrade.optimize.hyperopt_interface import IHyperOpt
from freqtrade.resolvers import IResolver
logger = logging.getLogger(__name__)
class HyperOptResolver(IResolver):
"""
This class contains all the logic to load custom hyperopt class
"""
__slots__ = ['hyperopt']
def __init__(self, config: Optional[Dict] = None) -> None:
"""
Load the custom class from config parameter
:param config: configuration dictionary or None
"""
config = config or {}
# Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt
hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT
self.hyperopt = self._load_hyperopt(hyperopt_name, extra_dir=config.get('hyperopt_path'))
def _load_hyperopt(
self, hyperopt_name: str, extra_dir: Optional[str] = None) -> IHyperOpt:
"""
Search and loads the specified hyperopt.
:param hyperopt_name: name of the module to import
:param extra_dir: additional directory to search for the given hyperopt
:return: HyperOpt instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = [
current_path.parent.parent.joinpath('user_data/hyperopts'),
current_path,
]
if extra_dir:
# Add extra hyperopt directory on top of search paths
abs_paths.insert(0, Path(extra_dir))
for _path in abs_paths:
hyperopt = self._search_object(directory=_path, object_type=IHyperOpt,
object_name=hyperopt_name)
if hyperopt:
logger.info('Using resolved hyperopt %s from \'%s\'', hyperopt_name, _path)
return hyperopt
raise ImportError(
"Impossible to load Hyperopt '{}'. This class does not exist"
" or contains Python code errors".format(hyperopt_name)
)

View File

@ -0,0 +1,61 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom objects
"""
import importlib.util
import inspect
import logging
from pathlib import Path
from typing import Optional, Type, Any
logger = logging.getLogger(__name__)
class IResolver(object):
"""
This class contains all the logic to load custom classes
"""
@staticmethod
def _get_valid_object(object_type, module_path: Path,
object_name: str) -> Optional[Type[Any]]:
"""
Returns the first object with matching object_type and object_name in the path given.
:param object_type: object_type (class)
:param module_path: absolute path to the module
:param object_name: Class name of the object
:return: class or None
"""
# Generate spec based on absolute path
spec = importlib.util.spec_from_file_location('unknown', str(module_path))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
valid_objects_gen = (
obj for name, obj in inspect.getmembers(module, inspect.isclass)
if object_name == name and object_type in obj.__bases__
)
return next(valid_objects_gen, None)
@staticmethod
def _search_object(directory: Path, object_type, object_name: str,
kwargs: dict = {}) -> Optional[Any]:
"""
Search for the objectname in the given directory
:param directory: relative or absolute directory path
:return: object instance
"""
logger.debug('Searching for %s %s in \'%s\'', object_type.__name__, object_name, directory)
for entry in directory.iterdir():
# Only consider python files
if not str(entry).endswith('.py'):
logger.debug('Ignoring %s', entry)
continue
obj = IResolver._get_valid_object(
object_type, Path.resolve(directory.joinpath(entry)), object_name
)
if obj:
return obj(**kwargs)
return None

View File

@ -3,24 +3,23 @@
""" """
This module load custom strategies This module load custom strategies
""" """
import importlib.util
import inspect import inspect
import logging import logging
import os
import tempfile import tempfile
from base64 import urlsafe_b64decode from base64 import urlsafe_b64decode
from collections import OrderedDict from collections import OrderedDict
from pathlib import Path from pathlib import Path
from typing import Dict, Optional, Type from typing import Dict, Optional
from freqtrade import constants from freqtrade import constants
from freqtrade.resolvers import IResolver
from freqtrade.strategy import import_strategy from freqtrade.strategy import import_strategy
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class StrategyResolver(object): class StrategyResolver(IResolver):
""" """
This class contains all the logic to load custom strategy class This class contains all the logic to load custom strategy class
""" """
@ -103,15 +102,16 @@ class StrategyResolver(object):
:param extra_dir: additional directory to search for the given strategy :param extra_dir: additional directory to search for the given strategy
:return: Strategy instance or None :return: Strategy instance or None
""" """
current_path = os.path.dirname(os.path.realpath(__file__)) current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
abs_paths = [ abs_paths = [
os.path.join(os.getcwd(), 'user_data', 'strategies'), Path.cwd().joinpath('user_data/strategies'),
current_path, current_path,
] ]
if extra_dir: if extra_dir:
# Add extra strategy directory on top of search paths # Add extra strategy directory on top of search paths
abs_paths.insert(0, extra_dir) abs_paths.insert(0, Path(extra_dir).resolve())
if ":" in strategy_name: if ":" in strategy_name:
logger.info("loading base64 endocded strategy") logger.info("loading base64 endocded strategy")
@ -124,16 +124,17 @@ class StrategyResolver(object):
temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8')) temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))
temp.joinpath("__init__.py").touch() temp.joinpath("__init__.py").touch()
strategy_name = os.path.splitext(name)[0] strategy_name = strat[0]
# register temp path with the bot # register temp path with the bot
abs_paths.insert(0, str(temp.resolve())) abs_paths.insert(0, temp.resolve())
for path in abs_paths: for _path in abs_paths:
try: try:
strategy = self._search_strategy(path, strategy_name=strategy_name, config=config) strategy = self._search_object(directory=_path, object_type=IStrategy,
object_name=strategy_name, kwargs={'config': config})
if strategy: if strategy:
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path) logger.info('Using resolved strategy %s from \'%s\'', strategy_name, _path)
strategy._populate_fun_len = len( strategy._populate_fun_len = len(
inspect.getfullargspec(strategy.populate_indicators).args) inspect.getfullargspec(strategy.populate_indicators).args)
strategy._buy_fun_len = len( strategy._buy_fun_len = len(
@ -143,49 +144,9 @@ class StrategyResolver(object):
return import_strategy(strategy, config=config) return import_strategy(strategy, config=config)
except FileNotFoundError: except FileNotFoundError:
logger.warning('Path "%s" does not exist', path) logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd()))
raise ImportError( raise ImportError(
"Impossible to load Strategy '{}'. This class does not exist" "Impossible to load Strategy '{}'. This class does not exist"
" or contains Python code errors".format(strategy_name) " or contains Python code errors".format(strategy_name)
) )
@staticmethod
def _get_valid_strategies(module_path: str, strategy_name: str) -> Optional[Type[IStrategy]]:
"""
Returns a list of all possible strategies for the given module_path
:param module_path: absolute path to the module
:param strategy_name: Class name of the strategy
:return: Tuple with (name, class) or None
"""
# Generate spec based on absolute path
spec = importlib.util.spec_from_file_location('unknown', module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
valid_strategies_gen = (
obj for name, obj in inspect.getmembers(module, inspect.isclass)
if strategy_name == name and IStrategy in obj.__bases__
)
return next(valid_strategies_gen, None)
@staticmethod
def _search_strategy(directory: str, strategy_name: str, config: dict) -> Optional[IStrategy]:
"""
Search for the strategy_name in the given directory
:param directory: relative or absolute directory path
:return: name of the strategy class
"""
logger.debug('Searching for strategy %s in \'%s\'', strategy_name, directory)
for entry in os.listdir(directory):
# Only consider python files
if not entry.endswith('.py'):
logger.debug('Ignoring %s', entry)
continue
strategy = StrategyResolver._get_valid_strategies(
os.path.abspath(os.path.join(directory, entry)), strategy_name
)
if strategy:
return strategy(config)
return None

View File

@ -4,7 +4,6 @@ import logging
from datetime import datetime from datetime import datetime
from functools import reduce from functools import reduce
from typing import Dict, Optional from typing import Dict, Optional
from collections import namedtuple
from unittest.mock import MagicMock, PropertyMock from unittest.mock import MagicMock, PropertyMock
import arrow import arrow
@ -13,7 +12,7 @@ from telegram import Chat, Message, Update
from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.edge import Edge from freqtrade.edge import Edge, PairInfo
from freqtrade.freqtradebot import FreqtradeBot from freqtrade.freqtradebot import FreqtradeBot
logging.getLogger('').setLevel(logging.INFO) logging.getLogger('').setLevel(logging.INFO)
@ -46,18 +45,22 @@ def get_patched_exchange(mocker, config, api_mock=None, id='bittrex') -> Exchang
return exchange return exchange
def patch_wallet(mocker, free=999.9) -> None:
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(
return_value=free
))
def patch_edge(mocker) -> None: def patch_edge(mocker) -> None:
# "ETH/BTC", # "ETH/BTC",
# "LTC/BTC", # "LTC/BTC",
# "XRP/BTC", # "XRP/BTC",
# "NEO/BTC" # "NEO/BTC"
pair_info = namedtuple(
'pair_info',
'stoploss, winrate, risk_reward_ratio, required_risk_reward, expectancy')
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock( mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={ return_value={
'NEO/BTC': pair_info(-0.20, 0.66, 3.71, 0.50, 1.71), 'NEO/BTC': PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25),
'LTC/BTC': pair_info(-0.21, 0.66, 3.71, 0.50, 1.71), 'LTC/BTC': PairInfo(-0.21, 0.66, 3.71, 0.50, 1.71, 11, 20),
} }
)) ))
mocker.patch('freqtrade.edge.Edge.stoploss', MagicMock(return_value=-0.20)) mocker.patch('freqtrade.edge.Edge.stoploss', MagicMock(return_value=-0.20))

View File

@ -898,65 +898,10 @@ def make_fetch_ohlcv_mock(data):
return fetch_ohlcv_mock return fetch_ohlcv_mock
def test_get_candle_history(default_conf, mocker): @pytest.mark.asyncio
api_mock = MagicMock() async def test___async_get_candle_history_sort(default_conf, mocker):
tick = [ def sort_data(data, key):
[ return sorted(data, key=key)
1511686200000, # unix timestamp ms
1, # open
2, # high
3, # low
4, # close
5, # volume (in quote currency)
]
]
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True})
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick))
exchange = get_patched_exchange(mocker, default_conf, api_mock)
# retrieve original ticker
ticks = exchange.get_candle_history('ETH/BTC', default_conf['ticker_interval'])
assert ticks[0][0] == 1511686200000
assert ticks[0][1] == 1
assert ticks[0][2] == 2
assert ticks[0][3] == 3
assert ticks[0][4] == 4
assert ticks[0][5] == 5
# change ticker and ensure tick changes
new_tick = [
[
1511686210000, # unix timestamp ms
6, # open
7, # high
8, # low
9, # close
10, # volume (in quote currency)
]
]
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(new_tick))
exchange = get_patched_exchange(mocker, default_conf, api_mock)
ticks = exchange.get_candle_history('ETH/BTC', default_conf['ticker_interval'])
assert ticks[0][0] == 1511686210000
assert ticks[0][1] == 6
assert ticks[0][2] == 7
assert ticks[0][3] == 8
assert ticks[0][4] == 9
assert ticks[0][5] == 10
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
"get_candle_history", "fetch_ohlcv",
pair='ABCD/BTC', tick_interval=default_conf['ticker_interval'])
with pytest.raises(OperationalException, match=r'Exchange .* does not support.*'):
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NotSupported)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_candle_history(pair='ABCD/BTC', tick_interval=default_conf['ticker_interval'])
def test_get_candle_history_sort(default_conf, mocker):
api_mock = MagicMock()
# GDAX use-case (real data from GDAX) # GDAX use-case (real data from GDAX)
# This ticker history is ordered DESC (newest first, oldest last) # This ticker history is ordered DESC (newest first, oldest last)
@ -972,13 +917,15 @@ def test_get_candle_history_sort(default_conf, mocker):
[1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687], [1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687],
[1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867] [1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867]
] ]
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) exchange = get_patched_exchange(mocker, default_conf)
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick)) exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
sort_mock = mocker.patch('freqtrade.exchange.sorted', MagicMock(side_effect=sort_data))
exchange = get_patched_exchange(mocker, default_conf, api_mock)
# Test the ticker history sort # Test the ticker history sort
ticks = exchange.get_candle_history('ETH/BTC', default_conf['ticker_interval']) res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval'])
assert res[0] == 'ETH/BTC'
ticks = res[1]
assert sort_mock.call_count == 1
assert ticks[0][0] == 1527830400000 assert ticks[0][0] == 1527830400000
assert ticks[0][1] == 0.07649 assert ticks[0][1] == 0.07649
assert ticks[0][2] == 0.07651 assert ticks[0][2] == 0.07651
@ -1007,11 +954,15 @@ def test_get_candle_history_sort(default_conf, mocker):
[1527830100000, 0.076695, 0.07671, 0.07624171, 0.07671, 1.80689244], [1527830100000, 0.076695, 0.07671, 0.07624171, 0.07671, 1.80689244],
[1527830400000, 0.07671, 0.07674399, 0.07629216, 0.07655213, 2.31452783] [1527830400000, 0.07671, 0.07674399, 0.07629216, 0.07655213, 2.31452783]
] ]
type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
api_mock.fetch_ohlcv = MagicMock(side_effect=make_fetch_ohlcv_mock(tick)) # Reset sort mock
exchange = get_patched_exchange(mocker, default_conf, api_mock) sort_mock = mocker.patch('freqtrade.exchange.sorted', MagicMock(side_effect=sort_data))
# Test the ticker history sort # Test the ticker history sort
ticks = exchange.get_candle_history('ETH/BTC', default_conf['ticker_interval']) res = await exchange._async_get_candle_history('ETH/BTC', default_conf['ticker_interval'])
assert res[0] == 'ETH/BTC'
ticks = res[1]
# Sorted not called again - data is already in order
assert sort_mock.call_count == 0
assert ticks[0][0] == 1527827700000 assert ticks[0][0] == 1527827700000
assert ticks[0][1] == 0.07659999 assert ticks[0][1] == 0.07659999
assert ticks[0][2] == 0.0766 assert ticks[0][2] == 0.0766

View File

@ -7,7 +7,7 @@ import pytest
from freqtrade.optimize.__init__ import load_tickerdata_file from freqtrade.optimize.__init__ import load_tickerdata_file
from freqtrade.optimize.hyperopt import Hyperopt, start from freqtrade.optimize.hyperopt import Hyperopt, start
from freqtrade.strategy.resolver import StrategyResolver from freqtrade.resolvers import StrategyResolver
from freqtrade.tests.conftest import log_has, patch_exchange from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.tests.optimize.test_backtesting import get_args from freqtrade.tests.optimize.test_backtesting import get_args

View File

@ -2,6 +2,7 @@
import logging import logging
from base64 import urlsafe_b64encode from base64 import urlsafe_b64encode
from os import path from os import path
from pathlib import Path
import warnings import warnings
import pytest import pytest
@ -10,7 +11,7 @@ from pandas import DataFrame
from freqtrade.strategy import import_strategy from freqtrade.strategy import import_strategy
from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.resolver import StrategyResolver from freqtrade.resolvers import StrategyResolver
def test_import_strategy(caplog): def test_import_strategy(caplog):
@ -40,21 +41,21 @@ def test_import_strategy(caplog):
def test_search_strategy(): def test_search_strategy():
default_config = {} default_config = {}
default_location = path.join(path.dirname( default_location = Path(__file__).parent.parent.joinpath('strategy').resolve()
path.realpath(__file__)), '..', '..', 'strategy'
)
assert isinstance( assert isinstance(
StrategyResolver._search_strategy( StrategyResolver._search_object(
default_location, directory=default_location,
config=default_config, object_type=IStrategy,
strategy_name='DefaultStrategy' kwargs={'config': default_config},
object_name='DefaultStrategy'
), ),
IStrategy IStrategy
) )
assert StrategyResolver._search_strategy( assert StrategyResolver._search_object(
default_location, directory=default_location,
config=default_config, object_type=IStrategy,
strategy_name='NotFoundStrategy' kwargs={'config': default_config},
object_name='NotFoundStrategy'
) is None ) is None
@ -77,7 +78,7 @@ def test_load_strategy_invalid_directory(result, caplog):
resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir) resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir)
assert ( assert (
'freqtrade.strategy.resolver', 'freqtrade.resolvers.strategy_resolver',
logging.WARNING, logging.WARNING,
'Path "{}" does not exist'.format(extra_dir), 'Path "{}" does not exist'.format(extra_dir),
) in caplog.record_tuples ) in caplog.record_tuples
@ -128,7 +129,7 @@ def test_strategy_override_minimal_roi(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.minimal_roi[0] == 0.5 assert resolver.strategy.minimal_roi[0] == 0.5
assert ('freqtrade.strategy.resolver', assert ('freqtrade.resolvers.strategy_resolver',
logging.INFO, logging.INFO,
"Override strategy 'minimal_roi' with value in config file: {'0': 0.5}." "Override strategy 'minimal_roi' with value in config file: {'0': 0.5}."
) in caplog.record_tuples ) in caplog.record_tuples
@ -143,7 +144,7 @@ def test_strategy_override_stoploss(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.stoploss == -0.5 assert resolver.strategy.stoploss == -0.5
assert ('freqtrade.strategy.resolver', assert ('freqtrade.resolvers.strategy_resolver',
logging.INFO, logging.INFO,
"Override strategy 'stoploss' with value in config file: -0.5." "Override strategy 'stoploss' with value in config file: -0.5."
) in caplog.record_tuples ) in caplog.record_tuples
@ -159,7 +160,7 @@ def test_strategy_override_ticker_interval(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.ticker_interval == 60 assert resolver.strategy.ticker_interval == 60
assert ('freqtrade.strategy.resolver', assert ('freqtrade.resolvers.strategy_resolver',
logging.INFO, logging.INFO,
"Override strategy 'ticker_interval' with value in config file: 60." "Override strategy 'ticker_interval' with value in config file: 60."
) in caplog.record_tuples ) in caplog.record_tuples
@ -175,7 +176,7 @@ def test_strategy_override_process_only_new_candles(caplog):
resolver = StrategyResolver(config) resolver = StrategyResolver(config)
assert resolver.strategy.process_only_new_candles assert resolver.strategy.process_only_new_candles
assert ('freqtrade.strategy.resolver', assert ('freqtrade.resolvers.strategy_resolver',
logging.INFO, logging.INFO,
"Override process_only_new_candles 'process_only_new_candles' " "Override process_only_new_candles 'process_only_new_candles' "
"with value in config file: True." "with value in config file: True."
@ -202,7 +203,7 @@ def test_strategy_override_order_types(caplog):
for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']: for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']:
assert resolver.strategy.order_types[method] == order_types[method] assert resolver.strategy.order_types[method] == order_types[method]
assert ('freqtrade.strategy.resolver', assert ('freqtrade.resolvers.strategy_resolver',
logging.INFO, logging.INFO,
"Override strategy 'order_types' with value in config file:" "Override strategy 'order_types' with value in config file:"
" {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'," " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit',"

View File

@ -3,7 +3,7 @@
import pandas import pandas
from freqtrade.optimize import load_data from freqtrade.optimize import load_data
from freqtrade.strategy.resolver import StrategyResolver from freqtrade.resolvers import StrategyResolver
_pairs = ['ETH/BTC'] _pairs = ['ETH/BTC']

View File

@ -18,7 +18,7 @@ from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType from freqtrade.rpc import RPCMessageType
from freqtrade.state import State from freqtrade.state import State
from freqtrade.strategy.interface import SellType, SellCheckTuple from freqtrade.strategy.interface import SellType, SellCheckTuple
from freqtrade.tests.conftest import log_has, patch_exchange, patch_edge from freqtrade.tests.conftest import log_has, patch_exchange, patch_edge, patch_wallet
# Functions for recurrent object patching # Functions for recurrent object patching
@ -181,17 +181,10 @@ def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mock
assert result == default_conf['stake_amount'] assert result == default_conf['stake_amount']
def test_get_trade_stake_amount_no_stake_amount(default_conf, def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None:
ticker,
limit_buy_order,
fee,
mocker) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch.multiple( patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5)
'freqtrade.exchange.Exchange',
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5)
)
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
with pytest.raises(DependencyException, match=r'.*stake amount.*'): with pytest.raises(DependencyException, match=r'.*stake amount.*'):
@ -206,12 +199,12 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf,
mocker) -> None: mocker) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
patch_wallet(mocker, free=default_conf['stake_amount'])
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}), buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount']),
get_fee=fee, get_fee=fee,
get_markets=markets get_markets=markets
) )
@ -299,7 +292,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, markets, caplog, mocker,
freqtrade = FreqtradeBot(edge_conf) freqtrade = FreqtradeBot(edge_conf)
freqtrade.active_pair_whitelist = ['NEO/BTC'] freqtrade.active_pair_whitelist = ['NEO/BTC']
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order) trade.update(limit_buy_order)
@ -339,7 +332,7 @@ def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, markets,
freqtrade = FreqtradeBot(edge_conf) freqtrade = FreqtradeBot(edge_conf)
freqtrade.active_pair_whitelist = ['NEO/BTC'] freqtrade.active_pair_whitelist = ['NEO/BTC']
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
trade = Trade.query.first() trade = Trade.query.first()
trade.update(limit_buy_order) trade.update(limit_buy_order)
@ -521,11 +514,11 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
fee, markets, mocker) -> None: fee, markets, mocker) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
get_ticker=ticker, get_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}), buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5),
get_fee=fee, get_fee=fee,
get_markets=markets get_markets=markets
) )
@ -1108,7 +1101,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade, value=(True, True)) patch_get_signal(freqtrade, value=(True, True))
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
@ -1164,7 +1157,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade, value=(True, False)) patch_get_signal(freqtrade, value=(True, False))
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trade() freqtrade.create_trade()
@ -1197,7 +1190,7 @@ def test_handle_trade_experimental(
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
trade = Trade.query.first() trade = Trade.query.first()
@ -1801,7 +1794,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
} }
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
@ -1833,7 +1826,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
} }
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
trade = Trade.query.first() trade = Trade.query.first()
@ -1895,7 +1888,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
@ -1925,7 +1918,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m
} }
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trade() freqtrade.create_trade()
@ -1957,7 +1950,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, markets, caplog,
default_conf['trailing_stop'] = True default_conf['trailing_stop'] = True
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
@ -1992,7 +1985,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
default_conf['trailing_stop_positive'] = 0.01 default_conf['trailing_stop_positive'] = 0.01
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
trade = Trade.query.first() trade = Trade.query.first()
@ -2052,7 +2045,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee,
default_conf['trailing_stop_positive_offset'] = 0.011 default_conf['trailing_stop_positive_offset'] = 0.011
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trade() freqtrade.create_trade()
trade = Trade.query.first() trade = Trade.query.first()
@ -2111,7 +2104,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
} }
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
freqtrade.create_trade() freqtrade.create_trade()

View File

@ -4,6 +4,7 @@ from unittest.mock import MagicMock
def test_sync_wallet_at_boot(mocker, default_conf): def test_sync_wallet_at_boot(mocker, default_conf):
default_conf['dry_run'] = False
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value={ get_balances=MagicMock(return_value={
@ -29,6 +30,7 @@ def test_sync_wallet_at_boot(mocker, default_conf):
assert freqtrade.wallets.wallets['GAS'].free == 0.260739 assert freqtrade.wallets.wallets['GAS'].free == 0.260739
assert freqtrade.wallets.wallets['GAS'].used == 0.0 assert freqtrade.wallets.wallets['GAS'].used == 0.0
assert freqtrade.wallets.wallets['GAS'].total == 0.260739 assert freqtrade.wallets.wallets['GAS'].total == 0.260739
assert freqtrade.wallets.get_free('BNT') == 1.0
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
@ -55,9 +57,11 @@ def test_sync_wallet_at_boot(mocker, default_conf):
assert freqtrade.wallets.wallets['GAS'].free == 0.270739 assert freqtrade.wallets.wallets['GAS'].free == 0.270739
assert freqtrade.wallets.wallets['GAS'].used == 0.1 assert freqtrade.wallets.wallets['GAS'].used == 0.1
assert freqtrade.wallets.wallets['GAS'].total == 0.260439 assert freqtrade.wallets.wallets['GAS'].total == 0.260439
assert freqtrade.wallets.get_free('GAS') == 0.270739
def test_sync_wallet_missing_data(mocker, default_conf): def test_sync_wallet_missing_data(mocker, default_conf):
default_conf['dry_run'] = False
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
get_balances=MagicMock(return_value={ get_balances=MagicMock(return_value={
@ -82,3 +86,4 @@ def test_sync_wallet_missing_data(mocker, default_conf):
assert freqtrade.wallets.wallets['GAS'].free == 0.260739 assert freqtrade.wallets.wallets['GAS'].free == 0.260739
assert freqtrade.wallets.wallets['GAS'].used is None assert freqtrade.wallets.wallets['GAS'].used is None
assert freqtrade.wallets.wallets['GAS'].total == 0.260739 assert freqtrade.wallets.wallets['GAS'].total == 0.260739
assert freqtrade.wallets.get_free('GAS') == 0.260739

View File

@ -29,6 +29,17 @@ class Wallets(object):
self.wallets: Dict[str, Any] = {} self.wallets: Dict[str, Any] = {}
self.update() self.update()
def get_free(self, currency) -> float:
if self.exchange._conf['dry_run']:
return 999.9
balance = self.wallets.get(currency)
if balance and balance.free:
return balance.free
else:
return 0
def update(self) -> None: def update(self) -> None:
balances = self.exchange.get_balances() balances = self.exchange.get_balances()

View File

@ -1,4 +1,4 @@
ccxt==1.17.522 ccxt==1.17.539
SQLAlchemy==1.2.14 SQLAlchemy==1.2.14
python-telegram-bot==11.1.0 python-telegram-bot==11.1.0
arrow==0.12.1 arrow==0.12.1
@ -7,13 +7,13 @@ requests==2.20.1
urllib3==1.24.1 urllib3==1.24.1
wrapt==1.10.11 wrapt==1.10.11
pandas==0.23.4 pandas==0.23.4
scikit-learn==0.20.0 scikit-learn==0.20.1
joblib==0.13.0 joblib==0.13.0
scipy==1.1.0 scipy==1.1.0
jsonschema==2.6.0 jsonschema==2.6.0
numpy==1.15.4 numpy==1.15.4
TA-Lib==0.4.17 TA-Lib==0.4.17
pytest==4.0.0 pytest==4.0.1
pytest-mock==1.10.0 pytest-mock==1.10.0
pytest-asyncio==0.9.0 pytest-asyncio==0.9.0
pytest-cov==2.6.0 pytest-cov==2.6.0

View File

@ -44,7 +44,7 @@ from freqtrade.arguments import Arguments, TimeRange
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.optimize.backtesting import setup_configuration from freqtrade.optimize.backtesting import setup_configuration
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.strategy.resolver import StrategyResolver from freqtrade.resolvers import StrategyResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_CONF: Dict[str, Any] = {} _CONF: Dict[str, Any] = {}

View File

@ -27,7 +27,7 @@ import plotly.graph_objs as go
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade import constants from freqtrade import constants
from freqtrade.strategy.resolver import StrategyResolver from freqtrade.resolvers import StrategyResolver
import freqtrade.optimize as optimize import freqtrade.optimize as optimize
import freqtrade.misc as misc import freqtrade.misc as misc