diff --git a/docs/edge.md b/docs/edge.md
index b208cb318..a4acffc44 100644
--- a/docs/edge.md
+++ b/docs/edge.md
@@ -3,159 +3,198 @@
 This page explains how to use Edge Positioning module in your bot in order to enter into a trade only if the trade has a reasonable win rate and risk reward ratio, and consequently adjust your position size and stoploss.
 
 !!! Warning
-    Edge positioning is not compatible with dynamic whitelist. it overrides dynamic whitelist.
+    Edge positioning is not compatible with dynamic whitelist. If enabled, it overrides the dynamic whitelist option.
 
 !!! Note
-    Edge won't consider anything else than buy/sell/stoploss signals. So trailing stoploss, ROI, and everything else will be ignored in its calculation.
+    Edge does not consider anything else than buy/sell/stoploss signals. So trailing stoploss, ROI, and everything else are ignored in its calculation.
 
 ## Introduction
-Trading is all about probability. No one can claim that he has a strategy working all the time. You have to assume that sometimes you lose.
-But it doesn't mean there is no rule, it only means rules should work "most of the time". Let's play a game: we toss a coin, heads: I give you 10$, tails: You give me 10$. Is it an interesting game ? no, it is quite boring, isn't it?
-But let's say the probability that we have heads is 80%, and the probability that we have tails is 20%. Now it is becoming interesting ...
-That means 10$ x 80% versus 10$ x 20%. 8$ versus 2$. That means over time you will win 8$ risking only 2$ on each toss of coin.
-Let's complicate it more: you win 80% of the time but only 2$, I win 20% of the time but 8$. The calculation is: 80% * 2$ versus 20% * 8$. It is becoming boring again because overtime you win $1.6$ (80% x 2$) and me $1.6 (20% * 8$) too.
-The question is: How do you calculate that? how do you know if you wanna play?
+Trading is all about probability. No one can claim that he has a strategy working all the time. You have to assume that sometimes you lose.
+
+But it doesn't mean there is no rule, it only means rules should work "most of the time". Let's play a game: we toss a coin, heads: I give you 10$, tails: you give me 10$. Is it an interesting game? No, it's quite boring, isn't it?
+
+But let's say the probability that we have heads is 80% (because our coin has the displaced distribution of mass or other defect), and the probability that we have tails is 20%. Now it is becoming interesting...
+
+That means 10$ X 80% versus 10$ X 20%. 8$ versus 2$. That means over time you will win 8$ risking only 2$ on each toss of coin.
+
+Let's complicate it more: you win 80% of the time but only 2$, I win 20% of the time but 8$. The calculation is: 80% X 2$ versus 20% X 8$. It is becoming boring again because overtime you win $1.6$ (80% X 2$) and me $1.6 (20% X 8$) too.
+
+The question is: How do you calculate that? How do you know if you wanna play?
+
 The answer comes to two factors:
 - Win Rate
 - Risk Reward Ratio
 
-
 ### Win Rate
-Means over X trades what is the percentage of winning trades to total number of trades (note that we don't consider how much you gained but only If you won or not).
+Win Rate (*W*) is is the mean over some amount of trades (*N*) what is the percentage of winning trades to total number of trades (note that we don't consider how much you gained but only if you won or not).
 
+    W = (Number of winning trades) / (Total number of trades) = (Number of winning trades) / N
 
-`W = (Number of winning trades) / (Total number of trades)`
+Complementary Loss Rate (*L*) is defined as
+
+    L = (Number of losing trades) / (Total number of trades) = (Number of losing trades) / N
+
+or, which is the same, as
+
+    R = 1 – W
 
 ### Risk Reward Ratio
-Risk Reward Ratio is a formula used to measure the expected gains of a given investment against the risk of loss. It is basically what you potentially win divided by what you potentially lose:
+Risk Reward Ratio (*R*) is a formula used to measure the expected gains of a given investment against the risk of loss. It is basically what you potentially win divided by what you potentially lose:
 
-`R = Profit / Loss`
+    R = Profit / Loss
 
 Over time, on many trades, you can calculate your risk reward by dividing your average profit on winning trades by your average loss on losing trades:
 
-`Average profit = (Sum of profits) / (Number of winning trades)`
+    Average profit = (Sum of profits) / (Number of winning trades)
 
-`Average loss = (Sum of losses) / (Number of losing trades)`
+    Average loss = (Sum of losses) / (Number of losing trades)
 
-`R = (Average profit) / (Average loss)`
+    R = (Average profit) / (Average loss)
 
 ### Expectancy
+At this point we can combine *W* and *R* to create an expectancy ratio. This is a simple process of multiplying the risk reward ratio by the percentage of winning trades and subtracting the percentage of losing trades, which is calculated as follows:
 
-At this point we can combine W and R to create an expectancy ratio. This is a simple process of multiplying the risk reward ratio by the percentage of winning trades, and subtracting the percentage of losing trades, which is calculated as follows:
-
-Expectancy Ratio = (Risk Reward Ratio x Win Rate) – Loss Rate
+    Expectancy Ratio = (Risk Reward Ratio X Win Rate) – Loss Rate = (R X W) – L
 
 So lets say your Win rate is 28% and your Risk Reward Ratio is 5:
 
-`Expectancy = (5 * 0.28) - 0.72 = 0.68`
+    Expectancy = (5 X 0.28) – 0.72 = 0.68
 
-Superficially, this means that on average you expect this strategy’s trades to return .68 times the size of your losers. This is important for two reasons: First, it may seem obvious, but you know right away that you have a positive return. Second, you now have a number you can compare to other candidate systems to make decisions about which ones you employ.
+Superficially, this means that on average you expect this strategy’s trades to return .68 times the size of your loses. This is important for two reasons: First, it may seem obvious, but you know right away that you have a positive return. Second, you now have a number you can compare to other candidate systems to make decisions about which ones you employ.
 
 It is important to remember that any system with an expectancy greater than 0 is profitable using past data. The key is finding one that will be profitable in the future.
 
-You can also use this number to evaluate the effectiveness of modifications to this system.
+You can also use this value to evaluate the effectiveness of modifications to this system.
 
-**NOTICE:** It's important to keep in mind that Edge is testing your expectancy using historical data , there's no guarantee that you will have a similar edge in the future. It's still vital to do this testing in order to build confidence in your methodology, but be wary of "curve-fitting" your approach to the historical data as things are unlikely to play out the exact same way for future trades.
+**NOTICE:** It's important to keep in mind that Edge is testing your expectancy using historical data, there's no guarantee that you will have a similar edge in the future. It's still vital to do this testing in order to build confidence in your methodology, but be wary of "curve-fitting" your approach to the historical data as things are unlikely to play out the exact same way for future trades.
 
 ## How does it work?
-If enabled in config, Edge will go through historical data with a range of stoplosses in order to find buy and sell/stoploss signals. It then calculates win rate and expectancy over X trades for each stoploss. Here is an example:
+If enabled in config, Edge will go through historical data with a range of stoplosses in order to find buy and sell/stoploss signals. It then calculates win rate and expectancy over *N* trades for each stoploss. Here is an example:
 
 | Pair   |      Stoploss      |  Win Rate | Risk Reward Ratio | Expectancy |
 |----------|:-------------:|-------------:|------------------:|-----------:|
-| XZC/ETH  |  -0.03        |   0.52       |1.359670           | 0.228      |
 | XZC/ETH  |  -0.01        |   0.50       |1.176384           | 0.088      |
 | XZC/ETH  |  -0.02        |   0.51       |1.115941           | 0.079      |
+| XZC/ETH  |  -0.03        |   0.52       |1.359670           | 0.228      |
+| XZC/ETH  |  -0.04        |   0.51       |1.234539           | 0.117      |
 
 The goal here is to find the best stoploss for the strategy in order to have the maximum expectancy. In the above example stoploss at 3% leads to the maximum expectancy according to historical data.
 
-Edge then forces stoploss to your strategy dynamically.
+Edge module then forces stoploss value it evaluated to your strategy dynamically.
 
 ### Position size
-Edge dictates the stake amount for each trade to the bot according to the following factors:
+Edge also dictates the stake amount for each trade to the bot according to the following factors:
 
 - Allowed capital at risk
 - Stoploss
 
 Allowed capital at risk is calculated as follows:
 
-**allowed capital at risk** = **capital_available_percentage** X **allowed risk per trade**
+    Allowed capital at risk = (Capital available_percentage) X (Allowed risk per trade)
 
-**Stoploss** is calculated as described above against historical data.
+Stoploss is calculated as described above against historical data.
 
 Your position size then will be:
 
-**position size** = **allowed capital at risk** / **stoploss**
+    Position size = (Allowed capital at risk) / Stoploss
 
-Example:
-Let's say the stake currency is ETH and you have 10 ETH on the exchange, your **capital_available_percentage** is 50% and you would allow 1% of risk for each trade. thus your available capital for trading is **10 x 0.5 = 5 ETH** and allowed capital at risk would be **5 x 0.01 = 0.05 ETH**. 
-Let's assume Edge has calculated that for **XLM/ETH** market your stoploss should be at 2%. So your position size will be **0.05 / 0.02 = 2.5ETH**.
-Bot takes a position of 2.5ETH on XLM/ETH (call it trade 1). Up next, you receive another buy signal while trade 1 is still open. This time on BTC/ETH market. Edge calculated stoploss for this market at 4%. So your position size would be 0.05 / 0.04 = 1.25ETH (call it trade 2).
-Note that available capital for trading didn’t change for trade 2 even if you had already trade 1. The available capital doesn’t mean the free amount on your wallet.
-Now you have two trades open. The Bot receives yet another buy signal for another market: **ADA/ETH**. This time the stoploss is calculated at 1%. So your position size is **0.05 / 0.01 = 5ETH**. But there are already 4ETH blocked in two previous trades. So the position size for this third trade would be 1ETH.
-Available capital doesn’t change before a position is sold. Let’s assume that trade 1 receives a sell signal and it is sold with a profit of 1ETH. Your total capital on exchange would be 11 ETH and the available capital for trading becomes 5.5ETH. 
-So the Bot receives another buy signal for trade 4 with a stoploss at 2% then your position size would be **0.055 / 0.02 = 2.75**.
+Example:
+
+Let's say the stake currency is ETH and you have 10 ETH on the exchange, your capital available percentage is 50% and you would allow 1% of risk for each trade. thus your available capital for trading is **10 x 0.5 = 5 ETH** and allowed capital at risk would be **5 x 0.01 = 0.05 ETH**.
+
+Let's assume Edge has calculated that for **XLM/ETH** market your stoploss should be at 2%. So your position size will be **0.05 / 0.02 = 2.5 ETH**.
+
+Bot takes a position of 2.5 ETH on XLM/ETH (call it trade 1). Up next, you receive another buy signal while trade 1 is still open. This time on **BTC/ETH** market. Edge calculated stoploss for this market at 4%. So your position size would be 0.05 / 0.04 = 1.25 ETH (call it trade 2).
+
+Note that available capital for trading didn’t change for trade 2 even if you had already trade 1. The available capital doesn’t mean the free amount on your wallet.
+
+Now you have two trades open. The bot receives yet another buy signal for another market: **ADA/ETH**. This time the stoploss is calculated at 1%. So your position size is **0.05 / 0.01 = 5 ETH**. But there are already 3.75 ETH blocked in two previous trades. So the position size for this third trade would be **5 – 3.75 = 1.25 ETH**.
+
+Available capital doesn’t change before a position is sold. Let’s assume that trade 1 receives a sell signal and it is sold with a profit of 1 ETH. Your total capital on exchange would be 11 ETH and the available capital for trading becomes 5.5 ETH.
+
+So the Bot receives another buy signal for trade 4 with a stoploss at 2% then your position size would be **0.055 / 0.02 = 2.75 ETH**.
 
 ## Configurations
-Edge has following configurations:
+Edge module has following configuration options:
 
 #### enabled
-If true, then Edge will run periodically.
-(default to false)
+If true, then Edge will run periodically.
+
+(defaults to false)
 
 #### process_throttle_secs
-How often should Edge run in seconds? 
-(default to 3600 so one hour)
+How often should Edge run in seconds?
+
+(defaults to 3600 so one hour)
 
 #### calculate_since_number_of_days
 Number of days of data against which Edge calculates Win Rate, Risk Reward and Expectancy
-Note that it downloads historical data so increasing this number would lead to slowing down the bot.
-(default to 7)
+Note that it downloads historical data so increasing this number would lead to slowing down the bot.
+
+(defaults to 7)
 
 #### capital_available_percentage
-This is the percentage of the total capital on exchange in stake currency. 
-As an example if you have 10 ETH available in your wallet on the exchange and this value is 0.5 (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers it as available capital.
-(default to 0.5)
+This is the percentage of the total capital on exchange in stake currency.
+
+As an example if you have 10 ETH available in your wallet on the exchange and this value is 0.5 (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers it as available capital.
+
+(defaults to 0.5)
 
 #### allowed_risk
-Percentage of allowed risk per trade.
-(default to 0.01 [1%])
+Percentage of allowed risk per trade.
+
+(defaults to 0.01 so 1%)
 
 #### stoploss_range_min
-Minimum stoploss.
-(default to -0.01)
+Minimum stoploss.
+
+(defaults to -0.01)
 
 #### stoploss_range_max
-Maximum stoploss.
-(default to -0.10)
+Maximum stoploss.
+
+(defaults to -0.10)
 
 #### stoploss_range_step
-As an example if this is set to -0.01 then Edge will test the strategy for [-0.01, -0,02, -0,03 ..., -0.09, -0.10] ranges.
-Note than having a smaller step means having a bigger range which could lead to slow calculation. 
-if you set this parameter to -0.001, you then slow down the Edge calculation by a factor of 10. 
-(default to -0.01)
+As an example if this is set to -0.01 then Edge will test the strategy for \[-0.01, -0,02, -0,03 ..., -0.09, -0.10\] ranges.
+Note than having a smaller step means having a bigger range which could lead to slow calculation.
+
+If you set this parameter to -0.001, you then slow down the Edge calculation by a factor of 10.
+
+(defaults to -0.01)
 
 #### minimum_winrate
-It filters pairs which don't have at least minimum_winrate.
-This comes handy if you want to be conservative and don't comprise win rate in favor of risk reward ratio.
-(default to 0.60)
+It filters out pairs which don't have at least minimum_winrate.
+
+This comes handy if you want to be conservative and don't comprise win rate in favour of risk reward ratio.
+
+(defaults to 0.60)
 
 #### minimum_expectancy
-It filters paris which have an expectancy lower than this number .
-Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ return.
-(default to 0.20)
+It filters out pairs which have the expectancy lower than this number.
+
+Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ return.
+
+(defaults to 0.20)
 
 #### min_trade_number
-When calculating W and R and E (expectancy) against historical data, you always want to have a minimum number of trades. The more this number is the more Edge is reliable. Having a win rate of 100% on a single trade doesn't mean anything at all. But having a win rate of 70% over past 100 trades means clearly something. 
-(default to 10, it is highly recommended not to decrease this number)
+When calculating *W*, *R* and *E* (expectancy) against historical data, you always want to have a minimum number of trades. The more this number is the more Edge is reliable.
+
+Having a win rate of 100% on a single trade doesn't mean anything at all. But having a win rate of 70% over past 100 trades means clearly something.
+
+(defaults to 10, it is highly recommended not to decrease this number)
 
 #### max_trade_duration_minute
-Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.
-**NOTICE:** While configuring this value, you should take into consideration your ticker interval. as an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. default value is set assuming your strategy interval is relatively small (1m or 5m, etc).
-(default to 1 day, 1440 = 60 * 24)
+Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.
+
+**NOTICE:** While configuring this value, you should take into consideration your ticker interval. As an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. Default value is set assuming your strategy interval is relatively small (1m or 5m, etc.).
+
+(defaults to 1 day, i.e. to 60 * 24 = 1440 minutes)
 
 #### remove_pumps
-Edge will remove sudden pumps in a given market while going through historical data. However, given that pumps happen very often in crypto markets, we recommend you keep this off.
-(default to false)
+Edge will remove sudden pumps in a given market while going through historical data. However, given that pumps happen very often in crypto markets, we recommend you keep this off.
+
+(defaults to false)
 
 
 ## Running Edge independently
@@ -199,14 +238,14 @@ python3 ./freqtrade/main.py edge --stoplosses=-0.01,-0.1,-0.001 #min,max,step
 python3 ./freqtrade/main.py edge --timerange=20181110-20181113
 ```
 
-Doing --timerange=-200 will get the last 200 timeframes from your inputdata. You can also specify specific dates, or a range span indexed by start and stop.
+Doing `--timerange=-200` will get the last 200 timeframes from your inputdata. You can also specify specific dates, or a range span indexed by start and stop.
 
 The full timerange specification:
 
-* Use last 123 tickframes of data: --timerange=-123
-* Use first 123 tickframes of data: --timerange=123-
-* Use tickframes from line 123 through 456: --timerange=123-456
-* Use tickframes till 2018/01/31: --timerange=-20180131
-* Use tickframes since 2018/01/31: --timerange=20180131-
-* Use tickframes since 2018/01/31 till 2018/03/01 : --timerange=20180131-20180301
-* Use tickframes between POSIX timestamps 1527595200 1527618600: --timerange=1527595200-1527618600
+* Use last 123 tickframes of data: `--timerange=-123`
+* Use first 123 tickframes of data: `--timerange=123-`
+* Use tickframes from line 123 through 456: `--timerange=123-456`
+* Use tickframes till 2018/01/31: `--timerange=-20180131`
+* Use tickframes since 2018/01/31: `--timerange=20180131-`
+* Use tickframes since 2018/01/31 till 2018/03/01 : `--timerange=20180131-20180301`
+* Use tickframes between POSIX timestamps 1527595200 1527618600: `--timerange=1527595200-1527618600`
diff --git a/docs/webhook-config.md b/docs/webhook-config.md
index e5509d6c9..2b5365e32 100644
--- a/docs/webhook-config.md
+++ b/docs/webhook-config.md
@@ -41,7 +41,6 @@ Possible parameters are:
 
 * exchange
 * pair
-* market_url
 * limit
 * stake_amount
 * stake_amount_fiat
@@ -56,7 +55,6 @@ Possible parameters are:
 * exchange
 * pair
 * gain
-* market_url
 * limit
 * amount
 * open_rate
diff --git a/freqtrade/arguments.py b/freqtrade/arguments.py
index 9b1b9a925..62f22befc 100644
--- a/freqtrade/arguments.py
+++ b/freqtrade/arguments.py
@@ -6,9 +6,7 @@ import argparse
 import os
 import re
 from typing import List, NamedTuple, Optional
-
 import arrow
-
 from freqtrade import __version__, constants
 
 
@@ -55,6 +53,11 @@ class Arguments(object):
         """
         parsed_arg = self.parser.parse_args(self.args)
 
+        # Workaround issue in argparse with action='append' and default value
+        # (see https://bugs.python.org/issue16399)
+        if parsed_arg.config is None:
+            parsed_arg.config = [constants.DEFAULT_CONFIG]
+
         return parsed_arg
 
     def common_args_parser(self) -> None:
@@ -63,7 +66,7 @@ class Arguments(object):
         """
         self.parser.add_argument(
             '-v', '--verbose',
-            help='verbose mode (-vv for more, -vvv to get all messages)',
+            help='Verbose mode (-vv for more, -vvv to get all messages).',
             action='count',
             dest='loglevel',
             default=0,
@@ -75,15 +78,16 @@ class Arguments(object):
         )
         self.parser.add_argument(
             '-c', '--config',
-            help='specify configuration file (default: %(default)s)',
+            help='Specify configuration file (default: %(default)s). '
+                 'Multiple --config options may be used.',
             dest='config',
-            default='config.json',
+            action='append',
             type=str,
             metavar='PATH',
         )
         self.parser.add_argument(
             '-d', '--datadir',
-            help='path to backtest data',
+            help='Path to backtest data.',
             dest='datadir',
             default=None,
             type=str,
@@ -91,7 +95,7 @@ class Arguments(object):
         )
         self.parser.add_argument(
             '-s', '--strategy',
-            help='specify strategy class name (default: %(default)s)',
+            help='Specify strategy class name (default: %(default)s).',
             dest='strategy',
             default='DefaultStrategy',
             type=str,
@@ -99,14 +103,14 @@ class Arguments(object):
         )
         self.parser.add_argument(
             '--strategy-path',
-            help='specify additional strategy lookup path',
+            help='Specify additional strategy lookup path.',
             dest='strategy_path',
             type=str,
             metavar='PATH',
         )
         self.parser.add_argument(
             '--customhyperopt',
-            help='specify hyperopt class name (default: %(default)s)',
+            help='Specify hyperopt class name (default: %(default)s).',
             dest='hyperopt',
             default=constants.DEFAULT_HYPEROPT,
             type=str,
@@ -114,8 +118,8 @@ class Arguments(object):
         )
         self.parser.add_argument(
             '--dynamic-whitelist',
-            help='dynamically generate and update whitelist'
-                 ' based on 24h BaseVolume (default: %(const)s)'
+            help='Dynamically generate and update whitelist'
+                 ' based on 24h BaseVolume (default: %(const)s).'
                  ' DEPRECATED.',
             dest='dynamic_whitelist',
             const=constants.DYNAMIC_WHITELIST,
@@ -126,7 +130,7 @@ class Arguments(object):
         self.parser.add_argument(
             '--db-url',
             help='Override trades database URL, this is useful if dry_run is enabled'
-                 ' or in custom deployments (default: %(default)s)',
+                 ' or in custom deployments (default: %(default)s).',
             dest='db_url',
             type=str,
             metavar='PATH',
@@ -139,7 +143,7 @@ class Arguments(object):
         """
         parser.add_argument(
             '--eps', '--enable-position-stacking',
-            help='Allow buying the same pair multiple times (position stacking)',
+            help='Allow buying the same pair multiple times (position stacking).',
             action='store_true',
             dest='position_stacking',
             default=False
@@ -148,20 +152,20 @@ class Arguments(object):
         parser.add_argument(
             '--dmmp', '--disable-max-market-positions',
             help='Disable applying `max_open_trades` during backtest '
-                 '(same as setting `max_open_trades` to a very high number)',
+                 '(same as setting `max_open_trades` to a very high number).',
             action='store_false',
             dest='use_max_market_positions',
             default=True
         )
         parser.add_argument(
             '-l', '--live',
-            help='using live data',
+            help='Use live data.',
             action='store_true',
             dest='live',
         )
         parser.add_argument(
             '-r', '--refresh-pairs-cached',
-            help='refresh the pairs files in tests/testdata with the latest data from the '
+            help='Refresh the pairs files in tests/testdata with the latest data from the '
                  'exchange. Use it if you want to run your backtesting with up-to-date data.',
             action='store_true',
             dest='refresh_pairs',
@@ -178,8 +182,8 @@ class Arguments(object):
         )
         parser.add_argument(
             '--export',
-            help='export backtest results, argument are: trades\
-                  Example --export=trades',
+            help='Export backtest results, argument are: trades. '
+                 'Example --export=trades',
             type=str,
             default=None,
             dest='export',
@@ -203,14 +207,14 @@ class Arguments(object):
         """
         parser.add_argument(
             '-r', '--refresh-pairs-cached',
-            help='refresh the pairs files in tests/testdata with the latest data from the '
+            help='Refresh the pairs files in tests/testdata with the latest data from the '
                  'exchange. Use it if you want to run your edge with up-to-date data.',
             action='store_true',
             dest='refresh_pairs',
         )
         parser.add_argument(
             '--stoplosses',
-            help='defines a range of stoploss against which edge will assess the strategy '
+            help='Defines a range of stoploss against which edge will assess the strategy '
                  'the format is "min,max,step" (without any space).'
                  'example: --stoplosses=-0.01,-0.1,-0.001',
             type=str,
@@ -226,14 +230,14 @@ class Arguments(object):
         """
         parser.add_argument(
             '-i', '--ticker-interval',
-            help='specify ticker interval (1m, 5m, 30m, 1h, 1d)',
+            help='Specify ticker interval (1m, 5m, 30m, 1h, 1d).',
             dest='ticker_interval',
             type=str,
         )
 
         parser.add_argument(
             '--timerange',
-            help='specify what timerange of data to use.',
+            help='Specify what timerange of data to use.',
             default=None,
             type=str,
             dest='timerange',
@@ -246,7 +250,7 @@ class Arguments(object):
         """
         parser.add_argument(
             '--eps', '--enable-position-stacking',
-            help='Allow buying the same pair multiple times (position stacking)',
+            help='Allow buying the same pair multiple times (position stacking).',
             action='store_true',
             dest='position_stacking',
             default=False
@@ -255,14 +259,14 @@ class Arguments(object):
         parser.add_argument(
             '--dmmp', '--disable-max-market-positions',
             help='Disable applying `max_open_trades` during backtest '
-                 '(same as setting `max_open_trades` to a very high number)',
+                 '(same as setting `max_open_trades` to a very high number).',
             action='store_false',
             dest='use_max_market_positions',
             default=True
         )
         parser.add_argument(
             '-e', '--epochs',
-            help='specify number of epochs (default: %(default)d)',
+            help='Specify number of epochs (default: %(default)d).',
             dest='epochs',
             default=constants.HYPEROPT_EPOCH,
             type=int,
@@ -271,7 +275,7 @@ class Arguments(object):
         parser.add_argument(
             '-s', '--spaces',
             help='Specify which parameters to hyperopt. Space separate list. \
-                  Default: %(default)s',
+                  Default: %(default)s.',
             choices=['all', 'buy', 'sell', 'roi', 'stoploss'],
             default='all',
             nargs='+',
@@ -288,19 +292,19 @@ class Arguments(object):
         subparsers = self.parser.add_subparsers(dest='subparser')
 
         # Add backtesting subcommand
-        backtesting_cmd = subparsers.add_parser('backtesting', help='backtesting module')
+        backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.')
         backtesting_cmd.set_defaults(func=backtesting.start)
         self.optimizer_shared_options(backtesting_cmd)
         self.backtesting_options(backtesting_cmd)
 
         # Add edge subcommand
-        edge_cmd = subparsers.add_parser('edge', help='edge module')
+        edge_cmd = subparsers.add_parser('edge', help='Edge module.')
         edge_cmd.set_defaults(func=edge_cli.start)
         self.optimizer_shared_options(edge_cmd)
         self.edge_options(edge_cmd)
 
         # Add hyperopt subcommand
-        hyperopt_cmd = subparsers.add_parser('hyperopt', help='hyperopt module')
+        hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.')
         hyperopt_cmd.set_defaults(func=hyperopt.start)
         self.optimizer_shared_options(hyperopt_cmd)
         self.hyperopt_options(hyperopt_cmd)
@@ -364,7 +368,7 @@ class Arguments(object):
         """
         self.parser.add_argument(
             '--pairs-file',
-            help='File containing a list of pairs to download',
+            help='File containing a list of pairs to download.',
             dest='pairs_file',
             default=None,
             metavar='PATH',
@@ -372,7 +376,7 @@ class Arguments(object):
 
         self.parser.add_argument(
             '--export',
-            help='Export files to given dir',
+            help='Export files to given dir.',
             dest='export',
             default=None,
             metavar='PATH',
@@ -380,16 +384,17 @@ class Arguments(object):
 
         self.parser.add_argument(
             '-c', '--config',
-            help='specify configuration file, used for additional exchange parameters',
+            help='Specify configuration file (default: %(default)s). '
+                 'Multiple --config options may be used.',
             dest='config',
-            default=None,
+            action='append',
             type=str,
             metavar='PATH',
         )
 
         self.parser.add_argument(
             '--days',
-            help='Download data for number of days',
+            help='Download data for given number of days.',
             dest='days',
             type=int,
             metavar='INT',
@@ -398,7 +403,7 @@ class Arguments(object):
 
         self.parser.add_argument(
             '--exchange',
-            help='Exchange name (default: %(default)s). Only valid if no config is provided',
+            help='Exchange name (default: %(default)s). Only valid if no config is provided.',
             dest='exchange',
             type=str,
             default='bittrex'
@@ -407,7 +412,7 @@ class Arguments(object):
         self.parser.add_argument(
             '-t', '--timeframes',
             help='Specify which tickers to download. Space separated list. \
-                  Default: %(default)s',
+                  Default: %(default)s.',
             choices=['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h',
                      '6h', '8h', '12h', '1d', '3d', '1w'],
             default=['1m', '5m'],
@@ -417,7 +422,7 @@ class Arguments(object):
 
         self.parser.add_argument(
             '--erase',
-            help='Clean all existing data for the selected exchange/pairs/timeframes',
+            help='Clean all existing data for the selected exchange/pairs/timeframes.',
             dest='erase',
             action='store_true'
         )
diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py
index d972f50b8..bddf60028 100644
--- a/freqtrade/configuration.py
+++ b/freqtrade/configuration.py
@@ -13,6 +13,8 @@ from jsonschema.exceptions import ValidationError, best_match
 
 from freqtrade import OperationalException, constants
 from freqtrade.state import RunMode
+from freqtrade.misc import deep_merge_dicts
+
 logger = logging.getLogger(__name__)
 
 
@@ -45,8 +47,18 @@ class Configuration(object):
         Extract information for sys.argv and load the bot configuration
         :return: Configuration dictionary
         """
-        logger.info('Using config: %s ...', self.args.config)
-        config = self._load_config_file(self.args.config)
+        config: Dict[str, Any] = {}
+        # Now expecting a list of config filenames here, not a string
+        for path in self.args.config:
+            logger.info('Using config: %s ...', path)
+            # Merge config options, overwriting old values
+            config = deep_merge_dicts(self._load_config_file(path), config)
+
+        if 'internals' not in config:
+            config['internals'] = {}
+
+        logger.info('Validating configuration ...')
+        self._validate_config(config)
 
         # Set strategy if not specified in config and or if it's non default
         if self.args.strategy != constants.DEFAULT_STRATEGY or not config.get('strategy'):
@@ -93,11 +105,7 @@ class Configuration(object):
                 f'Config file "{path}" not found!'
                 ' Please create a config file or check whether it exists.')
 
-        if 'internals' not in conf:
-            conf['internals'] = {}
-        logger.info('Validating configuration ...')
-
-        return self._validate_config(conf)
+        return conf
 
     def _load_common_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
         """
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
index 1b3e25249..d0fafbd26 100644
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -3,6 +3,7 @@
 """
 bot constants
 """
+DEFAULT_CONFIG = 'config.json'
 DYNAMIC_WHITELIST = 20  # pairs
 PROCESS_THROTTLE_SECS = 5  # sec
 TICKER_INTERVAL = 5  # min
diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py
index baef811de..b4dfa5624 100644
--- a/freqtrade/edge/__init__.py
+++ b/freqtrade/edge/__init__.py
@@ -351,91 +351,93 @@ class Edge():
         return result
 
     def _detect_next_stop_or_sell_point(self, buy_column, sell_column, date_column,
-                                        ohlc_columns, stoploss, pair, start_point=0):
+                                        ohlc_columns, stoploss, pair):
         """
-        Iterate through ohlc_columns recursively in order to find the next trade
+        Iterate through ohlc_columns in order to find the next trade
         Next trade opens from the first buy signal noticed to
         The sell or stoploss signal after it.
-        It then calls itself cutting OHLC, buy_column, sell_colum and date_column
-        Cut from (the exit trade index) + 1
+        It then cuts OHLC, buy_column, sell_column and date_column.
+        Cut from (the exit trade index) + 1.
+
         Author: https://github.com/mishaker
         """
 
         result: list = []
-        open_trade_index = utf1st.find_1st(buy_column, 1, utf1st.cmp_equal)
+        start_point = 0
 
-        # return empty if we don't find trade entry (i.e. buy==1) or
-        # we find a buy but at the of array
-        if open_trade_index == -1 or open_trade_index == len(buy_column) - 1:
-            return []
-        else:
-            open_trade_index += 1  # when a buy signal is seen,
-            # trade opens in reality on the next candle
+        while True:
+            open_trade_index = utf1st.find_1st(buy_column, 1, utf1st.cmp_equal)
 
-        stop_price_percentage = stoploss + 1
-        open_price = ohlc_columns[open_trade_index, 0]
-        stop_price = (open_price * stop_price_percentage)
+            # Return empty if we don't find trade entry (i.e. buy==1) or
+            # we find a buy but at the end of array
+            if open_trade_index == -1 or open_trade_index == len(buy_column) - 1:
+                break
+            else:
+                # When a buy signal is seen,
+                # trade opens in reality on the next candle
+                open_trade_index += 1
 
-        # Searching for the index where stoploss is hit
-        stop_index = utf1st.find_1st(
-            ohlc_columns[open_trade_index:, 2], stop_price, utf1st.cmp_smaller)
+            stop_price_percentage = stoploss + 1
+            open_price = ohlc_columns[open_trade_index, 0]
+            stop_price = (open_price * stop_price_percentage)
 
-        # If we don't find it then we assume stop_index will be far in future (infinite number)
-        if stop_index == -1:
-            stop_index = float('inf')
+            # Searching for the index where stoploss is hit
+            stop_index = utf1st.find_1st(
+                ohlc_columns[open_trade_index:, 2], stop_price, utf1st.cmp_smaller)
 
-        # Searching for the index where sell is hit
-        sell_index = utf1st.find_1st(sell_column[open_trade_index:], 1, utf1st.cmp_equal)
+            # If we don't find it then we assume stop_index will be far in future (infinite number)
+            if stop_index == -1:
+                stop_index = float('inf')
 
-        # If we don't find it then we assume sell_index will be far in future (infinite number)
-        if sell_index == -1:
-            sell_index = float('inf')
+            # Searching for the index where sell is hit
+            sell_index = utf1st.find_1st(sell_column[open_trade_index:], 1, utf1st.cmp_equal)
 
-        # Check if we don't find any stop or sell point (in that case trade remains open)
-        # It is not interesting for Edge to consider it so we simply ignore the trade
-        # And stop iterating there is no more entry
-        if stop_index == sell_index == float('inf'):
-            return []
+            # If we don't find it then we assume sell_index will be far in future (infinite number)
+            if sell_index == -1:
+                sell_index = float('inf')
 
-        if stop_index <= sell_index:
-            exit_index = open_trade_index + stop_index
-            exit_type = SellType.STOP_LOSS
-            exit_price = stop_price
-        elif stop_index > sell_index:
-            # if exit is SELL then we exit at the next candle
-            exit_index = open_trade_index + sell_index + 1
+            # Check if we don't find any stop or sell point (in that case trade remains open)
+            # It is not interesting for Edge to consider it so we simply ignore the trade
+            # And stop iterating there is no more entry
+            if stop_index == sell_index == float('inf'):
+                break
 
-            # check if we have the next candle
-            if len(ohlc_columns) - 1 < exit_index:
-                return []
+            if stop_index <= sell_index:
+                exit_index = open_trade_index + stop_index
+                exit_type = SellType.STOP_LOSS
+                exit_price = stop_price
+            elif stop_index > sell_index:
+                # If exit is SELL then we exit at the next candle
+                exit_index = open_trade_index + sell_index + 1
 
-            exit_type = SellType.SELL_SIGNAL
-            exit_price = ohlc_columns[exit_index, 0]
+                # Check if we have the next candle
+                if len(ohlc_columns) - 1 < exit_index:
+                    break
 
-        trade = {'pair': pair,
-                 'stoploss': stoploss,
-                 'profit_percent': '',
-                 'profit_abs': '',
-                 'open_time': date_column[open_trade_index],
-                 'close_time': date_column[exit_index],
-                 'open_index': start_point + open_trade_index,
-                 'close_index': start_point + exit_index,
-                 'trade_duration': '',
-                 'open_rate': round(open_price, 15),
-                 'close_rate': round(exit_price, 15),
-                 'exit_type': exit_type
-                 }
+                exit_type = SellType.SELL_SIGNAL
+                exit_price = ohlc_columns[exit_index, 0]
 
-        result.append(trade)
+            trade = {'pair': pair,
+                     'stoploss': stoploss,
+                     'profit_percent': '',
+                     'profit_abs': '',
+                     'open_time': date_column[open_trade_index],
+                     'close_time': date_column[exit_index],
+                     'open_index': start_point + open_trade_index,
+                     'close_index': start_point + exit_index,
+                     'trade_duration': '',
+                     'open_rate': round(open_price, 15),
+                     'close_rate': round(exit_price, 15),
+                     'exit_type': exit_type
+                     }
 
-        # Calling again the same function recursively but giving
-        # it a view of exit_index till the end of array
-        return result + self._detect_next_stop_or_sell_point(
-            buy_column[exit_index:],
-            sell_column[exit_index:],
-            date_column[exit_index:],
-            ohlc_columns[exit_index:],
-            stoploss,
-            pair,
-            (start_point + exit_index)
-        )
+            result.append(trade)
+
+            # Giving a view of exit_index till the end of array
+            buy_column = buy_column[exit_index:]
+            sell_column = sell_column[exit_index:]
+            date_column = date_column[exit_index:]
+            ohlc_columns = ohlc_columns[exit_index:]
+            start_point += exit_index
+
+        return result
diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py
index 204ed971e..f6db04da6 100644
--- a/freqtrade/exchange/__init__.py
+++ b/freqtrade/exchange/__init__.py
@@ -1,2 +1,3 @@
 from freqtrade.exchange.exchange import Exchange  # noqa: F401
 from freqtrade.exchange.kraken import Kraken  # noqa: F401
+from freqtrade.exchange.binance import Binance  # noqa: F401
diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py
new file mode 100644
index 000000000..127f4e916
--- /dev/null
+++ b/freqtrade/exchange/binance.py
@@ -0,0 +1,26 @@
+""" Binance exchange subclass """
+import logging
+from typing import Dict
+
+from freqtrade.exchange import Exchange
+
+logger = logging.getLogger(__name__)
+
+
+class Binance(Exchange):
+
+    _ft_has: Dict = {
+        "stoploss_on_exchange": True,
+    }
+
+    def get_order_book(self, pair: str, limit: int = 100) -> dict:
+        """
+        get order book level 2 from exchange
+
+        20180619: binance support limits but only on specific range
+        """
+        limit_range = [5, 10, 20, 50, 100, 500, 1000]
+        # get next-higher step in the limit_range list
+        limit = min(list(filter(lambda x: limit <= x, limit_range)))
+
+        return super().get_order_book(pair, limit)
diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
index 0ca939ec5..f5503002b 100644
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -24,7 +24,7 @@ API_RETRY_COUNT = 4
 # Urls to exchange markets, insert quote and base with .format()
 _EXCHANGE_URLS = {
     ccxt.bittrex.__name__: '/Market/Index?MarketName={quote}-{base}',
-    ccxt.binance.__name__: '/tradeDetail.html?symbol={base}_{quote}'
+    ccxt.binance.__name__: '/tradeDetail.html?symbol={base}_{quote}',
 }
 
 
@@ -69,11 +69,17 @@ class Exchange(object):
     _conf: Dict = {}
     _params: Dict = {}
 
+    # Dict to specify which options each exchange implements
+    # TODO: this should be merged with attributes from subclasses
+    # To avoid having to copy/paste this to all subclasses.
+    _ft_has = {
+        "stoploss_on_exchange": False,
+    }
+
     def __init__(self, config: dict) -> None:
         """
         Initializes this module with the given config,
-        it does basic validation whether the specified
-        exchange and pairs are valid.
+        it does basic validation whether the specified exchange and pairs are valid.
         :return: None
         """
         self._conf.update(config)
@@ -236,11 +242,11 @@ class Exchange(object):
                 raise OperationalException(
                     f'Exchange {self.name} does not support market orders.')
 
-        if order_types.get('stoploss_on_exchange'):
-            if self.name != 'Binance':
-                raise OperationalException(
-                    'On exchange stoploss is not supported for %s.' % self.name
-                )
+        if (order_types.get("stoploss_on_exchange")
+                and not self._ft_has.get("stoploss_on_exchange", False)):
+            raise OperationalException(
+                'On exchange stoploss is not supported for %s.' % self.name
+            )
 
     def validate_order_time_in_force(self, order_time_in_force: Dict) -> None:
         """
@@ -282,104 +288,96 @@ class Exchange(object):
             price = ceil(big_price) / pow(10, symbol_prec)
         return price
 
-    def buy(self, pair: str, ordertype: str, amount: float,
-            rate: float, time_in_force) -> Dict:
-        if self._conf['dry_run']:
-            order_id = f'dry_run_buy_{randint(0, 10**6)}'
-            self._dry_run_open_orders[order_id] = {
-                'pair': pair,
-                'price': rate,
-                'amount': amount,
-                'type': ordertype,
-                'side': 'buy',
-                'remaining': 0.0,
-                'datetime': arrow.utcnow().isoformat(),
-                'status': 'closed',
-                'fee': None
-            }
-            return {'id': order_id}
+    def dry_run_order(self, pair: str, ordertype: str, side: str, amount: float,
+                      rate: float, params: Dict = {}) -> Dict[str, Any]:
+        order_id = f'dry_run_{side}_{randint(0, 10**6)}'
+        dry_order = {  # TODO: additional entry should be added for stoploss limit
+            "id": order_id,
+            'pair': pair,
+            'price': rate,
+            'amount': amount,
+            "cost": amount * rate,
+            'type': ordertype,
+            'side': 'buy',
+            'remaining': amount,
+            'datetime': arrow.utcnow().isoformat(),
+            'status': "open",
+            'fee': None,
+            "info": {}
+        }
+        self._store_dry_order(dry_order)
+        return dry_order
 
+    def _store_dry_order(self, dry_order: Dict) -> None:
+        closed_order = dry_order.copy()
+        if closed_order["type"] in ["market", "limit"]:
+            closed_order.update({
+                "status": "closed",
+                "filled": closed_order["amount"],
+                "remaining": 0
+                })
+        self._dry_run_open_orders[closed_order["id"]] = closed_order
+
+    def create_order(self, pair: str, ordertype: str, side: str, amount: float,
+                     rate: float, params: Dict = {}) -> Dict:
         try:
             # Set the precision for amount and price(rate) as accepted by the exchange
             amount = self.symbol_amount_prec(pair, amount)
             rate = self.symbol_price_prec(pair, rate) if ordertype != 'market' else None
 
-            params = self._params.copy()
-            if time_in_force != 'gtc':
-                params.update({'timeInForce': time_in_force})
-
-            return self._api.create_order(pair, ordertype, 'buy',
+            return self._api.create_order(pair, ordertype, side,
                                           amount, rate, params)
 
         except ccxt.InsufficientFunds as e:
             raise DependencyException(
-                f'Insufficient funds to create limit buy order on market {pair}.'
-                f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
+                f'Insufficient funds to create {ordertype} {side} order on market {pair}.'
+                f'Tried to {side} amount {amount} at rate {rate} (total {rate*amount}).'
                 f'Message: {e}')
         except ccxt.InvalidOrder as e:
             raise DependencyException(
-                f'Could not create limit buy order on market {pair}.'
-                f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
+                f'Could not create {ordertype} {side} order on market {pair}.'
+                f'Tried to {side} amount {amount} at rate {rate} (total {rate*amount}).'
                 f'Message: {e}')
         except (ccxt.NetworkError, ccxt.ExchangeError) as e:
             raise TemporaryError(
-                f'Could not place buy order due to {e.__class__.__name__}. Message: {e}')
+                f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}')
         except ccxt.BaseError as e:
             raise OperationalException(e)
 
+    def buy(self, pair: str, ordertype: str, amount: float,
+            rate: float, time_in_force) -> Dict:
+
+        if self._conf['dry_run']:
+            dry_order = self.dry_run_order(pair, ordertype, "buy", amount, rate)
+            return dry_order
+
+        params = self._params.copy()
+        if time_in_force != 'gtc':
+            params.update({'timeInForce': time_in_force})
+
+        return self.create_order(pair, ordertype, 'buy', amount, rate, params)
+
     def sell(self, pair: str, ordertype: str, amount: float,
              rate: float, time_in_force='gtc') -> Dict:
+
         if self._conf['dry_run']:
-            order_id = f'dry_run_sell_{randint(0, 10**6)}'
-            self._dry_run_open_orders[order_id] = {
-                'pair': pair,
-                'price': rate,
-                'amount': amount,
-                'type': ordertype,
-                'side': 'sell',
-                'remaining': 0.0,
-                'datetime': arrow.utcnow().isoformat(),
-                'status': 'closed'
-            }
-            return {'id': order_id}
+            dry_order = self.dry_run_order(pair, ordertype, "sell", amount, rate)
+            return dry_order
 
-        try:
-            # Set the precision for amount and price(rate) as accepted by the exchange
-            amount = self.symbol_amount_prec(pair, amount)
-            rate = self.symbol_price_prec(pair, rate) if ordertype != 'market' else None
+        params = self._params.copy()
+        if time_in_force != 'gtc':
+            params.update({'timeInForce': time_in_force})
 
-            params = self._params.copy()
-            if time_in_force != 'gtc':
-                params.update({'timeInForce': time_in_force})
-
-            return self._api.create_order(pair, ordertype, 'sell',
-                                          amount, rate, params)
-
-        except ccxt.InsufficientFunds as e:
-            raise DependencyException(
-                f'Insufficient funds to create limit sell order on market {pair}.'
-                f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
-                f'Message: {e}')
-        except ccxt.InvalidOrder as e:
-            raise DependencyException(
-                f'Could not create limit sell order on market {pair}.'
-                f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
-                f'Message: {e}')
-        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
-            raise TemporaryError(
-                f'Could not place sell order due to {e.__class__.__name__}. Message: {e}')
-        except ccxt.BaseError as e:
-            raise OperationalException(e)
+        return self.create_order(pair, ordertype, 'sell', amount, rate, params)
 
     def stoploss_limit(self, pair: str, amount: float, stop_price: float, rate: float) -> Dict:
         """
         creates a stoploss limit order.
         NOTICE: it is not supported by all exchanges. only binance is tested for now.
+        TODO: implementation maybe needs to be moved to the binance subclass
         """
+        ordertype = "stop_loss_limit"
 
-        # Set the precision for amount and price(rate) as accepted by the exchange
-        amount = self.symbol_amount_prec(pair, amount)
-        rate = self.symbol_price_prec(pair, rate)
         stop_price = self.symbol_price_prec(pair, stop_price)
 
         # Ensure rate is less than stop price
@@ -388,50 +386,17 @@ class Exchange(object):
                 'In stoploss limit order, stop price should be more than limit price')
 
         if self._conf['dry_run']:
-            order_id = f'dry_run_buy_{randint(0, 10**6)}'
-            self._dry_run_open_orders[order_id] = {
-                'info': {},
-                'id': order_id,
-                'pair': pair,
-                'price': stop_price,
-                'amount': amount,
-                'type': 'stop_loss_limit',
-                'side': 'sell',
-                'remaining': amount,
-                'datetime': arrow.utcnow().isoformat(),
-                'status': 'open',
-                'fee': None
-            }
-            return self._dry_run_open_orders[order_id]
+            dry_order = self.dry_run_order(
+                pair, ordertype, "sell", amount, stop_price)
+            return dry_order
 
-        try:
+        params = self._params.copy()
+        params.update({'stopPrice': stop_price})
 
-            params = self._params.copy()
-            params.update({'stopPrice': stop_price})
-
-            order = self._api.create_order(pair, 'stop_loss_limit', 'sell',
-                                           amount, rate, params)
-            logger.info('stoploss limit order added for %s. '
-                        'stop price: %s. limit: %s' % (pair, stop_price, rate))
-            return order
-
-        except ccxt.InsufficientFunds as e:
-            raise DependencyException(
-                f'Insufficient funds to place stoploss limit order on market {pair}. '
-                f'Tried to put a stoploss amount {amount} with '
-                f'stop {stop_price} and limit {rate} (total {rate*amount}).'
-                f'Message: {e}')
-        except ccxt.InvalidOrder as e:
-            raise DependencyException(
-                f'Could not place stoploss limit order on market {pair}.'
-                f'Tried to place stoploss amount {amount} with '
-                f'stop {stop_price} and limit {rate} (total {rate*amount}).'
-                f'Message: {e}')
-        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
-            raise TemporaryError(
-                f'Could not place stoploss limit order due to {e.__class__.__name__}. Message: {e}')
-        except ccxt.BaseError as e:
-            raise OperationalException(e)
+        order = self.create_order(pair, ordertype, 'sell', amount, rate, params)
+        logger.info('stoploss limit order added for %s. '
+                    'stop price: %s. limit: %s' % (pair, stop_price, rate))
+        return order
 
     @retrier
     def get_balance(self, currency: str) -> float:
@@ -578,7 +543,7 @@ class Exchange(object):
         interval_in_sec = constants.TICKER_INTERVAL_MINUTES[ticker_interval] * 60
 
         return not ((self._pairs_last_refresh_time.get((pair, ticker_interval), 0)
-                    + interval_in_sec) >= arrow.utcnow().timestamp)
+                     + interval_in_sec) >= arrow.utcnow().timestamp)
 
     @retrier_async
     async def _async_get_candle_history(self, pair: str, tick_interval: str,
@@ -637,9 +602,6 @@ class Exchange(object):
     def get_order(self, order_id: str, pair: str) -> Dict:
         if self._conf['dry_run']:
             order = self._dry_run_open_orders[order_id]
-            order.update({
-                'id': order_id
-            })
             return order
         try:
             return self._api.fetch_order(order_id, pair)
@@ -659,18 +621,8 @@ class Exchange(object):
 
         Notes:
         20180619: bittrex doesnt support limits -.-
-        20180619: binance support limits but only on specific range
         """
         try:
-            if self._api.name == 'Binance':
-                limit_range = [5, 10, 20, 50, 100, 500, 1000]
-                # get next-higher step in the limit_range list
-                limit = min(list(filter(lambda x: limit <= x, limit_range)))
-                # above script works like loop below (but with slightly better performance):
-                #   for limitx in limit_range:
-                #        if limit <= limitx:
-                #           limit = limitx
-                #           break
 
             return self._api.fetch_l2_order_book(pair, limit)
         except ccxt.NotSupported as e:
@@ -702,16 +654,6 @@ class Exchange(object):
         except ccxt.BaseError as e:
             raise OperationalException(e)
 
-    def get_pair_detail_url(self, pair: str) -> str:
-        try:
-            url_base = self._api.urls.get('www')
-            base, quote = pair.split('/')
-
-            return url_base + _EXCHANGE_URLS[self._api.id].format(base=base, quote=quote)
-        except KeyError:
-            logger.warning('Could not get exchange url for %s', self.name)
-            return ""
-
     @retrier
     def get_markets(self) -> List[dict]:
         try:
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index 28a7c9146..dce3136df 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -7,7 +7,7 @@ import logging
 import time
 import traceback
 from datetime import datetime
-from typing import Any, Callable, Dict, List, Optional
+from typing import Any, Callable, Dict, List, Optional, Tuple
 
 import arrow
 from requests.exceptions import RequestException
@@ -159,24 +159,20 @@ class FreqtradeBot(object):
             self.pairlists.refresh_pairlist()
             self.active_pair_whitelist = self.pairlists.whitelist
 
-            # Calculating Edge positiong
+            # Calculating Edge positioning
             if self.edge:
                 self.edge.calculate()
                 self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist)
 
             # Query trades from persistence layer
-            trades = Trade.query.filter(Trade.is_open.is_(True)).all()
+            trades = Trade.get_open_trades()
 
             # Extend active-pair whitelist with pairs from open trades
-            # ensures that tickers are downloaded for open trades
-            self.active_pair_whitelist.extend([trade.pair for trade in trades
-                                               if trade.pair not in self.active_pair_whitelist])
+            # It ensures that tickers are downloaded for open trades
+            self._extend_whitelist_with_trades(self.active_pair_whitelist, trades)
 
-            # Create pair-whitelist tuple with (pair, ticker_interval)
-            pair_whitelist_tuple = [(pair, self.config['ticker_interval'])
-                                    for pair in self.active_pair_whitelist]
             # Refreshing candles
-            self.dataprovider.refresh(pair_whitelist_tuple,
+            self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
                                       self.strategy.informative_pairs())
 
             # First process current opened trades
@@ -206,6 +202,18 @@ class FreqtradeBot(object):
             self.state = State.STOPPED
         return state_changed
 
+    def _extend_whitelist_with_trades(self, whitelist: List[str], trades: List[Any]):
+        """
+        Extend whitelist with pairs from open trades
+        """
+        whitelist.extend([trade.pair for trade in trades if trade.pair not in whitelist])
+
+    def _create_pair_whitelist(self, pairs: List[str]) -> List[Tuple[str, str]]:
+        """
+        Create pair-whitelist tuple with (pair, ticker_interval)
+        """
+        return [(pair, self.config['ticker_interval']) for pair in pairs]
+
     def get_target_bid(self, pair: str, tick: Dict = None) -> float:
         """
         Calculates bid target between current ask price and last price
@@ -256,7 +264,7 @@ class FreqtradeBot(object):
         avaliable_amount = self.wallets.get_free(self.config['stake_currency'])
 
         if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
-            open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())
+            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
@@ -314,7 +322,7 @@ class FreqtradeBot(object):
         whitelist = copy.deepcopy(self.active_pair_whitelist)
 
         # Remove currently opened and latest pairs from whitelist
-        for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
+        for trade in Trade.get_open_trades():
             if trade.pair in whitelist:
                 whitelist.remove(trade.pair)
                 logger.debug('Ignoring %s in pair whitelist', trade.pair)
@@ -371,7 +379,6 @@ class FreqtradeBot(object):
         :return: None
         """
         pair_s = pair.replace('_', '/')
-        pair_url = self.exchange.get_pair_detail_url(pair)
         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']
@@ -436,7 +443,6 @@ class FreqtradeBot(object):
             'type': RPCMessageType.BUY_NOTIFICATION,
             'exchange': self.exchange.name.capitalize(),
             'pair': pair_s,
-            'market_url': pair_url,
             'limit': buy_limit_filled_price,
             'stake_amount': stake_amount,
             'stake_currency': stake_currency,
@@ -844,7 +850,6 @@ class FreqtradeBot(object):
         profit_trade = trade.calc_profit(rate=limit)
         current_rate = self.exchange.get_ticker(trade.pair)['bid']
         profit_percent = trade.calc_profit_percent(limit)
-        pair_url = self.exchange.get_pair_detail_url(trade.pair)
         gain = "profit" if profit_percent > 0 else "loss"
 
         msg = {
@@ -852,7 +857,6 @@ class FreqtradeBot(object):
             'exchange': trade.exchange.capitalize(),
             'pair': trade.pair,
             'gain': gain,
-            'market_url': pair_url,
             'limit': limit,
             'amount': trade.amount,
             'open_rate': trade.open_rate,
diff --git a/freqtrade/misc.py b/freqtrade/misc.py
index d03187d77..38f758669 100644
--- a/freqtrade/misc.py
+++ b/freqtrade/misc.py
@@ -113,3 +113,21 @@ def format_ms_time(date: int) -> str:
     : epoch-string in ms
     """
     return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S')
+
+
+def deep_merge_dicts(source, destination):
+    """
+    >>> a = { 'first' : { 'rows' : { 'pass' : 'dog', 'number' : '1' } } }
+    >>> b = { 'first' : { 'rows' : { 'fail' : 'cat', 'number' : '5' } } }
+    >>> merge(b, a) == { 'first' : { 'rows' : { 'pass' : 'dog', 'fail' : 'cat', 'number' : '5' } } }
+    True
+    """
+    for key, value in source.items():
+        if isinstance(value, dict):
+            # get node or create one
+            node = destination.setdefault(key, {})
+            deep_merge_dicts(value, node)
+        else:
+            destination[key] = value
+
+    return destination
diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py
index f9b34fc64..f603b139f 100644
--- a/freqtrade/persistence.py
+++ b/freqtrade/persistence.py
@@ -5,7 +5,7 @@ This module contains the class to persist trades into SQLite
 import logging
 from datetime import datetime
 from decimal import Decimal
-from typing import Any, Dict, Optional
+from typing import Any, Dict, List, Optional
 
 import arrow
 from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String,
@@ -371,3 +371,10 @@ class Trade(_DECL_BASE):
             .filter(Trade.is_open.is_(True))\
             .scalar()
         return total_open_stake_amount or 0
+
+    @staticmethod
+    def get_open_trades() -> List[Any]:
+        """
+        Query trades from persistence layer
+        """
+        return Trade.query.filter(Trade.is_open.is_(True)).all()
diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py
index e83d9d41b..af64c3d67 100644
--- a/freqtrade/rpc/rpc.py
+++ b/freqtrade/rpc/rpc.py
@@ -83,7 +83,7 @@ class RPC(object):
         a remotely exposed function
         """
         # Fetch open trade
-        trades = Trade.query.filter(Trade.is_open.is_(True)).all()
+        trades = Trade.get_open_trades()
         if not trades:
             raise RPCException('no active trade')
         else:
@@ -103,7 +103,6 @@ class RPC(object):
                 results.append(dict(
                     trade_id=trade.id,
                     pair=trade.pair,
-                    market_url=self._freqtrade.exchange.get_pair_detail_url(trade.pair),
                     date=arrow.get(trade.open_date),
                     open_rate=trade.open_rate,
                     close_rate=trade.close_rate,
@@ -118,7 +117,7 @@ class RPC(object):
             return results
 
     def _rpc_status_table(self) -> DataFrame:
-        trades = Trade.query.filter(Trade.is_open.is_(True)).all()
+        trades = Trade.get_open_trades()
         if not trades:
             raise RPCException('no active order')
         else:
@@ -366,7 +365,7 @@ class RPC(object):
 
         if trade_id == 'all':
             # Execute sell for all open orders
-            for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
+            for trade in Trade.get_open_trades():
                 _exec_forcesell(trade)
             Trade.session.flush()
             return
@@ -442,7 +441,7 @@ class RPC(object):
         if self._freqtrade.state != State.RUNNING:
             raise RPCException('trader is not running')
 
-        return Trade.query.filter(Trade.is_open.is_(True)).all()
+        return Trade.get_open_trades()
 
     def _rpc_whitelist(self) -> Dict:
         """ Returns the currently active whitelist"""
diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py
index 3ce7c9167..9caa7288f 100644
--- a/freqtrade/rpc/telegram.py
+++ b/freqtrade/rpc/telegram.py
@@ -125,7 +125,7 @@ class Telegram(RPC):
             else:
                 msg['stake_amount_fiat'] = 0
 
-            message = ("*{exchange}:* Buying [{pair}]({market_url})\n"
+            message = ("*{exchange}:* Buying {pair}\n"
                        "with limit `{limit:.8f}\n"
                        "({stake_amount:.6f} {stake_currency}").format(**msg)
 
@@ -137,7 +137,7 @@ class Telegram(RPC):
             msg['amount'] = round(msg['amount'], 8)
             msg['profit_percent'] = round(msg['profit_percent'] * 100, 2)
 
-            message = ("*{exchange}:* Selling [{pair}]({market_url})\n"
+            message = ("*{exchange}:* Selling {pair}\n"
                        "*Limit:* `{limit:.8f}`\n"
                        "*Amount:* `{amount:.8f}`\n"
                        "*Open Rate:* `{open_rate:.8f}`\n"
@@ -193,7 +193,7 @@ class Telegram(RPC):
 
             messages = [
                 "*Trade ID:* `{trade_id}`\n"
-                "*Current Pair:* [{pair}]({market_url})\n"
+                "*Current Pair:* {pair}\n"
                 "*Open Since:* `{date}`\n"
                 "*Amount:* `{amount}`\n"
                 "*Open Rate:* `{open_rate:.8f}`\n"
diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py
index 4fadfd4b7..3b8d3ad6f 100644
--- a/freqtrade/tests/exchange/test_exchange.py
+++ b/freqtrade/tests/exchange/test_exchange.py
@@ -12,12 +12,16 @@ import pytest
 from pandas import DataFrame
 
 from freqtrade import DependencyException, OperationalException, TemporaryError
-from freqtrade.exchange import Exchange, Kraken
+from freqtrade.exchange import Exchange, Kraken, Binance
 from freqtrade.exchange.exchange import API_RETRY_COUNT
 from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
 from freqtrade.resolvers.exchange_resolver import ExchangeResolver
 
 
+# Make sure to always keep one exchange here which is NOT subclassed!!
+EXCHANGES = ['bittrex', 'binance', 'kraken', ]
+
+
 # Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines
 def get_mock_coro(return_value):
     async def mock_coro(*args, **kwargs):
@@ -26,16 +30,17 @@ def get_mock_coro(return_value):
     return Mock(wraps=mock_coro)
 
 
-def ccxt_exceptionhandlers(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs):
+def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
+                           fun, mock_ccxt_fun, **kwargs):
     with pytest.raises(TemporaryError):
         api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         getattr(exchange, fun)(**kwargs)
     assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
 
     with pytest.raises(OperationalException):
         api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         getattr(exchange, fun)(**kwargs)
     assert api_mock.__dict__[mock_ccxt_fun].call_count == 1
 
@@ -113,7 +118,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
     mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
     mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
     mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
-    exchange = ExchangeResolver('Binance', default_conf).exchange
+    exchange = ExchangeResolver('Bittrex', default_conf).exchange
     assert isinstance(exchange, Exchange)
     assert log_has_re(r"No .* specific subclass found. Using the generic class instead.",
                       caplog.record_tuples)
@@ -122,6 +127,15 @@ def test_exchange_resolver(default_conf, mocker, caplog):
     exchange = ExchangeResolver('Kraken', default_conf).exchange
     assert isinstance(exchange, Exchange)
     assert isinstance(exchange, Kraken)
+    assert not isinstance(exchange, Binance)
+    assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
+                          caplog.record_tuples)
+
+    exchange = ExchangeResolver('Binance', default_conf).exchange
+    assert isinstance(exchange, Exchange)
+    assert isinstance(exchange, Binance)
+    assert not isinstance(exchange, Kraken)
+
     assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
                           caplog.record_tuples)
 
@@ -443,6 +457,58 @@ def test_exchange_has(default_conf, mocker):
     assert not exchange.exchange_has("deadbeef")
 
 
+@pytest.mark.parametrize("side", [
+    ("buy"),
+    ("sell")
+])
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_dry_run_order(default_conf, mocker, side, exchange_name):
+    default_conf['dry_run'] = True
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
+
+    order = exchange.dry_run_order(
+        pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
+    assert 'id' in order
+    assert f'dry_run_{side}_' in order["id"]
+
+
+@pytest.mark.parametrize("side", [
+    ("buy"),
+    ("sell")
+])
+@pytest.mark.parametrize("ordertype,rate", [
+    ("market", None),
+    ("limit", 200),
+    ("stop_loss_limit", 200)
+])
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_create_order(default_conf, mocker, side, ordertype, rate, exchange_name):
+    api_mock = MagicMock()
+    order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6))
+    api_mock.create_order = MagicMock(return_value={
+        'id': order_id,
+        'info': {
+            'foo': 'bar'
+        }
+    })
+    default_conf['dry_run'] = False
+    mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
+    mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
+
+    order = exchange.create_order(
+        pair='ETH/BTC', ordertype=ordertype, side=side, amount=1, rate=200)
+
+    assert 'id' in order
+    assert 'info' in order
+    assert order['id'] == order_id
+    assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
+    assert api_mock.create_order.call_args[0][1] == ordertype
+    assert api_mock.create_order.call_args[0][2] == side
+    assert api_mock.create_order.call_args[0][3] == 1
+    assert api_mock.create_order.call_args[0][4] is rate
+
+
 def test_buy_dry_run(default_conf, mocker):
     default_conf['dry_run'] = True
     exchange = get_patched_exchange(mocker, default_conf)
@@ -453,7 +519,8 @@ def test_buy_dry_run(default_conf, mocker):
     assert 'dry_run_buy_' in order['id']
 
 
-def test_buy_prod(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_buy_prod(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
     order_type = 'market'
@@ -467,7 +534,7 @@ def test_buy_prod(default_conf, mocker):
     default_conf['dry_run'] = False
     mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
     mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
     order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
                          amount=1, rate=200, time_in_force=time_in_force)
@@ -498,25 +565,25 @@ def test_buy_prod(default_conf, mocker):
     # test exception handling
     with pytest.raises(DependencyException):
         api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.buy(pair='ETH/BTC', ordertype=order_type,
                      amount=1, rate=200, time_in_force=time_in_force)
 
     with pytest.raises(DependencyException):
         api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.buy(pair='ETH/BTC', ordertype=order_type,
                      amount=1, rate=200, time_in_force=time_in_force)
 
     with pytest.raises(TemporaryError):
         api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.buy(pair='ETH/BTC', ordertype=order_type,
                      amount=1, rate=200, time_in_force=time_in_force)
 
     with pytest.raises(OperationalException):
         api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.buy(pair='ETH/BTC', ordertype=order_type,
                      amount=1, rate=200, time_in_force=time_in_force)
 
@@ -621,7 +688,8 @@ def test_sell_dry_run(default_conf, mocker):
     assert 'dry_run_sell_' in order['id']
 
 
-def test_sell_prod(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_sell_prod(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
     order_type = 'market'
@@ -635,7 +703,7 @@ def test_sell_prod(default_conf, mocker):
 
     mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
     mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
     order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
 
@@ -660,22 +728,22 @@ def test_sell_prod(default_conf, mocker):
     # test exception handling
     with pytest.raises(DependencyException):
         api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
 
     with pytest.raises(DependencyException):
         api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
 
     with pytest.raises(TemporaryError):
         api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
 
     with pytest.raises(OperationalException):
         api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
 
 
@@ -686,23 +754,24 @@ def test_get_balance_dry_run(default_conf, mocker):
     assert exchange.get_balance(currency='BTC') == 999.9
 
 
-def test_get_balance_prod(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_balance_prod(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
     default_conf['dry_run'] = False
 
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
     assert exchange.get_balance(currency='BTC') == 123.4
 
     with pytest.raises(OperationalException):
         api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
         exchange.get_balance(currency='BTC')
 
     with pytest.raises(TemporaryError, match=r'.*balance due to malformed exchange response:.*'):
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         mocker.patch('freqtrade.exchange.Exchange.get_balances', MagicMock(return_value={}))
         exchange.get_balance(currency='BTC')
 
@@ -713,7 +782,8 @@ def test_get_balances_dry_run(default_conf, mocker):
     assert exchange.get_balances() == {}
 
 
-def test_get_balances_prod(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_balances_prod(default_conf, mocker, exchange_name):
     balance_item = {
         'free': 10.0,
         'total': 10.0,
@@ -727,17 +797,18 @@ def test_get_balances_prod(default_conf, mocker):
         '3ST': balance_item
     })
     default_conf['dry_run'] = False
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     assert len(exchange.get_balances()) == 3
     assert exchange.get_balances()['1ST']['free'] == 10.0
     assert exchange.get_balances()['1ST']['total'] == 10.0
     assert exchange.get_balances()['1ST']['used'] == 0.0
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            "get_balances", "fetch_balance")
 
 
-def test_get_tickers(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_tickers(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     tick = {'ETH/BTC': {
           'symbol': 'ETH/BTC',
@@ -752,7 +823,7 @@ def test_get_tickers(default_conf, mocker):
       }
       }
     api_mock.fetch_tickers = MagicMock(return_value=tick)
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     # retrieve original ticker
     tickers = exchange.get_tickers()
 
@@ -763,20 +834,21 @@ def test_get_tickers(default_conf, mocker):
     assert tickers['BCH/BTC']['bid'] == 0.6
     assert tickers['BCH/BTC']['ask'] == 0.5
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            "get_tickers", "fetch_tickers")
 
     with pytest.raises(OperationalException):
         api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.get_tickers()
 
     api_mock.fetch_tickers = MagicMock(return_value={})
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     exchange.get_tickers()
 
 
-def test_get_ticker(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_ticker(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     tick = {
         'symbol': 'ETH/BTC',
@@ -786,7 +858,7 @@ def test_get_ticker(default_conf, mocker):
     }
     api_mock.fetch_ticker = MagicMock(return_value=tick)
     api_mock.markets = {'ETH/BTC': {}}
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     # retrieve original ticker
     ticker = exchange.get_ticker(pair='ETH/BTC')
 
@@ -801,7 +873,7 @@ def test_get_ticker(default_conf, mocker):
         'last': 42,
     }
     api_mock.fetch_ticker = MagicMock(return_value=tick)
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
     # if not caching the result we should get the same ticker
     # if not fetching a new result we should get the cached ticker
@@ -820,20 +892,21 @@ def test_get_ticker(default_conf, mocker):
     exchange.get_ticker(pair='ETH/BTC', refresh=False)
     assert api_mock.fetch_ticker.call_count == 0
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            "get_ticker", "fetch_ticker",
                            pair='ETH/BTC', refresh=True)
 
     api_mock.fetch_ticker = MagicMock(return_value={})
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     exchange.get_ticker(pair='ETH/BTC', refresh=True)
 
     with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'):
         exchange.get_ticker(pair='XRP/ETH', refresh=True)
 
 
-def test_get_history(default_conf, mocker, caplog):
-    exchange = get_patched_exchange(mocker, default_conf)
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_history(default_conf, mocker, caplog, exchange_name):
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
     tick = [
         [
             arrow.utcnow().timestamp * 1000,  # unix timestamp ms
@@ -912,7 +985,8 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
 
 
 @pytest.mark.asyncio
-async def test__async_get_candle_history(default_conf, mocker, caplog):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_name):
     tick = [
         [
             arrow.utcnow().timestamp * 1000,  # unix timestamp ms
@@ -925,11 +999,10 @@ async def test__async_get_candle_history(default_conf, mocker, caplog):
     ]
 
     caplog.set_level(logging.DEBUG)
-    exchange = get_patched_exchange(mocker, default_conf)
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
     # Monkey-patch async function
     exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
 
-    exchange = Exchange(default_conf)
     pair = 'ETH/BTC'
     res = await exchange._async_get_candle_history(pair, "5m")
     assert type(res) is tuple
@@ -948,7 +1021,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog):
     api_mock = MagicMock()
     with pytest.raises(OperationalException, match=r'Could not fetch ticker data*'):
         api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         await exchange._async_get_candle_history(pair, "5m",
                                                  (arrow.utcnow().timestamp - 2000) * 1000)
 
@@ -1001,12 +1074,13 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog):
     assert log_has("Async code raised an exception: TypeError", caplog.record_tuples)
 
 
-def test_get_order_book(default_conf, mocker, order_book_l2):
-    default_conf['exchange']['name'] = 'binance'
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_order_book(default_conf, mocker, order_book_l2, exchange_name):
+    default_conf['exchange']['name'] = exchange_name
     api_mock = MagicMock()
 
     api_mock.fetch_l2_order_book = order_book_l2
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     order_book = exchange.get_order_book(pair='ETH/BTC', limit=10)
     assert 'bids' in order_book
     assert 'asks' in order_book
@@ -1014,19 +1088,20 @@ def test_get_order_book(default_conf, mocker, order_book_l2):
     assert len(order_book['asks']) == 10
 
 
-def test_get_order_book_exception(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_order_book_exception(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     with pytest.raises(OperationalException):
         api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NotSupported)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.get_order_book(pair='ETH/BTC', limit=50)
     with pytest.raises(TemporaryError):
         api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NetworkError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.get_order_book(pair='ETH/BTC', limit=50)
     with pytest.raises(OperationalException):
         api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.BaseError)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.get_order_book(pair='ETH/BTC', limit=50)
 
 
@@ -1039,8 +1114,9 @@ def make_fetch_ohlcv_mock(data):
     return fetch_ohlcv_mock
 
 
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
 @pytest.mark.asyncio
-async def test___async_get_candle_history_sort(default_conf, mocker):
+async def test___async_get_candle_history_sort(default_conf, mocker, exchange_name):
     def sort_data(data, key):
         return sorted(data, key=key)
 
@@ -1058,7 +1134,7 @@ async def test___async_get_candle_history_sort(default_conf, mocker):
         [1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687],
         [1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867]
     ]
-    exchange = get_patched_exchange(mocker, default_conf)
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
     exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
     sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data))
     # Test the ticker history sort
@@ -1120,36 +1196,39 @@ async def test___async_get_candle_history_sort(default_conf, mocker):
     assert ticks[9][5] == 2.31452783
 
 
-def test_cancel_order_dry_run(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
     default_conf['dry_run'] = True
-    exchange = get_patched_exchange(mocker, default_conf)
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
     assert exchange.cancel_order(order_id='123', pair='TKN/BTC') is None
 
 
 # Ensure that if not dry_run, we should call API
-def test_cancel_order(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_cancel_order(default_conf, mocker, exchange_name):
     default_conf['dry_run'] = False
     api_mock = MagicMock()
     api_mock.cancel_order = MagicMock(return_value=123)
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123
 
     with pytest.raises(DependencyException):
         api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.cancel_order(order_id='_', pair='TKN/BTC')
     assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            "cancel_order", "cancel_order",
                            order_id='_', pair='TKN/BTC')
 
 
-def test_get_order(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_order(default_conf, mocker, exchange_name):
     default_conf['dry_run'] = True
     order = MagicMock()
     order.myid = 123
-    exchange = get_patched_exchange(mocker, default_conf)
+    exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
     exchange._dry_run_open_orders['X'] = order
     print(exchange.get_order('X', 'TKN/BTC'))
     assert exchange.get_order('X', 'TKN/BTC').myid == 123
@@ -1157,67 +1236,32 @@ def test_get_order(default_conf, mocker):
     default_conf['dry_run'] = False
     api_mock = MagicMock()
     api_mock.fetch_order = MagicMock(return_value=456)
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     assert exchange.get_order('X', 'TKN/BTC') == 456
 
     with pytest.raises(DependencyException):
         api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder)
-        exchange = get_patched_exchange(mocker, default_conf, api_mock)
+        exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
         exchange.get_order(order_id='_', pair='TKN/BTC')
     assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            'get_order', 'fetch_order',
                            order_id='_', pair='TKN/BTC')
 
 
-def test_name(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_name(default_conf, mocker, exchange_name):
     mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
-    default_conf['exchange']['name'] = 'binance'
+    default_conf['exchange']['name'] = exchange_name
     exchange = Exchange(default_conf)
 
-    assert exchange.name == 'Binance'
+    assert exchange.name == exchange_name.title()
+    assert exchange.id == exchange_name
 
 
-def test_id(default_conf, mocker):
-    mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
-    default_conf['exchange']['name'] = 'binance'
-    exchange = Exchange(default_conf)
-    assert exchange.id == 'binance'
-
-
-def test_get_pair_detail_url(default_conf, mocker, caplog):
-    mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
-    default_conf['exchange']['name'] = 'binance'
-    exchange = Exchange(default_conf)
-
-    url = exchange.get_pair_detail_url('TKN/ETH')
-    assert 'TKN' in url
-    assert 'ETH' in url
-
-    url = exchange.get_pair_detail_url('LOOONG/BTC')
-    assert 'LOOONG' in url
-    assert 'BTC' in url
-
-    default_conf['exchange']['name'] = 'bittrex'
-    exchange = Exchange(default_conf)
-
-    url = exchange.get_pair_detail_url('TKN/ETH')
-    assert 'TKN' in url
-    assert 'ETH' in url
-
-    url = exchange.get_pair_detail_url('LOOONG/BTC')
-    assert 'LOOONG' in url
-    assert 'BTC' in url
-
-    default_conf['exchange']['name'] = 'poloniex'
-    exchange = Exchange(default_conf)
-    url = exchange.get_pair_detail_url('LOOONG/BTC')
-    assert '' == url
-    assert log_has('Could not get exchange url for Poloniex', caplog.record_tuples)
-
-
-def test_get_trades_for_order(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_trades_for_order(default_conf, mocker, exchange_name):
     order_id = 'ABCD-ABCD'
     since = datetime(2018, 5, 5)
     default_conf["dry_run"] = False
@@ -1244,13 +1288,13 @@ def test_get_trades_for_order(default_conf, mocker):
                                                         'amount': 0.2340606,
                                                         'fee': {'cost': 0.06179, 'currency': 'BTC'}
                                                         }])
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
     orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
     assert len(orders) == 1
     assert orders[0]['price'] == 165
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            'get_trades_for_order', 'fetch_my_trades',
                            order_id=order_id, pair='LTC/BTC', since=since)
 
@@ -1258,10 +1302,11 @@ def test_get_trades_for_order(default_conf, mocker):
     assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == []
 
 
-def test_get_markets(default_conf, mocker, markets):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_markets(default_conf, mocker, markets, exchange_name):
     api_mock = MagicMock()
     api_mock.fetch_markets = markets
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
     ret = exchange.get_markets()
     assert isinstance(ret, list)
     assert len(ret) == 9
@@ -1269,11 +1314,12 @@ def test_get_markets(default_conf, mocker, markets):
     assert ret[0]["id"] == "ethbtc"
     assert ret[0]["symbol"] == "ETH/BTC"
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            'get_markets', 'fetch_markets')
 
 
-def test_get_fee(default_conf, mocker):
+@pytest.mark.parametrize("exchange_name", EXCHANGES)
+def test_get_fee(default_conf, mocker, exchange_name):
     api_mock = MagicMock()
     api_mock.calculate_fee = MagicMock(return_value={
         'type': 'taker',
@@ -1281,11 +1327,11 @@ def test_get_fee(default_conf, mocker):
         'rate': 0.025,
         'cost': 0.05
     })
-    exchange = get_patched_exchange(mocker, default_conf, api_mock)
+    exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
 
     assert exchange.get_fee() == 0.025
 
-    ccxt_exceptionhandlers(mocker, default_conf, api_mock,
+    ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
                            'get_fee', 'calculate_fee')
 
 
diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py
index bb685cad5..2de2668e8 100644
--- a/freqtrade/tests/rpc/test_rpc.py
+++ b/freqtrade/tests/rpc/test_rpc.py
@@ -51,7 +51,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
     assert {
         'trade_id': 1,
         'pair': 'ETH/BTC',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'date': ANY,
         'open_rate': 1.099e-05,
         'close_rate': None,
@@ -72,7 +71,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
     assert {
         'trade_id': 1,
         'pair': 'ETH/BTC',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'date': ANY,
         'open_rate': 1.099e-05,
         'close_rate': None,
diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py
index 686a92469..9964973e1 100644
--- a/freqtrade/tests/rpc/test_rpc_telegram.py
+++ b/freqtrade/tests/rpc/test_rpc_telegram.py
@@ -5,7 +5,7 @@
 import re
 from datetime import datetime
 from random import randint
-from unittest.mock import MagicMock, ANY
+from unittest.mock import MagicMock
 
 import arrow
 import pytest
@@ -183,7 +183,6 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         get_ticker=ticker,
-        get_pair_detail_url=MagicMock(),
         get_fee=fee,
         get_markets=markets
     )
@@ -195,7 +194,6 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
         _rpc_trade_status=MagicMock(return_value=[{
             'trade_id': 1,
             'pair': 'ETH/BTC',
-            'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
             'date': arrow.utcnow(),
             'open_rate': 1.099e-05,
             'close_rate': None,
@@ -270,7 +268,7 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
     telegram._status(bot=MagicMock(), update=update)
 
     assert msg_mock.call_count == 1
-    assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0]
+    assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0]
 
 
 def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
@@ -721,7 +719,6 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'profit',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.172e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -776,7 +773,6 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.044e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -796,7 +792,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
                  return_value=15000.0)
     rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
     mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
-    mocker.patch('freqtrade.exchange.Exchange.get_pair_detail_url', MagicMock())
     mocker.patch.multiple(
         'freqtrade.exchange.Exchange',
         get_ticker=ticker,
@@ -823,7 +818,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'market_url': ANY,
         'limit': 1.098e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -1100,7 +1094,6 @@ def test_send_msg_buy_notification(default_conf, mocker) -> None:
         'type': RPCMessageType.BUY_NOTIFICATION,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.099e-05,
         'stake_amount': 0.001,
         'stake_amount_fiat': 0.0,
@@ -1108,7 +1101,7 @@ def test_send_msg_buy_notification(default_conf, mocker) -> None:
         'fiat_currency': 'USD'
     })
     assert msg_mock.call_args[0][0] \
-        == '*Bittrex:* Buying [ETH/BTC](https://bittrex.com/Market/Index?MarketName=BTC-ETH)\n' \
+        == '*Bittrex:* Buying ETH/BTC\n' \
            'with limit `0.00001099\n' \
            '(0.001000 BTC,0.000 USD)`'
 
@@ -1129,7 +1122,6 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
         'exchange': 'Binance',
         'pair': 'KEY/ETH',
         'gain': 'loss',
-        'market_url': 'https://www.binance.com/tradeDetail.html?symbol=KEY_ETH',
         'limit': 3.201e-05,
         'amount': 1333.3333333333335,
         'open_rate': 7.5e-05,
@@ -1141,8 +1133,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
         'sell_reason': SellType.STOP_LOSS.value
     })
     assert msg_mock.call_args[0][0] \
-        == ('*Binance:* Selling [KEY/ETH]'
-            '(https://www.binance.com/tradeDetail.html?symbol=KEY_ETH)\n'
+        == ('*Binance:* Selling KEY/ETH\n'
             '*Limit:* `0.00003201`\n'
             '*Amount:* `1333.33333333`\n'
             '*Open Rate:* `0.00007500`\n'
@@ -1156,7 +1147,6 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
         'exchange': 'Binance',
         'pair': 'KEY/ETH',
         'gain': 'loss',
-        'market_url': 'https://www.binance.com/tradeDetail.html?symbol=KEY_ETH',
         'limit': 3.201e-05,
         'amount': 1333.3333333333335,
         'open_rate': 7.5e-05,
@@ -1167,8 +1157,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
         'sell_reason': SellType.STOP_LOSS.value
     })
     assert msg_mock.call_args[0][0] \
-        == ('*Binance:* Selling [KEY/ETH]'
-            '(https://www.binance.com/tradeDetail.html?symbol=KEY_ETH)\n'
+        == ('*Binance:* Selling KEY/ETH\n'
             '*Limit:* `0.00003201`\n'
             '*Amount:* `1333.33333333`\n'
             '*Open Rate:* `0.00007500`\n'
@@ -1256,7 +1245,6 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
         'type': RPCMessageType.BUY_NOTIFICATION,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.099e-05,
         'stake_amount': 0.001,
         'stake_amount_fiat': 0.0,
@@ -1264,7 +1252,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
         'fiat_currency': None
     })
     assert msg_mock.call_args[0][0] \
-        == '*Bittrex:* Buying [ETH/BTC](https://bittrex.com/Market/Index?MarketName=BTC-ETH)\n' \
+        == '*Bittrex:* Buying ETH/BTC\n' \
            'with limit `0.00001099\n' \
            '(0.001000 BTC)`'
 
@@ -1284,7 +1272,6 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
         'exchange': 'Binance',
         'pair': 'KEY/ETH',
         'gain': 'loss',
-        'market_url': 'https://www.binance.com/tradeDetail.html?symbol=KEY_ETH',
         'limit': 3.201e-05,
         'amount': 1333.3333333333335,
         'open_rate': 7.5e-05,
@@ -1296,8 +1283,7 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
         'sell_reason': SellType.STOP_LOSS.value
     })
     assert msg_mock.call_args[0][0] \
-        == '*Binance:* Selling [KEY/ETH]' \
-           '(https://www.binance.com/tradeDetail.html?symbol=KEY_ETH)\n' \
+        == '*Binance:* Selling KEY/ETH\n' \
            '*Limit:* `0.00003201`\n' \
            '*Amount:* `1333.33333333`\n' \
            '*Open Rate:* `0.00007500`\n' \
diff --git a/freqtrade/tests/rpc/test_rpc_webhook.py b/freqtrade/tests/rpc/test_rpc_webhook.py
index 002308815..da7aec0a6 100644
--- a/freqtrade/tests/rpc/test_rpc_webhook.py
+++ b/freqtrade/tests/rpc/test_rpc_webhook.py
@@ -48,7 +48,6 @@ def test_send_msg(default_conf, mocker):
         'type': RPCMessageType.BUY_NOTIFICATION,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
-        'market_url': "http://mockedurl/ETH_BTC",
         'limit': 0.005,
         'stake_amount': 0.8,
         'stake_amount_fiat': 500,
@@ -73,7 +72,6 @@ def test_send_msg(default_conf, mocker):
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': "profit",
-        'market_url': "http://mockedurl/ETH_BTC",
         'limit': 0.005,
         'amount': 0.8,
         'open_rate': 0.004,
@@ -127,7 +125,6 @@ def test_exception_send_msg(default_conf, mocker, caplog):
         'type': RPCMessageType.BUY_NOTIFICATION,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
-        'market_url': "http://mockedurl/ETH_BTC",
         'limit': 0.005,
         'stake_amount': 0.8,
         'stake_amount_fiat': 500,
diff --git a/freqtrade/tests/test_arguments.py b/freqtrade/tests/test_arguments.py
index 042d43ed2..0952d1c5d 100644
--- a/freqtrade/tests/test_arguments.py
+++ b/freqtrade/tests/test_arguments.py
@@ -16,7 +16,7 @@ def test_parse_args_none() -> None:
 
 def test_parse_args_defaults() -> None:
     args = Arguments([], '').get_parsed_arg()
-    assert args.config == 'config.json'
+    assert args.config == ['config.json']
     assert args.strategy_path is None
     assert args.datadir is None
     assert args.loglevel == 0
@@ -24,10 +24,15 @@ def test_parse_args_defaults() -> None:
 
 def test_parse_args_config() -> None:
     args = Arguments(['-c', '/dev/null'], '').get_parsed_arg()
-    assert args.config == '/dev/null'
+    assert args.config == ['/dev/null']
 
     args = Arguments(['--config', '/dev/null'], '').get_parsed_arg()
-    assert args.config == '/dev/null'
+    assert args.config == ['/dev/null']
+
+    args = Arguments(['--config', '/dev/null',
+                      '--config', '/dev/zero'],
+                     '').get_parsed_arg()
+    assert args.config == ['/dev/null', '/dev/zero']
 
 
 def test_parse_args_db_url() -> None:
@@ -139,7 +144,7 @@ def test_parse_args_backtesting_custom() -> None:
         'TestStrategy'
         ]
     call_args = Arguments(args, '').get_parsed_arg()
-    assert call_args.config == 'test_conf.json'
+    assert call_args.config == ['test_conf.json']
     assert call_args.live is True
     assert call_args.loglevel == 0
     assert call_args.subparser == 'backtesting'
@@ -158,7 +163,7 @@ def test_parse_args_hyperopt_custom() -> None:
         '--spaces', 'buy'
     ]
     call_args = Arguments(args, '').get_parsed_arg()
-    assert call_args.config == 'test_conf.json'
+    assert call_args.config == ['test_conf.json']
     assert call_args.epochs == 20
     assert call_args.loglevel == 0
     assert call_args.subparser == 'hyperopt'
diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py
index 67445238b..51098baaa 100644
--- a/freqtrade/tests/test_configuration.py
+++ b/freqtrade/tests/test_configuration.py
@@ -1,15 +1,15 @@
 # pragma pylint: disable=missing-docstring, protected-access, invalid-name
 
 import json
-from argparse import Namespace
 import logging
+from argparse import Namespace
+from copy import deepcopy
 from unittest.mock import MagicMock
 
 import pytest
-from jsonschema import validate, ValidationError, Draft4Validator
+from jsonschema import Draft4Validator, ValidationError, validate
 
-from freqtrade import constants
-from freqtrade import OperationalException
+from freqtrade import OperationalException, constants
 from freqtrade.arguments import Arguments
 from freqtrade.configuration import Configuration, set_loggers
 from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
@@ -50,18 +50,49 @@ def test_load_config_file(default_conf, mocker, caplog) -> None:
     validated_conf = configuration._load_config_file('somefile')
     assert file_mock.call_count == 1
     assert validated_conf.items() >= default_conf.items()
-    assert 'internals' in validated_conf
-    assert log_has('Validating configuration ...', caplog.record_tuples)
 
 
 def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
     default_conf['max_open_trades'] = 0
-    file_mock = mocker.patch('freqtrade.configuration.open', mocker.mock_open(
+    mocker.patch('freqtrade.configuration.open', mocker.mock_open(
         read_data=json.dumps(default_conf)
     ))
 
-    Configuration(Namespace())._load_config_file('somefile')
-    assert file_mock.call_count == 1
+    args = Arguments([], '').get_parsed_arg()
+    configuration = Configuration(args)
+    validated_conf = configuration.load_config()
+
+    assert validated_conf['max_open_trades'] == 0
+    assert 'internals' in validated_conf
+    assert log_has('Validating configuration ...', caplog.record_tuples)
+
+
+def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
+    conf1 = deepcopy(default_conf)
+    conf2 = deepcopy(default_conf)
+    del conf1['exchange']['key']
+    del conf1['exchange']['secret']
+    del conf2['exchange']['name']
+    conf2['exchange']['pair_whitelist'] += ['NANO/BTC']
+
+    config_files = [conf1, conf2]
+
+    configsmock = MagicMock(side_effect=config_files)
+    mocker.patch('freqtrade.configuration.Configuration._load_config_file', configsmock)
+
+    arg_list = ['-c', 'test_conf.json', '--config', 'test2_conf.json', ]
+    args = Arguments(arg_list, '').get_parsed_arg()
+    configuration = Configuration(args)
+    validated_conf = configuration.load_config()
+
+    exchange_conf = default_conf['exchange']
+    assert validated_conf['exchange']['name'] == exchange_conf['name']
+    assert validated_conf['exchange']['key'] == exchange_conf['key']
+    assert validated_conf['exchange']['secret'] == exchange_conf['secret']
+    assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist']
+    assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist']
+
+    assert 'internals' in validated_conf
     assert log_has('Validating configuration ...', caplog.record_tuples)
 
 
diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py
index a0ac6ee99..2f66a5153 100644
--- a/freqtrade/tests/test_freqtradebot.py
+++ b/freqtrade/tests/test_freqtradebot.py
@@ -1872,7 +1872,6 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'profit',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.172e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -1919,7 +1918,6 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.044e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -1974,7 +1972,6 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.08801e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -2146,7 +2143,6 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'profit',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.172e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
@@ -2194,7 +2190,6 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
         'limit': 1.044e-05,
         'amount': 90.99181073703367,
         'open_rate': 1.099e-05,
diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py
index 7aae98ebe..51c95a4a9 100644
--- a/freqtrade/tests/test_main.py
+++ b/freqtrade/tests/test_main.py
@@ -22,7 +22,7 @@ def test_parse_args_backtesting(mocker) -> None:
     main(['backtesting'])
     assert backtesting_mock.call_count == 1
     call_args = backtesting_mock.call_args[0][0]
-    assert call_args.config == 'config.json'
+    assert call_args.config == ['config.json']
     assert call_args.live is False
     assert call_args.loglevel == 0
     assert call_args.subparser == 'backtesting'
@@ -35,7 +35,7 @@ def test_main_start_hyperopt(mocker) -> None:
     main(['hyperopt'])
     assert hyperopt_mock.call_count == 1
     call_args = hyperopt_mock.call_args[0][0]
-    assert call_args.config == 'config.json'
+    assert call_args.config == ['config.json']
     assert call_args.loglevel == 0
     assert call_args.subparser == 'hyperopt'
     assert call_args.func is not None
diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py
index be6efc2ff..a9519e693 100644
--- a/freqtrade/tests/test_persistence.py
+++ b/freqtrade/tests/test_persistence.py
@@ -629,3 +629,48 @@ def test_adjust_stop_loss(limit_buy_order, limit_sell_order, fee):
     assert round(trade.stop_loss, 8) == 1.26
     assert trade.max_rate == 1.4
     assert trade.initial_stop_loss == 0.95
+
+
+def test_get_open(default_conf, fee):
+    init(default_conf)
+
+    # Simulate dry_run entries
+    trade = Trade(
+        pair='ETH/BTC',
+        stake_amount=0.001,
+        amount=123.0,
+        fee_open=fee.return_value,
+        fee_close=fee.return_value,
+        open_rate=0.123,
+        exchange='bittrex',
+        open_order_id='dry_run_buy_12345'
+    )
+    Trade.session.add(trade)
+
+    trade = Trade(
+        pair='ETC/BTC',
+        stake_amount=0.001,
+        amount=123.0,
+        fee_open=fee.return_value,
+        fee_close=fee.return_value,
+        open_rate=0.123,
+        exchange='bittrex',
+        is_open=False,
+        open_order_id='dry_run_sell_12345'
+    )
+    Trade.session.add(trade)
+
+    # Simulate prod entry
+    trade = Trade(
+        pair='ETC/BTC',
+        stake_amount=0.001,
+        amount=123.0,
+        fee_open=fee.return_value,
+        fee_close=fee.return_value,
+        open_rate=0.123,
+        exchange='bittrex',
+        open_order_id='prod_buy_12345'
+    )
+    Trade.session.add(trade)
+
+    assert len(Trade.get_open_trades()) == 2
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 34d59d802..859d80482 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,7 +1,7 @@
 # Include all requirements to run the bot.
 -r requirements.txt
 
-flake8==3.7.6
+flake8==3.7.7
 flake8-type-annotations==0.1.0
 flake8-tidy-imports==2.0.0
 pytest==4.3.0
diff --git a/requirements.txt b/requirements.txt
index cd1d89c6d..a72198ac3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-ccxt==1.18.281
+ccxt==1.18.313
 SQLAlchemy==1.2.18
 python-telegram-bot==11.1.0
 arrow==0.13.1
@@ -6,12 +6,12 @@ cachetools==3.1.0
 requests==2.21.0
 urllib3==1.24.1
 wrapt==1.11.1
-numpy==1.16.1
+numpy==1.16.2
 pandas==0.24.1
-scikit-learn==0.20.2
+scikit-learn==0.20.3
 joblib==0.13.2
 scipy==1.2.1
-jsonschema==2.6.0
+jsonschema==3.0.1
 TA-Lib==0.4.17
 tabulate==0.8.3
 coinmarketcap==5.0.3
diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py
index c8fd08747..5dee41bdd 100755
--- a/scripts/download_backtest_data.py
+++ b/scripts/download_backtest_data.py
@@ -5,12 +5,14 @@ import json
 import sys
 from pathlib import Path
 import arrow
+from typing import Any, Dict
 
-from freqtrade import arguments
+from freqtrade.arguments import Arguments
 from freqtrade.arguments import TimeRange
 from freqtrade.exchange import Exchange
 from freqtrade.data.history import download_pair_history
 from freqtrade.configuration import Configuration, set_loggers
+from freqtrade.misc import deep_merge_dicts
 
 import logging
 logging.basicConfig(
@@ -21,7 +23,7 @@ set_loggers(0)
 
 DEFAULT_DL_PATH = 'user_data/data'
 
-arguments = arguments.Arguments(sys.argv[1:], 'download utility')
+arguments = Arguments(sys.argv[1:], 'download utility')
 arguments.testdata_dl_options()
 args = arguments.parse_args()
 
@@ -29,7 +31,13 @@ timeframes = args.timeframes
 
 if args.config:
     configuration = Configuration(args)
-    config = configuration._load_config_file(args.config)
+
+    config: Dict[str, Any] = {}
+    # Now expecting a list of config filenames here, not a string
+    for path in args.config:
+        print('Using config: %s ...', path)
+        # Merge config options, overwriting old values
+        config = deep_merge_dicts(configuration._load_config_file(path), config)
 
     config['stake_currency'] = ''
     # Ensure we do not use Exchange credentials