Merge branch 'freqtrade:develop' into develop

This commit is contained in:
hippocritical
2023-08-02 20:02:55 +02:00
committed by GitHub
99 changed files with 1223 additions and 759 deletions
+4 -4
View File
@@ -13,11 +13,11 @@ repos:
- id: mypy
exclude: build_helpers
additional_dependencies:
- types-cachetools==5.3.0.5
- types-cachetools==5.3.0.6
- types-filelock==3.2.7
- types-requests==2.31.0.1
- types-tabulate==0.9.0.2
- types-python-dateutil==2.8.19.13
- types-requests==2.31.0.2
- types-tabulate==0.9.0.3
- types-python-dateutil==2.8.19.14
- SQLAlchemy==2.0.19
# stages: [push]
+2 -2
View File
@@ -206,6 +206,6 @@
"recursive_strategy_search": false,
"add_config_files": [],
"reduce_df_footprint": false,
"dataformat_ohlcv": "json",
"dataformat_trades": "jsongz"
"dataformat_ohlcv": "feather",
"dataformat_trades": "feather"
}
+4
View File
@@ -324,6 +324,7 @@ A backtesting result will look like that:
| Days win/draw/lose | 12 / 82 / 25 |
| Avg. Duration Winners | 4:23:00 |
| Avg. Duration Loser | 6:55:00 |
| Max Consecutive Wins / Loss | 3 / 4 |
| Rejected Entry signals | 3089 |
| Entry/Exit Timeouts | 0 / 0 |
| Canceled Trade Entries | 34 |
@@ -428,6 +429,7 @@ It contains some useful key metrics about performance of your strategy on backte
| Days win/draw/lose | 12 / 82 / 25 |
| Avg. Duration Winners | 4:23:00 |
| Avg. Duration Loser | 6:55:00 |
| Max Consecutive Wins / Loss | 3 / 4 |
| Rejected Entry signals | 3089 |
| Entry/Exit Timeouts | 0 / 0 |
| Canceled Trade Entries | 34 |
@@ -467,6 +469,7 @@ It contains some useful key metrics about performance of your strategy on backte
- `Best day` / `Worst day`: Best and worst day based on daily profit.
- `Days win/draw/lose`: Winning / Losing days (draws are usually days without closed trade).
- `Avg. Duration Winners` / `Avg. Duration Loser`: Average durations for winning and losing trades.
- `Max Consecutive Wins / Loss`: Maximum consecutive wins/losses in a row.
- `Rejected Entry signals`: Trade entry signals that could not be acted upon due to `max_open_trades` being reached.
- `Entry/Exit Timeouts`: Entry/exit orders which did not fill (only applicable if custom pricing is used).
- `Canceled Trade Entries`: Number of trades that have been canceled by user request via `adjust_entry_price`.
@@ -534,6 +537,7 @@ Since backtesting lacks some detailed information about what happens within a ca
- ROI
- exits are compared to high - but the ROI value is used (e.g. ROI = 2%, high=5% - so the exit will be at 2%)
- exits are never "below the candle", so a ROI of 2% may result in a exit at 2.4% if low was at 2.4% profit
- ROI entries which came into effect on the triggering candle (e.g. `120: 0.02` for 1h candles, from `60: 0.05`) will use the candle's open as exit rate
- Force-exits caused by `<N>=-1` ROI entries use low as exit value, unless N falls on the candle open (e.g. `120: -1` for 1h candles)
- Stoploss exits happen exactly at stoploss price, even if low was lower, but the loss will be `2 * fees` higher than the stoploss price
- Stoploss is evaluated before ROI within one candle. So you can often see more trades with the `stoploss` exit reason comparing to the results obtained with the same strategy in the Dry Run/Live Trade modes
+2 -2
View File
@@ -251,8 +251,8 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `db_url` | Declares database URL to use. NOTE: This defaults to `sqlite:///tradesv3.dryrun.sqlite` if `dry_run` is `true`, and to `sqlite:///tradesv3.sqlite` for production instances. <br> **Datatype:** String, SQLAlchemy connect string
| `logfile` | Specifies logfile name. Uses a rolling strategy for log file rotation for 10 files with the 1MB limit per file. <br> **Datatype:** String
| `add_config_files` | Additional config files. These files will be loaded and merged with the current config file. The files are resolved relative to the initial file.<br> *Defaults to `[]`*. <br> **Datatype:** List of strings
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `json`*. <br> **Datatype:** String
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `jsongz`*. <br> **Datatype:** String
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `feather`*. <br> **Datatype:** String
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `feather`*. <br> **Datatype:** String
| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage (and decreasing train/inference timing in FreqAI). (Currently only affects FreqAI use-cases) <br> **Datatype:** Boolean. <br> Default: `False`.
### Parameters in the strategy
+33 -33
View File
@@ -27,11 +27,11 @@ usage: freqtrade download-data [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[--exchange EXCHANGE]
[-t TIMEFRAMES [TIMEFRAMES ...]] [--erase]
[--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}]
[--data-format-trades {json,jsongz,hdf5}]
[--data-format-trades {json,jsongz,hdf5,feather}]
[--trading-mode {spot,margin,futures}]
[--prepend]
optional arguments:
options:
-h, --help show this help message and exit
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space-
@@ -48,8 +48,7 @@ optional arguments:
--dl-trades Download trades instead of OHLCV data. The bot will
resample trades to the desired timeframe as specified
as --timeframes/-t.
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
config is provided.
--exchange EXCHANGE Exchange name. Only valid if no config is provided.
-t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...]
Specify which tickers to download. Space-separated
list. Default: `1m 5m`.
@@ -57,17 +56,18 @@ optional arguments:
exchange/pairs/timeframes.
--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}
Storage format for downloaded candle (OHLCV) data.
(default: `json`).
--data-format-trades {json,jsongz,hdf5}
(default: `feather`).
--data-format-trades {json,jsongz,hdf5,feather}
Storage format for downloaded trades data. (default:
`jsongz`).
`feather`).
--trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures}
Select Trading mode
--prepend Allow data prepending. (Data-appending is disabled)
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are:
--logfile FILE, --log-file FILE
Log to the file specified. Special values are:
'syslog', 'journald'. See the documentation for more
details.
-V, --version show program's version number and exit
@@ -157,7 +157,7 @@ Freqtrade currently supports the following data-formats:
* `json` - plain "text" json files
* `jsongz` - a gzip-zipped version of json files
* `hdf5` - a high performance datastore
* `feather` - a dataformat based on Apache Arrow (OHLCV only)
* `feather` - a dataformat based on Apache Arrow
* `parquet` - columnar datastore (OHLCV only)
By default, OHLCV data is stored as `json` data, while trades data is stored as `jsongz` data.
@@ -255,7 +255,7 @@ usage: freqtrade convert-data [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[--trading-mode {spot,margin,futures}]
[--candle-types {spot,futures,mark,index,premiumIndex,funding_rate} [{spot,futures,mark,index,premiumIndex,funding_rate} ...]]
optional arguments:
options:
-h, --help show this help message and exit
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space-
@@ -266,19 +266,20 @@ optional arguments:
Destination format for data conversion.
--erase Clean all existing data for the selected
exchange/pairs/timeframes.
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
config is provided.
--exchange EXCHANGE Exchange name. Only valid if no config is provided.
-t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...]
Specify which tickers to download. Space-separated
list. Default: `1m 5m`.
--trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures}
Select Trading mode
--candle-types {spot,futures,mark,index,premiumIndex,funding_rate} [{spot,futures,mark,index,premiumIndex,funding_rate} ...]
Select candle type to use
Select candle type to convert. Defaults to all
available types.
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are:
--logfile FILE, --log-file FILE
Log to the file specified. Special values are:
'syslog', 'journald'. See the documentation for more
details.
-V, --version show program's version number and exit
@@ -291,7 +292,6 @@ Common arguments:
Path to directory with historical backtesting data.
--userdir PATH, --user-data-dir PATH
Path to userdata directory.
```
### Example converting data
@@ -314,7 +314,7 @@ usage: freqtrade convert-trade-data [-h] [-v] [--logfile FILE] [-V] [-c PATH]
{json,jsongz,hdf5,feather,parquet}
[--erase] [--exchange EXCHANGE]
optional arguments:
options:
-h, --help show this help message and exit
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space-
@@ -325,12 +325,12 @@ optional arguments:
Destination format for data conversion.
--erase Clean all existing data for the selected
exchange/pairs/timeframes.
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
config is provided.
--exchange EXCHANGE Exchange name. Only valid if no config is provided.
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are:
--logfile FILE, --log-file FILE
Log to the file specified. Special values are:
'syslog', 'journald'. See the documentation for more
details.
-V, --version show program's version number and exit
@@ -367,9 +367,9 @@ usage: freqtrade trades-to-ohlcv [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[-t TIMEFRAMES [TIMEFRAMES ...]]
[--exchange EXCHANGE]
[--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}]
[--data-format-trades {json,jsongz,hdf5}]
[--data-format-trades {json,jsongz,hdf5,feather}]
optional arguments:
options:
-h, --help show this help message and exit
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space-
@@ -377,18 +377,18 @@ optional arguments:
-t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...]
Specify which tickers to download. Space-separated
list. Default: `1m 5m`.
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
config is provided.
--exchange EXCHANGE Exchange name. Only valid if no config is provided.
--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}
Storage format for downloaded candle (OHLCV) data.
(default: `json`).
--data-format-trades {json,jsongz,hdf5}
(default: `feather`).
--data-format-trades {json,jsongz,hdf5,feather}
Storage format for downloaded trades data. (default:
`jsongz`).
`feather`).
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are:
--logfile FILE, --log-file FILE
Log to the file specified. Special values are:
'syslog', 'journald'. See the documentation for more
details.
-V, --version show program's version number and exit
@@ -422,13 +422,12 @@ usage: freqtrade list-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--trading-mode {spot,margin,futures}]
[--show-timerange]
optional arguments:
options:
-h, --help show this help message and exit
--exchange EXCHANGE Exchange name (default: `bittrex`). Only valid if no
config is provided.
--exchange EXCHANGE Exchange name. Only valid if no config is provided.
--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}
Storage format for downloaded candle (OHLCV) data.
(default: `json`).
(default: `feather`).
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space-
separated.
@@ -439,7 +438,8 @@ optional arguments:
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are:
--logfile FILE, --log-file FILE
Log to the file specified. Special values are:
'syslog', 'journald'. See the documentation for more
details.
-V, --version show program's version number and exit
@@ -474,7 +474,7 @@ ETH/USDT 5m, 15m, 30m, 1h, 2h, 4h
By default, `download-data` sub-command downloads Candles (OHLCV) data. Some exchanges also provide historic trade-data via their API.
This data can be useful if you need many different timeframes, since it is only downloaded once, and then resampled locally to the desired timeframes.
Since this data is large by default, the files use gzip by default. They are stored in your data-directory with the naming convention of `<pair>-trades.json.gz` (`ETH_BTC-trades.json.gz`). Incremental mode is also supported, as for historic OHLCV data, so downloading the data once per week with `--days 8` will create an incremental data-repository.
Since this data is large by default, the files use the feather fileformat by default. They are stored in your data-directory with the naming convention of `<pair>-trades.feather` (`ETH_BTC-trades.feather`). Incremental mode is also supported, as for historic OHLCV data, so downloading the data once per week with `--days 8` will create an incremental data-repository.
To use this mode, simply add `--dl-trades` to your call. This will swap the download method to download trades, and resamples the data locally.
+7 -2
View File
@@ -433,9 +433,14 @@ While this strategy is most likely too simple to provide consistent profit, it s
`range` property may also be used with `DecimalParameter` and `CategoricalParameter`. `RealParameter` does not provide this property due to infinite search space.
??? Hint "Performance tip"
During normal hyperopting, indicators are calculated once and supplied to each epoch, linearly increasing RAM usage as a factor of increasing cores. As this also has performance implications, hyperopt provides `--analyze-per-epoch` which will move the execution of `populate_indicators()` to the epoch process, calculating a single value per parameter per epoch instead of using the `.range` functionality. In this case, `.range` functionality will only return the actually used value. This will reduce RAM usage, but increase CPU usage. However, your hyperopting run will be less likely to fail due to Out Of Memory (OOM) issues.
During normal hyperopting, indicators are calculated once and supplied to each epoch, linearly increasing RAM usage as a factor of increasing cores. As this also has performance implications, there are two alternatives to reduce RAM usage
In either case, you should try to use space ranges as small as possible this will improve CPU/RAM usage in both scenarios.
* Move `ema_short` and `ema_long` calculations from `populate_indicators()` to `populate_entry_trend()`. Since `populate_entry_trend()` gonna be calculated every epochs, you don't need to use `.range` functionality.
* hyperopt provides `--analyze-per-epoch` which will move the execution of `populate_indicators()` to the epoch process, calculating a single value per parameter per epoch instead of using the `.range` functionality. In this case, `.range` functionality will only return the actually used value.
These alternatives will reduce RAM usage, but increase CPU usage. However, your hyperopting run will be less likely to fail due to Out Of Memory (OOM) issues.
Whether you are using `.range` functionality or the alternatives above, you should try to use space ranges as small as possible since this will improve CPU/RAM usage.
## Optimizing protections
+1 -1
View File
@@ -211,7 +211,7 @@ The user is responsible for providing a server or local file that returns a JSON
```json
{
"pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"],
"refresh_period": 1800,
"refresh_period": 1800
}
```
+2 -2
View File
@@ -4,8 +4,8 @@ This section will highlight a few projects from members of the community.
- [Example freqtrade strategies](https://github.com/freqtrade/freqtrade-strategies/)
- [FrequentHippo - Grafana dashboard with dry/live runs and backtests](http://frequenthippo.ddns.net:3000/) (by hippocritical).
- [Online pairlist generator](http://pairlist.robot.co.network/) (by Blood4rc).
- [Freqtrade Backtesting Project](http://bt.robot.co.network/) (by Blood4rc).
- [Online pairlist generator](https://remotepairlist.com/) (by Blood4rc).
- [Freqtrade Backtesting Project](https://bt.robot.co.network/) (by Blood4rc).
- [Freqtrade analysis notebook](https://github.com/froggleston/freqtrade_analysis_notebook) (by Froggleston).
- [TUI for freqtrade](https://github.com/froggleston/freqtrade-frogtrade9000) (by Froggleston).
- [Bot Academy](https://botacademy.ddns.net/) (by stash86) - Blog about crypto bot projects.
+3 -3
View File
@@ -1,6 +1,6 @@
markdown==3.3.7
mkdocs==1.4.3
mkdocs-material==9.1.18
markdown==3.4.4
mkdocs==1.5.2
mkdocs-material==9.1.21
mdx_truly_sane_lists==1.3
pymdown-extensions==10.1
jinja2==3.1.2
+1 -1
View File
@@ -1,5 +1,5 @@
""" Freqtrade bot """
__version__ = '2023.7.dev'
__version__ = '2023.8.dev'
if 'dev' in __version__:
from pathlib import Path
+2 -2
View File
@@ -435,12 +435,12 @@ AVAILABLE_CLI_OPTIONS = {
),
"dataformat_ohlcv": Arg(
'--data-format-ohlcv',
help='Storage format for downloaded candle (OHLCV) data. (default: `json`).',
help='Storage format for downloaded candle (OHLCV) data. (default: `feather`).',
choices=constants.AVAILABLE_DATAHANDLERS,
),
"dataformat_trades": Arg(
'--data-format-trades',
help='Storage format for downloaded trades data. (default: `jsongz`).',
help='Storage format for downloaded trades data. (default: `feather`).',
choices=constants.AVAILABLE_DATAHANDLERS_TRADES,
),
"show_timerange": Arg(
+1
View File
@@ -3,4 +3,5 @@
from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.configuration.config_validation import validate_config_consistency
from freqtrade.configuration.configuration import Configuration
from freqtrade.configuration.detect_environment import running_in_docker
from freqtrade.configuration.timerange import TimeRange
+3 -3
View File
@@ -153,7 +153,7 @@ CONF_SCHEMA = {
},
},
'amount_reserve_percent': {'type': 'number', 'minimum': 0.0, 'maximum': 0.5},
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True, 'minimum': -1},
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
'trailing_stop': {'type': 'boolean'},
'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1},
@@ -446,12 +446,12 @@ CONF_SCHEMA = {
'dataformat_ohlcv': {
'type': 'string',
'enum': AVAILABLE_DATAHANDLERS,
'default': 'json'
'default': 'feather'
},
'dataformat_trades': {
'type': 'string',
'enum': AVAILABLE_DATAHANDLERS_TRADES,
'default': 'jsongz'
'default': 'feather'
},
'position_adjustment_enable': {'type': 'boolean'},
'max_entry_position_adjustment': {'type': ['integer', 'number'], 'minimum': -1},
+30 -21
View File
@@ -5,7 +5,7 @@ import logging
from copy import copy
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, List, Literal, Optional, Union
import numpy as np
import pandas as pd
@@ -15,6 +15,7 @@ from freqtrade.exceptions import OperationalException
from freqtrade.misc import json_load
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
from freqtrade.persistence import LocalTrade, Trade, init_db
from freqtrade.types import BacktestHistoryEntryType, BacktestResultType
logger = logging.getLogger(__name__)
@@ -128,7 +129,7 @@ def load_backtest_metadata(filename: Union[Path, str]) -> Dict[str, Any]:
raise OperationalException('Unexpected error while loading backtest metadata.') from e
def load_backtest_stats(filename: Union[Path, str]) -> Dict[str, Any]:
def load_backtest_stats(filename: Union[Path, str]) -> BacktestResultType:
"""
Load backtest statistics file.
:param filename: pathlib.Path object, or string pointing to the file.
@@ -147,21 +148,21 @@ def load_backtest_stats(filename: Union[Path, str]) -> Dict[str, Any]:
# Legacy list format does not contain metadata.
if isinstance(data, dict):
data['metadata'] = load_backtest_metadata(filename)
return data
def load_and_merge_backtest_result(strategy_name: str, filename: Path, results: Dict[str, Any]):
"""
Load one strategy from multi-strategy result
and merge it with results
Load one strategy from multi-strategy result and merge it with results
:param strategy_name: Name of the strategy contained in the result
:param filename: Backtest-result-filename to load
:param results: dict to merge the result to.
"""
bt_data = load_backtest_stats(filename)
for k in ('metadata', 'strategy'):
k: Literal['metadata', 'strategy']
for k in ('metadata', 'strategy'): # type: ignore
results[k][strategy_name] = bt_data[k][strategy_name]
results['metadata'][strategy_name]['filename'] = filename.stem
comparison = bt_data['strategy_comparison']
for i in range(len(comparison)):
if comparison[i]['key'] == strategy_name:
@@ -170,27 +171,36 @@ def load_and_merge_backtest_result(strategy_name: str, filename: Path, results:
def _get_backtest_files(dirname: Path) -> List[Path]:
# Weird glob expression here avoids including .meta.json files.
return list(reversed(sorted(dirname.glob('backtest-result-*-[0-9][0-9].json'))))
def get_backtest_resultlist(dirname: Path):
def get_backtest_resultlist(dirname: Path) -> List[BacktestHistoryEntryType]:
"""
Get list of backtest results read from metadata files
"""
results = []
for filename in _get_backtest_files(dirname):
metadata = load_backtest_metadata(filename)
if not metadata:
continue
for s, v in metadata.items():
results.append({
'filename': filename.name,
'strategy': s,
'run_id': v['run_id'],
'backtest_start_time': v['backtest_start_time'],
return [
{
'filename': filename.stem,
'strategy': s,
'run_id': v['run_id'],
'backtest_start_time': v['backtest_start_time'],
}
for filename in _get_backtest_files(dirname)
for s, v in load_backtest_metadata(filename).items()
if v
]
})
return results
def delete_backtest_result(file_abs: Path):
"""
Delete backtest result file and corresponding metadata file.
"""
# *.meta.json
logger.info(f"Deleting backtest result file: {file_abs.name}")
file_abs_meta = file_abs.with_suffix('.meta.json')
file_abs.unlink()
file_abs_meta.unlink()
def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, str],
@@ -211,7 +221,6 @@ def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, s
'strategy_comparison': [],
}
# Weird glob expression here avoids including .meta.json files.
for filename in _get_backtest_files(dirname):
metadata = load_backtest_metadata(filename)
if not metadata:
+7 -1
View File
@@ -96,8 +96,14 @@ def ohlcv_fill_up_missing_data(dataframe: DataFrame, timeframe: str, pair: str)
'volume': 'sum'
}
timeframe_minutes = timeframe_to_minutes(timeframe)
resample_interval = f'{timeframe_minutes}min'
if timeframe_minutes >= 43200 and timeframe_minutes < 525600:
# Monthly candles need special treatment to stick to the 1st of the month
resample_interval = f'{timeframe}S'
elif timeframe_minutes > 43200:
resample_interval = timeframe
# Resample to create "NAN" values
df = dataframe.resample(f'{timeframe_minutes}min', on='date').agg(ohlcv_dict)
df = dataframe.resample(resample_interval, on='date').agg(ohlcv_dict)
# Forwardfill close for missing columns
df['close'] = df['close'].fillna(method='ffill')
+1 -1
View File
@@ -310,7 +310,7 @@ class DataProvider:
timeframe=timeframe or self._config['timeframe'],
datadir=self._config['datadir'],
timerange=timerange,
data_format=self._config.get('dataformat_ohlcv', 'json'),
data_format=self._config['dataformat_ohlcv'],
candle_type=_candle_type,
)
+4 -4
View File
@@ -69,7 +69,7 @@ def load_data(datadir: Path,
fill_up_missing: bool = True,
startup_candles: int = 0,
fail_without_data: bool = False,
data_format: str = 'json',
data_format: str = 'feather',
candle_type: CandleType = CandleType.SPOT,
user_futures_funding_rate: Optional[int] = None,
) -> Dict[str, DataFrame]:
@@ -394,7 +394,7 @@ def _download_trades_history(exchange: Exchange,
def refresh_backtest_trades_data(exchange: Exchange, pairs: List[str], datadir: Path,
timerange: TimeRange, new_pairs_days: int = 30,
erase: bool = False, data_format: str = 'jsongz') -> List[str]:
erase: bool = False, data_format: str = 'feather') -> List[str]:
"""
Refresh stored trades data for backtesting and hyperopt operations.
Used by freqtrade download-data subcommand.
@@ -427,8 +427,8 @@ def convert_trades_to_ohlcv(
datadir: Path,
timerange: TimeRange,
erase: bool = False,
data_format_ohlcv: str = 'json',
data_format_trades: str = 'jsongz',
data_format_ohlcv: str = 'feather',
data_format_trades: str = 'feather',
candle_type: CandleType = CandleType.SPOT
) -> None:
"""
+1 -1
View File
@@ -427,6 +427,6 @@ def get_datahandler(datadir: Path, data_format: Optional[str] = None,
"""
if not data_handler:
HandlerClass = get_datahandlerclass(data_format or 'json')
HandlerClass = get_datahandlerclass(data_format or 'feather')
data_handler = HandlerClass(datadir)
return data_handler
+3 -3
View File
@@ -115,7 +115,7 @@ class Edge:
exchange=self.exchange,
timeframe=self.strategy.timeframe,
timerange=timerange_startup,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=self.config.get('candle_type_def', CandleType.SPOT),
)
# Download informative pairs too
@@ -132,7 +132,7 @@ class Edge:
exchange=self.exchange,
timeframe=timeframe,
timerange=timerange_startup,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=self.config.get('candle_type_def', CandleType.SPOT),
)
@@ -142,7 +142,7 @@ class Edge:
timeframe=self.strategy.timeframe,
timerange=self._timerange,
startup_candles=self.strategy.startup_candle_count,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=self.config.get('candle_type_def', CandleType.SPOT),
)
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -635,7 +635,7 @@ class FreqaiDataDrawer:
timeframe=tf,
pair=pair,
timerange=timerange,
data_format=self.config.get("dataformat_ohlcv", "json"),
data_format=self.config.get("dataformat_ohlcv", "feather"),
candle_type=self.config.get("candle_type_def", CandleType.SPOT),
)
+1 -1
View File
@@ -50,7 +50,7 @@ def download_all_data_for_training(dp: DataProvider, config: Config) -> None:
timerange=timerange,
new_pairs_days=new_pairs_days,
erase=False,
data_format=config.get("dataformat_ohlcv", "json"),
data_format=config.get("dataformat_ohlcv", "feather"),
trading_mode=config.get("trading_mode", "spot"),
prepend=config.get("prepend_data", False),
)
+4 -1
View File
@@ -1383,7 +1383,10 @@ class FreqtradeBot(LoggingMixin):
latest_candle_close_date = timeframe_to_next_date(self.strategy.timeframe,
latest_candle_open_date)
# Check if new candle
if order_obj and latest_candle_close_date > order_obj.order_date_utc:
if (
order_obj and order_obj.side == trade.entry_side
and latest_candle_close_date > order_obj.order_date_utc
):
# New candle
proposed_rate = self.exchange.get_rate(
trade.pair, side='entry', is_short=trade.is_short, refresh=True)
+7
View File
@@ -116,6 +116,13 @@ def file_load_json(file: Path):
return pairdata
def is_file_in_dir(file: Path, directory: Path) -> bool:
"""
Helper function to check if file is in directory.
"""
return file.is_file() and file.parent.samefile(directory)
def pair_to_filename(pair: str) -> str:
for ch in ['/', ' ', '.', '@', '$', '+', ':']:
pair = pair.replace(ch, '_')
+6 -5
View File
@@ -39,6 +39,7 @@ from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.types import BacktestResultType, get_BacktestResultType_default
from freqtrade.util.binance_mig import migrate_binance_futures_data
from freqtrade.wallets import Wallets
@@ -77,7 +78,7 @@ class Backtesting:
LoggingMixin.show_output = False
self.config = config
self.results: Dict[str, Any] = {}
self.results: BacktestResultType = get_BacktestResultType_default()
self.trade_id_counter: int = 0
self.order_id_counter: int = 0
@@ -239,7 +240,7 @@ class Backtesting:
timerange=self.timerange,
startup_candles=self.config['startup_candle_count'],
fail_without_data=True,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=self.config.get('candle_type_def', CandleType.SPOT)
)
@@ -268,7 +269,7 @@ class Backtesting:
timerange=self.timerange,
startup_candles=0,
fail_without_data=True,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=self.config.get('candle_type_def', CandleType.SPOT)
)
else:
@@ -282,7 +283,7 @@ class Backtesting:
timerange=self.timerange,
startup_candles=0,
fail_without_data=True,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=CandleType.FUNDING_RATE
)
@@ -294,7 +295,7 @@ class Backtesting:
timerange=self.timerange,
startup_candles=0,
fail_without_data=True,
data_format=self.config.get('dataformat_ohlcv', 'json'),
data_format=self.config['dataformat_ohlcv'],
candle_type=CandleType.from_string(self.exchange.get_option("mark_ohlcv_price"))
)
# Combine data to avoid combining the data per trade.
@@ -1,5 +1,6 @@
# flake8: noqa: F401
from freqtrade.optimize.optimize_reports.bt_output import (generate_edge_table,
generate_wins_draws_losses,
show_backtest_result,
show_backtest_results,
show_sorted_pairlist,
@@ -14,5 +15,4 @@ from freqtrade.optimize.optimize_reports.optimize_reports import (
generate_all_periodic_breakdown_stats, generate_backtest_stats, generate_daily_stats,
generate_exit_reason_stats, generate_pair_metrics, generate_periodic_breakdown_stats,
generate_rejected_signals, generate_strategy_comparison, generate_strategy_stats,
generate_tag_metrics, generate_trade_signal_candles, generate_trading_stats,
generate_wins_draws_losses)
generate_tag_metrics, generate_trade_signal_candles, generate_trading_stats)
@@ -5,8 +5,8 @@ from tabulate import tabulate
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config
from freqtrade.misc import decimals_per_coin, round_coin_value
from freqtrade.optimize.optimize_reports.optimize_reports import (generate_periodic_breakdown_stats,
generate_wins_draws_losses)
from freqtrade.optimize.optimize_reports.optimize_reports import generate_periodic_breakdown_stats
from freqtrade.types import BacktestResultType
logger = logging.getLogger(__name__)
@@ -30,6 +30,16 @@ def _get_line_header(first_column: str, stake_currency: str,
'Win Draw Loss Win%']
def generate_wins_draws_losses(wins, draws, losses):
if wins > 0 and losses == 0:
wl_ratio = '100'
elif wins == 0:
wl_ratio = '0'
else:
wl_ratio = f'{100.0 / (wins + draws + losses) * wins:.1f}' if losses > 0 else '100'
return f'{wins:>4} {draws:>4} {losses:>4} {wl_ratio:>4}'
def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: str) -> str:
"""
Generates and returns a text table for the given backtest data and the results dataframe
@@ -261,6 +271,9 @@ def text_table_add_metrics(strat_results: Dict) -> str:
f"{strat_results['draw_days']} / {strat_results['losing_days']}"),
('Avg. Duration Winners', f"{strat_results['winner_holding_avg']}"),
('Avg. Duration Loser', f"{strat_results['loser_holding_avg']}"),
('Max Consecutive Wins / Loss',
f"{strat_results['max_consecutive_wins']} / {strat_results['max_consecutive_losses']}"
if 'max_consecutive_losses' in strat_results else 'N/A'),
('Rejected Entry signals', strat_results.get('rejected_signals', 'N/A')),
('Entry/Exit Timeouts',
f"{strat_results.get('timedout_entry_orders', 'N/A')} / "
@@ -351,7 +364,7 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency:
print()
def show_backtest_results(config: Config, backtest_stats: Dict):
def show_backtest_results(config: Config, backtest_stats: BacktestResultType):
stake_currency = config['stake_currency']
for strategy, results in backtest_stats['strategy'].items():
@@ -371,7 +384,7 @@ def show_backtest_results(config: Config, backtest_stats: Dict):
print('\nFor more details, please look at the detail tables above')
def show_sorted_pairlist(config: Config, backtest_stats: Dict):
def show_sorted_pairlist(config: Config, backtest_stats: BacktestResultType):
if config.get('backtest_show_pair_list', False):
for strategy, results in backtest_stats['strategy'].items():
print(f"Pairs for Strategy {strategy}: \n[")
@@ -2,18 +2,17 @@ import logging
from pathlib import Path
from typing import Dict
from pandas import DataFrame
from freqtrade.constants import LAST_BT_RESULT_FN
from freqtrade.misc import file_dump_joblib, file_dump_json
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
from freqtrade.types import BacktestResultType
logger = logging.getLogger(__name__)
def store_backtest_stats(
recordfilename: Path, stats: Dict[str, DataFrame], dtappendix: str) -> None:
recordfilename: Path, stats: BacktestResultType, dtappendix: str) -> None:
"""
Stores backtest results
:param recordfilename: Path object, which can either be a filename or a directory.
@@ -31,9 +30,13 @@ def store_backtest_stats(
# Store metadata separately.
file_dump_json(get_backtest_metadata_filename(filename), stats['metadata'])
del stats['metadata']
# Don't mutate the original stats dict.
stats_copy = {
'strategy': stats['strategy'],
'strategy_comparison': stats['strategy_comparison'],
}
file_dump_json(filename, stats)
file_dump_json(filename, stats_copy)
latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN)
file_dump_json(latest_filename, {'latest_backtest': str(filename.name)})
@@ -1,15 +1,17 @@
import logging
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Union
from typing import Any, Dict, List, Tuple, Union
from pandas import DataFrame, concat, to_datetime
import numpy as np
from pandas import DataFrame, Series, concat, to_datetime
from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT, IntOrInf
from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum,
calculate_expectancy, calculate_market_change,
calculate_max_drawdown, calculate_sharpe, calculate_sortino)
from freqtrade.misc import decimals_per_coin, round_coin_value
from freqtrade.types import BacktestResultType
logger = logging.getLogger(__name__)
@@ -57,16 +59,6 @@ def generate_rejected_signals(preprocessed_df: Dict[str, DataFrame],
return rejected_candles_only
def generate_wins_draws_losses(wins, draws, losses):
if wins > 0 and losses == 0:
wl_ratio = '100'
elif wins == 0:
wl_ratio = '0'
else:
wl_ratio = f'{100.0 / (wins + draws + losses) * wins:.1f}' if losses > 0 else '100'
return f'{wins:>4} {draws:>4} {losses:>4} {wl_ratio:>4}'
def _generate_result_line(result: DataFrame, starting_balance: int, first_column: str) -> Dict:
"""
Generate one result dict, with "first_column" as key.
@@ -262,6 +254,23 @@ def generate_all_periodic_breakdown_stats(trade_list: List) -> Dict[str, List]:
return result
def calc_streak(dataframe: DataFrame) -> Tuple[int, int]:
"""
Calculate consecutive win and loss streaks
:param dataframe: Dataframe containing the trades dataframe, with profit_ratio column
:return: Tuple containing consecutive wins and losses
"""
df = Series(np.where(dataframe['profit_ratio'] > 0, 'win', 'loss')).to_frame('result')
df['streaks'] = df['result'].ne(df['result'].shift()).cumsum().rename('streaks')
df['counter'] = df['streaks'].groupby(df['streaks']).cumcount() + 1
res = df.groupby(df['result']).max()
#
cons_wins = int(res.loc['win', 'counter']) if 'win' in res.index else 0
cons_losses = int(res.loc['loss', 'counter']) if 'loss' in res.index else 0
return cons_wins, cons_losses
def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
""" Generate overall trade statistics """
if len(results) == 0:
@@ -273,6 +282,8 @@ def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
'holding_avg': timedelta(),
'winner_holding_avg': timedelta(),
'loser_holding_avg': timedelta(),
'max_consecutive_wins': 0,
'max_consecutive_losses': 0,
}
winning_trades = results.loc[results['profit_ratio'] > 0]
@@ -285,6 +296,7 @@ def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
if not winning_trades.empty else timedelta())
loser_holding_avg = (timedelta(minutes=round(losing_trades['trade_duration'].mean()))
if not losing_trades.empty else timedelta())
winstreak, loss_streak = calc_streak(results)
return {
'wins': len(winning_trades),
@@ -297,6 +309,8 @@ def generate_trading_stats(results: DataFrame) -> Dict[str, Any]:
'winner_holding_avg_s': winner_holding_avg.total_seconds(),
'loser_holding_avg': loser_holding_avg,
'loser_holding_avg_s': loser_holding_avg.total_seconds(),
'max_consecutive_wins': winstreak,
'max_consecutive_losses': loss_streak,
}
@@ -522,7 +536,7 @@ def generate_strategy_stats(pairlist: List[str],
def generate_backtest_stats(btdata: Dict[str, DataFrame],
all_results: Dict[str, Dict[str, Union[DataFrame, Dict]]],
min_date: datetime, max_date: datetime
) -> Dict[str, Any]:
) -> BacktestResultType:
"""
:param btdata: Backtest data
:param all_results: backtest result - dictionary in the form:
@@ -531,7 +545,7 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame],
:param max_date: Backtest end date
:return: Dictionary containing results per strategy and a strategy summary.
"""
result: Dict[str, Any] = {
result: BacktestResultType = {
'metadata': {},
'strategy': {},
'strategy_comparison': [],
+1 -1
View File
@@ -55,7 +55,7 @@ def init_plotscript(config, markets: List, startup_candles: int = 0):
timeframe=config['timeframe'],
timerange=timerange,
startup_candles=startup_candles,
data_format=config.get('dataformat_ohlcv', 'json'),
data_format=config['dataformat_ohlcv'],
candle_type=config.get('candle_type_def', CandleType.SPOT)
)
+3 -2
View File
@@ -3,15 +3,16 @@ Remote PairList provider
Provides pair list fetched from a remote source
"""
import json
import logging
from pathlib import Path
from typing import Any, Dict, List, Tuple
import rapidjson
import requests
from cachetools import TTLCache
from freqtrade import __version__
from freqtrade.configuration.load_config import CONFIG_PARSE_MODE
from freqtrade.constants import Config
from freqtrade.exceptions import OperationalException
from freqtrade.exchange.types import Tickers
@@ -236,7 +237,7 @@ class RemotePairList(IPairList):
if file_path.exists():
with file_path.open() as json_file:
# Load the JSON data into a dictionary
jsonparse = json.load(json_file)
jsonparse = rapidjson.load(json_file, parse_mode=CONFIG_PARSE_MODE)
try:
pairlist = self.process_json(jsonparse)
+25 -5
View File
@@ -2,6 +2,7 @@ import asyncio
import logging
from copy import deepcopy
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List
from fastapi import APIRouter, BackgroundTasks, Depends
@@ -9,16 +10,18 @@ from fastapi.exceptions import HTTPException
from freqtrade.configuration.config_validation import validate_config_consistency
from freqtrade.constants import Config
from freqtrade.data.btanalysis import get_backtest_resultlist, load_and_merge_backtest_result
from freqtrade.data.btanalysis import (delete_backtest_result, get_backtest_resultlist,
load_and_merge_backtest_result)
from freqtrade.enums import BacktestState
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exchange.common import remove_exchange_credentials
from freqtrade.misc import deep_merge_dicts
from freqtrade.misc import deep_merge_dicts, is_file_in_dir
from freqtrade.rpc.api_server.api_schemas import (BacktestHistoryEntry, BacktestRequest,
BacktestResponse)
from freqtrade.rpc.api_server.deps import get_config
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
from freqtrade.rpc.rpc import RPCException
from freqtrade.types import get_BacktestResultType_default
logger = logging.getLogger(__name__)
@@ -67,7 +70,7 @@ def __run_backtest_bg(btconfig: Config):
ApiBG.bt['bt'].enable_protections = btconfig.get('enable_protections', False)
ApiBG.bt['bt'].strategylist = [strat]
ApiBG.bt['bt'].results = {}
ApiBG.bt['bt'].results = get_BacktestResultType_default()
ApiBG.bt['bt'].load_prior_backtest()
ApiBG.bt['bt'].abort = False
@@ -245,13 +248,16 @@ def api_backtest_history(config=Depends(get_config)):
tags=['webserver', 'backtest'])
def api_backtest_history_result(filename: str, strategy: str, config=Depends(get_config)):
# Get backtest result history, read from metadata files
fn = config['user_data_dir'] / 'backtest_results' / filename
bt_results_base: Path = config['user_data_dir'] / 'backtest_results'
fn = (bt_results_base / filename).with_suffix('.json')
results: Dict[str, Any] = {
'metadata': {},
'strategy': {},
'strategy_comparison': [],
}
if not is_file_in_dir(fn, bt_results_base):
raise HTTPException(status_code=404, detail="File not found.")
load_and_merge_backtest_result(strategy, fn, results)
return {
"status": "ended",
@@ -261,3 +267,17 @@ def api_backtest_history_result(filename: str, strategy: str, config=Depends(get
"status_msg": "Historic result",
"backtest_result": results,
}
@router.delete('/backtest/history/{file}', response_model=List[BacktestHistoryEntry],
tags=['webserver', 'backtest'])
def api_delete_backtest_history_entry(file: str, config=Depends(get_config)):
# Get backtest result history, read from metadata files
bt_results_base: Path = config['user_data_dir'] / 'backtest_results'
file_abs = (bt_results_base / file).with_suffix('.json')
# Ensure file is in backtest_results directory
if not is_file_in_dir(file_abs, bt_results_base):
raise HTTPException(status_code=404, detail="File not found.")
delete_backtest_result(file_abs)
return get_backtest_resultlist(config['user_data_dir'] / 'backtest_results')
+1
View File
@@ -520,6 +520,7 @@ class BacktestResponse(BaseModel):
backtest_result: Optional[Dict[str, Any]]
# TODO: This is a copy of BacktestHistoryEntryType
class BacktestHistoryEntry(BaseModel):
filename: str
strategy: str
+11 -3
View File
@@ -49,7 +49,8 @@ logger = logging.getLogger(__name__)
# 2.28: Switch reload endpoint to Post
# 2.29: Add /exchanges endpoint
# 2.30: new /pairlists endpoint
API_VERSION = 2.30
# 2.31: new /backtest/history/ delete endpoint
API_VERSION = 2.31
# Public API, requires no auth.
router_public = APIRouter()
@@ -268,7 +269,10 @@ def pair_history(pair: str, timeframe: str, timerange: str, strategy: str,
'timerange': timerange,
'freqaimodel': freqaimodel if freqaimodel else config.get('freqaimodel'),
})
return RPC._rpc_analysed_history_full(config, pair, timeframe, exchange)
try:
return RPC._rpc_analysed_history_full(config, pair, timeframe, exchange)
except Exception as e:
raise HTTPException(status_code=502, detail=str(e))
@router.get('/plot_config', response_model=PlotConfig, tags=['candle data'])
@@ -283,7 +287,10 @@ def plot_config(strategy: Optional[str] = None, config=Depends(get_config),
config1.update({
'strategy': strategy
})
try:
return PlotConfig.parse_obj(RPC._rpc_plot_config_with_strategy(config1))
except Exception as e:
raise HTTPException(status_code=502, detail=str(e))
@router.get('/strategies', response_model=StrategyListResponse, tags=['strategy'])
@@ -308,7 +315,8 @@ def get_strategy(strategy: str, config=Depends(get_config)):
extra_dir=config_.get('strategy_path'))
except OperationalException:
raise HTTPException(status_code=404, detail='Strategy not found')
except Exception as e:
raise HTTPException(status_code=502, detail=str(e))
return {
'strategy': strategy_obj.get_strategy_name(),
'code': strategy_obj.__source__,
+1 -1
View File
@@ -30,7 +30,7 @@ async def ui_version():
}
def is_relative_to(path, base) -> bool:
def is_relative_to(path: Path, base: Path) -> bool:
# Helper function simulating behaviour of is_relative_to, which was only added in python 3.9
try:
path.relative_to(base)
+2 -1
View File
@@ -8,6 +8,7 @@ from fastapi import Depends, FastAPI
from fastapi.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse
from freqtrade.configuration import running_in_docker
from freqtrade.constants import Config
from freqtrade.exceptions import OperationalException
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
@@ -182,7 +183,7 @@ class ApiServer(RPCHandler):
rest_port = self._config['api_server']['listen_port']
logger.info(f'Starting HTTP Server at {rest_ip}:{rest_port}')
if not IPv4Address(rest_ip).is_loopback:
if not IPv4Address(rest_ip).is_loopback and not running_in_docker():
logger.warning("SECURITY WARNING - Local Rest Server listening to external connections")
logger.warning("SECURITY WARNING - This is insecure please set to your loopback,"
"e.g 127.0.0.1 in config.json")
+4 -4
View File
@@ -1179,8 +1179,8 @@ class RPC:
""" Analyzed dataframe in Dict form """
_data, last_analyzed = self.__rpc_analysed_dataframe_raw(pair, timeframe, limit)
return self._convert_dataframe_to_dict(self._freqtrade.config['strategy'],
pair, timeframe, _data, last_analyzed)
return RPC._convert_dataframe_to_dict(self._freqtrade.config['strategy'],
pair, timeframe, _data, last_analyzed)
def __rpc_analysed_dataframe_raw(
self,
@@ -1262,7 +1262,7 @@ class RPC:
pairs=[pair],
timeframe=timeframe,
timerange=timerange_parsed,
data_format=config.get('dataformat_ohlcv', 'json'),
data_format=config['dataformat_ohlcv'],
candle_type=config.get('candle_type_def', CandleType.SPOT),
startup_candles=startup_candles,
)
@@ -1277,7 +1277,7 @@ class RPC:
df_analyzed = trim_dataframe(df_analyzed, timerange_parsed, startup_candles=startup_candles)
return RPC._convert_dataframe_to_dict(strategy.get_strategy_name(), pair, timeframe,
df_analyzed, dt_now())
df_analyzed.copy(), dt_now())
def _rpc_plot_config(self) -> Dict[str, Any]:
if (self._freqtrade.strategy.plot_config and
+5 -1
View File
@@ -1 +1,5 @@
from freqtrade.types.valid_exchanges_type import ValidExchangesType # noqa: F401
# flake8: noqa: F401
from freqtrade.types.backtest_result_type import (BacktestHistoryEntryType, BacktestMetadataType,
BacktestResultType,
get_BacktestResultType_default)
from freqtrade.types.valid_exchanges_type import ValidExchangesType
+27
View File
@@ -0,0 +1,27 @@
from typing import Any, Dict, List
from typing_extensions import TypedDict
class BacktestMetadataType(TypedDict):
run_id: str
backtest_start_time: int
class BacktestResultType(TypedDict):
metadata: Dict[str, Any] # BacktestMetadataType
strategy: Dict[str, Any]
strategy_comparison: List[Any]
def get_BacktestResultType_default() -> BacktestResultType:
return {
'metadata': {},
'strategy': {},
'strategy_comparison': [],
}
class BacktestHistoryEntryType(BacktestMetadataType):
filename: str
strategy: str
+1 -1
View File
@@ -64,7 +64,7 @@ def migrate_binance_futures_data(config: Config):
return
from freqtrade.data.history.idatahandler import get_datahandler
dhc = get_datahandler(config['datadir'], config.get('dataformat_ohlcv', 'json'))
dhc = get_datahandler(config['datadir'], config['dataformat_ohlcv'])
paircombs = dhc.ohlcv_get_available_data(
config['datadir'],
+6 -6
View File
@@ -7,7 +7,7 @@
-r docs/requirements-docs.txt
coveralls==3.3.1
ruff==0.0.278
ruff==0.0.282
mypy==1.4.1
pre-commit==3.3.3
pytest==7.4.0
@@ -20,11 +20,11 @@ isort==5.12.0
time-machine==2.11.0
# Convert jupyter notebooks to markdown documents
nbconvert==7.7.0
nbconvert==7.7.3
# mypy types
types-cachetools==5.3.0.5
types-cachetools==5.3.0.6
types-filelock==3.2.7
types-requests==2.31.0.1
types-tabulate==0.9.0.2
types-python-dateutil==2.8.19.13
types-requests==2.31.0.2
types-tabulate==0.9.0.3
types-python-dateutil==2.8.19.14
+9 -9
View File
@@ -1,10 +1,10 @@
numpy==1.25.1; python_version > '3.8'
numpy==1.25.2; python_version > '3.8'
numpy==1.24.3; python_version <= '3.8'
pandas==2.0.3
pandas-ta==0.3.14b
ccxt==4.0.34
cryptography==41.0.2; platform_machine != 'armv7l'
ccxt==4.0.47
cryptography==41.0.3; platform_machine != 'armv7l'
cryptography==40.0.1; platform_machine == 'armv7l'
aiohttp==3.8.5
SQLAlchemy==2.0.19
@@ -14,8 +14,8 @@ httpx>=0.24.1
arrow==1.2.3
cachetools==5.3.1
requests==2.31.0
urllib3==2.0.3
jsonschema==4.18.3
urllib3==2.0.4
jsonschema==4.18.4
TA-Lib==0.4.27
technical==1.4.0
tabulate==0.9.0
@@ -24,7 +24,7 @@ jinja2==3.1.2
tables==3.8.0
blosc==1.11.1
joblib==1.3.1
rich==13.4.2
rich==13.5.2
pyarrow==12.0.1; platform_machine != 'armv7l'
# find first, C search in arrays
@@ -39,10 +39,10 @@ orjson==3.9.2
sdnotify==0.3.2
# API Server
fastapi==0.100.0
fastapi==0.100.1
pydantic==1.10.11
uvicorn==0.23.0
pyjwt==2.7.0
uvicorn==0.23.2
pyjwt==2.8.0
aiofiles==23.1.0
psutil==5.9.5
+2 -8
View File
@@ -1389,8 +1389,6 @@ def test_convert_data_trades(mocker, testdatadir):
def test_start_list_data(testdatadir, capsys):
args = [
"list-data",
"--data-format-ohlcv",
"json",
"--datadir",
str(testdatadir),
]
@@ -1398,14 +1396,14 @@ def test_start_list_data(testdatadir, capsys):
pargs['config'] = None
start_list_data(pargs)
captured = capsys.readouterr()
assert "Found 17 pair / timeframe combinations." in captured.out
assert "Found 16 pair / timeframe combinations." in captured.out
assert "\n| Pair | Timeframe | Type |\n" in captured.out
assert "\n| UNITTEST/BTC | 1m, 5m, 8m, 30m | spot |\n" in captured.out
args = [
"list-data",
"--data-format-ohlcv",
"json",
"feather",
"--pairs", "XRP/ETH",
"--datadir",
str(testdatadir),
@@ -1421,8 +1419,6 @@ def test_start_list_data(testdatadir, capsys):
args = [
"list-data",
"--data-format-ohlcv",
"json",
"--trading-mode", "futures",
"--datadir",
str(testdatadir),
@@ -1439,8 +1435,6 @@ def test_start_list_data(testdatadir, capsys):
args = [
"list-data",
"--data-format-ohlcv",
"json",
"--pairs", "XRP/ETH",
"--datadir",
str(testdatadir),
+1
View File
@@ -526,6 +526,7 @@ def get_default_conf(testdatadir):
"disableparamexport": True,
"internals": {},
"export": "none",
"dataformat_ohlcv": "feather",
"candle_type_def": CandleType.SPOT,
}
return configuration
+71 -4
View File
@@ -135,6 +135,73 @@ def test_ohlcv_fill_up_missing_data2(caplog):
f"{len(data)} - after: {len(data2)}.*", caplog)
def test_ohlcv_to_dataframe_1M():
# Monthly ticks from 2019-09-01 to 2023-07-01
ticks = [
[1567296000000, 8042.08, 10475.54, 7700.67, 8041.96, 608742.1109999999],
[1569888000000, 8285.31, 10408.48, 7172.76, 9150.0, 2439561.887],
[1572566400000, 9149.88, 9550.0, 6510.19, 7542.93, 4042674.725],
[1575158400000, 7541.08, 7800.0, 6427.0, 7189.0, 4063882.296],
[1577836800000, 7189.43, 9599.0, 6863.44, 9364.51, 5165281.358],
[1580515200000, 9364.5, 10540.0, 8450.0, 8531.98, 4581788.124],
[1583020800000, 8532.5, 9204.0, 3621.81, 6407.1, 10859497.479],
[1585699200000, 6407.1, 9479.77, 6140.0, 8624.76, 11276526.968],
[1588291200000, 8623.61, 10080.0, 7940.0, 9446.43, 12469561.02],
[1590969600000, 9446.49, 10497.25, 8816.4, 9138.87, 6684044.201],
[1593561600000, 9138.88, 11488.0, 8900.0, 11343.68, 5709327.926],
[1596240000000, 11343.67, 12499.42, 10490.0, 11658.11, 6746487.129],
[1598918400000, 11658.11, 12061.07, 9808.58, 10773.0, 6442697.051],
[1601510400000, 10773.0, 14140.0, 10371.03, 13783.73, 7404103.004],
[1604188800000, 13783.73, 19944.0, 13195.0, 19720.0, 12328272.549],
[1606780800000, 19722.09, 29376.7, 17555.0, 28951.68, 10067314.24],
[1609459200000, 28948.19, 42125.51, 27800.0, 33126.21, 12408873.079],
[1612137600000, 33125.11, 58472.14, 32322.47, 45163.36, 8784474.482],
[1614556800000, 45162.64, 61950.0, 44972.49, 58807.24, 9459821.267],
[1617235200000, 58810.99, 64986.11, 46930.43, 57684.16, 7895051.389],
[1619827200000, 57688.29, 59654.0, 28688.0, 37243.38, 16790964.443],
[1622505600000, 37244.36, 41413.0, 28780.01, 35031.39, 23474519.886],
[1625097600000, 35031.39, 48168.6, 29242.24, 41448.11, 16932491.175],
[1627776000000, 41448.1, 50600.0, 37291.0, 47150.32, 13645800.254],
[1630454400000, 47150.32, 52950.0, 39503.58, 43796.57, 10734742.869],
[1633046400000, 43799.49, 67150.0, 43260.01, 61348.61, 9111112.847],
[1635724800000, 61347.14, 69198.7, 53245.0, 56975.0, 7111424.463],
[1638316800000, 56978.06, 59100.0, 40888.89, 46210.56, 8404449.024],
[1640995200000, 46210.57, 48000.0, 32853.83, 38439.04, 11047479.277],
[1643673600000, 38439.04, 45847.5, 34303.7, 43155.0, 10910339.91],
[1646092800000, 43155.0, 48200.0, 37134.0, 45506.0, 10459721.586],
[1648771200000, 45505.9, 47448.0, 37550.0, 37614.5, 8463568.862],
[1651363200000, 37614.4, 40071.7, 26631.0, 31797.8, 14463715.774],
[1654041600000, 31797.9, 31986.1, 17593.2, 19923.5, 20710810.306],
[1656633600000, 19923.3, 24700.0, 18780.1, 23290.1, 20582518.513],
[1659312000000, 23290.1, 25200.0, 19508.0, 20041.5, 17221921.557],
[1661990400000, 20041.4, 22850.0, 18084.3, 19411.7, 21935261.414],
[1664582400000, 19411.6, 21088.0, 17917.8, 20482.0, 16625843.584],
[1667260800000, 20482.1, 21473.7, 15443.2, 17153.3, 18460614.013],
[1669852800000, 17153.4, 18400.0, 16210.0, 16537.6, 9702408.711],
[1672531200000, 16537.5, 23962.7, 16488.0, 23119.4, 14732180.645],
[1675209600000, 23119.5, 25347.6, 21338.0, 23129.6, 15025197.415],
[1677628800000, 23129.7, 29184.8, 19521.6, 28454.9, 23317458.541],
[1680307200000, 28454.8, 31059.0, 26919.3, 29223.0, 14654208.219],
[1682899200000, 29223.0, 29840.0, 25751.0, 27201.1, 13328157.284],
[1685577600000, 27201.1, 31500.0, 24777.0, 30460.2, 14099299.273],
[1688169600000, 30460.2, 31850.0, 28830.0, 29338.8, 8760361.377]
]
data = ohlcv_to_dataframe(ticks, '1M', pair="UNITTEST/USDT",
fill_missing=False, drop_incomplete=False)
assert len(data) == len(ticks)
assert data.iloc[0]['date'].strftime('%Y-%m-%d') == '2019-09-01'
assert data.iloc[-1]['date'].strftime('%Y-%m-%d') == '2023-07-01'
# Test with filling missing data
data = ohlcv_to_dataframe(ticks, '1M', pair="UNITTEST/USDT",
fill_missing=True, drop_incomplete=False)
assert len(data) == len(ticks)
assert data.iloc[0]['date'].strftime('%Y-%m-%d') == '2019-09-01'
assert data.iloc[-1]['date'].strftime('%Y-%m-%d') == '2023-07-01'
def test_ohlcv_drop_incomplete(caplog):
timeframe = '1d'
ticks = [
@@ -304,8 +371,8 @@ def test_convert_ohlcv_format(default_conf, testdatadir, tmpdir, file_base, cand
files_temp = []
files_new = []
for file in file_base:
file_orig = testdatadir / f"{prependix}{file}.json"
file_temp = tmpdir1 / f"{prependix}{file}.json"
file_orig = testdatadir / f"{prependix}{file}.feather"
file_temp = tmpdir1 / f"{prependix}{file}.feather"
file_new = tmpdir1 / f"{prependix}{file}.json.gz"
IDataHandler.create_dir_if_needed(file_temp)
copyfile(file_orig, file_temp)
@@ -327,7 +394,7 @@ def test_convert_ohlcv_format(default_conf, testdatadir, tmpdir, file_base, cand
convert_ohlcv_format(
default_conf,
convert_from='json',
convert_from='feather',
convert_to='jsongz',
erase=False,
)
@@ -341,7 +408,7 @@ def test_convert_ohlcv_format(default_conf, testdatadir, tmpdir, file_base, cand
convert_ohlcv_format(
default_conf,
convert_from='jsongz',
convert_to='json',
convert_to='feather',
erase=True,
)
for file in (files_temp):
+8 -14
View File
@@ -20,7 +20,7 @@ from tests.conftest import log_has, log_has_re
def test_datahandler_ohlcv_get_pairs(testdatadir):
pairs = JsonDataHandler.ohlcv_get_pairs(testdatadir, '5m', candle_type=CandleType.SPOT)
pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, '5m', candle_type=CandleType.SPOT)
# Convert to set to avoid failures due to sorting
assert set(pairs) == {'UNITTEST/BTC', 'XLM/BTC', 'ETH/BTC', 'TRX/BTC', 'LTC/BTC',
'XMR/BTC', 'ZEC/BTC', 'ADA/BTC', 'ETC/BTC', 'NXT/BTC',
@@ -32,7 +32,7 @@ def test_datahandler_ohlcv_get_pairs(testdatadir):
pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, '5m', candle_type=CandleType.SPOT)
assert set(pairs) == {'UNITTEST/BTC'}
pairs = JsonDataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK)
pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.MARK)
assert set(pairs) == {'UNITTEST/USDT:USDT', 'XRP/USDT:USDT'}
pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, '1h', candle_type=CandleType.FUTURES)
@@ -79,7 +79,7 @@ def test_rebuild_pair_from_filename(input, expected):
def test_datahandler_ohlcv_get_available_data(testdatadir):
paircombs = JsonDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT)
paircombs = FeatherDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT)
# Convert to set to avoid failures due to sorting
assert set(paircombs) == {
('UNITTEST/BTC', '5m', CandleType.SPOT),
@@ -98,10 +98,9 @@ def test_datahandler_ohlcv_get_available_data(testdatadir):
('XRP/ETH', '5m', CandleType.SPOT),
('UNITTEST/BTC', '30m', CandleType.SPOT),
('UNITTEST/BTC', '8m', CandleType.SPOT),
('NOPAIR/XXX', '4m', CandleType.SPOT),
}
paircombs = JsonDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.FUTURES)
paircombs = FeatherDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.FUTURES)
# Convert to set to avoid failures due to sorting
assert set(paircombs) == {
('UNITTEST/USDT:USDT', '1h', 'mark'),
@@ -140,16 +139,11 @@ def test_jsondatahandler_ohlcv_purge(mocker, testdatadir):
def test_jsondatahandler_ohlcv_load(testdatadir, caplog):
dh = JsonDataHandler(testdatadir)
df = dh.ohlcv_load('XRP/ETH', '5m', 'spot')
assert len(df) == 712
df_mark = dh.ohlcv_load('UNITTEST/USDT:USDT', '1h', candle_type="mark")
assert len(df_mark) == 100
df = dh.ohlcv_load('UNITTEST/BTC', '1m', 'spot')
assert len(df) > 0
df_no_mark = dh.ohlcv_load('UNITTEST/USDT', '1h', 'spot')
assert len(df_no_mark) == 0
# Failure case (empty array)
# # Failure case (empty array)
df1 = dh.ohlcv_load('NOPAIR/XXX', '4m', 'spot')
assert len(df1) == 0
assert log_has("Could not load data for NOPAIR/XXX.", caplog)
@@ -444,7 +438,7 @@ def test_generic_datahandler_ohlcv_load_and_resave(
tmpdir2 = tmpdir1 / 'futures'
tmpdir2.mkdir()
# Load data from one common file
dhbase = get_datahandler(testdatadir, 'json')
dhbase = get_datahandler(testdatadir, 'feather')
ohlcv = dhbase._ohlcv_load(pair, timeframe, None, candle_type=candle_type)
assert isinstance(ohlcv, DataFrame)
assert len(ohlcv) > 0
+6 -5
View File
@@ -63,9 +63,10 @@ def test_historic_ohlcv(mocker, default_conf, ohlcv_history):
def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history):
hdf5loadmock = MagicMock(return_value=ohlcv_history)
jsonloadmock = MagicMock(return_value=ohlcv_history)
featherloadmock = MagicMock(return_value=ohlcv_history)
mocker.patch("freqtrade.data.history.hdf5datahandler.HDF5DataHandler._ohlcv_load", hdf5loadmock)
mocker.patch("freqtrade.data.history.jsondatahandler.JsonDataHandler._ohlcv_load", jsonloadmock)
mocker.patch("freqtrade.data.history.featherdatahandler.FeatherDataHandler._ohlcv_load",
featherloadmock)
default_conf["runmode"] = RunMode.BACKTEST
exchange = get_patched_exchange(mocker, default_conf)
@@ -73,17 +74,17 @@ def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history):
data = dp.historic_ohlcv("UNITTEST/BTC", "5m")
assert isinstance(data, DataFrame)
hdf5loadmock.assert_not_called()
jsonloadmock.assert_called_once()
featherloadmock.assert_called_once()
# Switching to dataformat hdf5
hdf5loadmock.reset_mock()
jsonloadmock.reset_mock()
featherloadmock.reset_mock()
default_conf["dataformat_ohlcv"] = "hdf5"
dp = DataProvider(default_conf, exchange)
data = dp.historic_ohlcv("UNITTEST/BTC", "5m")
assert isinstance(data, DataFrame)
hdf5loadmock.assert_called_once()
jsonloadmock.assert_not_called()
featherloadmock.assert_not_called()
@pytest.mark.parametrize('candle_type', [
+18 -16
View File
@@ -68,7 +68,7 @@ def test_load_data_7min_timeframe(caplog, testdatadir) -> None:
def test_load_data_1min_timeframe(ohlcv_history, mocker, caplog, testdatadir) -> None:
mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history)
file = testdatadir / 'UNITTEST_BTC-1m.json'
file = testdatadir / 'UNITTEST_BTC-1m.feather'
load_data(datadir=testdatadir, timeframe='1m', pairs=['UNITTEST/BTC'])
assert file.is_file()
assert not log_has(
@@ -79,7 +79,7 @@ def test_load_data_1min_timeframe(ohlcv_history, mocker, caplog, testdatadir) ->
def test_load_data_mark(ohlcv_history, mocker, caplog, testdatadir) -> None:
mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history)
file = testdatadir / 'futures/UNITTEST_USDT_USDT-1h-mark.json'
file = testdatadir / 'futures/UNITTEST_USDT_USDT-1h-mark.feather'
load_data(datadir=testdatadir, timeframe='1h', pairs=['UNITTEST/BTC'], candle_type='mark')
assert file.is_file()
assert not log_has(
@@ -90,7 +90,7 @@ def test_load_data_mark(ohlcv_history, mocker, caplog, testdatadir) -> None:
def test_load_data_startup_candles(mocker, testdatadir) -> None:
ltfmock = mocker.patch(
'freqtrade.data.history.jsondatahandler.JsonDataHandler._ohlcv_load',
'freqtrade.data.history.featherdatahandler.FeatherDataHandler._ohlcv_load',
MagicMock(return_value=DataFrame()))
timerange = TimeRange('date', None, 1510639620, 0)
load_pair_history(pair='UNITTEST/BTC', timeframe='1m',
@@ -112,7 +112,7 @@ def test_load_data_with_new_pair_1min(ohlcv_history_list, mocker, caplog,
tmpdir1 = Path(tmpdir)
mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history_list)
exchange = get_patched_exchange(mocker, default_conf)
file = tmpdir1 / 'MEME_BTC-1m.json'
file = tmpdir1 / 'MEME_BTC-1m.feather'
# do not download a new pair if refresh_pairs isn't set
load_pair_history(datadir=tmpdir1, timeframe='1m', pair='MEME/BTC', candle_type=candle_type)
@@ -280,10 +280,10 @@ def test_download_pair_history(
mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=ohlcv_history_list)
exchange = get_patched_exchange(mocker, default_conf)
tmpdir1 = Path(tmpdir)
file1_1 = tmpdir1 / f'{subdir}MEME_BTC-1m{file_tail}.json'
file1_5 = tmpdir1 / f'{subdir}MEME_BTC-5m{file_tail}.json'
file2_1 = tmpdir1 / f'{subdir}CFI_BTC-1m{file_tail}.json'
file2_5 = tmpdir1 / f'{subdir}CFI_BTC-5m{file_tail}.json'
file1_1 = tmpdir1 / f'{subdir}MEME_BTC-1m{file_tail}.feather'
file1_5 = tmpdir1 / f'{subdir}MEME_BTC-5m{file_tail}.feather'
file2_1 = tmpdir1 / f'{subdir}CFI_BTC-1m{file_tail}.feather'
file2_5 = tmpdir1 / f'{subdir}CFI_BTC-5m{file_tail}.feather'
assert not file1_1.is_file()
assert not file2_1.is_file()
@@ -326,7 +326,7 @@ def test_download_pair_history2(mocker, default_conf, testdatadir) -> None:
[1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
]
json_dump_mock = mocker.patch(
'freqtrade.data.history.jsondatahandler.JsonDataHandler.ohlcv_store',
'freqtrade.data.history.featherdatahandler.FeatherDataHandler.ohlcv_store',
return_value=None)
mocker.patch(f'{EXMS}.get_historic_ohlcv', return_value=tick)
exchange = get_patched_exchange(mocker, default_conf)
@@ -386,7 +386,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None:
def test_init(default_conf) -> None:
assert {} == load_data(
datadir=Path(''),
datadir=Path(),
pairs=[],
timeframe=default_conf['timeframe']
)
@@ -395,14 +395,14 @@ def test_init(default_conf) -> None:
def test_init_with_refresh(default_conf, mocker) -> None:
exchange = get_patched_exchange(mocker, default_conf)
refresh_data(
datadir=Path(''),
datadir=Path(),
pairs=[],
timeframe=default_conf['timeframe'],
exchange=exchange,
candle_type=CandleType.SPOT
)
assert {} == load_data(
datadir=Path(''),
datadir=Path(),
pairs=[],
timeframe=default_conf['timeframe']
)
@@ -627,8 +627,8 @@ def test_download_trades_history(trades_history, mocker, default_conf, testdatad
def test_convert_trades_to_ohlcv(testdatadir, tmpdir, caplog):
tmpdir1 = Path(tmpdir)
pair = 'XRP/ETH'
file1 = tmpdir1 / 'XRP_ETH-1m.json'
file5 = tmpdir1 / 'XRP_ETH-5m.json'
file1 = tmpdir1 / 'XRP_ETH-1m.feather'
file5 = tmpdir1 / 'XRP_ETH-5m.feather'
filetrades = tmpdir1 / 'XRP_ETH-trades.json.gz'
copyfile(testdatadir / file1.name, file1)
copyfile(testdatadir / file5.name, file5)
@@ -641,6 +641,7 @@ def test_convert_trades_to_ohlcv(testdatadir, tmpdir, caplog):
tr = TimeRange.parse_timerange('20191011-20191012')
convert_trades_to_ohlcv([pair], timeframes=['1m', '5m'],
data_format_trades='jsongz',
datadir=tmpdir1, timerange=tr, erase=True)
assert log_has("Deleting existing data for pair XRP/ETH, interval 1m.", caplog)
@@ -648,11 +649,12 @@ def test_convert_trades_to_ohlcv(testdatadir, tmpdir, caplog):
df_1m = load_pair_history(datadir=tmpdir1, timeframe="1m", pair=pair)
df_5m = load_pair_history(datadir=tmpdir1, timeframe="5m", pair=pair)
assert df_1m.equals(dfbak_1m)
assert df_5m.equals(dfbak_5m)
assert_frame_equal(dfbak_1m, df_1m, check_exact=True)
assert_frame_equal(dfbak_5m, df_5m, check_exact=True)
assert not log_has('Could not convert NoDatapair to OHLCV.', caplog)
convert_trades_to_ohlcv(['NoDatapair'], timeframes=['1m', '5m'],
data_format_trades='jsongz',
datadir=tmpdir1, timerange=tr, erase=True)
assert log_has('Could not convert NoDatapair to OHLCV.', caplog)
+2
View File
@@ -544,6 +544,8 @@ class TestCCXTExchange:
if exchangename in ('bittrex'):
# For some weired reason, this test returns random lengths for bittrex.
pytest.skip("Exchange doesn't provide stable ohlcv history")
if exchangename in ('bitvavo'):
pytest.skip("Exchange Downtime ")
if not exc._ft_has['ohlcv_has_history']:
pytest.skip("Exchange does not support candle history")
+38 -1
View File
@@ -21,7 +21,7 @@ from freqtrade.data.history import get_timerange
from freqtrade.enums import CandleType, ExitType, RunMode
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exchange.exchange import timeframe_to_next_date
from freqtrade.optimize.backtest_caching import get_strategy_run_id
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename, get_strategy_run_id
from freqtrade.optimize.backtesting import Backtesting
from freqtrade.persistence import LocalTrade, Trade
from freqtrade.resolvers import StrategyResolver
@@ -1998,3 +1998,40 @@ def test_get_strategy_run_id(default_conf_usdt):
strategy = StrategyResolver.load_strategy(default_conf_usdt)
x = get_strategy_run_id(strategy)
assert isinstance(x, str)
def test_get_backtest_metadata_filename():
# Test with a file path
filename = Path('backtest_results.json')
expected = Path('backtest_results.meta.json')
assert get_backtest_metadata_filename(filename) == expected
# Test with a file path with multiple dots in the name
filename = Path('/path/to/backtest.results.json')
expected = Path('/path/to/backtest.results.meta.json')
assert get_backtest_metadata_filename(filename) == expected
# Test with a file path with no parent directory
filename = Path('backtest_results.json')
expected = Path('backtest_results.meta.json')
assert get_backtest_metadata_filename(filename) == expected
# Test with a string file path
filename = '/path/to/backtest_results.json'
expected = Path('/path/to/backtest_results.meta.json')
assert get_backtest_metadata_filename(filename) == expected
# Test with a string file path with no extension
filename = '/path/to/backtest_results'
expected = Path('/path/to/backtest_results.meta')
assert get_backtest_metadata_filename(filename) == expected
# Test with a string file path with multiple dots in the name
filename = '/path/to/backtest.results.json'
expected = Path('/path/to/backtest.results.meta.json')
assert get_backtest_metadata_filename(filename) == expected
# Test with a string file path with no parent directory
filename = 'backtest_results.json'
expected = Path('backtest_results.meta.json')
assert get_backtest_metadata_filename(filename) == expected
+31 -3
View File
@@ -23,7 +23,8 @@ from freqtrade.optimize.optimize_reports import (generate_backtest_stats, genera
store_backtest_analysis_results,
store_backtest_stats, text_table_bt_results,
text_table_exit_reason, text_table_strategy)
from freqtrade.optimize.optimize_reports.optimize_reports import _get_resample_from_period
from freqtrade.optimize.optimize_reports.optimize_reports import (_get_resample_from_period,
calc_streak)
from freqtrade.resolvers.strategy_resolver import StrategyResolver
from freqtrade.util import dt_ts
from freqtrade.util.datetime_helpers import dt_from_ts, dt_utc
@@ -212,7 +213,8 @@ def test_store_backtest_stats(testdatadir, mocker):
dump_mock = mocker.patch('freqtrade.optimize.optimize_reports.bt_storage.file_dump_json')
store_backtest_stats(testdatadir, {'metadata': {}}, '2022_01_01_15_05_13')
data = {'metadata': {}, 'strategy': {}, 'strategy_comparison': []}
store_backtest_stats(testdatadir, data, '2022_01_01_15_05_13')
assert dump_mock.call_count == 3
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
@@ -220,7 +222,7 @@ def test_store_backtest_stats(testdatadir, mocker):
dump_mock.reset_mock()
filename = testdatadir / 'testresult.json'
store_backtest_stats(filename, {'metadata': {}}, '2022_01_01_15_05_13')
store_backtest_stats(filename, data, '2022_01_01_15_05_13')
assert dump_mock.call_count == 3
assert isinstance(dump_mock.call_args_list[0][0][0], Path)
# result will be testdatadir / testresult-<timestamp>.json
@@ -348,6 +350,32 @@ def test_generate_trading_stats(testdatadir):
assert res['losses'] == 0
def test_calc_streak(testdatadir):
df = pd.DataFrame({
'profit_ratio': [0.05, -0.02, -0.03, -0.05, 0.01, 0.02, 0.03, 0.04, -0.02, -0.03],
})
# 4 consecutive wins, 3 consecutive losses
res = calc_streak(df)
assert res == (4, 3)
assert isinstance(res[0], int)
assert isinstance(res[1], int)
# invert situation
df1 = df.copy()
df1['profit_ratio'] = df1['profit_ratio'] * -1
assert calc_streak(df1) == (3, 4)
df_empty = pd.DataFrame({
'profit_ratio': [],
})
assert df_empty.empty
assert calc_streak(df_empty) == (0, 0)
filename = testdatadir / "backtest_results/backtest-result.json"
bt_data = load_backtest_data(filename)
assert calc_streak(bt_data) == (7, 18)
def test_text_table_exit_reason():
results = pd.DataFrame(
+1 -1
View File
@@ -35,7 +35,7 @@ def test_gen_pairlist_with_local_file(mocker, rpl_config):
mock_file_path.exists.return_value = True
jsonparse = json.loads(mock_file.read.return_value)
mocker.patch('freqtrade.plugins.pairlist.RemotePairList.json.load', return_value=jsonparse)
mocker.patch('freqtrade.plugins.pairlist.RemotePairList.rapidjson.load', return_value=jsonparse)
rpl_config['pairlists'] = [
{
+41 -4
View File
@@ -1476,6 +1476,12 @@ def test_api_pair_history(botclient, mocker):
"&timerange=20180111-20180112")
assert_response(rc, 422)
# Invalid strategy
rc = client_get(client,
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}11")
assert_response(rc, 502)
# Working
rc = client_get(client,
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
@@ -1510,8 +1516,7 @@ def test_api_pair_history(botclient, mocker):
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
f"&timerange=20200111-20200112&strategy={CURRENT_TEST_STRATEGY}")
assert_response(rc, 502)
assert rc.json()['error'] == ("Error querying /api/v1/pair_history: "
"No data for UNITTEST/BTC, 5m in 20200111-20200112 found.")
assert rc.json()['detail'] == ("No data for UNITTEST/BTC, 5m in 20200111-20200112 found.")
def test_api_plot_config(botclient, mocker):
@@ -1548,6 +1553,10 @@ def test_api_plot_config(botclient, mocker):
assert_response(rc)
assert rc.json()['subplots'] == {}
rc = client_get(client, f"{BASE_URI}/plot_config?strategy=NotAStrategy")
assert_response(rc, 502)
assert rc.json()['detail'] is not None
mocker.patch('freqtrade.rpc.api_server.api_v1.get_rpc_optional', return_value=None)
rc = client_get(client, f"{BASE_URI}/plot_config")
@@ -1798,7 +1807,7 @@ def test_list_available_pairs(botclient):
rc = client_get(client, f"{BASE_URI}/available_pairs")
assert_response(rc)
assert rc.json()['length'] == 13
assert rc.json()['length'] == 12
assert isinstance(rc.json()['pairs'], list)
rc = client_get(client, f"{BASE_URI}/available_pairs?timeframe=5m")
@@ -2000,7 +2009,7 @@ def test_api_backtest_history(botclient, mocker, testdatadir):
result = rc.json()
assert len(result) == 3
fn = result[0]['filename']
assert fn == "backtest-result_multistrat.json"
assert fn == "backtest-result_multistrat"
strategy = result[0]['strategy']
rc = client_get(client, f"{BASE_URI}/backtest/history/result?filename={fn}&strategy={strategy}")
assert_response(rc)
@@ -2014,6 +2023,34 @@ def test_api_backtest_history(botclient, mocker, testdatadir):
assert result2['backtest_result']['strategy'][strategy]
def test_api_delete_backtest_history_entry(botclient, mocker, tmp_path: Path):
ftbot, client = botclient
# Create a temporary directory and file
bt_results_base = tmp_path / "backtest_results"
bt_results_base.mkdir()
file_path = bt_results_base / "test.json"
file_path.touch()
meta_path = file_path.with_suffix('.meta.json')
meta_path.touch()
rc = client_delete(client, f"{BASE_URI}/backtest/history/randomFile.json")
assert_response(rc, 503)
assert rc.json()['detail'] == 'Bot is not in the correct state.'
ftbot.config['user_data_dir'] = tmp_path
ftbot.config['runmode'] = RunMode.WEBSERVER
rc = client_delete(client, f"{BASE_URI}/backtest/history/randomFile.json")
assert rc.status_code == 404
assert rc.json()['detail'] == 'File not found.'
rc = client_delete(client, f"{BASE_URI}/backtest/history/{file_path.name}")
assert rc.status_code == 200
assert not file_path.exists()
assert not meta_path.exists()
def test_health(botclient):
ftbot, client = botclient
+4 -4
View File
@@ -21,10 +21,10 @@ def test_binance_mig_data_conversion(default_conf_usdt, tmpdir, testdatadir):
futures_dst = tmpdir / 'futures'
futures_dst.mkdir()
files = [
'-1h-mark.json',
'-1h-futures.json',
'-8h-funding_rate.json',
'-8h-mark.json',
'-1h-mark.feather',
'-1h-futures.feather',
'-8h-funding_rate.feather',
'-8h-mark.feather',
]
# Copy files to tmpdir and rename to old naming
+2 -2
View File
@@ -2793,7 +2793,7 @@ def test_manage_open_orders_entry(
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234)
# check it does cancel buy orders over the time limit
# check it does cancel entry orders over the time limit
freqtrade.manage_open_orders()
assert cancel_order_mock.call_count == 1
assert rpc_mock.call_count == 2
@@ -2801,7 +2801,7 @@ def test_manage_open_orders_entry(
select(Trade).filter(Trade.open_order_id.is_(open_trade.open_order_id))).all()
nb_trades = len(trades)
assert nb_trades == 0
# Custom user buy-timeout is never called
# Custom user entry-timeout is never called
assert freqtrade.strategy.check_entry_timeout.call_count == 0
# Entry adjustment is never called
assert freqtrade.strategy.adjust_entry_price.call_count == 0
+22
View File
@@ -474,6 +474,28 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
assert pytest.approx(trade.orders[1].amount) == 30.150753768 * leverage
assert pytest.approx(trade.orders[-1].amount) == 61.538461232 * leverage
# Full exit
mocker.patch(f'{EXMS}._dry_is_price_crossed', return_value=False)
freqtrade.strategy.custom_exit = MagicMock(return_value='Exit now')
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=2.02)
freqtrade.process()
trade = Trade.get_trades().first()
assert len(trade.orders) == 5
assert trade.orders[-1].side == trade.exit_side
assert trade.orders[-1].status == 'open'
assert trade.orders[-1].price == 2.02
assert pytest.approx(trade.amount) == 91.689215 * leverage
assert pytest.approx(trade.orders[-1].amount) == 91.689215 * leverage
assert freqtrade.strategy.adjust_entry_price.call_count == 0
# Process again, should not adjust entry price
freqtrade.process()
trade = Trade.get_trades().first()
assert len(trade.orders) == 5
assert trade.orders[-1].status == 'open'
assert trade.orders[-1].price == 2.02
# Adjust entry price cannot be called - this is an exit order
assert freqtrade.strategy.adjust_entry_price.call_count == 0
@pytest.mark.parametrize('leverage', [1, 2])
def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, leverage) -> None:
+19 -1
View File
@@ -8,7 +8,7 @@ import pandas as pd
import pytest
from freqtrade.misc import (dataframe_to_json, decimals_per_coin, deep_merge_dicts, file_dump_json,
file_load_json, json_to_dataframe, pair_to_filename,
file_load_json, is_file_in_dir, json_to_dataframe, pair_to_filename,
parse_db_uri_for_logging, plural, render_template,
render_template_with_fallback, round_coin_value, safe_value_fallback,
safe_value_fallback2)
@@ -64,6 +64,24 @@ def test_file_load_json(mocker, testdatadir) -> None:
assert ret
def test_is_file_in_dir(tmp_path):
# Create a temporary directory and file
dir_path = tmp_path / "subdir"
dir_path.mkdir()
file_path = dir_path / "test.txt"
file_path.touch()
# Test that the function returns True when the file is in the directory
assert is_file_in_dir(file_path, dir_path) is True
# Test that the function returns False when the file is not in the directory
assert is_file_in_dir(file_path, tmp_path) is False
file_path2 = tmp_path / "../../test2.txt"
assert is_file_in_dir(file_path2, tmp_path) is False
@pytest.mark.parametrize("pair,expected_result", [
("ETH/BTC", 'ETH_BTC'),
("ETH/USDT", 'ETH_USDT'),
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
-102
View File
@@ -1,102 +0,0 @@
[
[1636959600000, 1.21431, 1.2198, 1.20895, 1.20895, null],
[1636963200000, 1.20902, 1.21106, 1.19972, 1.20968, null],
[1636966800000, 1.20968, 1.21876, 1.20791, 1.20998, null],
[1636970400000, 1.20999, 1.21043, 1.20442, 1.20859, null],
[1636974000000, 1.20858, 1.20933, 1.20154, 1.20581, null],
[1636977600000, 1.20584, 1.20775, 1.20065, 1.20337, null],
[1636981200000, 1.20342, 1.2097, 1.19327, 1.19792, null],
[1636984800000, 1.19796, 1.1982, 1.18611, 1.19024, null],
[1636988400000, 1.19025, 1.19177, 1.18373, 1.18771, null],
[1636992000000, 1.18768, 1.19109, 1.18095, 1.1887, null],
[1636995600000, 1.18869, 1.18968, 1.18355, 1.18387, null],
[1636999200000, 1.18388, 1.18729, 1.17753, 1.18138, null],
[1637002800000, 1.18145, 1.18684, 1.17799, 1.18463, null],
[1637006400000, 1.18464, 1.18474, 1.17368, 1.17652, null],
[1637010000000, 1.17653, 1.18185, 1.16557, 1.17979, null],
[1637013600000, 1.17979, 1.18113, 1.16934, 1.18014, null],
[1637017200000, 1.18014, 1.18015, 1.16999, 1.17214, null],
[1637020800000, 1.17214, 1.17217, 1.12958, 1.14209, null],
[1637024400000, 1.14255, 1.14666, 1.10933, 1.14198, null],
[1637028000000, 1.14197, 1.14419, 1.12766, 1.12999, null],
[1637031600000, 1.12999, 1.13522, 1.11142, 1.12177, null],
[1637035200000, 1.12176, 1.13211, 1.10579, 1.1288, null],
[1637038800000, 1.12871, 1.13243, 1.12142, 1.12316, null],
[1637042400000, 1.12323, 1.1262, 1.11489, 1.12429, null],
[1637046000000, 1.12406, 1.12727, 1.11835, 1.1249, null],
[1637049600000, 1.12485, 1.13047, 1.1211, 1.12931, null],
[1637053200000, 1.12931, 1.13346, 1.10256, 1.10267, null],
[1637056800000, 1.10266, 1.10412, 1.04149, 1.0928, null],
[1637060400000, 1.09277, 1.09856, 1.08371, 1.09093, null],
[1637064000000, 1.09094, 1.09512, 1.079, 1.08003, null],
[1637067600000, 1.0802, 1.09914, 1.08016, 1.09515, null],
[1637071200000, 1.09518, 1.11627, 1.0937, 1.10985, null],
[1637074800000, 1.10985, 1.11353, 1.09618, 1.10071, null],
[1637078400000, 1.09989, 1.10852, 1.09763, 1.10461, null],
[1637082000000, 1.10459, 1.10837, 1.09662, 1.09847, null],
[1637085600000, 1.09858, 1.10506, 1.08687, 1.08716, null],
[1637089200000, 1.08677, 1.10096, 1.08151, 1.09271, null],
[1637092800000, 1.09245, 1.09269, 1.06592, 1.08025, null],
[1637096400000, 1.08026, 1.09732, 1.07953, 1.09527, null],
[1637100000000, 1.09527, 1.10506, 1.09524, 1.09933, null],
[1637103600000, 1.09933, 1.10205, 1.08761, 1.08785, null],
[1637107200000, 1.08763, 1.09518, 1.07646, 1.07999, null],
[1637110800000, 1.07997, 1.0978, 1.07651, 1.07936, null],
[1637114400000, 1.07932, 1.08758, 1.07352, 1.07603, null],
[1637118000000, 1.07604, 1.08542, 1.05931, 1.06764, null],
[1637121600000, 1.06788, 1.07848, 1.06045, 1.07608, null],
[1637125200000, 1.07613, 1.08797, 1.07293, 1.08377, null],
[1637128800000, 1.08379, 1.08567, 1.07428, 1.07942, null],
[1637132400000, 1.07958, 1.09472, 1.07356, 1.08713, null],
[1637136000000, 1.08714, 1.09149, 1.08018, 1.08021, null],
[1637139600000, 1.08021, 1.08021, 1.0668, 1.07032, null],
[1637143200000, 1.07042, 1.10563, 1.07034, 1.10255, null],
[1637146800000, 1.10284, 1.10954, 1.09767, 1.10685, null],
[1637150400000, 1.10669, 1.10848, 1.10157, 1.10537, null],
[1637154000000, 1.10537, 1.11263, 1.09554, 1.09585, null],
[1637157600000, 1.09569, 1.10051, 1.08402, 1.08431, null],
[1637161200000, 1.08444, 1.08942, 1.07569, 1.08489, null],
[1637164800000, 1.08498, 1.09581, 1.07939, 1.09485, null],
[1637168400000, 1.09443, 1.09793, 1.08778, 1.0944, null],
[1637172000000, 1.09445, 1.10227, 1.09376, 1.0992, null],
[1637175600000, 1.0992, 1.10189, 1.09216, 1.09474, null],
[1637179200000, 1.09476, 1.10198, 1.09045, 1.0993, null],
[1637182800000, 1.09934, 1.09959, 1.08755, 1.0948, null],
[1637186400000, 1.09483, 1.09519, 1.08532, 1.0923, null],
[1637190000000, 1.0923, 1.09876, 1.0874, 1.095, null],
[1637193600000, 1.09503, 1.10673, 1.09047, 1.10441, null],
[1637197200000, 1.10437, 1.16166, 1.09815, 1.12902, null],
[1637200800000, 1.12875, 1.15094, 1.1242, 1.13764, null],
[1637204400000, 1.13795, 1.14262, 1.12341, 1.12423, null],
[1637208000000, 1.12424, 1.14806, 1.11333, 1.1142, null],
[1637211600000, 1.11435, 1.12608, 1.11085, 1.11436, null],
[1637215200000, 1.11398, 1.11718, 1.10538, 1.11388, null],
[1637218800000, 1.1139, 1.11452, 1.09674, 1.1072, null],
[1637222400000, 1.10725, 1.10999, 1.10209, 1.10706, null],
[1637226000000, 1.10712, 1.10712, 1.07747, 1.08658, null],
[1637229600000, 1.08692, 1.09865, 1.0807, 1.09767, null],
[1637233200000, 1.09768, 1.10211, 1.08348, 1.08409, null],
[1637236800000, 1.08423, 1.09498, 1.08002, 1.08259, null],
[1637240400000, 1.0827, 1.08773, 1.06597, 1.07719, null],
[1637244000000, 1.07718, 1.08075, 1.06678, 1.07077, null],
[1637247600000, 1.07029, 1.07824, 1.04568, 1.05497, null],
[1637251200000, 1.05591, 1.06325, 1.03957, 1.04032, null],
[1637254800000, 1.04051, 1.05342, 1.01557, 1.04158, null],
[1637258400000, 1.04153, 1.05436, 1.04122, 1.05208, null],
[1637262000000, 1.05207, 1.05948, 1.04961, 1.05515, null],
[1637265600000, 1.05516, 1.05927, 1.04767, 1.04808, null],
[1637269200000, 1.04789, 1.05622, 1.04191, 1.04587, null],
[1637272800000, 1.04575, 1.05336, 1.03405, 1.03941, null],
[1637276400000, 1.03931, 1.04614, 1.02868, 1.0411, null],
[1637280000000, 1.04093, 1.05672, 1.0295, 1.05495, null],
[1637283600000, 1.05495, 1.0553, 1.03548, 1.03595, null],
[1637287200000, 1.0359, 1.04585, 1.02026, 1.02312, null],
[1637290800000, 1.0242, 1.02908, 1.01788, 1.02871, null],
[1637294400000, 1.02871, 1.04474, 1.02584, 1.04247, null],
[1637298000000, 1.04251, 1.04654, 1.03685, 1.0449, null],
[1637301600000, 1.0449, 1.04971, 1.04109, 1.04452, null],
[1637305200000, 1.04456, 1.04875, 1.03802, 1.04268, null],
[1637308800000, 1.04239, 1.06573, 1.04164, 1.05717, null],
[1637312400000, 1.05721, 1.06464, 1.05619, 1.05896, null],
[1637316000000, 1.05893, 1.05918, 1.04976, 1.05188, null]
]
Binary file not shown.
-102
View File
@@ -1,102 +0,0 @@
[
[ 1637110800000, 1.0801, 1.09758, 1.07654, 1.07925, 3153694.607359 ],
[ 1637114400000, 1.07896, 1.0875, 1.07351, 1.07616, 2697616.070908 ],
[ 1637118000000, 1.07607, 1.08521, 1.05896, 1.06804, 4014666.826073 ],
[ 1637121600000, 1.06848, 1.07846, 1.06067, 1.07629, 3764015.567745 ],
[ 1637125200000, 1.07647, 1.08791, 1.07309, 1.0839, 1669038.113726 ],
[ 1637128800000, 1.08414, 1.0856, 1.07431, 1.0794, 1921068.874499 ],
[ 1637132400000, 1.0798, 1.09499, 1.07363, 1.08721, 2491096.863582 ],
[ 1637136000000, 1.08688, 1.09133, 1.08004, 1.08011, 1983486.794272 ],
[ 1637139600000, 1.08017, 1.08027, 1.06667, 1.07039, 3429247.985309 ],
[ 1637143200000, 1.07054, 1.10699, 1.07038, 1.10284, 4554151.954177 ],
[ 1637146800000, 1.10315, 1.10989, 1.09781, 1.1071, 2012983.10465 ],
[ 1637150400000, 1.10627, 1.10849, 1.10155, 1.10539, 1117804.08918 ],
[ 1637154000000, 1.10545, 1.11299, 1.09574, 1.09604, 2252781.33926 ],
[ 1637157600000, 1.09583, 1.10037, 1.08402, 1.08404, 1882359.279342 ],
[ 1637161200000, 1.08433, 1.08924, 1.07583, 1.08543, 1826745.82579 ],
[ 1637164800000, 1.08571, 1.09622, 1.07946, 1.09496, 1651730.678891 ],
[ 1637168400000, 1.09509, 1.0979, 1.0878, 1.0945, 1081210.614598 ],
[ 1637172000000, 1.09483, 1.10223, 1.09362, 1.09922, 1065998.492028 ],
[ 1637175600000, 1.09916, 1.10201, 1.09226, 1.09459, 924935.492048 ],
[ 1637179200000, 1.09458, 1.10196, 1.09051, 1.09916, 1253539.625345 ],
[ 1637182800000, 1.09939, 1.09948, 1.08751, 1.09485, 1066269.190094 ],
[ 1637186400000, 1.0949, 1.095, 1.08537, 1.09229, 924726.680514 ],
[ 1637190000000, 1.0923, 1.09877, 1.08753, 1.09522, 1150213.905599 ],
[ 1637193600000, 1.09538, 1.10675, 1.09058, 1.10453, 1489867.578178 ],
[ 1637197200000, 1.10446, 1.16313, 1.0978, 1.12907, 10016166.026355 ],
[ 1637200800000, 1.1287, 1.15367, 1.12403, 1.1381, 7167920.053752 ],
[ 1637204400000, 1.13818, 1.14242, 1.12358, 1.1244, 2665326.190545 ],
[ 1637208000000, 1.12432, 1.14864, 1.11061, 1.11447, 9340547.947608 ],
[ 1637211600000, 1.114, 1.12618, 1.10911, 1.11412, 11759138.472952 ],
[ 1637215200000, 1.11381, 1.11701, 1.10507, 1.1136, 3104670.727264 ],
[ 1637218800000, 1.11433, 1.1145, 1.09682, 1.10715, 2522287.830673 ],
[ 1637222400000, 1.1073, 1.11, 1.10224, 1.10697, 2021691.204473 ],
[ 1637226000000, 1.10622, 1.10707, 1.07727, 1.08674, 3679010.223352 ],
[ 1637229600000, 1.08651, 1.09861, 1.08065, 1.09771, 2041421.476307 ],
[ 1637233200000, 1.09784, 1.102, 1.08339, 1.08399, 1920597.122813 ],
[ 1637236800000, 1.08458, 1.09523, 1.07961, 1.08263, 2403158.337373 ],
[ 1637240400000, 1.08309, 1.08959, 1.06094, 1.07703, 4425686.808376 ],
[ 1637244000000, 1.07702, 1.08064, 1.063, 1.07049, 3361334.048801 ],
[ 1637247600000, 1.07126, 1.07851, 1.04538, 1.0562, 5865602.611111 ],
[ 1637251200000, 1.05616, 1.06326, 1.0395, 1.04074, 4206860.947352 ],
[ 1637254800000, 1.04023, 1.0533, 1.01478, 1.0417, 5641193.647291 ],
[ 1637258400000, 1.04177, 1.05444, 1.04132, 1.05204, 1819341.083656 ],
[ 1637262000000, 1.05201, 1.05962, 1.04964, 1.05518, 1567923.362515 ],
[ 1637265600000, 1.05579, 1.05924, 1.04772, 1.04773, 1794108.065606 ],
[ 1637269200000, 1.0484, 1.05622, 1.04183, 1.04544, 1936537.403899 ],
[ 1637272800000, 1.04543, 1.05331, 1.03396, 1.03892, 2839486.418143 ],
[ 1637276400000, 1.03969, 1.04592, 1.02886, 1.04086, 3116275.899177 ],
[ 1637280000000, 1.0409, 1.05681, 1.02922, 1.05481, 4671209.916896 ],
[ 1637283600000, 1.05489, 1.05538, 1.03539, 1.03599, 2566357.247547 ],
[ 1637287200000, 1.03596, 1.04606, 1.02038, 1.02428, 3441834.238546 ],
[ 1637290800000, 1.02483, 1.0291, 1.01785, 1.0285, 2678602.729339 ],
[ 1637294400000, 1.0287, 1.0446, 1.0259, 1.04264, 2303621.340808 ],
[ 1637298000000, 1.04313, 1.04676, 1.03662, 1.04499, 2426475.439485 ],
[ 1637301600000, 1.0451, 1.04971, 1.041, 1.04448, 2088365.810515 ],
[ 1637305200000, 1.04473, 1.04845, 1.03801, 1.04227, 2222396.213472 ],
[ 1637308800000, 1.04211, 1.06965, 1.04168, 1.05711, 3267643.936025 ],
[ 1637312400000, 1.0569, 1.06578, 1.05626, 1.05844, 1512848.016057 ],
[ 1637316000000, 1.05814, 1.05916, 1.04923, 1.05464, 1710694.805693 ],
[ 1637319600000, 1.05484, 1.05731, 1.0458, 1.05359, 1587100.45253 ],
[ 1637323200000, 1.05382, 1.06063, 1.05156, 1.05227, 1409095.236152 ],
[ 1637326800000, 1.05256, 1.06489, 1.04996, 1.06471, 1879315.174541 ],
[ 1637330400000, 1.06491, 1.1036, 1.06489, 1.09439, 6212842.71216 ],
[ 1637334000000, 1.09441, 1.10252, 1.082, 1.08879, 4833417.181969 ],
[ 1637337600000, 1.08866, 1.09485, 1.07538, 1.09045, 2554438.746366 ],
[ 1637341200000, 1.09058, 1.09906, 1.08881, 1.09039, 1961024.28963 ],
[ 1637344800000, 1.09063, 1.09447, 1.08555, 1.09041, 1427538.639232 ],
[ 1637348400000, 1.09066, 1.09521, 1.088, 1.09332, 847724.821691 ],
[ 1637352000000, 1.09335, 1.09489, 1.08402, 1.08501, 1035043.133874 ],
[ 1637355600000, 1.08474, 1.08694, 1.08, 1.08606, 969952.892274 ],
[ 1637359200000, 1.08601, 1.09, 1.08201, 1.08476, 1105782.581808 ],
[ 1637362800000, 1.08463, 1.09245, 1.08201, 1.08971, 1334467.438673 ],
[ 1637366400000, 1.0897, 1.09925, 1.08634, 1.09049, 2460070.020396 ],
[ 1637370000000, 1.0908, 1.10002, 1.09002, 1.09845, 1210028.489394 ],
[ 1637373600000, 1.09785, 1.09791, 1.08944, 1.08962, 1261987.295847 ],
[ 1637377200000, 1.08951, 1.0919, 1.08429, 1.08548, 1124938.783404 ],
[ 1637380800000, 1.08536, 1.09, 1.08424, 1.08783, 1330935.680168 ],
[ 1637384400000, 1.0877, 1.08969, 1.08266, 1.08617, 874900.746037 ],
[ 1637388000000, 1.08622, 1.09224, 1.0843, 1.0889, 1240184.759178 ],
[ 1637391600000, 1.08917, 1.0909, 1.08408, 1.08535, 706148.380072 ],
[ 1637395200000, 1.08521, 1.08857, 1.07829, 1.08349, 1713832.050838 ],
[ 1637398800000, 1.08343, 1.08841, 1.08272, 1.0855, 696597.06327 ],
[ 1637402400000, 1.08553, 1.0898, 1.08353, 1.08695, 1104159.802108 ],
[ 1637406000000, 1.08703, 1.09838, 1.08635, 1.09695, 1404001.384389 ],
[ 1637409600000, 1.09695, 1.10175, 1.09024, 1.09278, 1219090.620484 ],
[ 1637413200000, 1.093, 1.09577, 1.08615, 1.08792, 994797.546591 ],
[ 1637416800000, 1.08793, 1.09239, 1.08572, 1.08725, 1251685.429497 ],
[ 1637420400000, 1.08721, 1.08767, 1.06029, 1.06556, 3955719.53631 ],
[ 1637424000000, 1.06553, 1.07385, 1.06169, 1.07257, 1868359.179534 ],
[ 1637427600000, 1.07266, 1.0745, 1.06759, 1.07261, 1015134.469304 ],
[ 1637431200000, 1.07255, 1.0974, 1.06819, 1.09369, 4377675.964829 ],
[ 1637434800000, 1.09368, 1.09562, 1.08899, 1.09036, 914791.699929 ],
[ 1637438400000, 1.09085, 1.09262, 1.08855, 1.09214, 661436.936672 ],
[ 1637442000000, 1.0924, 1.09475, 1.08874, 1.09282, 593143.283519 ],
[ 1637445600000, 1.09301, 1.09638, 1.09154, 1.09611, 603952.916221 ],
[ 1637449200000, 1.09569, 1.09828, 1.09301, 1.09747, 676053.591571 ],
[ 1637452800000, 1.09742, 1.09822, 1.09011, 1.0902, 1375704.506469 ],
[ 1637456400000, 1.0901, 1.09311, 1.08619, 1.08856, 928706.03929 ],
[ 1637460000000, 1.08855, 1.08941, 1.07401, 1.08035, 2669150.388642 ],
[ 1637463600000, 1.08016, 1.08341, 1.07448, 1.07672, 1604049.131307 ],
[ 1637467200000, 1.07685, 1.08229, 1.07552, 1.0765, 1153357.274076 ]
]
Binary file not shown.
-102
View File
@@ -1,102 +0,0 @@
[
[1636956000000, 1.20932, 1.21787, 1.20763, 1.21431, null],
[1636959600000, 1.21431, 1.2198, 1.20895, 1.20895, null],
[1636963200000, 1.20902, 1.21106, 1.19972, 1.20968, null],
[1636966800000, 1.20968, 1.21876, 1.20791, 1.20998, null],
[1636970400000, 1.20999, 1.21043, 1.20442, 1.20859, null],
[1636974000000, 1.20858, 1.20933, 1.20154, 1.20581, null],
[1636977600000, 1.20584, 1.20775, 1.20065, 1.20337, null],
[1636981200000, 1.20342, 1.2097, 1.19327, 1.19792, null],
[1636984800000, 1.19796, 1.1982, 1.18611, 1.19024, null],
[1636988400000, 1.19025, 1.19177, 1.18373, 1.18771, null],
[1636992000000, 1.18768, 1.19109, 1.18095, 1.1887, null],
[1636995600000, 1.18869, 1.18968, 1.18355, 1.18387, null],
[1636999200000, 1.18388, 1.18729, 1.17753, 1.18138, null],
[1637002800000, 1.18145, 1.18684, 1.17799, 1.18463, null],
[1637006400000, 1.18464, 1.18474, 1.17368, 1.17652, null],
[1637010000000, 1.17653, 1.18185, 1.16557, 1.17979, null],
[1637013600000, 1.17979, 1.18113, 1.16934, 1.18014, null],
[1637017200000, 1.18014, 1.18015, 1.16999, 1.17214, null],
[1637020800000, 1.17214, 1.17217, 1.12958, 1.14209, null],
[1637024400000, 1.14255, 1.14666, 1.10933, 1.14198, null],
[1637028000000, 1.14197, 1.14419, 1.12766, 1.12999, null],
[1637031600000, 1.12999, 1.13522, 1.11142, 1.12177, null],
[1637035200000, 1.12176, 1.13211, 1.10579, 1.1288, null],
[1637038800000, 1.12871, 1.13243, 1.12142, 1.12316, null],
[1637042400000, 1.12323, 1.1262, 1.11489, 1.12429, null],
[1637046000000, 1.12406, 1.12727, 1.11835, 1.1249, null],
[1637049600000, 1.12485, 1.13047, 1.1211, 1.12931, null],
[1637053200000, 1.12931, 1.13346, 1.10256, 1.10267, null],
[1637056800000, 1.10266, 1.10412, 1.04149, 1.0928, null],
[1637060400000, 1.09277, 1.09856, 1.08371, 1.09093, null],
[1637064000000, 1.09094, 1.09512, 1.079, 1.08003, null],
[1637067600000, 1.0802, 1.09914, 1.08016, 1.09515, null],
[1637071200000, 1.09518, 1.11627, 1.0937, 1.10985, null],
[1637074800000, 1.10985, 1.11353, 1.09618, 1.10071, null],
[1637078400000, 1.09989, 1.10852, 1.09763, 1.10461, null],
[1637082000000, 1.10459, 1.10837, 1.09662, 1.09847, null],
[1637085600000, 1.09858, 1.10506, 1.08687, 1.08716, null],
[1637089200000, 1.08677, 1.10096, 1.08151, 1.09271, null],
[1637092800000, 1.09245, 1.09269, 1.06592, 1.08025, null],
[1637096400000, 1.08026, 1.09732, 1.07953, 1.09527, null],
[1637100000000, 1.09527, 1.10506, 1.09524, 1.09933, null],
[1637103600000, 1.09933, 1.10205, 1.08761, 1.08785, null],
[1637107200000, 1.08763, 1.09518, 1.07646, 1.07999, null],
[1637110800000, 1.07997, 1.0978, 1.07651, 1.07936, null],
[1637114400000, 1.07932, 1.08758, 1.07352, 1.07603, null],
[1637118000000, 1.07604, 1.08542, 1.05931, 1.06764, null],
[1637121600000, 1.06788, 1.07848, 1.06045, 1.07608, null],
[1637125200000, 1.07613, 1.08797, 1.07293, 1.08377, null],
[1637128800000, 1.08379, 1.08567, 1.07428, 1.07942, null],
[1637132400000, 1.07958, 1.09472, 1.07356, 1.08713, null],
[1637136000000, 1.08714, 1.09149, 1.08018, 1.08021, null],
[1637139600000, 1.08021, 1.08021, 1.0668, 1.07032, null],
[1637143200000, 1.07042, 1.10563, 1.07034, 1.10255, null],
[1637146800000, 1.10284, 1.10954, 1.09767, 1.10685, null],
[1637150400000, 1.10669, 1.10848, 1.10157, 1.10537, null],
[1637154000000, 1.10537, 1.11263, 1.09554, 1.09585, null],
[1637157600000, 1.09569, 1.10051, 1.08402, 1.08431, null],
[1637161200000, 1.08444, 1.08942, 1.07569, 1.08489, null],
[1637164800000, 1.08498, 1.09581, 1.07939, 1.09485, null],
[1637168400000, 1.09443, 1.09793, 1.08778, 1.0944, null],
[1637172000000, 1.09445, 1.10227, 1.09376, 1.0992, null],
[1637175600000, 1.0992, 1.10189, 1.09216, 1.09474, null],
[1637179200000, 1.09476, 1.10198, 1.09045, 1.0993, null],
[1637182800000, 1.09934, 1.09959, 1.08755, 1.0948, null],
[1637186400000, 1.09483, 1.09519, 1.08532, 1.0923, null],
[1637190000000, 1.0923, 1.09876, 1.0874, 1.095, null],
[1637193600000, 1.09503, 1.10673, 1.09047, 1.10441, null],
[1637197200000, 1.10437, 1.16166, 1.09815, 1.12902, null],
[1637200800000, 1.12875, 1.15094, 1.1242, 1.13764, null],
[1637204400000, 1.13795, 1.14262, 1.12341, 1.12423, null],
[1637208000000, 1.12424, 1.14806, 1.11333, 1.1142, null],
[1637211600000, 1.11435, 1.12608, 1.11085, 1.11436, null],
[1637215200000, 1.11398, 1.11718, 1.10538, 1.11388, null],
[1637218800000, 1.1139, 1.11452, 1.09674, 1.1072, null],
[1637222400000, 1.10725, 1.10999, 1.10209, 1.10706, null],
[1637226000000, 1.10712, 1.10712, 1.07747, 1.08658, null],
[1637229600000, 1.08692, 1.09865, 1.0807, 1.09767, null],
[1637233200000, 1.09768, 1.10211, 1.08348, 1.08409, null],
[1637236800000, 1.08423, 1.09498, 1.08002, 1.08259, null],
[1637240400000, 1.0827, 1.08773, 1.06597, 1.07719, null],
[1637244000000, 1.07718, 1.08075, 1.06678, 1.07077, null],
[1637247600000, 1.07029, 1.07824, 1.04568, 1.05497, null],
[1637251200000, 1.05591, 1.06325, 1.03957, 1.04032, null],
[1637254800000, 1.04051, 1.05342, 1.01557, 1.04158, null],
[1637258400000, 1.04153, 1.05436, 1.04122, 1.05208, null],
[1637262000000, 1.05207, 1.05948, 1.04961, 1.05515, null],
[1637265600000, 1.05516, 1.05927, 1.04767, 1.04808, null],
[1637269200000, 1.04789, 1.05622, 1.04191, 1.04587, null],
[1637272800000, 1.04575, 1.05336, 1.03405, 1.03941, null],
[1637276400000, 1.03931, 1.04614, 1.02868, 1.0411, null],
[1637280000000, 1.04093, 1.05672, 1.0295, 1.05495, null],
[1637283600000, 1.05495, 1.0553, 1.03548, 1.03595, null],
[1637287200000, 1.0359, 1.04585, 1.02026, 1.02312, null],
[1637290800000, 1.0242, 1.02908, 1.01788, 1.02871, null],
[1637294400000, 1.02871, 1.04474, 1.02584, 1.04247, null],
[1637298000000, 1.04251, 1.04654, 1.03685, 1.0449, null],
[1637301600000, 1.0449, 1.04971, 1.04109, 1.04452, null],
[1637305200000, 1.04456, 1.04875, 1.03802, 1.04268, null],
[1637308800000, 1.04239, 1.06573, 1.04164, 1.05717, null],
[1637312400000, 1.05721, 1.06464, 1.05619, 1.06051, null]
]
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
[[1637193600017,0.0001,0.0,0.0,0.0,0.0],[1637222400007,0.0001,0.0,0.0,0.0,0.0],[1637251200011,0.0001,0.0,0.0,0.0,0.0],[1637280000000,0.0001,0.0,0.0,0.0,0.0],[1637308800000,0.0001,0.0,0.0,0.0,0.0],[1637337600005,0.0001,0.0,0.0,0.0,0.0],[1637366400012,0.00013046,0.0,0.0,0.0,0.0],[1637395200000,0.0001,0.0,0.0,0.0,0.0],[1637424000007,0.0001,0.0,0.0,0.0,0.0],[1637452800000,0.00013862,0.0,0.0,0.0,0.0],[1637481600006,0.0001,0.0,0.0,0.0,0.0],[1637510400000,0.00019881,0.0,0.0,0.0,0.0],[1637539200004,0.00013991,0.0,0.0,0.0,0.0],[1637568000000,0.0001,0.0,0.0,0.0,0.0],[1637596800000,0.0001,0.0,0.0,0.0,0.0],[1637625600004,0.0001,0.0,0.0,0.0,0.0],[1637654400010,0.0001,0.0,0.0,0.0,0.0],[1637683200005,0.00017402,0.0,0.0,0.0,0.0],[1637712000001,0.00016775,0.0,0.0,0.0,0.0],[1637740800003,0.00033523,0.0,0.0,0.0,0.0],[1637769600010,0.0001,0.0,0.0,0.0,0.0],[1637798400000,0.00020066,0.0,0.0,0.0,0.0],[1637827200010,0.00034381,0.0,0.0,0.0,0.0],[1637856000000,0.00032096,0.0,0.0,0.0,0.0],[1637884800000,0.00058316,0.0,0.0,0.0,0.0],[1637913600000,0.0001646,0.0,0.0,0.0,0.0],[1637942400016,0.0001,0.0,0.0,0.0,0.0],[1637971200005,0.0001,0.0,0.0,0.0,0.0],[1638000000008,0.0001,0.0,0.0,0.0,0.0],[1638028800007,0.0001,0.0,0.0,0.0,0.0],[1638057600018,0.0001,0.0,0.0,0.0,0.0],[1638086400000,0.0001,0.0,0.0,0.0,0.0],[1638115200004,0.0001,0.0,0.0,0.0,0.0],[1638144000002,0.0001,0.0,0.0,0.0,0.0],[1638172800004,0.0001,0.0,0.0,0.0,0.0],[1638201600000,0.0001,0.0,0.0,0.0,0.0],[1638230400000,0.0001,0.0,0.0,0.0,0.0],[1638259200006,0.0001,0.0,0.0,0.0,0.0],[1638288000000,0.0001,0.0,0.0,0.0,0.0],[1638316800000,0.0001,0.0,0.0,0.0,0.0],[1638345600000,0.0001,0.0,0.0,0.0,0.0],[1638374400001,0.0001,0.0,0.0,0.0,0.0],[1638403200000,0.0001,0.0,0.0,0.0,0.0],[1638432000007,0.0001,0.0,0.0,0.0,0.0],[1638460800008,0.0001,0.0,0.0,0.0,0.0],[1638489600004,0.0001,0.0,0.0,0.0,0.0],[1638518400002,0.0001,0.0,0.0,0.0,0.0],[1638547200006,0.0001,0.0,0.0,0.0,0.0],[1638576000006,0.0001,0.0,0.0,0.0,0.0],[1638604800004,-0.00219334,0.0,0.0,0.0,0.0],[1638633600000,0.0001,0.0,0.0,0.0,0.0],[1638662400003,0.00006147,0.0,0.0,0.0,0.0],[1638691200008,0.0001,0.0,0.0,0.0,0.0],[1638720000007,0.0001,0.0,0.0,0.0,0.0],[1638748800009,0.0001,0.0,0.0,0.0,0.0],[1638777600001,0.0001,0.0,0.0,0.0,0.0],[1638806400000,0.0001,0.0,0.0,0.0,0.0],[1638835200018,0.0001,0.0,0.0,0.0,0.0],[1638864000000,0.0001,0.0,0.0,0.0,0.0],[1638892800000,0.0001,0.0,0.0,0.0,0.0],[1638921600000,0.0001,0.0,0.0,0.0,0.0],[1638950400018,0.0001,0.0,0.0,0.0,0.0],[1638979200010,0.0001,0.0,0.0,0.0,0.0],[1639008000010,0.0001,0.0,0.0,0.0,0.0],[1639036800000,0.0001,0.0,0.0,0.0,0.0],[1639065600000,0.0001,0.0,0.0,0.0,0.0],[1639094400000,0.0001,0.0,0.0,0.0,0.0],[1639123200008,0.0001,0.0,0.0,0.0,0.0],[1639152000012,0.00008995,0.0,0.0,0.0,0.0],[1639180800009,0.0001,0.0,0.0,0.0,0.0],[1639209600008,-0.00002574,0.0,0.0,0.0,0.0],[1639238400000,-0.00002024,0.0,0.0,0.0,0.0],[1639267200001,-0.00008282,0.0,0.0,0.0,0.0],[1639296000015,0.0001,0.0,0.0,0.0,0.0],[1639324800011,0.00008752,0.0,0.0,0.0,0.0],[1639353600006,0.0001,0.0,0.0,0.0,0.0],[1639382400019,0.0001,0.0,0.0,0.0,0.0],[1639411200000,0.0001,0.0,0.0,0.0,0.0],[1639440000004,0.00007825,0.0,0.0,0.0,0.0],[1639468800000,0.00007108,0.0,0.0,0.0,0.0],[1639497600015,0.0001,0.0,0.0,0.0,0.0],[1639526400000,0.0001,0.0,0.0,0.0,0.0],[1639555200008,0.0001,0.0,0.0,0.0,0.0],[1639584000005,0.0001,0.0,0.0,0.0,0.0],[1639612800006,0.0001,0.0,0.0,0.0,0.0],[1639641600009,0.0001,0.0,0.0,0.0,0.0],[1639670400000,0.0001,0.0,0.0,0.0,0.0],[1639699200000,0.0001,0.0,0.0,0.0,0.0],[1639728000005,0.0001,0.0,0.0,0.0,0.0],[1639756800006,0.0001,0.0,0.0,0.0,0.0],[1639785600014,0.0001,0.0,0.0,0.0,0.0]]
Binary file not shown.
File diff suppressed because one or more lines are too long