Repository: freqtrade/freqtrade

Issue:
KeyError: 'stopPrice'
## Step 1: Have you search for this issue before posting it?

yes, nothing found

## Step 2: Describe your environment

Python Version: Python 3.7.3
CCXT version: ccxt==1.18.992
Branch: Develop
Last Commit ID: eeecdd4e5a5f70ba2e7943bf3c6aaf4b5742e5b3

  
## Step 3: Describe the problem:

*Explain the problem you have encountered*

### Steps to reproduce:

  1. config file - stoploss on exchange - true
  2. FT started normally
  3. buy signal found -> see log below
  
### Observed Results:

  * Telegram message "Process Died"
  

### Relevant code exceptions or logs:

  ```
2019-08-06 22:44:55,366 - freqtrade.freqtradebot - INFO - Found no buy signals for whitelisted currencies. Trying again..
2019-08-06 22:44:59,196 - freqtrade.freqtradebot - INFO - Found no buy signals for whitelisted currencies. Trying again..
2019-08-06 22:45:05,099 - freqtrade.freqtradebot - INFO - Buy signal found: about create a new trade with stake_amount: 980 ...
2019-08-06 22:45:05,099 - freqtrade.freqtradebot - INFO - Using Last Ask / Last Price
2019-08-06 22:45:05,400 - freqtrade.rpc.rpc_manager - INFO - Sending rpc message: {'type': buy, 'exchange': 'Binance', 'pair': 'BTC/USDT', 'limit': 11642.115000000002, 'order_type': 'limit', 'stake_amount': 980, 'stake_currency': 'USDT', 'fiat_currency': 'USD'}
2019-08-06 22:45:05,928 - freqtrade.wallets - INFO - Wallets synced.
/root/freqtrade/.env/lib/python3.7/site-packages/arrow/factory.py:201: ArrowParseWarning: The .get() parsing method without a format string will parse more strictly in version 0.15.0.See https://github.com/crsmithdev/arrow/issues/612 for more details.
  ArrowParseWarning,
2019-08-06 22:45:05,986 - freqtrade.wallets - INFO - Wallets synced.
2019-08-06 22:45:07,044 - freqtrade.freqtradebot - INFO - Found open order for Trade(id=1, pair=BTC/USDT, amount=0.08417714, open_rate=11642.11500000, open_since=just now)
2019-08-06 22:45:07,045 - freqtrade.freqtradebot - INFO - Applying fee on amount for Trade(id=1, pair=BTC/USDT, amount=0.08417714, open_rate=11642.11500000, open_since=just now) failed: myTrade-Dict empty found
2019-08-06 22:45:07,046 - freqtrade.persistence - INFO - Updating trade (id=1) ...
2019-08-06 22:45:07,046 - freqtrade.persistence - INFO - LIMIT_BUY has been fulfilled for Trade(id=1, pair=BTC/USDT, amount=0.08417714, open_rate=11642.11500000, open_since=just now).
2019-08-06 22:45:07,632 - freqtrade.freqtradebot - INFO - Found no buy signals for whitelisted currencies. Trying again..
2019-08-06 22:45:11,045 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File "/root/freqtrade/freqtrade/main.py", line 49, in main
    worker.run()
  File "/root/freqtrade/freqtrade/worker.py", line 72, in run
    state = self._worker(old_state=state)
  File "/root/freqtrade/freqtrade/worker.py", line 110, in _worker
    self._throttle(func=self._process, min_secs=throttle_secs)
  File "/root/freqtrade/freqtrade/worker.py", line 123, in _throttle
    result = func(*args, **kwargs)
  File "/root/freqtrade/freqtrade/worker.py", line 134, in _process
    state_changed = self.freqtrade.process()
  File "/root/freqtrade/freqtrade/freqtradebot.py", line 135, in process
    state_changed |= self.process_maybe_execute_sell(trade)
  File "/root/freqtrade/freqtrade/freqtradebot.py", line 451, in process_maybe_execute_sell
    result = self.handle_stoploss_on_exchange(trade)
  File "/root/freqtrade/freqtrade/freqtradebot.py", line 683, in handle_stoploss_on_exchange
    self.handle_trailing_stoploss_on_exchange(trade, stoploss_order)
  File "/root/freqtrade/freqtrade/freqtradebot.py", line 696, in handle_trailing_stoploss_on_exchange
    if trade.stop_loss > float(order['info']['stopPrice']):
KeyError: 'stopPrice'
2019-08-06 22:45:11,099 - freqtrade.rpc.rpc_manager - INFO - Sending rpc message: {'type': status, 'status': 'process died'}
2019-08-06 22:45:11,143 - freqtrade.freqtradebot - INFO - Cleaning up modules ...
2019-08-06 22:45:11,143 - freqtrade.rpc.rpc_manager - INFO - Cleaning up rpc modules ...
(.env) (base) root@vmanager6003:~/freqtrade# 

  ```



Discussion:
how does your `order_types` configuration look like?

order-types table:

    "order_types": {
    "buy": "limit",
    "sell": "limit",
    "stoploss": "limit",
    "stoploss_on_exchange": true,
    "stoploss_on_exchange_interval": 60
    },


DB I did not re-use, I was deleting the old one before starting the bot


    "dry_run": true,
    "db_url": "sqlite:///strategyA_test.sqlite",

Yesterday I restarted the bot after the crash without stoploss on exchange so far he completed 2 dry trades without crashing



Your comments don't line up ... you say you had stoploss on exchange on (in the first comment) - but now you show "dry_run" to be `true` ... ? 

if it's dry-run, then stoploss cannot be on the exchange...
oh okay - thats the issue? "stoploss on exchange" is not working in dry-run?

I was assuming that somehow FT would ignore that setting being in dry-run and would automatically work with off exchange stop loss
> oh okay - thats the issue? "stoploss on exchange" is not working in dry-run?

It cannot work by definition, because that's stoploss which is executed on the exchange and freqtrade in the dry-run mode does not create any orders on exchange...

> I was assuming that somehow FT would ignore that setting being in dry-run and would automatically work with off exchange stop loss

This may have sense, as a proposal for an enhancement. If not, a check for graceful exit should be added when we load/verify configuration...

@xmatthias, what do you think? (I'm not familiar with this part of the codebase yet)

i'll need to check - i know we disable it for backtesting - and i thought we do for dry-run too... (simply overiding the value of `order_types['stoploss_on_exhange']`)
well apparently we don't - it's implemented for backtesting, but not for dry-run ... :-1: 

Resolving patch:
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -20,7 +20,7 @@
 from freqtrade.persistence import Trade
 from freqtrade.rpc import RPCManager, RPCMessageType
 from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver
-from freqtrade.state import State
+from freqtrade.state import State, RunMode
 from freqtrade.strategy.interface import SellType, IStrategy
 from freqtrade.wallets import Wallets
 
@@ -75,6 +75,12 @@ def __init__(self, config: Dict[str, Any]) -> None:
         persistence.init(self.config.get('db_url', None),
                          clean_open_orders=self.config.get('dry_run', False))
 
+        # Stoploss on exchange does not make sense, therefore we need to disable that.
+        if (self.dataprovider.runmode == RunMode.DRY_RUN and
+           self.strategy.order_types.get('stoploss_on_exchange', False)):
+            logger.info("Disabling stoploss_on_exchange during dry-run.")
+            self.strategy.order_types['stoploss_on_exchange'] = False
+            config['order_types']['stoploss_on_exchange'] = False
         # Set initial bot state from config
         initial_state = self.config.get('initial_state')
         self.state = State[initial_state.upper()] if initial_state else State.STOPPED


Test patch:
diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py
--- a/freqtrade/tests/strategy/test_strategy.py
+++ b/freqtrade/tests/strategy/test_strategy.py
@@ -15,7 +15,7 @@
 from freqtrade.strategy import import_strategy
 from freqtrade.strategy.default_strategy import DefaultStrategy
 from freqtrade.strategy.interface import IStrategy
-from freqtrade.tests.conftest import log_has_re
+from freqtrade.tests.conftest import log_has_re, log_has
 
 
 def test_import_strategy(caplog):
@@ -257,12 +257,9 @@ def test_strategy_override_order_types(caplog):
     for method in ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']:
         assert resolver.strategy.order_types[method] == order_types[method]
 
-    assert ('freqtrade.resolvers.strategy_resolver',
-            logging.INFO,
-            "Override strategy 'order_types' with value in config file:"
-            " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit',"
-            " 'stoploss_on_exchange': True}."
-            ) in caplog.record_tuples
+    assert log_has("Override strategy 'order_types' with value in config file:"
+                   " {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit',"
+                   " 'stoploss_on_exchange': True}.", caplog.record_tuples)
 
     config = {
         'strategy': 'DefaultStrategy',
diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py
--- a/freqtrade/tests/test_freqtradebot.py
+++ b/freqtrade/tests/test_freqtradebot.py
@@ -16,7 +16,7 @@
 from freqtrade.freqtradebot import FreqtradeBot
 from freqtrade.persistence import Trade
 from freqtrade.rpc import RPCMessageType
-from freqtrade.state import State
+from freqtrade.state import State, RunMode
 from freqtrade.strategy.interface import SellCheckTuple, SellType
 from freqtrade.tests.conftest import (get_patched_freqtradebot,
                                       get_patched_worker, log_has, log_has_re,
@@ -130,7 +130,77 @@ def throttled_func(nb_assets=-1):
     assert result == -1
 
 
-def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None:
+def test_order_dict_dry_run(default_conf, mocker, caplog) -> None:
+    patch_RPCManager(mocker)
+    patch_exchange(mocker)
+    mocker.patch.multiple(
+        'freqtrade.exchange.Exchange',
+        get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
+    )
+    conf = default_conf.copy()
+    conf['runmode'] = RunMode.DRY_RUN
+    conf['order_types'] = {
+        'buy': 'market',
+        'sell': 'limit',
+        'stoploss': 'limit',
+        'stoploss_on_exchange': True,
+    }
+
+    freqtrade = FreqtradeBot(conf)
+    assert log_has("Disabling stoploss_on_exchange during dry-run.", caplog.record_tuples)
+    assert not freqtrade.strategy.order_types['stoploss_on_exchange']
+
+    caplog.clear()
+    # is left untouched
+    conf = default_conf.copy()
+    conf['runmode'] = RunMode.DRY_RUN
+    conf['order_types'] = {
+        'buy': 'market',
+        'sell': 'limit',
+        'stoploss': 'limit',
+        'stoploss_on_exchange': False,
+    }
+    freqtrade = FreqtradeBot(conf)
+    assert not freqtrade.strategy.order_types['stoploss_on_exchange']
+    assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog.record_tuples)
+
+
+def test_order_dict_live(default_conf, mocker, caplog) -> None:
+    patch_RPCManager(mocker)
+    patch_exchange(mocker)
+    mocker.patch.multiple(
+        'freqtrade.exchange.Exchange',
+        get_balance=MagicMock(return_value=default_conf['stake_amount'] * 2)
+    )
+    conf = default_conf.copy()
+    conf['runmode'] = RunMode.LIVE
+    conf['order_types'] = {
+        'buy': 'market',
+        'sell': 'limit',
+        'stoploss': 'limit',
+        'stoploss_on_exchange': True,
+    }
+
+    freqtrade = FreqtradeBot(conf)
+    assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog.record_tuples)
+    assert freqtrade.strategy.order_types['stoploss_on_exchange']
+
+    caplog.clear()
+    # is left untouched
+    conf = default_conf.copy()
+    conf['runmode'] = RunMode.LIVE
+    conf['order_types'] = {
+        'buy': 'market',
+        'sell': 'limit',
+        'stoploss': 'limit',
+        'stoploss_on_exchange': False,
+    }
+    freqtrade = FreqtradeBot(conf)
+    assert not freqtrade.strategy.order_types['stoploss_on_exchange']
+    assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog.record_tuples)
+
+
+def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
     patch_RPCManager(mocker)
     patch_exchange(mocker)
     mocker.patch.multiple(