Merge branch 'develop' into stoploss_market

This commit is contained in:
Matthias 2020-01-22 20:51:52 +01:00
commit bc4c469797
11 changed files with 38 additions and 27 deletions

View File

@ -2,6 +2,7 @@
# Downloaded from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib # Downloaded from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib
# Invoke-WebRequest -Uri "https://download.lfd.uci.edu/pythonlibs/xxxxxxx/TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" -OutFile "TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" # Invoke-WebRequest -Uri "https://download.lfd.uci.edu/pythonlibs/xxxxxxx/TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" -OutFile "TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl"
python -m pip install --upgrade pip
pip install build_helpers\TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl pip install build_helpers\TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl
pip install -r requirements-dev.txt pip install -r requirements-dev.txt

View File

@ -63,8 +63,7 @@ class FreqtradeBot:
self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config)
persistence.init(self.config.get('db_url', None), persistence.init(self.config.get('db_url', None), clean_open_orders=self.config['dry_run'])
clean_open_orders=self.config.get('dry_run', False))
self.wallets = Wallets(self.config, self.exchange) self.wallets = Wallets(self.config, self.exchange)
@ -219,7 +218,7 @@ class FreqtradeBot:
return trades_created return trades_created
def get_target_bid(self, pair: str, tick: Dict = None) -> float: def get_buy_rate(self, pair: str, tick: Dict = None) -> float:
""" """
Calculates bid target between current ask price and last price Calculates bid target between current ask price and last price
:return: float: Price :return: float: Price
@ -436,7 +435,7 @@ class FreqtradeBot:
buy_limit_requested = price buy_limit_requested = price
else: else:
# Calculate price # Calculate price
buy_limit_requested = self.get_target_bid(pair) buy_limit_requested = self.get_buy_rate(pair)
min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested) min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested)
if min_stake_amount is not None and min_stake_amount > stake_amount: if min_stake_amount is not None and min_stake_amount > stake_amount:
@ -926,7 +925,7 @@ class FreqtradeBot:
# if stoploss is on exchange and we are on dry_run mode, # if stoploss is on exchange and we are on dry_run mode,
# we consider the sell price stop price # we consider the sell price stop price
if self.config.get('dry_run', False) and sell_type == 'stoploss' \ if self.config['dry_run'] and sell_type == 'stoploss' \
and self.strategy.order_types['stoploss_on_exchange']: and self.strategy.order_types['stoploss_on_exchange']:
limit = trade.stop_loss limit = trade.stop_loss

View File

@ -70,7 +70,7 @@ def generate_text_table_sell_reason(data: Dict[str, Dict], results: DataFrame) -
for reason, count in results['sell_reason'].value_counts().iteritems(): for reason, count in results['sell_reason'].value_counts().iteritems():
result = results.loc[results['sell_reason'] == reason] result = results.loc[results['sell_reason'] == reason]
profit = len(result[result['profit_abs'] >= 0]) profit = len(result[result['profit_abs'] >= 0])
loss = len(result[results['profit_abs'] < 0]) loss = len(result[result['profit_abs'] < 0])
profit_mean = round(result['profit_percent'].mean() * 100.0, 2) profit_mean = round(result['profit_percent'].mean() * 100.0, 2)
tabular_data.append([reason.value, count, profit, loss, profit_mean]) tabular_data.append([reason.value, count, profit, loss, profit_mean])
return tabulate(tabular_data, headers=headers, tablefmt="pipe") return tabulate(tabular_data, headers=headers, tablefmt="pipe")

View File

@ -88,7 +88,7 @@ class RPC:
""" """
config = self._freqtrade.config config = self._freqtrade.config
val = { val = {
'dry_run': config.get('dry_run', False), 'dry_run': config['dry_run'],
'stake_currency': config['stake_currency'], 'stake_currency': config['stake_currency'],
'stake_amount': config['stake_amount'], 'stake_amount': config['stake_amount'],
'minimal_roi': config['minimal_roi'].copy(), 'minimal_roi': config['minimal_roi'].copy(),
@ -337,7 +337,7 @@ class RPC:
'stake': stake_currency, 'stake': stake_currency,
}) })
if total == 0.0: if total == 0.0:
if self._freqtrade.config.get('dry_run', False): if self._freqtrade.config['dry_run']:
raise RPCException('Running in Dry Run, balances are not available.') raise RPCException('Running in Dry Run, balances are not available.')
else: else:
raise RPCException('All balances are zero.') raise RPCException('All balances are zero.')
@ -351,7 +351,7 @@ class RPC:
'symbol': symbol, 'symbol': symbol,
'value': value, 'value': value,
'stake': stake_currency, 'stake': stake_currency,
'note': 'Simulated balances' if self._freqtrade.config.get('dry_run', False) else '' 'note': 'Simulated balances' if self._freqtrade.config['dry_run'] else ''
} }
def _rpc_start(self) -> Dict[str, str]: def _rpc_start(self) -> Dict[str, str]:

View File

@ -62,7 +62,7 @@ class RPCManager:
logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.") logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.")
def startup_messages(self, config, pairlist) -> None: def startup_messages(self, config, pairlist) -> None:
if config.get('dry_run', False): if config['dry_run']:
self.send_msg({ self.send_msg({
'type': RPCMessageType.WARNING_NOTIFICATION, 'type': RPCMessageType.WARNING_NOTIFICATION,
'status': 'Dry run is enabled. All trades are simulated.' 'status': 'Dry run is enabled. All trades are simulated.'

View File

@ -389,9 +389,11 @@ class IStrategy(ABC):
trade.adjust_stop_loss(high or current_rate, stop_loss_value) trade.adjust_stop_loss(high or current_rate, stop_loss_value)
# evaluate if the stoploss was hit if stoploss is not on exchange # evaluate if the stoploss was hit if stoploss is not on exchange
# in Dry-Run, this handles stoploss logic as well, as the logic will not be different to
# regular stoploss handling.
if ((self.stoploss is not None) and if ((self.stoploss is not None) and
(trade.stop_loss >= current_rate) and (trade.stop_loss >= current_rate) and
(not self.order_types.get('stoploss_on_exchange'))): (not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])):
sell_type = SellType.STOP_LOSS sell_type = SellType.STOP_LOSS

View File

@ -27,7 +27,8 @@ class SampleHyperOptLoss(IHyperOptLoss):
Defines the default loss function for hyperopt Defines the default loss function for hyperopt
This is intended to give you some inspiration for your own loss function. This is intended to give you some inspiration for your own loss function.
The Function needs to return a number (float) - which becomes for better backtest results. The Function needs to return a number (float) - which becomes smaller for better backtest
results.
""" """
@staticmethod @staticmethod

View File

@ -1,6 +1,6 @@
# requirements without requirements installable via conda # requirements without requirements installable via conda
# mainly used for Raspberry pi installs # mainly used for Raspberry pi installs
ccxt==1.21.56 ccxt==1.21.76
SQLAlchemy==1.3.12 SQLAlchemy==1.3.12
python-telegram-bot==12.3.0 python-telegram-bot==12.3.0
arrow==0.15.5 arrow==0.15.5

View File

@ -8,7 +8,7 @@ flake8==3.7.9
flake8-type-annotations==0.1.0 flake8-type-annotations==0.1.0
flake8-tidy-imports==4.0.0 flake8-tidy-imports==4.0.0
mypy==0.761 mypy==0.761
pytest==5.3.2 pytest==5.3.3
pytest-asyncio==0.10.0 pytest-asyncio==0.10.0
pytest-cov==2.8.1 pytest-cov==2.8.1
pytest-mock==2.0.0 pytest-mock==2.0.0

View File

@ -323,7 +323,7 @@ def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) ->
configuration = Configuration(Arguments(arglist).get_parsed_arg()) configuration = Configuration(Arguments(arglist).get_parsed_arg())
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
assert validated_conf.get('dry_run') is expected assert validated_conf['dry_run'] is expected
def test_load_custom_strategy(default_conf, mocker) -> None: def test_load_custom_strategy(default_conf, mocker) -> None:

View File

@ -912,7 +912,7 @@ def test_balance_fully_ask_side(mocker, default_conf) -> None:
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
MagicMock(return_value={'ask': 20, 'last': 10})) MagicMock(return_value={'ask': 20, 'last': 10}))
assert freqtrade.get_target_bid('ETH/BTC') == 20 assert freqtrade.get_buy_rate('ETH/BTC') == 20
def test_balance_fully_last_side(mocker, default_conf) -> None: def test_balance_fully_last_side(mocker, default_conf) -> None:
@ -921,7 +921,7 @@ def test_balance_fully_last_side(mocker, default_conf) -> None:
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
MagicMock(return_value={'ask': 20, 'last': 10})) MagicMock(return_value={'ask': 20, 'last': 10}))
assert freqtrade.get_target_bid('ETH/BTC') == 10 assert freqtrade.get_buy_rate('ETH/BTC') == 10
def test_balance_bigger_last_ask(mocker, default_conf) -> None: def test_balance_bigger_last_ask(mocker, default_conf) -> None:
@ -929,7 +929,7 @@ def test_balance_bigger_last_ask(mocker, default_conf) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
MagicMock(return_value={'ask': 5, 'last': 10})) MagicMock(return_value={'ask': 5, 'last': 10}))
assert freqtrade.get_target_bid('ETH/BTC') == 5 assert freqtrade.get_buy_rate('ETH/BTC') == 5
def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
@ -938,10 +938,10 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
stake_amount = 2 stake_amount = 2
bid = 0.11 bid = 0.11
get_bid = MagicMock(return_value=bid) buy_rate_mock = MagicMock(return_value=bid)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.FreqtradeBot', 'freqtrade.freqtradebot.FreqtradeBot',
get_target_bid=get_bid, get_buy_rate=buy_rate_mock,
_get_min_pair_stake_amount=MagicMock(return_value=1) _get_min_pair_stake_amount=MagicMock(return_value=1)
) )
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']}) buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
@ -958,7 +958,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
pair = 'ETH/BTC' pair = 'ETH/BTC'
assert freqtrade.execute_buy(pair, stake_amount) assert freqtrade.execute_buy(pair, stake_amount)
assert get_bid.call_count == 1 assert buy_rate_mock.call_count == 1
assert buy_mm.call_count == 1 assert buy_mm.call_count == 1
call_args = buy_mm.call_args_list[0][1] call_args = buy_mm.call_args_list[0][1]
assert call_args['pair'] == pair assert call_args['pair'] == pair
@ -975,8 +975,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
# Test calling with price # Test calling with price
fix_price = 0.06 fix_price = 0.06
assert freqtrade.execute_buy(pair, stake_amount, fix_price) assert freqtrade.execute_buy(pair, stake_amount, fix_price)
# Make sure get_target_bid wasn't called again # Make sure get_buy_rate wasn't called again
assert get_bid.call_count == 1 assert buy_rate_mock.call_count == 1
assert buy_mm.call_count == 2 assert buy_mm.call_count == 2
call_args = buy_mm.call_args_list[1][1] call_args = buy_mm.call_args_list[1][1]
@ -1319,6 +1319,14 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
order_types=freqtrade.strategy.order_types, order_types=freqtrade.strategy.order_types,
stop_price=0.00002344 * 0.95) stop_price=0.00002344 * 0.95)
# price fell below stoploss, so dry-run sells trade.
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
'bid': 0.00002144,
'ask': 0.00002146,
'last': 0.00002144
}))
assert freqtrade.handle_trade(trade) is True
def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog, def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog,
limit_buy_order, limit_sell_order) -> None: limit_buy_order, limit_sell_order) -> None:
@ -3495,7 +3503,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o
def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
""" """
test if function get_target_bid will return the order book price test if function get_buy_rate will return the order book price
instead of the ask rate instead of the ask rate
""" """
patch_exchange(mocker) patch_exchange(mocker)
@ -3513,13 +3521,13 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
default_conf['telegram']['enabled'] = False default_conf['telegram']['enabled'] = False
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
assert freqtrade.get_target_bid('ETH/BTC') == 0.043935 assert freqtrade.get_buy_rate('ETH/BTC') == 0.043935
assert ticker_mock.call_count == 0 assert ticker_mock.call_count == 0
def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None: def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None:
""" """
test if function get_target_bid will return the ask rate (since its value is lower) test if function get_buy_rate will return the ask rate (since its value is lower)
instead of the order book rate (even if enabled) instead of the order book rate (even if enabled)
""" """
patch_exchange(mocker) patch_exchange(mocker)
@ -3538,7 +3546,7 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None:
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
# orderbook shall be used even if tickers would be lower. # orderbook shall be used even if tickers would be lower.
assert freqtrade.get_target_bid('ETH/BTC') != 0.042 assert freqtrade.get_buy_rate('ETH/BTC') != 0.042
assert ticker_mock.call_count == 0 assert ticker_mock.call_count == 0