Merge branch 'freqtrade:develop' into develop
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
""" Freqtrade bot """
|
||||
__version__ = '2023.7.dev'
|
||||
__version__ = '2023.8.dev'
|
||||
|
||||
if 'dev' in __version__:
|
||||
from pathlib import Path
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, '_')
|
||||
|
||||
@@ -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': [],
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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'],
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -526,6 +526,7 @@ def get_default_conf(testdatadir):
|
||||
"disableparamexport": True,
|
||||
"internals": {},
|
||||
"export": "none",
|
||||
"dataformat_ohlcv": "feather",
|
||||
"candle_type_def": CandleType.SPOT,
|
||||
}
|
||||
return configuration
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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'] = [
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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'),
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
BIN
Binary file not shown.
Vendored
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -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.
@@ -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
@@ -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
Reference in New Issue
Block a user