Repository: freqtrade/freqtrade
Pull request: Backtest no fees / custom fees
## Summary
Allow specifying fee percentage to be applied during backtesting.

This can be usefull if you'r exchange account has some rabates applied (for high volume, for example), which ccxt does not detect (it'll always show the basic fees for each exchange).

This can also be "abused" to add slippage, by specifying a slightly higher value than what the exchange applies.
It can also be applied to test a "ideal" world, where no fees apply (although that is unrealistic).

closes #2223
Remotely related to #1059, which explains behaviour of different exchanges related to this ... this PR only applies to backtesting, so will not fix this issue.

## Quick changelog

- Add `--fee` parameter to backtest/hyperopt/edge commands



Problem statement:
support the configuration of slippage for backtest/hyperopts
idea :
As a trader
i want to configure a slippage when i test a strategy that deal with low volume or xxx/btc that have low decimals digits of precision 
so that the backtest look closer to live and the objective function take slippage into account for optimization

i can write test case if needed to exchange the how to.

have a nice day.

Hints from issue discussion:
> i can write test case if needed to exchange the how to.

Yes, please.

It can be as simple as subtracting some slippage percent from the profit of each trade.
Then the slippage percent should simply be added to the config, this is easiest part.

you call it slippage - but in theory, a simple `--fee` parameter would accomplish the same (i wanted to add this anyway, as it will allow complete offline backtesting / hyperopting) (which i would have needed during my last flight ...).

currently, we use what the exchange gives us (0.0025 IIRC - for binance).

By using 0.0030 instead of 0.0025, you would have added a 5% slippage ... or am i seeing this wrong / too simple?

Commit message: Add --fee to documentation

Patch to review:
diff --git a/docs/backtesting.md b/docs/backtesting.md
index 75aba6c73a1..cc3f1102668 100644
--- a/docs/backtesting.md
+++ b/docs/backtesting.md
@@ -72,6 +72,17 @@ The exported trades can be used for [further analysis](#further-backtest-result-
 freqtrade backtesting --export trades --export-filename=backtest_samplestrategy.json
 ```
 
+#### Supplying custom fee value
+
+Sometimes your account has certain fee rabates, which are not visible to ccxt.
+To account for this in backtesting, you can use `--fee 0.001` to supply this value to backtesting.
+This fee must be a percentage, and will be applied twice (once for trade entry, and once for trade exit).
+
+```bash
+freqtrade backtesting --fee 0.001
+```
+
+
 #### Running backtest with smaller testset by using timerange
 
 Use the `--timerange` argument to change how much of the testset you want to use.
diff --git a/docs/bot-usage.md b/docs/bot-usage.md
index f44400e3229..e28ac9970fc 100644
--- a/docs/bot-usage.md
+++ b/docs/bot-usage.md
@@ -168,22 +168,25 @@ Backtesting also uses the config specified via `-c/--config`.
 
 ```
 usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE]
-                           [--max_open_trades MAX_OPEN_TRADES]
-                           [--stake_amount STAKE_AMOUNT] [-r] [--eps] [--dmmp]
-                           [-l]
-                           [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
-                           [--export EXPORT] [--export-filename PATH]
+                             [--max_open_trades INT]
+                             [--stake_amount STAKE_AMOUNT] [--fee FLOAT]
+                             [--eps] [--dmmp]
+                             [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
+                             [--export EXPORT] [--export-filename PATH]
 
 optional arguments:
   -h, --help            show this help message and exit
   -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL
-                        Specify ticker interval (1m, 5m, 30m, 1h, 1d).
+                        Specify ticker interval (`1m`, `5m`, `30m`, `1h`,
+                        `1d`).
   --timerange TIMERANGE
                         Specify what timerange of data to use.
-  --max_open_trades MAX_OPEN_TRADES
+  --max_open_trades INT
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
+  --fee FLOAT           Specify fee %. Should be in %, will be applied twice
+                        (on trade entry and exit).
   --eps, --enable-position-stacking
                         Allow buying the same pair multiple times (position
                         stacking).
@@ -193,19 +196,21 @@ optional arguments:
                         number).
   --strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
                         Provide a space-separated list of strategies to
-                        backtest Please note that ticker-interval needs to be
+                        backtest. Please note that ticker-interval needs to be
                         set either in config or via command line. When using
-                        this together with --export trades, the strategy-name
-                        is injected into the filename (so backtest-data.json
-                        becomes backtest-data-DefaultStrategy.json
-  --export EXPORT       Export backtest results, argument are: trades. Example
-                        --export=trades
+                        this together with `--export trades`, the strategy-
+                        name is injected into the filename (so `backtest-
+                        data.json` becomes `backtest-data-
+                        DefaultStrategy.json`
+  --export EXPORT       Export backtest results, argument are: trades.
+                        Example: `--export=trades`
   --export-filename PATH
-                        Save backtest results to this filename requires
-                        --export to be set as well Example --export-
-                        filename=user_data/backtest_results/backtest_today.json
-                        (default: user_data/backtest_results/backtest-
-                        result.json)
+                        Save backtest results to the file with this filename
+                        (default: `user_data/backtest_results/backtest-
+                        result.json`). Requires `--export` to be set as well.
+                        Example: `--export-filename=user_data/backtest_results
+                        /backtest_today.json`
+
 ```
 
 ### Getting historic data for backtesting
@@ -222,13 +227,13 @@ to find optimal parameter values for your stategy.
 ```
 usage: freqtrade hyperopt [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE]
                           [--max_open_trades INT]
-                          [--stake_amount STAKE_AMOUNT] [-r]
+                          [--stake_amount STAKE_AMOUNT] [--fee FLOAT]
                           [--customhyperopt NAME] [--hyperopt-path PATH]
                           [--eps] [-e INT]
                           [-s {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]]
-                          [--dmmp] [--print-all] [--no-color] [-j JOBS]
-                          [--random-state INT] [--min-trades INT] [--continue]
-                          [--hyperopt-loss NAME]
+                          [--dmmp] [--print-all] [--no-color] [--print-json]
+                          [-j JOBS] [--random-state INT] [--min-trades INT]
+                          [--continue] [--hyperopt-loss NAME]
 
 optional arguments:
   -h, --help            show this help message and exit
@@ -241,6 +246,8 @@ optional arguments:
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
+  --fee FLOAT           Specify fee %. Should be in %, will be applied twice
+                        (on trade entry and exit).
   --customhyperopt NAME
                         Specify hyperopt class name (default:
                         `DefaultHyperOpts`).
@@ -260,6 +267,7 @@ optional arguments:
   --print-all           Print all results, not only the best ones.
   --no-color            Disable colorization of hyperopt results. May be
                         useful if you are redirecting output to a file.
+  --print-json          Print best result detailization in JSON format.
   -j JOBS, --job-workers JOBS
                         The number of concurrently running jobs for
                         hyperoptimization (hyperopt worker processes). If -1
@@ -278,8 +286,8 @@ optional arguments:
                         generate completely different results, since the
                         target for optimization is different. Built-in
                         Hyperopt-loss-functions are: DefaultHyperOptLoss,
-                        OnlyProfitHyperOptLoss, SharpeHyperOptLoss.
-                        (default: `DefaultHyperOptLoss`).
+                        OnlyProfitHyperOptLoss, SharpeHyperOptLoss.(default:
+                        `DefaultHyperOptLoss`).
 ```
 
 ## Edge commands
@@ -288,25 +296,28 @@ To know your trade expectancy and winrate against historical data, you can use E
 
 ```
 usage: freqtrade edge [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE]
-                    [--max_open_trades MAX_OPEN_TRADES]
-                    [--stake_amount STAKE_AMOUNT] [-r]
-                    [--stoplosses STOPLOSS_RANGE]
+                      [--max_open_trades INT] [--stake_amount STAKE_AMOUNT]
+                      [--fee FLOAT] [--stoplosses STOPLOSS_RANGE]
 
 optional arguments:
   -h, --help            show this help message and exit
   -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL
-                        Specify ticker interval (1m, 5m, 30m, 1h, 1d).
+                        Specify ticker interval (`1m`, `5m`, `30m`, `1h`,
+                        `1d`).
   --timerange TIMERANGE
                         Specify what timerange of data to use.
-  --max_open_trades MAX_OPEN_TRADES
+  --max_open_trades INT
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
+  --fee FLOAT           Specify fee %. Should be in %, will be applied twice
+                        (on trade entry and exit).
   --stoplosses STOPLOSS_RANGE
-                        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
+                        Defines a range of stoploss values against which edge
+                        will assess the strategy. The format is "min,max,step"
+                        (without any space). Example:
+                        `--stoplosses=-0.01,-0.1,-0.001`
+
 ```
 
 To understand edge and how to read the results, please read the [edge documentation](edge.md).
diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
index da9bef0b0f2..c2c09eb4097 100644
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -15,7 +15,7 @@
 ARGS_MAIN = ARGS_COMMON + ARGS_STRATEGY + ["db_url", "sd_notify"]
 
 ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange",
-                        "max_open_trades", "stake_amount"]
+                        "max_open_trades", "stake_amount", "fee"]
 
 ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
                                         "strategy_list", "export", "exportfilename"]
diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py
index caf34b5e3ee..9f7f7b82fef 100644
--- a/freqtrade/configuration/cli_options.py
+++ b/freqtrade/configuration/cli_options.py
@@ -144,6 +144,12 @@ def __init__(self, *args, **kwargs):
         default=os.path.join('user_data', 'backtest_results',
                              'backtest-result.json'),
     ),
+    "fee": Arg(
+        '--fee',
+        help='Specify fee %%. Should be in %%, will be applied twice (on trade entry and exit).',
+        type=float,
+        metavar='FLOAT',
+    ),
     # Edge
     "stoploss_range": Arg(
         '--stoplosses',
diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py
index 764593d0fba..ccbc41f34a3 100644
--- a/freqtrade/configuration/configuration.py
+++ b/freqtrade/configuration/configuration.py
@@ -210,6 +210,10 @@ def _process_optimize_options(self, config: Dict[str, Any]) -> None:
                              logstring='Parameter --stake_amount detected, '
                              'overriding stake_amount to: {} ...')
 
+        self._args_to_config(config, argname='fee',
+                             logstring='Parameter --fee detected, '
+                             'setting fee to: {} ...')
+
         self._args_to_config(config, argname='timerange',
                              logstring='Parameter --timerange detected: {} ...')
 
@@ -323,7 +327,8 @@ def _args_to_config(self, config: Dict[str, Any], argname: str,
                         sample: logfun=len (prints the length of the found
                         configuration instead of the content)
         """
-        if argname in self.args and self.args[argname]:
+        if (argname in self.args and self.args[argname] is not None
+            and self.args[argname] is not False):
 
             config.update({argname: self.args[argname]})
             if logfun:
diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py
index 66a777ce5d9..2655fbc65a3 100644
--- a/freqtrade/edge/__init__.py
+++ b/freqtrade/edge/__init__.py
@@ -77,8 +77,10 @@ def __init__(self, config: Dict[str, Any], exchange, strategy) -> None:
 
         self._timerange: TimeRange = TimeRange.parse_timerange("%s-" % arrow.now().shift(
             days=-1 * self._since_number_of_days).format('YYYYMMDD'))
-
-        self.fee = self.exchange.get_fee()
+        if config.get('fee'):
+            self.fee = config['fee']
+        else:
+            self.fee = self.exchange.get_fee()
 
     def calculate(self) -> bool:
         pairs = self.config['exchange']['pair_whitelist']
diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py
index 89aff86f60f..759d7b72d3b 100644
--- a/freqtrade/optimize/backtesting.py
+++ b/freqtrade/optimize/backtesting.py
@@ -63,9 +63,12 @@ def __init__(self, config: Dict[str, Any]) -> None:
         self.config['exchange']['uid'] = ''
         self.config['dry_run'] = True
         self.strategylist: List[IStrategy] = []
-
         self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange
-        self.fee = self.exchange.get_fee()
+
+        if config.get('fee'):
+            self.fee = config['fee']
+        else:
+            self.fee = self.exchange.get_fee()
 
         if self.config.get('runmode') != RunMode.HYPEROPT:
             self.dataprovider = DataProvider(self.config, self.exchange)


Code review comments:
diff_hunk: @@ -168,22 +168,25 @@ Backtesting also uses the config specified via `-c/--config`.
 
 ```
 usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE]
-                           [--max_open_trades MAX_OPEN_TRADES]
-                           [--stake_amount STAKE_AMOUNT] [-r] [--eps] [--dmmp]
-                           [-l]
-                           [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
-                           [--export EXPORT] [--export-filename PATH]
+                             [--max_open_trades INT]
+                             [--stake_amount STAKE_AMOUNT] [--fee FLOAT]
+                             [--eps] [--dmmp]
+                             [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
+                             [--export EXPORT] [--export-filename PATH]
 
 optional arguments:
   -h, --help            show this help message and exit
   -i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL
-                        Specify ticker interval (1m, 5m, 30m, 1h, 1d).
+                        Specify ticker interval (`1m`, `5m`, `30m`, `1h`,
+                        `1d`).
   --timerange TIMERANGE
                         Specify what timerange of data to use.
-  --max_open_trades MAX_OPEN_TRADES
+  --max_open_trades INT
                         Specify max_open_trades to use.
   --stake_amount STAKE_AMOUNT
                         Specify stake_amount.
+  --fee FLOAT           Specify fee %. Should be in %, will be applied twice
path: docs/bot-usage.md
text: @user1:
It's not percentage, it's ratio, like in stoploss value (1.0 corresponds to 100%, -0.001 here is -0.1%, not -0.001%)

The term 'percentage' is incorrectly used in the bot docs, comments and messages, where 'ratio' is to be used, but this is a different story.

No '%' sign should be used here at least, it's not amount of percents...