homebrewlibra 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- homebrewlibra-0.1.0/LICENSE.txt +13 -0
- homebrewlibra-0.1.0/PKG-INFO +112 -0
- homebrewlibra-0.1.0/README.md +60 -0
- homebrewlibra-0.1.0/homebrewlibra/core/__init__.py +16 -0
- homebrewlibra-0.1.0/homebrewlibra/core/fields.py +82 -0
- homebrewlibra-0.1.0/homebrewlibra/core/timestamp.py +33 -0
- homebrewlibra-0.1.0/homebrewlibra/data_tools/__init__.py +63 -0
- homebrewlibra-0.1.0/homebrewlibra/data_tools/create_window.py +184 -0
- homebrewlibra-0.1.0/homebrewlibra/data_tools/pars_args.py +170 -0
- homebrewlibra-0.1.0/homebrewlibra/gantt_chart/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart.py +183 -0
- homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart2PDF.py +96 -0
- homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart_daily.py +66 -0
- homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart_m.py +108 -0
- homebrewlibra-0.1.0/homebrewlibra/gantt_chart/requirements.txt +3 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_console/__init__.py +11 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_csv/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_csv/csv_io_helper.py +58 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_csv/load_csv.py +35 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/combine_df.md +834 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/combine_df.py +836 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/data_stats.py +49 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/get_columns.py +28 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/make_lag.py +25 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/make_normalize.py +42 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_data/set_default_index.py +34 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_date/__init__.py +1 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_date/monthly_date_ranges.py +96 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_exchange/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_exchange/exchange_io_helper.py +210 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_exchange/exchange_io_kraken.py +155 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_feather/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_feather/feather_io_helper.py +62 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_io/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_io/select_files.py +22 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_io/step_execution.py +65 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_json/__init__.py +2 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_json/json_io_helper.py +93 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/convert_timestamp_date.py +4 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/convert_trades_to_OHLCV.py +321 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/grouping_trades.py +505 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/market_sides.py +105 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/split_trades_to_sides.py +179 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_market/trades_fields.py +4 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_plot/__init__.py +0 -0
- homebrewlibra-0.1.0/homebrewlibra/helper_plot/plot_tools.py +202 -0
- homebrewlibra-0.1.0/homebrewlibra/requirements.txt +11 -0
- homebrewlibra-0.1.0/pyproject.toml +88 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright (c) 2026, Alexey Glebov
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
10
|
+
|
|
11
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
12
|
+
|
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: homebrewlibra
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Modular utilities for market data processing and analysis
|
|
5
|
+
License-File: LICENSE.txt
|
|
6
|
+
Author: Alex Glebov
|
|
7
|
+
Author-email: alex.glebov@gmail.com
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Provides-Extra: chart
|
|
17
|
+
Provides-Extra: data
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Provides-Extra: docs
|
|
20
|
+
Provides-Extra: exchange
|
|
21
|
+
Provides-Extra: io
|
|
22
|
+
Provides-Extra: market
|
|
23
|
+
Provides-Extra: model
|
|
24
|
+
Provides-Extra: plot
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: black (>=24.0) ; extra == "dev"
|
|
27
|
+
Requires-Dist: ccxt (>=4.5) ; extra == "all"
|
|
28
|
+
Requires-Dist: ccxt (>=4.5) ; extra == "exchange"
|
|
29
|
+
Requires-Dist: homebrewlibra[io] ; extra == "data"
|
|
30
|
+
Requires-Dist: homebrewlibra[io] ; extra == "exchange"
|
|
31
|
+
Requires-Dist: homebrewlibra[io] ; extra == "market"
|
|
32
|
+
Requires-Dist: keras (>=3.0) ; extra == "model"
|
|
33
|
+
Requires-Dist: matplotlib (>=3.10) ; extra == "all"
|
|
34
|
+
Requires-Dist: matplotlib (>=3.10) ; extra == "chart"
|
|
35
|
+
Requires-Dist: matplotlib (>=3.10) ; extra == "plot"
|
|
36
|
+
Requires-Dist: mkdocs (>=1.6) ; extra == "docs"
|
|
37
|
+
Requires-Dist: numpy (>=1.24.0)
|
|
38
|
+
Requires-Dist: pandas (>=2.0.0)
|
|
39
|
+
Requires-Dist: pyarrow (>=14.0.0) ; extra == "all"
|
|
40
|
+
Requires-Dist: pyarrow (>=14.0.0) ; extra == "io"
|
|
41
|
+
Requires-Dist: pytest (>=8.0) ; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-cov (>=5.0) ; extra == "test"
|
|
43
|
+
Requires-Dist: requests (>=2.32) ; extra == "all"
|
|
44
|
+
Requires-Dist: requests (>=2.32) ; extra == "exchange"
|
|
45
|
+
Requires-Dist: ruff (>=0.5) ; extra == "dev"
|
|
46
|
+
Requires-Dist: tensorflow (>=2.20) ; extra == "model"
|
|
47
|
+
Project-URL: Homepage, https://github.com/Alex-Glebov/HomeBrewLibra
|
|
48
|
+
Project-URL: Issues, https://github.com/Alex-Glebov/HomeBrewLibra/issues
|
|
49
|
+
Project-URL: Repository, https://github.com/Alex-Glebov/HomeBrewLibra
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
|
|
52
|
+
# HomeBrewLibra
|
|
53
|
+
|
|
54
|
+
Modular utilities for market data processing and analysis.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
### Minimal (core only)
|
|
59
|
+
```bash
|
|
60
|
+
pip install homebrewlibra
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### With I/O helpers (feather, CSV)
|
|
64
|
+
```bash
|
|
65
|
+
pip install "homebrewlibra[io]"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### For data preparation (market + data + I/O)
|
|
69
|
+
```bash
|
|
70
|
+
pip install "homebrewlibra[market,data,io]"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### For exchange connectivity
|
|
74
|
+
```bash
|
|
75
|
+
pip install "homebrewlibra[exchange]"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### With plotting
|
|
79
|
+
```bash
|
|
80
|
+
pip install "homebrewlibra[plot]"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Everything (backward compatible)
|
|
84
|
+
```bash
|
|
85
|
+
pip install "homebrewlibra[all]"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Subpackages
|
|
89
|
+
|
|
90
|
+
| Subpackage | Extras | Dependencies | Description |
|
|
91
|
+
|------------|--------|-------------|-------------|
|
|
92
|
+
| `core` | (none) | pandas, numpy | Shared utilities: timestamp parsing, field mapping |
|
|
93
|
+
| `helper_market` | `[market]` | core | Trade splitting, grouping, peak detection helpers |
|
|
94
|
+
| `helper_data` | `[data]` | core | DataFrame combination, index analysis |
|
|
95
|
+
| `helper_feather` | `[io]` | pyarrow | Feather file I/O |
|
|
96
|
+
| `helper_csv` | `[io]` | (none) | CSV file I/O |
|
|
97
|
+
| `helper_io` | `[io]` | core | Step execution, file caching |
|
|
98
|
+
| `helper_exchange` | `[exchange]` | ccxt, requests | Exchange API connectivity |
|
|
99
|
+
| `helper_plot` | `[plot]` | matplotlib | Plotting tools |
|
|
100
|
+
| `gantt_chart` | `[chart]` | matplotlib | Gantt chart generation |
|
|
101
|
+
|
|
102
|
+
## Changes in 0.1.0
|
|
103
|
+
|
|
104
|
+
- **Removed**: `helper_model` (ML utilities). Use `model-core` package instead.
|
|
105
|
+
- **Added**: `core` subpackage to break circular imports.
|
|
106
|
+
- **Added**: Optional extras `[io]`, `[market]`, `[data]`, `[exchange]`, `[plot]`, `[all]`.
|
|
107
|
+
- **Changed**: `pyproject.toml` now declares minimal core deps; heavy deps moved to extras.
|
|
108
|
+
|
|
109
|
+
## Contributing
|
|
110
|
+
|
|
111
|
+
Please [fork this project on GitHub](https://github.com/Alex-Glebov/HomeBrewLibra) and send a pull request.
|
|
112
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# HomeBrewLibra
|
|
2
|
+
|
|
3
|
+
Modular utilities for market data processing and analysis.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Minimal (core only)
|
|
8
|
+
```bash
|
|
9
|
+
pip install homebrewlibra
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### With I/O helpers (feather, CSV)
|
|
13
|
+
```bash
|
|
14
|
+
pip install "homebrewlibra[io]"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### For data preparation (market + data + I/O)
|
|
18
|
+
```bash
|
|
19
|
+
pip install "homebrewlibra[market,data,io]"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### For exchange connectivity
|
|
23
|
+
```bash
|
|
24
|
+
pip install "homebrewlibra[exchange]"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### With plotting
|
|
28
|
+
```bash
|
|
29
|
+
pip install "homebrewlibra[plot]"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Everything (backward compatible)
|
|
33
|
+
```bash
|
|
34
|
+
pip install "homebrewlibra[all]"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Subpackages
|
|
38
|
+
|
|
39
|
+
| Subpackage | Extras | Dependencies | Description |
|
|
40
|
+
|------------|--------|-------------|-------------|
|
|
41
|
+
| `core` | (none) | pandas, numpy | Shared utilities: timestamp parsing, field mapping |
|
|
42
|
+
| `helper_market` | `[market]` | core | Trade splitting, grouping, peak detection helpers |
|
|
43
|
+
| `helper_data` | `[data]` | core | DataFrame combination, index analysis |
|
|
44
|
+
| `helper_feather` | `[io]` | pyarrow | Feather file I/O |
|
|
45
|
+
| `helper_csv` | `[io]` | (none) | CSV file I/O |
|
|
46
|
+
| `helper_io` | `[io]` | core | Step execution, file caching |
|
|
47
|
+
| `helper_exchange` | `[exchange]` | ccxt, requests | Exchange API connectivity |
|
|
48
|
+
| `helper_plot` | `[plot]` | matplotlib | Plotting tools |
|
|
49
|
+
| `gantt_chart` | `[chart]` | matplotlib | Gantt chart generation |
|
|
50
|
+
|
|
51
|
+
## Changes in 0.1.0
|
|
52
|
+
|
|
53
|
+
- **Removed**: `helper_model` (ML utilities). Use `model-core` package instead.
|
|
54
|
+
- **Added**: `core` subpackage to break circular imports.
|
|
55
|
+
- **Added**: Optional extras `[io]`, `[market]`, `[data]`, `[exchange]`, `[plot]`, `[all]`.
|
|
56
|
+
- **Changed**: `pyproject.toml` now declares minimal core deps; heavy deps moved to extras.
|
|
57
|
+
|
|
58
|
+
## Contributing
|
|
59
|
+
|
|
60
|
+
Please [fork this project on GitHub](https://github.com/Alex-Glebov/HomeBrewLibra) and send a pull request.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Core utilities shared across homebrewlibra submodules.
|
|
2
|
+
|
|
3
|
+
This module contains low-level helpers with minimal external dependencies
|
|
4
|
+
to avoid circular imports between subpackages.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .timestamp import make_col_unique, parse_unix_ts, utc_string_to_timestamp
|
|
8
|
+
from .fields import check_fields, numercal_col
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"make_col_unique",
|
|
12
|
+
"parse_unix_ts",
|
|
13
|
+
"utc_string_to_timestamp",
|
|
14
|
+
"check_fields",
|
|
15
|
+
"numercal_col",
|
|
16
|
+
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Field name mapping and type coercion utilities."""
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, List, Union
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from pandas import DataFrame
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_fields_names_dict():
|
|
12
|
+
"""Lazy import to avoid circular deps at module level."""
|
|
13
|
+
from homebrewlibra.data_tools import __fields_names_dict
|
|
14
|
+
return __fields_names_dict
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def check_fields(df: DataFrame, fields_match: Dict[str, str] = None) -> Dict[str, str]:
|
|
18
|
+
"""Auto-detect or validate DataFrame column names against predefined field dictionaries.
|
|
19
|
+
|
|
20
|
+
Returns a standardized name-to-column mapping.
|
|
21
|
+
"""
|
|
22
|
+
logger.setLevel(logging.INFO)
|
|
23
|
+
if fields_match is None:
|
|
24
|
+
best_match = 0
|
|
25
|
+
best_dict = {}
|
|
26
|
+
fields_names_dict = _get_fields_names_dict()
|
|
27
|
+
for key, flds in fields_names_dict.items():
|
|
28
|
+
logger.debug(f"Processing dict {key}")
|
|
29
|
+
fields_matched = check_fields(df=df, fields_match=flds)
|
|
30
|
+
if len(fields_matched) == len(flds):
|
|
31
|
+
logger.debug(f"accepted dict {key}")
|
|
32
|
+
break
|
|
33
|
+
else:
|
|
34
|
+
if len(fields_matched) >= best_match:
|
|
35
|
+
best_dict = fields_matched
|
|
36
|
+
best_match = len(best_dict)
|
|
37
|
+
logger.debug(f"best match dict {key}")
|
|
38
|
+
return fields_matched if best_match == 0 else best_dict
|
|
39
|
+
else:
|
|
40
|
+
sv = ""
|
|
41
|
+
fields_matched = {}
|
|
42
|
+
for k, v in fields_match.items():
|
|
43
|
+
vs = str(v)
|
|
44
|
+
ks = str(k)
|
|
45
|
+
if ks.startswith("*"):
|
|
46
|
+
if sv != "" and any(df[sv] == v):
|
|
47
|
+
logger.debug(f" value '{v}' for key '{ks}' value found in '{sv}'")
|
|
48
|
+
fields_matched[ks] = sv
|
|
49
|
+
else:
|
|
50
|
+
logger.warning(f" value '{v}' for key '{k}' does not exist in field '{sv}'")
|
|
51
|
+
continue
|
|
52
|
+
else:
|
|
53
|
+
if ks in df.columns:
|
|
54
|
+
logger.debug(f" The key '{ks}' already exist as field name ")
|
|
55
|
+
fields_matched[ks] = ks
|
|
56
|
+
sv = ks
|
|
57
|
+
elif vs in df.columns:
|
|
58
|
+
logger.debug(f" The value '{vs}' for key '{ks}' exist as field ")
|
|
59
|
+
fields_matched[ks] = vs
|
|
60
|
+
sv = vs
|
|
61
|
+
else:
|
|
62
|
+
logger.warning(f" The value '{vs}' for key '{ks}' does not exist as field ")
|
|
63
|
+
continue
|
|
64
|
+
return fields_matched
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def numercal_col(df: DataFrame, col: Union[str, List[str]] = None, inplace: bool = True) -> DataFrame:
|
|
68
|
+
"""Convert specified DataFrame columns to numeric via pd.to_numeric."""
|
|
69
|
+
newdf = df if inplace else df.copy(deep=True)
|
|
70
|
+
if col is None:
|
|
71
|
+
return newdf
|
|
72
|
+
columns = []
|
|
73
|
+
if isinstance(col, str):
|
|
74
|
+
col = [col]
|
|
75
|
+
for icol in col:
|
|
76
|
+
if icol in df.columns:
|
|
77
|
+
columns.append(icol)
|
|
78
|
+
if len(columns) > 0:
|
|
79
|
+
newdf[columns] = newdf[columns].apply(pd.to_numeric, errors="coerce")
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(f"passed 'col={col}', but should be either list of strings or string ")
|
|
82
|
+
return newdf
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Timestamp and date utilities."""
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from pandas import DataFrame
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from homebrewlibra.helper_date import __TIMEFRAME_MAX
|
|
8
|
+
except ImportError:
|
|
9
|
+
__TIMEFRAME_MAX = 1e11 # seconds threshold
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def make_col_unique(df: DataFrame, unique_col: str = "trade_id", ignore_index: bool = False) -> DataFrame:
|
|
13
|
+
"""Drop duplicate rows based on a given column, keeping the first occurrence."""
|
|
14
|
+
if unique_col is not None:
|
|
15
|
+
df.drop_duplicates(unique_col, keep="first", inplace=True, ignore_index=ignore_index)
|
|
16
|
+
return df
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def parse_unix_ts(col):
|
|
20
|
+
"""Auto-detect seconds vs milliseconds and convert to datetime."""
|
|
21
|
+
if pd.api.types.is_datetime64_any_dtype(col):
|
|
22
|
+
return col
|
|
23
|
+
col = pd.to_numeric(col, errors="coerce")
|
|
24
|
+
if col.max() < __TIMEFRAME_MAX:
|
|
25
|
+
return pd.to_datetime(col, unit="s", utc=True)
|
|
26
|
+
else:
|
|
27
|
+
return pd.to_datetime(col, unit="ms", utc=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def utc_string_to_timestamp(date_string: str, date_format: str) -> datetime:
|
|
31
|
+
"""Convert a UTC date string to a timezone-aware datetime."""
|
|
32
|
+
dt_object = datetime.strptime(date_string, date_format)
|
|
33
|
+
return dt_object.replace(tzinfo=timezone.utc)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
DEFAULT_TRADES_COLUMNS = ['timestamp', 'id', 'price', 'amount', 'side', 'type', 'cost']
|
|
2
|
+
TRADES_2_OHLCV_COLUMNS = [
|
|
3
|
+
'date','open','high','low','close','volume',
|
|
4
|
+
'miscellaneous',
|
|
5
|
+
'volume_sell',
|
|
6
|
+
'volume_buy',
|
|
7
|
+
'customers_sell',
|
|
8
|
+
'customers_buy',
|
|
9
|
+
|
|
10
|
+
]
|
|
11
|
+
# fields in order as it come from exchange
|
|
12
|
+
CONVERT_TRADES_COLUMNS = {
|
|
13
|
+
'kraken': {
|
|
14
|
+
'price':'price',
|
|
15
|
+
'amount':'volume',
|
|
16
|
+
'timestamp':'timestamp',
|
|
17
|
+
'buy/sell': 'side',
|
|
18
|
+
'market/limit':'type',
|
|
19
|
+
'miscellaneous':'misc',
|
|
20
|
+
'trade_id':'id',
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
__OHLCVD_COLUMNS = ['date','open','high','low','close','volume']
|
|
25
|
+
__fields_names_kraken={
|
|
26
|
+
'amount':'volume',
|
|
27
|
+
'side' :'buy/sell',
|
|
28
|
+
'*buy' :'b',
|
|
29
|
+
'*sell' :'s',
|
|
30
|
+
'type' :'market/limit',
|
|
31
|
+
'*market':'m',
|
|
32
|
+
'*limit' :'l',
|
|
33
|
+
'price' :'price',
|
|
34
|
+
'id' :'trade_id',
|
|
35
|
+
'timestamp':'timestamp',
|
|
36
|
+
'cost':'misc'
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
__fields_names_freqtrade={
|
|
40
|
+
'amount' :'amount',
|
|
41
|
+
'side' :'side',
|
|
42
|
+
'*buy' :'buy',
|
|
43
|
+
'*sell' :'sell',
|
|
44
|
+
'type' :'type',
|
|
45
|
+
'*market' :'market',
|
|
46
|
+
'*limit' :'limit',
|
|
47
|
+
'price' :'price',
|
|
48
|
+
'id' :'id',
|
|
49
|
+
'timestamp':'timestamp',
|
|
50
|
+
'cost':'cost'
|
|
51
|
+
}
|
|
52
|
+
__fields_names_dict={
|
|
53
|
+
'kraken': __fields_names_kraken,
|
|
54
|
+
'freqtrade': __fields_names_freqtrade
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
__all__=[
|
|
58
|
+
'__TIMEFRAME_MAX',
|
|
59
|
+
'__TRADES_COLUMNS',
|
|
60
|
+
'__OHLCVD_COLUMNS',
|
|
61
|
+
'TRADES_2_OHLCV_COLUMNS',
|
|
62
|
+
'__fields_names_dict',
|
|
63
|
+
]
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Created on 27 Feb 2026
|
|
3
|
+
|
|
4
|
+
@author: alex
|
|
5
|
+
'''
|
|
6
|
+
import numpy as np
|
|
7
|
+
from typing import Tuple, Optional, Union
|
|
8
|
+
import logging
|
|
9
|
+
__logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def filter_valid_pivots(
|
|
15
|
+
pivots: np.ndarray,
|
|
16
|
+
n_time: int,
|
|
17
|
+
window_size: int = 1, # just current value )
|
|
18
|
+
horizon: int = 1,
|
|
19
|
+
) -> np.ndarray:
|
|
20
|
+
"""
|
|
21
|
+
Returns only those pivot indices (window END positions) that are valid,
|
|
22
|
+
i.e. have enough history before them and enough future after them.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
pivots : np.ndarray
|
|
27
|
+
Candidate window-end indices (0-based)
|
|
28
|
+
n_time : int
|
|
29
|
+
Total number of time steps in the data
|
|
30
|
+
window_size : int
|
|
31
|
+
Required length of input window
|
|
32
|
+
horizon : int
|
|
33
|
+
How many steps ahead the target is (0 = predict current end, 1 = next step, ...)
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
valid_pivots : np.ndarray
|
|
38
|
+
Filtered array of valid pivot indices, sorted if input was sorted
|
|
39
|
+
"""
|
|
40
|
+
pivots = np.asarray(pivots, dtype=np.int64).ravel()
|
|
41
|
+
|
|
42
|
+
# Minimum pivot index needed to have full window
|
|
43
|
+
min_pivot = window_size - 1
|
|
44
|
+
|
|
45
|
+
# Maximum pivot index so that pivot + horizon is still inside data
|
|
46
|
+
max_pivot = n_time - horizon -1#
|
|
47
|
+
|
|
48
|
+
valid_mask = (pivots >= min_pivot) & (pivots <= max_pivot)
|
|
49
|
+
|
|
50
|
+
return pivots[valid_mask]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def check_params_values(y, denominator, n_time, n_features, window_size, horizon, axis):
|
|
55
|
+
# ────────────────────────────────────────────────
|
|
56
|
+
# Prepare inputs
|
|
57
|
+
# ────────────────────────────────────────────────
|
|
58
|
+
if axis != 0:
|
|
59
|
+
raise NotImplementedError("Only axis=0 is currently supported.")
|
|
60
|
+
if denominator is None:
|
|
61
|
+
denominate_flag=False
|
|
62
|
+
denominator=np.ones(shape=(n_time))
|
|
63
|
+
else:
|
|
64
|
+
denominate_flag=True
|
|
65
|
+
denominator = np.asarray(denominator).ravel()
|
|
66
|
+
if len(denominator) != n_time:
|
|
67
|
+
raise ValueError("denominator length must match x_orig time dimension")
|
|
68
|
+
if y is not None and y.shape[0] != n_time:
|
|
69
|
+
raise ValueError("y must have same time dimension as x_orig")
|
|
70
|
+
if window_size < 1:
|
|
71
|
+
raise ValueError("window_size >= 1 required")
|
|
72
|
+
if horizon < 0:
|
|
73
|
+
raise ValueError("horizon >= 0 required")
|
|
74
|
+
|
|
75
|
+
return denominate_flag
|
|
76
|
+
# ────────────────────────────────────────────────
|
|
77
|
+
# Prepare inputs
|
|
78
|
+
# ────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
def create_window(
|
|
81
|
+
x_orig: np.ndarray,
|
|
82
|
+
y_orig: Optional[np.ndarray],
|
|
83
|
+
denominator: np.ndarray| None=None,
|
|
84
|
+
window_size: int=1, # just one current X
|
|
85
|
+
horizon: int = 0, # one ahead
|
|
86
|
+
axis: int = 0,
|
|
87
|
+
pivots: Optional[np.ndarray] = None,
|
|
88
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
Creates normalized sliding windows for Conv1D / time-series models.
|
|
92
|
+
|
|
93
|
+
For each valid pivot p (window END index):
|
|
94
|
+
- Takes x_orig[p - window_size + 1 : p + 1]
|
|
95
|
+
- Divides the ENTIRE window by denominator[p] (exactly the formula you wanted)
|
|
96
|
+
- Collects into 3D array (n_samples, window_size, n_features)
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
x_orig : np.ndarray
|
|
101
|
+
Features. Shape (n_time, n_features) or (n_time,) for univariate.
|
|
102
|
+
y_orig : np.ndarray or None
|
|
103
|
+
Targets. Shape (n_time,) or (n_time, n_targets).
|
|
104
|
+
denominator : np.ndarray
|
|
105
|
+
1D array of length n_time to divide by (value at window END).
|
|
106
|
+
window_size : int
|
|
107
|
+
Length of each input window (>= 1).
|
|
108
|
+
horizon : int
|
|
109
|
+
How many steps ahead the target is (default 1).
|
|
110
|
+
axis : int
|
|
111
|
+
Currently only 0 is supported (time axis). You can extend later.
|
|
112
|
+
pivots : np.ndarray
|
|
113
|
+
Candidate window-end indices (0-based). Only valid ones are kept.
|
|
114
|
+
If pivots is None → uses all possible valid positions.
|
|
115
|
+
|
|
116
|
+
Returns
|
|
117
|
+
-------
|
|
118
|
+
X_windows : np.ndarray
|
|
119
|
+
Shape (n_valid_windows, window_size, n_features)
|
|
120
|
+
→ ready for Conv1D / LSTM / Transformer input.
|
|
121
|
+
y_matched_or_indices : np.ndarray
|
|
122
|
+
If y_orig is not None → actual target values at (pivot + horizon)
|
|
123
|
+
If y_orig is None → array of target indices (so you can do y_orig[indices] later)
|
|
124
|
+
Creates normalized sliding windows.
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
# ────────────────────────────────────────────────
|
|
128
|
+
# Prepare inputs
|
|
129
|
+
# ────────────────────────────────────────────────
|
|
130
|
+
if x_orig.ndim == 1:
|
|
131
|
+
x_orig = x_orig.reshape(-1, 1)
|
|
132
|
+
n_time, n_features = x_orig.shape
|
|
133
|
+
if y_orig is not None:
|
|
134
|
+
y_orig = np.asarray(y_orig)
|
|
135
|
+
|
|
136
|
+
denominate_flag = check_params_values(y_orig, denominator, n_time, n_features, window_size, horizon, axis)
|
|
137
|
+
|
|
138
|
+
# ────────────────────────────────────────────────
|
|
139
|
+
# Determine pivots
|
|
140
|
+
# ────────────────────────────────────────────────
|
|
141
|
+
if pivots is None:
|
|
142
|
+
# Use every possible valid end position
|
|
143
|
+
pivots = np.arange(n_time)
|
|
144
|
+
|
|
145
|
+
valid_pivots = filter_valid_pivots(
|
|
146
|
+
pivots=pivots,
|
|
147
|
+
n_time=n_time,
|
|
148
|
+
window_size=window_size,
|
|
149
|
+
horizon=horizon,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
num_samples = len(valid_pivots)
|
|
153
|
+
|
|
154
|
+
if num_samples == 0:
|
|
155
|
+
# Early return with correct empty shapes
|
|
156
|
+
X_empty = np.empty((0, window_size, n_features), dtype=x_orig.dtype)
|
|
157
|
+
if y_orig is not None:
|
|
158
|
+
y_empty = np.empty((0,) + y_orig.shape[1:], dtype=y_orig.dtype)
|
|
159
|
+
else:
|
|
160
|
+
y_empty = np.empty((0,), dtype=np.int64)
|
|
161
|
+
return X_empty, y_empty
|
|
162
|
+
|
|
163
|
+
# ────────────────────────────────────────────────
|
|
164
|
+
# Build X windows
|
|
165
|
+
# ────────────────────────────────────────────────
|
|
166
|
+
X_windows = np.empty((num_samples, window_size, n_features), dtype=x_orig.dtype)
|
|
167
|
+
window_a = np.array(range(-window_size+1,+ 1))
|
|
168
|
+
for k, p in enumerate(valid_pivots):
|
|
169
|
+
window_data = x_orig[window_a+p] #(0 : window_size)+p ]
|
|
170
|
+
if denominate_flag:
|
|
171
|
+
window_data /= denominator[p]
|
|
172
|
+
X_windows[k] = window_data
|
|
173
|
+
# ────────────────────────────────────────────────
|
|
174
|
+
# Build targets / indices
|
|
175
|
+
# ────────────────────────────────────────────────
|
|
176
|
+
target_indices = valid_pivots + horizon
|
|
177
|
+
|
|
178
|
+
if y_orig is not None:
|
|
179
|
+
y_matched = y_orig[target_indices]
|
|
180
|
+
else:
|
|
181
|
+
y_matched = target_indices
|
|
182
|
+
|
|
183
|
+
return X_windows, y_matched
|
|
184
|
+
|