Skip to content

Commit

Permalink
Merge d9d5617 into 9968e4e
Browse files Browse the repository at this point in the history
  • Loading branch information
xmatthias committed Feb 27, 2021
2 parents 9968e4e + d9d5617 commit d6507bb
Show file tree
Hide file tree
Showing 38 changed files with 692 additions and 301 deletions.
138 changes: 98 additions & 40 deletions docs/backtesting.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/bot-usage.md
Expand Up @@ -56,6 +56,7 @@ optional arguments:
usage: freqtrade trade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--userdir PATH] [-s NAME] [--strategy-path PATH]
[--db-url PATH] [--sd-notify] [--dry-run]
[--dry-run-wallet DRY_RUN_WALLET]
optional arguments:
-h, --help show this help message and exit
Expand All @@ -66,6 +67,9 @@ optional arguments:
--sd-notify Notify systemd service manager.
--dry-run Enforce dry-run for trading (removes Exchange secrets
and simulates trades).
--dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET
Starting balance, used for backtesting / hyperopt and
dry-runs.
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
Expand Down
9 changes: 5 additions & 4 deletions docs/configuration.md
Expand Up @@ -49,7 +49,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `timeframe` | The timeframe (former ticker interval) to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** String
| `fiat_display_currency` | Fiat currency used to show your profits. [More information below](#what-values-can-be-used-for-fiat_display_currency). <br> **Datatype:** String
| `dry_run` | **Required.** Define if the bot must be in Dry Run or production mode. <br>*Defaults to `true`.* <br> **Datatype:** Boolean
| `dry_run_wallet` | Define the starting amount in stake currency for the simulated wallet used by the bot running in the Dry Run mode.<br>*Defaults to `1000`.* <br> **Datatype:** Float
| `dry_run_wallet` | Define the starting amount in stake currency for the simulated wallet used by the bot running in Dry Run mode.<br>*Defaults to `1000`.* <br> **Datatype:** Float
| `cancel_open_orders_on_exit` | Cancel open orders when the `/stop` RPC command is issued, `Ctrl+C` is pressed or the bot dies unexpectedly. When set to `true`, this allows you to use `/stop` to cancel unfilled and partially filled orders in the event of a market crash. It does not impact open positions. <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `process_only_new_candles` | Enable processing of indicators only when new candles arrive. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
| `minimal_roi` | **Required.** Set the threshold as ratio the bot will use to sell a trade. [More information below](#understand-minimal_roi). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Dict
Expand Down Expand Up @@ -218,11 +218,12 @@ To allow the bot to trade all the available `stake_currency` in your account (mi
"tradable_balance_ratio": 0.99,
```

!!! Note
This configuration will allow increasing / decreasing stakes depending on the performance of the bot (lower stake if bot is loosing, higher stakes if the bot has a winning record, since higher balances are available).
!!! Tip "Compounding profits"
This configuration will allow increasing / decreasing stakes depending on the performance of the bot (lower stake if bot is loosing, higher stakes if the bot has a winning record, since higher balances are available), and will result in profit compounding.

!!! Note "When using Dry-Run Mode"
When using `"stake_amount" : "unlimited",` in combination with Dry-Run, the balance will be simulated starting with a stake of `dry_run_wallet` which will evolve over time. It is therefore important to set `dry_run_wallet` to a sensible value (like 0.05 or 0.01 for BTC and 1000 or 100 for USDT, for example), otherwise it may simulate trades with 100 BTC (or more) or 0.05 USDT (or less) at once - which may not correspond to your real available balance or is less than the exchange minimal limit for the order amount for the stake currency.
When using `"stake_amount" : "unlimited",` in combination with Dry-Run, Backtesting or Hyperopt, the balance will be simulated starting with a stake of `dry_run_wallet` which will evolve over time.
It is therefore important to set `dry_run_wallet` to a sensible value (like 0.05 or 0.01 for BTC and 1000 or 100 for USDT, for example), otherwise it may simulate trades with 100 BTC (or more) or 0.05 USDT (or less) at once - which may not correspond to your real available balance or is less than the exchange minimal limit for the order amount for the stake currency.

--8<-- "includes/pricing.md"

Expand Down
6 changes: 5 additions & 1 deletion docs/hyperopt.md
Expand Up @@ -43,7 +43,8 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--max-open-trades INT]
[--stake-amount STAKE_AMOUNT] [--fee FLOAT]
[--hyperopt NAME] [--hyperopt-path PATH] [--eps]
[--dmmp] [--enable-protections] [-e INT]
[--dmmp] [--enable-protections]
[--dry-run-wallet DRY_RUN_WALLET] [-e INT]
[--spaces {all,buy,sell,roi,stoploss,trailing,default} [{all,buy,sell,roi,stoploss,trailing,default} ...]]
[--print-all] [--no-color] [--print-json] [-j JOBS]
[--random-state INT] [--min-trades INT]
Expand Down Expand Up @@ -82,6 +83,9 @@ optional arguments:
Enable protections for backtesting.Will slow
backtesting down by a considerable amount, but will
include configured protections
--dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET
Starting balance, used for backtesting / hyperopt and
dry-runs.
-e INT, --epochs INT Specify number of epochs (default: 100).
--spaces {all,buy,sell,roi,stoploss,trailing,default} [{all,buy,sell,roi,stoploss,trailing,default} ...]
Specify which parameters to hyperopt. Space-separated
Expand Down
2 changes: 1 addition & 1 deletion docs/strategy-customization.md
Expand Up @@ -709,7 +709,7 @@ To verify if a pair is currently locked, use `self.is_pair_locked(pair)`.
Locked pairs will always be rounded up to the next candle. So assuming a `5m` timeframe, a lock with `until` set to 10:18 will lock the pair until the candle from 10:15-10:20 will be finished.

!!! Warning
Locking pairs is not available during backtesting.
Manually locking pairs is not available during backtesting, only locks via Protections are allowed.

#### Pair locking example

Expand Down
6 changes: 3 additions & 3 deletions freqtrade/commands/arguments.py
Expand Up @@ -14,18 +14,18 @@

ARGS_STRATEGY = ["strategy", "strategy_path"]

ARGS_TRADE = ["db_url", "sd_notify", "dry_run"]
ARGS_TRADE = ["db_url", "sd_notify", "dry_run", "dry_run_wallet", ]

ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv",
"max_open_trades", "stake_amount", "fee"]

ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
"enable_protections",
"enable_protections", "dry_run_wallet",
"strategy_list", "export", "exportfilename"]

ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
"position_stacking", "use_max_market_positions",
"enable_protections",
"enable_protections", "dry_run_wallet",
"epochs", "spaces", "print_all",
"print_colorized", "print_json", "hyperopt_jobs",
"hyperopt_random_state", "hyperopt_min_trades",
Expand Down
6 changes: 5 additions & 1 deletion freqtrade/commands/cli_options.py
Expand Up @@ -110,6 +110,11 @@ def __init__(self, *args, **kwargs):
help='Enforce dry-run for trading (removes Exchange secrets and simulates trades).',
action='store_true',
),
"dry_run_wallet": Arg(
'--dry-run-wallet', '--starting-balance',
help='Starting balance, used for backtesting / hyperopt and dry-runs.',
type=float,
),
# Optimize common
"timeframe": Arg(
'-i', '--timeframe', '--ticker-interval',
Expand All @@ -128,7 +133,6 @@ def __init__(self, *args, **kwargs):
"stake_amount": Arg(
'--stake-amount',
help='Override the value of the `stake_amount` configuration setting.',
type=float,
),
# Backtesting
"position_stacking": Arg(
Expand Down
15 changes: 9 additions & 6 deletions freqtrade/commands/optimize_commands.py
Expand Up @@ -3,7 +3,8 @@

from freqtrade import constants
from freqtrade.configuration import setup_utils_configuration
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exceptions import OperationalException
from freqtrade.misc import round_coin_value
from freqtrade.state import RunMode


Expand All @@ -22,11 +23,13 @@ def setup_optimize_configuration(args: Dict[str, Any], method: RunMode) -> Dict[
RunMode.BACKTEST: 'backtesting',
RunMode.HYPEROPT: 'hyperoptimization',
}
if (method in no_unlimited_runmodes.keys() and
config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT):
raise DependencyException(
f'The value of `stake_amount` cannot be set as "{constants.UNLIMITED_STAKE_AMOUNT}" '
f'for {no_unlimited_runmodes[method]}')
if method in no_unlimited_runmodes.keys():
if (config['stake_amount'] != constants.UNLIMITED_STAKE_AMOUNT
and config['stake_amount'] > config['dry_run_wallet']):
wallet = round_coin_value(config['dry_run_wallet'], config['stake_currency'])
stake = round_coin_value(config['stake_amount'], config['stake_currency'])
raise OperationalException(f"Starting balance ({wallet}) "
f"is smaller than stake_amount {stake}.")

return config

Expand Down
17 changes: 13 additions & 4 deletions freqtrade/configuration/configuration.py
Expand Up @@ -214,9 +214,6 @@ def _process_optimize_options(self, config: Dict[str, Any]) -> None:
self._args_to_config(
config, argname='enable_protections',
logstring='Parameter --enable-protections detected, enabling Protections. ...')
# Setting max_open_trades to infinite if -1
if config.get('max_open_trades') == -1:
config['max_open_trades'] = float('inf')

if 'use_max_market_positions' in self.args and not self.args["use_max_market_positions"]:
config.update({'use_max_market_positions': False})
Expand All @@ -228,11 +225,23 @@ def _process_optimize_options(self, config: Dict[str, Any]) -> None:
'overriding max_open_trades to: %s ...', config.get('max_open_trades'))
elif config['runmode'] in NON_UTIL_MODES:
logger.info('Using max_open_trades: %s ...', config.get('max_open_trades'))
# Setting max_open_trades to infinite if -1
if config.get('max_open_trades') == -1:
config['max_open_trades'] = float('inf')

if self.args.get('stake_amount', None):
# Convert explicitly to float to support CLI argument for both unlimited and value
try:
self.args['stake_amount'] = float(self.args['stake_amount'])
except ValueError:
pass

self._args_to_config(config, argname='stake_amount',
logstring='Parameter --stake-amount detected, '
'overriding stake_amount to: {} ...')

self._args_to_config(config, argname='dry_run_wallet',
logstring='Parameter --dry-run-wallet detected, '
'overriding dry_run_wallet to: {} ...')
self._args_to_config(config, argname='fee',
logstring='Parameter --fee detected, '
'setting fee to: {} ...')
Expand Down
21 changes: 13 additions & 8 deletions freqtrade/data/btanalysis.py
Expand Up @@ -10,7 +10,7 @@

from freqtrade.constants import LAST_BT_RESULT_FN
from freqtrade.misc import json_load
from freqtrade.persistence import Trade, init_db
from freqtrade.persistence import LocalTrade, Trade, init_db


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -224,7 +224,7 @@ def evaluate_result_multi(results: pd.DataFrame, timeframe: str,
return df_final[df_final['open_trades'] > max_open_trades]


def trade_list_to_dataframe(trades: List[Trade]) -> pd.DataFrame:
def trade_list_to_dataframe(trades: List[LocalTrade]) -> pd.DataFrame:
"""
Convert list of Trade objects to pandas Dataframe
:param trades: List of trade objects
Expand Down Expand Up @@ -360,13 +360,14 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,

def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date',
value_col: str = 'profit_ratio'
) -> Tuple[float, pd.Timestamp, pd.Timestamp]:
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float]:
"""
Calculate max drawdown and the corresponding close dates
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
:param date_col: Column in DataFrame to use for dates (defaults to 'close_date')
:param value_col: Column in DataFrame to use for values (defaults to 'profit_ratio')
:return: Tuple (float, highdate, lowdate) with absolute max drawdown, high and low time
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue) with absolute max drawdown,
high and low time and high and low value.
:raise: ValueError if trade-dataframe was found empty.
"""
if len(trades) == 0:
Expand All @@ -382,13 +383,17 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date'
raise ValueError("No losing trade, therefore no drawdown.")
high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]['high_value'].idxmax(), date_col]
low_date = profit_results.loc[idxmin, date_col]
return abs(min(max_drawdown_df['drawdown'])), high_date, low_date
high_val = max_drawdown_df.loc[max_drawdown_df.iloc[:idxmin]
['high_value'].idxmax(), 'cumulative']
low_val = max_drawdown_df.loc[idxmin, 'cumulative']
return abs(min(max_drawdown_df['drawdown'])), high_date, low_date, high_val, low_val


def calculate_csum(trades: pd.DataFrame) -> Tuple[float, float]:
def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]:
"""
Calculate min/max cumsum of trades, to show if the wallet/stake amount ratio is sane
:param trades: DataFrame containing trades (requires columns close_date and profit_percent)
:param starting_balance: Add starting balance to results, to show the wallets high / low points
:return: Tuple (float, float) with cumsum of profit_abs
:raise: ValueError if trade-dataframe was found empty.
"""
Expand All @@ -397,7 +402,7 @@ def calculate_csum(trades: pd.DataFrame) -> Tuple[float, float]:

csum_df = pd.DataFrame()
csum_df['sum'] = trades['profit_abs'].cumsum()
csum_min = csum_df['sum'].min()
csum_max = csum_df['sum'].max()
csum_min = csum_df['sum'].min() + starting_balance
csum_max = csum_df['sum'].max() + starting_balance

return csum_min, csum_max
3 changes: 3 additions & 0 deletions freqtrade/exchange/exchange.py
Expand Up @@ -147,6 +147,9 @@ def __del__(self):
"""
Destructor - clean up async stuff
"""
self.close()

def close(self):
logger.debug("Exchange object destroyed, closing async loop")
if self._api_async and inspect.iscoroutinefunction(self._api_async.close):
asyncio.get_event_loop().run_until_complete(self._api_async.close())
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/freqtradebot.py
Expand Up @@ -932,7 +932,7 @@ def _check_and_execute_sell(self, trade: Trade, sell_rate: float,
Check and execute sell
"""
should_sell = self.strategy.should_sell(
trade, sell_rate, datetime.utcnow(), buy, sell,
trade, sell_rate, datetime.now(timezone.utc), buy, sell,
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
)

Expand Down

0 comments on commit d6507bb

Please sign in to comment.