Merge branch 'feat/short' into funding-fee-backtesting
This commit is contained in:
commit
b1a270a53d
@ -21,6 +21,7 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH]
|
||||
[--timeframe-detail TIMEFRAME_DETAIL]
|
||||
[--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
|
||||
[--export {none,trades}] [--export-filename PATH]
|
||||
[--breakdown {day,week,month} [{day,week,month} ...]]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@ -30,7 +31,7 @@ optional arguments:
|
||||
Specify what timerange of data to use.
|
||||
--data-format-ohlcv {json,jsongz,hdf5}
|
||||
Storage format for downloaded candle (OHLCV) data.
|
||||
(default: `None`).
|
||||
(default: `json`).
|
||||
--max-open-trades INT
|
||||
Override the value of the `max_open_trades`
|
||||
configuration setting.
|
||||
@ -65,8 +66,7 @@ optional arguments:
|
||||
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-
|
||||
SampleStrategy.json`
|
||||
data.json` becomes `backtest-data-SampleStrategy.json`
|
||||
--export {none,trades}
|
||||
Export backtest results (default: trades).
|
||||
--export-filename PATH
|
||||
@ -74,6 +74,8 @@ optional arguments:
|
||||
Requires `--export` to be set as well. Example:
|
||||
`--export-filename=user_data/backtest_results/backtest
|
||||
_today.json`
|
||||
--breakdown {day,week,month} [{day,week,month} ...]
|
||||
Show backtesting breakdown per [day, week, month].
|
||||
|
||||
Common arguments:
|
||||
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
||||
@ -429,6 +431,31 @@ It contains some useful key metrics about performance of your strategy on backte
|
||||
- `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command).
|
||||
- `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column.
|
||||
|
||||
### Daily / Weekly / Monthly breakdown
|
||||
|
||||
You can get an overview over daily / weekly or monthly results by using the `--breakdown <>` switch.
|
||||
|
||||
To visualize daily and weekly breakdowns, you can use the following:
|
||||
|
||||
``` bash
|
||||
freqtrade backtesting --strategy MyAwesomeStrategy --breakdown day month
|
||||
```
|
||||
|
||||
``` output
|
||||
======================== DAY BREAKDOWN =========================
|
||||
| Day | Tot Profit USDT | Wins | Draws | Losses |
|
||||
|------------+-------------------+--------+---------+----------|
|
||||
| 03/07/2021 | 200.0 | 2 | 0 | 0 |
|
||||
| 04/07/2021 | -50.31 | 0 | 0 | 2 |
|
||||
| 05/07/2021 | 220.611 | 3 | 2 | 0 |
|
||||
| 06/07/2021 | 150.974 | 3 | 0 | 2 |
|
||||
| 07/07/2021 | -70.193 | 1 | 0 | 2 |
|
||||
| 08/07/2021 | 212.413 | 2 | 0 | 3 |
|
||||
|
||||
```
|
||||
|
||||
The output will show a table containing the realized absolute Profit (in stake currency) for the given timeperiod, as well as wins, draws and losses that materialized (closed) on this day.
|
||||
|
||||
### Further backtest-result analysis
|
||||
|
||||
To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file).
|
||||
|
@ -667,6 +667,7 @@ usage: freqtrade hyperopt-show [-h] [-v] [--logfile FILE] [-V] [-c PATH]
|
||||
[--profitable] [-n INT] [--print-json]
|
||||
[--hyperopt-filename FILENAME] [--no-header]
|
||||
[--disable-param-export]
|
||||
[--breakdown {day,week,month} [{day,week,month} ...]]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@ -680,6 +681,8 @@ optional arguments:
|
||||
--no-header Do not print epoch details header.
|
||||
--disable-param-export
|
||||
Disable automatic hyperopt parameter export.
|
||||
--breakdown {day,week,month} [{day,week,month} ...]
|
||||
Show backtesting breakdown per [day, week, month].
|
||||
|
||||
Common arguments:
|
||||
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
||||
|
@ -23,7 +23,8 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv",
|
||||
|
||||
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
|
||||
"enable_protections", "dry_run_wallet", "timeframe_detail",
|
||||
"strategy_list", "export", "exportfilename"]
|
||||
"strategy_list", "export", "exportfilename",
|
||||
"backtest_breakdown"]
|
||||
|
||||
ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
|
||||
"position_stacking", "use_max_market_positions",
|
||||
@ -89,7 +90,7 @@ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable",
|
||||
|
||||
ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index",
|
||||
"print_json", "hyperoptexportfilename", "hyperopt_show_no_header",
|
||||
"disableparamexport"]
|
||||
"disableparamexport", "backtest_breakdown"]
|
||||
|
||||
NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes",
|
||||
"list-markets", "list-pairs", "list-strategies", "list-data",
|
||||
|
@ -193,6 +193,12 @@ AVAILABLE_CLI_OPTIONS = {
|
||||
type=float,
|
||||
metavar='FLOAT',
|
||||
),
|
||||
"backtest_breakdown": Arg(
|
||||
'--breakdown',
|
||||
help='Show backtesting breakdown per [day, week, month].',
|
||||
nargs='+',
|
||||
choices=constants.BACKTEST_BREAKDOWNS
|
||||
),
|
||||
# Edge
|
||||
"stoploss_range": Arg(
|
||||
'--stoplosses',
|
||||
|
@ -96,7 +96,7 @@ def start_hyperopt_show(args: Dict[str, Any]) -> None:
|
||||
if 'strategy_name' in metrics:
|
||||
strategy_name = metrics['strategy_name']
|
||||
show_backtest_result(strategy_name, metrics,
|
||||
metrics['stake_currency'])
|
||||
metrics['stake_currency'], config.get('backtest_breakdown', []))
|
||||
|
||||
HyperoptTools.try_export_params(config, strategy_name, val)
|
||||
|
||||
|
@ -269,8 +269,12 @@ class Configuration:
|
||||
self._args_to_config(config, argname='export',
|
||||
logstring='Parameter --export detected: {} ...')
|
||||
|
||||
self._args_to_config(config, argname='backtest_breakdown',
|
||||
logstring='Parameter --breakdown detected ...')
|
||||
|
||||
self._args_to_config(config, argname='disableparamexport',
|
||||
logstring='Parameter --disableparamexport detected: {} ...')
|
||||
|
||||
# Edge section:
|
||||
if 'stoploss_range' in self.args and self.args["stoploss_range"]:
|
||||
txt_range = eval(self.args["stoploss_range"])
|
||||
|
@ -32,6 +32,7 @@ AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
|
||||
'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter']
|
||||
AVAILABLE_PROTECTIONS = ['CooldownPeriod', 'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard']
|
||||
AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5']
|
||||
BACKTEST_BREAKDOWNS = ['day', 'week', 'month']
|
||||
DRY_RUN_WALLET = 1000
|
||||
DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S'
|
||||
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
|
||||
@ -150,6 +151,10 @@ CONF_SCHEMA = {
|
||||
'ignore_buying_expired_candle_after': {'type': 'number'},
|
||||
'trading_mode': {'type': 'string', 'enum': TRADING_MODES},
|
||||
'collateral_type': {'type': 'string', 'enum': COLLATERAL_TYPES},
|
||||
'backtest_breakdown': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string', 'enum': BACKTEST_BREAKDOWNS}
|
||||
},
|
||||
'bot_name': {'type': 'string'},
|
||||
'unfilledtimeout': {
|
||||
'type': 'object',
|
||||
|
@ -90,7 +90,6 @@ class Exchange:
|
||||
self._api: ccxt.Exchange = None
|
||||
self._api_async: ccxt_async.Exchange = None
|
||||
self._markets: Dict = {}
|
||||
self._leverage_brackets: Dict = {}
|
||||
|
||||
self._config.update(config)
|
||||
|
||||
@ -159,9 +158,6 @@ class Exchange:
|
||||
self._api_async = self._init_ccxt(
|
||||
exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config)
|
||||
|
||||
if self.trading_mode != TradingMode.SPOT:
|
||||
self.fill_leverage_brackets()
|
||||
|
||||
logger.info('Using Exchange "%s"', self.name)
|
||||
|
||||
if validate:
|
||||
@ -184,6 +180,10 @@ class Exchange:
|
||||
self.markets_refresh_interval: int = exchange_config.get(
|
||||
"markets_refresh_interval", 60) * 60
|
||||
|
||||
self._leverage_brackets: Dict = {}
|
||||
if self.trading_mode != TradingMode.SPOT:
|
||||
self.fill_leverage_brackets()
|
||||
|
||||
def __del__(self):
|
||||
"""
|
||||
Destructor - clean up async stuff
|
||||
@ -1707,6 +1707,7 @@ class Exchange:
|
||||
"""
|
||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
||||
allowed on each pair
|
||||
Not used if the exchange has a static max leverage value for the account or each pair
|
||||
"""
|
||||
return
|
||||
|
||||
@ -1716,7 +1717,15 @@ class Exchange:
|
||||
:param pair: The base/quote currency pair being traded
|
||||
:nominal_value: The total value of the trade in quote currency (collateral + debt)
|
||||
"""
|
||||
return 1.0
|
||||
market = self.markets[pair]
|
||||
if (
|
||||
'limits' in market and
|
||||
'leverage' in market['limits'] and
|
||||
'max' in market['limits']['leverage']
|
||||
):
|
||||
return market['limits']['leverage']['max']
|
||||
else:
|
||||
return 1.0
|
||||
|
||||
@retrier
|
||||
def _set_leverage(
|
||||
|
@ -169,21 +169,6 @@ class Ftx(Exchange):
|
||||
return safe_value_fallback2(order, order, 'id_stop', 'id')
|
||||
return order['id']
|
||||
|
||||
def fill_leverage_brackets(self):
|
||||
"""
|
||||
FTX leverage is static across the account, and doesn't change from pair to pair,
|
||||
so _leverage_brackets doesn't need to be set
|
||||
"""
|
||||
return
|
||||
|
||||
def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float:
|
||||
"""
|
||||
Returns the maximum leverage that a pair can be traded at, which is always 20 on ftx
|
||||
:param pair: Here for super method, not used on FTX
|
||||
:nominal_value: Here for super method, not used on FTX
|
||||
"""
|
||||
return 20.0
|
||||
|
||||
def _get_mark_price_history(
|
||||
self,
|
||||
pair: str,
|
||||
@ -191,7 +176,7 @@ class Ftx(Exchange):
|
||||
end: Optional[int]
|
||||
) -> Dict:
|
||||
"""
|
||||
Get's the mark price history for a pair
|
||||
Get's the index price history for a pair
|
||||
"""
|
||||
if end:
|
||||
params = {
|
||||
|
@ -139,40 +139,6 @@ class Kraken(Exchange):
|
||||
except ccxt.BaseError as e:
|
||||
raise OperationalException(e) from e
|
||||
|
||||
def fill_leverage_brackets(self):
|
||||
"""
|
||||
Assigns property _leverage_brackets to a dictionary of information about the leverage
|
||||
allowed on each pair
|
||||
"""
|
||||
leverages = {}
|
||||
|
||||
for pair, market in self.markets.items():
|
||||
leverages[pair] = [1]
|
||||
info = market['info']
|
||||
leverage_buy = info.get('leverage_buy', [])
|
||||
leverage_sell = info.get('leverage_sell', [])
|
||||
if len(leverage_buy) > 0 or len(leverage_sell) > 0:
|
||||
if leverage_buy != leverage_sell:
|
||||
logger.warning(
|
||||
f"The buy({leverage_buy}) and sell({leverage_sell}) leverage are not equal"
|
||||
"for {pair}. Please notify freqtrade because this has never happened before"
|
||||
)
|
||||
if max(leverage_buy) <= max(leverage_sell):
|
||||
leverages[pair] += [int(lev) for lev in leverage_buy]
|
||||
else:
|
||||
leverages[pair] += [int(lev) for lev in leverage_sell]
|
||||
else:
|
||||
leverages[pair] += [int(lev) for lev in leverage_buy]
|
||||
self._leverage_brackets = leverages
|
||||
|
||||
def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float:
|
||||
"""
|
||||
Returns the maximum leverage that a pair can be traded at
|
||||
:param pair: The base/quote currency pair being traded
|
||||
:nominal_value: Here for super class, not needed on Kraken
|
||||
"""
|
||||
return float(max(self._leverage_brackets[pair]))
|
||||
|
||||
def _set_leverage(
|
||||
self,
|
||||
leverage: float,
|
||||
|
@ -4,7 +4,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
from numpy import int64
|
||||
from pandas import DataFrame
|
||||
from pandas import DataFrame, to_datetime
|
||||
from tabulate import tabulate
|
||||
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT
|
||||
@ -189,7 +189,6 @@ def generate_strategy_comparison(all_results: Dict) -> List[Dict]:
|
||||
|
||||
|
||||
def generate_edge_table(results: dict) -> str:
|
||||
|
||||
floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', 'd', 'd')
|
||||
tabular_data = []
|
||||
headers = ['Pair', 'Stoploss', 'Win Rate', 'Risk Reward Ratio',
|
||||
@ -214,6 +213,41 @@ def generate_edge_table(results: dict) -> str:
|
||||
floatfmt=floatfmt, tablefmt="orgtbl", stralign="right") # type: ignore
|
||||
|
||||
|
||||
def _get_resample_from_period(period: str) -> str:
|
||||
if period == 'day':
|
||||
return '1d'
|
||||
if period == 'week':
|
||||
return '1w'
|
||||
if period == 'month':
|
||||
return '1M'
|
||||
raise ValueError(f"Period {period} is not supported.")
|
||||
|
||||
|
||||
def generate_periodic_breakdown_stats(trade_list: List, period: str) -> List[Dict[str, Any]]:
|
||||
results = DataFrame.from_records(trade_list)
|
||||
if len(results) == 0:
|
||||
return []
|
||||
results['close_date'] = to_datetime(results['close_date'], utc=True)
|
||||
resample_period = _get_resample_from_period(period)
|
||||
resampled = results.resample(resample_period, on='close_date')
|
||||
stats = []
|
||||
for name, day in resampled:
|
||||
profit_abs = day['profit_abs'].sum().round(10)
|
||||
wins = sum(day['profit_abs'] > 0)
|
||||
draws = sum(day['profit_abs'] == 0)
|
||||
loses = sum(day['profit_abs'] < 0)
|
||||
stats.append(
|
||||
{
|
||||
'date': name.strftime('%d/%m/%Y'),
|
||||
'profit_abs': profit_abs,
|
||||
'wins': wins,
|
||||
'draws': draws,
|
||||
'loses': loses
|
||||
}
|
||||
)
|
||||
return stats
|
||||
|
||||
|
||||
def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
|
||||
""" Generate overall trade statistics """
|
||||
if len(results) == 0:
|
||||
@ -329,7 +363,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||
results['open_timestamp'] = results['open_date'].view(int64) // 1e6
|
||||
results['close_timestamp'] = results['close_date'].view(int64) // 1e6
|
||||
|
||||
backtest_days = (max_date - min_date).days
|
||||
backtest_days = (max_date - min_date).days or 1
|
||||
strat_stats = {
|
||||
'trades': results.to_dict(orient='records'),
|
||||
'locks': [lock.to_json() for lock in content['locks']],
|
||||
@ -338,6 +372,8 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||
'results_per_pair': pair_results,
|
||||
'sell_reason_summary': sell_reason_stats,
|
||||
'left_open_trades': left_open_results,
|
||||
# 'days_breakdown_stats': days_breakdown_stats,
|
||||
|
||||
'total_trades': len(results),
|
||||
'total_volume': float(results['stake_amount'].sum()),
|
||||
'avg_stake_amount': results['stake_amount'].mean() if len(results) > 0 else 0,
|
||||
@ -354,7 +390,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
|
||||
'backtest_run_start_ts': content['backtest_start_time'],
|
||||
'backtest_run_end_ts': content['backtest_end_time'],
|
||||
|
||||
'trades_per_day': round(len(results) / backtest_days, 2) if backtest_days > 0 else 0,
|
||||
'trades_per_day': round(len(results) / backtest_days, 2),
|
||||
'market_change': market_change,
|
||||
'pairlist': list(btdata.keys()),
|
||||
'stake_amount': config['stake_amount'],
|
||||
@ -506,6 +542,28 @@ def text_table_sell_reason(sell_reason_stats: List[Dict[str, Any]], stake_curren
|
||||
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
|
||||
|
||||
|
||||
def text_table_periodic_breakdown(days_breakdown_stats: List[Dict[str, Any]],
|
||||
stake_currency: str, period: str) -> str:
|
||||
"""
|
||||
Generate small table with Backtest results by days
|
||||
:param days_breakdown_stats: Days breakdown metrics
|
||||
:param stake_currency: Stakecurrency used
|
||||
:return: pretty printed table with tabulate as string
|
||||
"""
|
||||
headers = [
|
||||
period.capitalize(),
|
||||
f'Tot Profit {stake_currency}',
|
||||
'Wins',
|
||||
'Draws',
|
||||
'Losses',
|
||||
]
|
||||
output = [[
|
||||
d['date'], round_coin_value(d['profit_abs'], stake_currency, False),
|
||||
d['wins'], d['draws'], d['loses'],
|
||||
] for d in days_breakdown_stats]
|
||||
return tabulate(output, headers=headers, tablefmt="orgtbl", stralign="right")
|
||||
|
||||
|
||||
def text_table_strategy(strategy_results, stake_currency: str) -> str:
|
||||
"""
|
||||
Generate summary table per strategy
|
||||
@ -557,7 +615,10 @@ def text_table_add_metrics(strat_results: Dict) -> str:
|
||||
strat_results['stake_currency'])),
|
||||
('Absolute profit ', round_coin_value(strat_results['profit_total_abs'],
|
||||
strat_results['stake_currency'])),
|
||||
('Total profit %', f"{round(strat_results['profit_total'] * 100, 2):}%"),
|
||||
('Total profit %', f"{round(strat_results['profit_total'] * 100, 2)}%"),
|
||||
('Trades per day', strat_results['trades_per_day']),
|
||||
('Avg. daily profit %',
|
||||
f"{round(strat_results['profit_total'] / strat_results['backtest_days'] * 100, 2)}%"),
|
||||
('Avg. stake amount', round_coin_value(strat_results['avg_stake_amount'],
|
||||
strat_results['stake_currency'])),
|
||||
('Total trade volume', round_coin_value(strat_results['total_volume'],
|
||||
@ -614,7 +675,8 @@ def text_table_add_metrics(strat_results: Dict) -> str:
|
||||
return message
|
||||
|
||||
|
||||
def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: str):
|
||||
def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency: str,
|
||||
backtest_breakdown=[]):
|
||||
"""
|
||||
Print results for one strategy
|
||||
"""
|
||||
@ -636,6 +698,15 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency:
|
||||
print(' LEFT OPEN TRADES REPORT '.center(len(table.splitlines()[0]), '='))
|
||||
print(table)
|
||||
|
||||
for period in backtest_breakdown:
|
||||
days_breakdown_stats = generate_periodic_breakdown_stats(
|
||||
trade_list=results['trades'], period=period)
|
||||
table = text_table_periodic_breakdown(days_breakdown_stats=days_breakdown_stats,
|
||||
stake_currency=stake_currency, period=period)
|
||||
if isinstance(table, str) and len(table) > 0:
|
||||
print(f' {period.upper()} BREAKDOWN '.center(len(table.splitlines()[0]), '='))
|
||||
print(table)
|
||||
|
||||
table = text_table_add_metrics(results)
|
||||
if isinstance(table, str) and len(table) > 0:
|
||||
print(' SUMMARY METRICS '.center(len(table.splitlines()[0]), '='))
|
||||
@ -650,7 +721,9 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||
stake_currency = config['stake_currency']
|
||||
|
||||
for strategy, results in backtest_stats['strategy'].items():
|
||||
show_backtest_result(strategy, results, stake_currency)
|
||||
show_backtest_result(
|
||||
strategy, results, stake_currency,
|
||||
config.get('backtest_breakdown', []))
|
||||
|
||||
if len(backtest_stats['strategy']) > 1:
|
||||
# Print Strategy summary table
|
||||
|
@ -590,10 +590,10 @@ def get_markets():
|
||||
'min': 0.0001,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': {
|
||||
'leverage_buy': ['2'],
|
||||
'leverage_sell': ['2'],
|
||||
'leverage': {
|
||||
'min': 1.0,
|
||||
'max': 2.0
|
||||
}
|
||||
},
|
||||
},
|
||||
'TKN/BTC': {
|
||||
@ -619,10 +619,10 @@ def get_markets():
|
||||
'min': 0.0001,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': {
|
||||
'leverage_buy': ['2', '3', '4', '5'],
|
||||
'leverage_sell': ['2', '3', '4', '5'],
|
||||
'leverage': {
|
||||
'min': 1.0,
|
||||
'max': 5.0
|
||||
}
|
||||
},
|
||||
},
|
||||
'BLK/BTC': {
|
||||
@ -647,10 +647,10 @@ def get_markets():
|
||||
'min': 0.0001,
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': {
|
||||
'leverage_buy': ['2', '3'],
|
||||
'leverage_sell': ['2', '3'],
|
||||
'leverage': {
|
||||
'min': 1.0,
|
||||
'max': 3.0
|
||||
},
|
||||
},
|
||||
},
|
||||
'LTC/BTC': {
|
||||
@ -676,10 +676,7 @@ def get_markets():
|
||||
'max': 500000,
|
||||
},
|
||||
},
|
||||
'info': {
|
||||
'leverage_buy': [],
|
||||
'leverage_sell': [],
|
||||
},
|
||||
'info': {},
|
||||
},
|
||||
'XRP/BTC': {
|
||||
'id': 'xrpbtc',
|
||||
@ -757,10 +754,7 @@ def get_markets():
|
||||
'max': None
|
||||
}
|
||||
},
|
||||
'info': {
|
||||
'leverage_buy': [],
|
||||
'leverage_sell': [],
|
||||
},
|
||||
'info': {},
|
||||
},
|
||||
'ETH/USDT': {
|
||||
'id': 'USDT-ETH',
|
||||
|
@ -3275,6 +3275,19 @@ def test__ccxt_config(
|
||||
assert exchange._ccxt_config == ccxt_config
|
||||
|
||||
|
||||
@pytest.mark.parametrize('pair,nominal_value,max_lev', [
|
||||
("ETH/BTC", 0.0, 2.0),
|
||||
("TKN/BTC", 100.0, 5.0),
|
||||
("BLK/BTC", 173.31, 3.0),
|
||||
("LTC/BTC", 0.0, 1.0),
|
||||
("TKN/USDT", 210.30, 1.0),
|
||||
])
|
||||
def test_get_max_leverage(default_conf, mocker, pair, nominal_value, max_lev):
|
||||
# Binance has a different method of getting the max leverage
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="kraken")
|
||||
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
|
||||
|
||||
|
||||
def test_get_mark_price():
|
||||
return
|
||||
|
||||
|
@ -250,20 +250,3 @@ def test_get_order_id(mocker, default_conf):
|
||||
}
|
||||
}
|
||||
assert exchange.get_order_id_conditional(order) == '1111'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('pair,nominal_value,max_lev', [
|
||||
("ADA/BTC", 0.0, 20.0),
|
||||
("BTC/EUR", 100.0, 20.0),
|
||||
("ZEC/USD", 173.31, 20.0),
|
||||
])
|
||||
def test_get_max_leverage_ftx(default_conf, mocker, pair, nominal_value, max_lev):
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="ftx")
|
||||
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
|
||||
|
||||
|
||||
def test_fill_leverage_brackets_ftx(default_conf, mocker):
|
||||
# FTX only has one account wide leverage, so there's no leverage brackets
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="ftx")
|
||||
exchange.fill_leverage_brackets()
|
||||
assert exchange._leverage_brackets == {}
|
||||
|
@ -295,42 +295,3 @@ def test_stoploss_adjust_kraken(mocker, default_conf, sl1, sl2, sl3, side):
|
||||
# Test with invalid order case ...
|
||||
order['type'] = 'stop_loss_limit'
|
||||
assert not exchange.stoploss_adjust(sl3, order, side=side)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('pair,nominal_value,max_lev', [
|
||||
("ADA/BTC", 0.0, 3.0),
|
||||
("BTC/EUR", 100.0, 5.0),
|
||||
("ZEC/USD", 173.31, 2.0),
|
||||
])
|
||||
def test_get_max_leverage_kraken(default_conf, mocker, pair, nominal_value, max_lev):
|
||||
exchange = get_patched_exchange(mocker, default_conf, id="kraken")
|
||||
exchange._leverage_brackets = {
|
||||
'ADA/BTC': ['2', '3'],
|
||||
'BTC/EUR': ['2', '3', '4', '5'],
|
||||
'ZEC/USD': ['2']
|
||||
}
|
||||
assert exchange.get_max_leverage(pair, nominal_value) == max_lev
|
||||
|
||||
|
||||
def test_fill_leverage_brackets_kraken(default_conf, mocker):
|
||||
api_mock = MagicMock()
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
|
||||
exchange.fill_leverage_brackets()
|
||||
|
||||
assert exchange._leverage_brackets == {
|
||||
'BLK/BTC': [1, 2, 3],
|
||||
'TKN/BTC': [1, 2, 3, 4, 5],
|
||||
'ETH/BTC': [1, 2],
|
||||
'LTC/BTC': [1],
|
||||
'XRP/BTC': [1],
|
||||
'NEO/BTC': [1],
|
||||
'BTT/BTC': [1],
|
||||
'ETH/USDT': [1],
|
||||
'LTC/USDT': [1],
|
||||
'LTC/USD': [1],
|
||||
'XLTCUSDT': [1],
|
||||
'LTC/ETH': [1],
|
||||
'NEO/USDT': [1],
|
||||
'TKN/USDT': [1],
|
||||
'XRP/USDT': [1]
|
||||
}
|
||||
|
@ -1121,6 +1121,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
'--timerange', '1510694220-1510700340',
|
||||
'--enable-position-stacking',
|
||||
'--disable-max-market-positions',
|
||||
'--breakdown', 'day',
|
||||
'--strategy-list',
|
||||
CURRENT_TEST_STRATEGY,
|
||||
'TestStrategyLegacyV1',
|
||||
@ -1149,6 +1150,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
||||
captured = capsys.readouterr()
|
||||
assert 'BACKTESTING REPORT' in captured.out
|
||||
assert 'SELL REASON STATS' in captured.out
|
||||
assert 'DAY BREAKDOWN' in captured.out
|
||||
assert 'LEFT OPEN TRADES REPORT' in captured.out
|
||||
assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out
|
||||
assert 'STRATEGY SUMMARY' in captured.out
|
||||
|
@ -13,8 +13,10 @@ from freqtrade.data import history
|
||||
from freqtrade.data.btanalysis import get_latest_backtest_filename, load_backtest_data
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.enums import SellType
|
||||
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, generate_daily_stats,
|
||||
generate_edge_table, generate_pair_metrics,
|
||||
from freqtrade.optimize.optimize_reports import (_get_resample_from_period, generate_backtest_stats,
|
||||
generate_daily_stats, generate_edge_table,
|
||||
generate_pair_metrics,
|
||||
generate_periodic_breakdown_stats,
|
||||
generate_sell_reason_stats,
|
||||
generate_strategy_comparison,
|
||||
generate_trading_stats, store_backtest_stats,
|
||||
@ -378,3 +380,31 @@ def test_generate_edge_table():
|
||||
assert generate_edge_table(results).count('| ETH/BTC |') == 1
|
||||
assert generate_edge_table(results).count(
|
||||
'| Risk Reward Ratio | Required Risk Reward | Expectancy |') == 1
|
||||
|
||||
|
||||
def test_generate_periodic_breakdown_stats(testdatadir):
|
||||
filename = testdatadir / "backtest-result_new.json"
|
||||
bt_data = load_backtest_data(filename).to_dict(orient='records')
|
||||
|
||||
res = generate_periodic_breakdown_stats(bt_data, 'day')
|
||||
assert isinstance(res, list)
|
||||
assert len(res) == 21
|
||||
day = res[0]
|
||||
assert 'date' in day
|
||||
assert 'draws' in day
|
||||
assert 'loses' in day
|
||||
assert 'wins' in day
|
||||
assert 'profit_abs' in day
|
||||
|
||||
# Select empty dataframe!
|
||||
res = generate_periodic_breakdown_stats([], 'day')
|
||||
assert res == []
|
||||
|
||||
|
||||
def test__get_resample_from_period():
|
||||
|
||||
assert _get_resample_from_period('day') == '1d'
|
||||
assert _get_resample_from_period('week') == '1w'
|
||||
assert _get_resample_from_period('month') == '1M'
|
||||
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
|
||||
_get_resample_from_period('noooo')
|
||||
|
@ -1969,7 +1969,7 @@ def test_handle_trade(
|
||||
assert trade.close_date is not None
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_handle_overlapping_signals(
|
||||
default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, is_short
|
||||
) -> None:
|
||||
@ -2045,7 +2045,7 @@ def test_handle_overlapping_signals(
|
||||
assert freqtrade.handle_trade(trades[0]) is True
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog,
|
||||
is_short) -> None:
|
||||
|
||||
@ -2087,7 +2087,7 @@ def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee,
|
||||
caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_handle_trade_use_sell_signal(
|
||||
default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog, is_short
|
||||
) -> None:
|
||||
@ -2129,7 +2129,7 @@ def test_handle_trade_use_sell_signal(
|
||||
caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_close_trade(
|
||||
default_conf_usdt, ticker_usdt, limit_order_open,
|
||||
limit_order, fee, mocker, is_short
|
||||
@ -2176,7 +2176,7 @@ def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog):
|
||||
assert ftbot.strategy.analyze.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_buy_usercustom(
|
||||
default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
|
||||
limit_sell_order_old, fee, mocker, is_short
|
||||
@ -2251,7 +2251,7 @@ def test_check_handle_timedout_buy_usercustom(
|
||||
assert freqtrade.strategy.check_buy_timeout.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_buy(
|
||||
default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
|
||||
limit_sell_order_old, fee, mocker, is_short
|
||||
@ -2292,7 +2292,7 @@ def test_check_handle_timedout_buy(
|
||||
assert freqtrade.strategy.check_buy_timeout.call_count == 0
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_cancelled_buy(
|
||||
default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
|
||||
limit_sell_order_old, fee, mocker, caplog, is_short
|
||||
@ -2325,7 +2325,7 @@ def test_check_handle_cancelled_buy(
|
||||
f"{'Sell' if is_short else 'Buy'} order cancelled on exchange for Trade.*", caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_buy_exception(
|
||||
default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
|
||||
is_short, fee, mocker
|
||||
@ -2354,7 +2354,7 @@ def test_check_handle_timedout_buy_exception(
|
||||
assert nb_trades == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_sell_usercustom(
|
||||
default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker,
|
||||
is_short, open_trade_usdt
|
||||
@ -2406,7 +2406,7 @@ def test_check_handle_timedout_sell_usercustom(
|
||||
assert freqtrade.strategy.check_sell_timeout.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_sell(
|
||||
default_conf_usdt, ticker_usdt, limit_sell_order_old,
|
||||
mocker, is_short, open_trade_usdt
|
||||
@ -2439,7 +2439,7 @@ def test_check_handle_timedout_sell(
|
||||
assert freqtrade.strategy.check_sell_timeout.call_count == 0
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_cancelled_sell(
|
||||
default_conf_usdt, ticker_usdt, limit_sell_order_old, open_trade_usdt,
|
||||
is_short, mocker, caplog
|
||||
@ -2471,7 +2471,7 @@ def test_check_handle_cancelled_sell(
|
||||
assert log_has_re("Sell order cancelled on exchange for Trade.*", caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_partial(
|
||||
default_conf_usdt, ticker_usdt, limit_buy_order_old_partial, is_short,
|
||||
open_trade, mocker
|
||||
@ -2503,7 +2503,7 @@ def test_check_handle_timedout_partial(
|
||||
assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_partial_fee(
|
||||
default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short,
|
||||
limit_buy_order_old_partial, trades_for_order,
|
||||
@ -2545,7 +2545,7 @@ def test_check_handle_timedout_partial_fee(
|
||||
assert pytest.approx(trades[0].fee_open) == 0.001
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_handle_timedout_partial_except(
|
||||
default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short,
|
||||
limit_buy_order_old_partial, trades_for_order,
|
||||
@ -2619,7 +2619,7 @@ def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_tr
|
||||
caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order,
|
||||
is_short) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
@ -2667,9 +2667,9 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order,
|
||||
assert log_has_re(r"Order .* for .* not cancelled.", caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@ pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
|
||||
indirect=['limit_buy_order_canceled_empty'])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
|
||||
indirect=['limit_buy_order_canceled_empty'])
|
||||
def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_short,
|
||||
limit_buy_order_canceled_empty) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
@ -2690,8 +2690,8 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_sho
|
||||
assert nofiy_mock.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@ pytest.mark.parametrize('cancelorder', [
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize('cancelorder', [
|
||||
{},
|
||||
{'remaining': None},
|
||||
'String Return value',
|
||||
@ -2789,7 +2789,7 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf_usdt) -> None:
|
||||
assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order'
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short, open_rate, amt", [
|
||||
@pytest.mark.parametrize("is_short, open_rate, amt", [
|
||||
(False, 2.0, 30.0),
|
||||
(True, 2.02, 29.70297029),
|
||||
])
|
||||
@ -2864,7 +2864,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_
|
||||
} == last_msg
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down,
|
||||
ticker_usdt_sell_up, mocker, is_short) -> None:
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
@ -2993,7 +2993,7 @@ def test_execute_trade_exit_custom_exit_price(
|
||||
} == last_msg
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
|
||||
default_conf_usdt, ticker_usdt, fee, is_short, ticker_usdt_sell_down,
|
||||
ticker_usdt_sell_up, mocker) -> None:
|
||||
@ -3091,7 +3091,7 @@ def test_execute_trade_exit_sloe_cancel_exception(
|
||||
assert log_has('Could not cancel stoploss order abcd', caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_execute_trade_exit_with_stoploss_on_exchange(
|
||||
default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, mocker) -> None:
|
||||
|
||||
@ -3309,7 +3309,7 @@ def test_execute_trade_exit_market_order(
|
||||
} == last_msg
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_usdt, fee, is_short,
|
||||
ticker_usdt_sell_up, mocker) -> None:
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
@ -3345,7 +3345,7 @@ def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_u
|
||||
assert mock_insuf.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type,is_short', [
|
||||
@pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,sell_type,is_short', [
|
||||
# Enable profit
|
||||
(True, 2.18, 2.2, False, True, SellType.SELL_SIGNAL.value, False),
|
||||
(True, 2.18, 2.2, False, True, SellType.SELL_SIGNAL.value, True),
|
||||
@ -3439,7 +3439,7 @@ def test_sell_not_enough_balance(default_conf_usdt, limit_order, limit_order_ope
|
||||
assert trade.amount != amnt
|
||||
|
||||
|
||||
@ pytest.mark.parametrize('amount_wallet,has_err', [
|
||||
@pytest.mark.parametrize('amount_wallet,has_err', [
|
||||
(95.29, False),
|
||||
(91.29, True)
|
||||
])
|
||||
@ -3476,7 +3476,7 @@ def test__safe_exit_amount(default_conf_usdt, fee, caplog, mocker, amount_wallet
|
||||
assert wallet_update.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
|
||||
ticker_usdt_sell_down, mocker, caplog, is_short) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
@ -3515,7 +3515,7 @@ def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
|
||||
assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open, is_short,
|
||||
fee, mocker) -> None:
|
||||
patch_RPCManager(mocker)
|
||||
@ -3561,7 +3561,7 @@ def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_op
|
||||
assert trade.sell_reason == SellType.ROI.value
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short,val1,val2", [
|
||||
@pytest.mark.parametrize("is_short,val1,val2", [
|
||||
(False, 1.5, 1.1),
|
||||
(True, 0.5, 0.9)
|
||||
])
|
||||
@ -3623,7 +3623,7 @@ def test_trailing_stop_loss(default_conf_usdt, limit_order_open,
|
||||
assert trade.sell_reason == SellType.TRAILING_STOP_LOSS.value
|
||||
|
||||
|
||||
@ pytest.mark.parametrize('offset,trail_if_reached,second_sl,is_short', [
|
||||
@pytest.mark.parametrize('offset,trail_if_reached,second_sl,is_short', [
|
||||
(0, False, 2.0394, False),
|
||||
(0.011, False, 2.0394, False),
|
||||
(0.055, True, 1.8, False),
|
||||
@ -3845,7 +3845,7 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock
|
||||
)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize(
|
||||
@pytest.mark.parametrize(
|
||||
'fee_par,fee_reduction_amount,use_ticker_usdt_rate,expected_log', [
|
||||
# basic, amount does not change
|
||||
({'cost': 0.008, 'currency': 'ETH'}, 0, False, None),
|
||||
@ -3898,7 +3898,7 @@ def test_get_real_amount(
|
||||
assert log_has(expected_log, caplog)
|
||||
|
||||
|
||||
@ pytest.mark.parametrize(
|
||||
@pytest.mark.parametrize(
|
||||
'fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [
|
||||
# basic, amount is reduced by fee
|
||||
(None, None, 0.001, 0.001, 7.992),
|
||||
@ -4050,7 +4050,7 @@ def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
|
||||
assert freqtrade.get_real_amount(trade, order) == amount
|
||||
|
||||
|
||||
@ pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
|
||||
@pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
|
||||
(8.0, 0.0, 10, 8),
|
||||
(8.0, 0.0, 0, 8),
|
||||
(8.0, 0.1, 0, 7.9),
|
||||
@ -4079,11 +4079,11 @@ def test_apply_fee_conditional(default_conf_usdt, fee, mocker,
|
||||
assert walletmock.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("delta, is_high_delta", [
|
||||
@pytest.mark.parametrize("delta, is_high_delta", [
|
||||
(0.1, False),
|
||||
(100, True),
|
||||
])
|
||||
@ pytest.mark.parametrize('is_short, open_rate', [
|
||||
@pytest.mark.parametrize('is_short, open_rate', [
|
||||
(False, 2.0),
|
||||
(True, 2.02),
|
||||
])
|
||||
@ -4129,7 +4129,7 @@ def test_order_book_depth_of_market(
|
||||
assert whitelist == default_conf_usdt['exchange']['pair_whitelist']
|
||||
|
||||
|
||||
@ pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [
|
||||
@pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [
|
||||
(False, 0.045, 0.046, 2, None),
|
||||
(True, 0.042, 0.046, 1, {'bids': [[]], 'asks': [[]]})
|
||||
])
|
||||
@ -4182,7 +4182,7 @@ def test_check_depth_of_market(default_conf_usdt, mocker, order_book_l2) -> None
|
||||
assert freqtrade._check_depth_of_market('ETH/BTC', conf, side=SignalDirection.LONG) is False
|
||||
|
||||
|
||||
@ pytest.mark.parametrize('is_short', [False, True])
|
||||
@pytest.mark.parametrize('is_short', [False, True])
|
||||
def test_order_book_ask_strategy(
|
||||
default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, is_short,
|
||||
limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None:
|
||||
@ -4263,7 +4263,7 @@ def test_startup_trade_reinit(default_conf_usdt, edge_conf, mocker):
|
||||
assert reinit_mock.call_count == 0
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open,
|
||||
caplog):
|
||||
default_conf_usdt['dry_run'] = True
|
||||
@ -4296,8 +4296,8 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_
|
||||
caplog)
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@ pytest.mark.parametrize("is_short,buy_calls,sell_calls", [
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short,buy_calls,sell_calls", [
|
||||
(False, 1, 2),
|
||||
(True, 2, 1),
|
||||
])
|
||||
@ -4325,8 +4325,8 @@ def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_order, lim
|
||||
assert sell_mock.call_count == sell_calls
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_check_for_open_trades(mocker, default_conf_usdt, fee, is_short):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
@ -4343,8 +4343,8 @@ def test_check_for_open_trades(mocker, default_conf_usdt, fee, is_short):
|
||||
assert 'Handle these trades manually' in freqtrade.rpc.send_msg.call_args[0][0]['status']
|
||||
|
||||
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog, is_short):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
@ -4370,8 +4370,8 @@ def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog, is_s
|
||||
assert len(Order.get_open_orders()) == 2
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, fee, is_short):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
@ -4434,8 +4434,8 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, f
|
||||
assert trade.fee_close_currency is not None
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog, is_short):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
|
||||
@ -4483,7 +4483,7 @@ def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog, is_sh
|
||||
r".* for order .*\.", caplog)
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_handle_insufficient_funds(mocker, default_conf_usdt, fee):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
mock_rlo = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.refind_lost_order')
|
||||
@ -4521,8 +4521,8 @@ def test_handle_insufficient_funds(mocker, default_conf_usdt, fee):
|
||||
assert mock_bof.call_count == 1
|
||||
|
||||
|
||||
@ pytest.mark.usefixtures("init_persistence")
|
||||
@ pytest.mark.parametrize("is_short", [False, True])
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog, is_short):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
Loading…
Reference in New Issue
Block a user