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.
Files changed (50) hide show
  1. homebrewlibra-0.1.0/LICENSE.txt +13 -0
  2. homebrewlibra-0.1.0/PKG-INFO +112 -0
  3. homebrewlibra-0.1.0/README.md +60 -0
  4. homebrewlibra-0.1.0/homebrewlibra/core/__init__.py +16 -0
  5. homebrewlibra-0.1.0/homebrewlibra/core/fields.py +82 -0
  6. homebrewlibra-0.1.0/homebrewlibra/core/timestamp.py +33 -0
  7. homebrewlibra-0.1.0/homebrewlibra/data_tools/__init__.py +63 -0
  8. homebrewlibra-0.1.0/homebrewlibra/data_tools/create_window.py +184 -0
  9. homebrewlibra-0.1.0/homebrewlibra/data_tools/pars_args.py +170 -0
  10. homebrewlibra-0.1.0/homebrewlibra/gantt_chart/__init__.py +0 -0
  11. homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart.py +183 -0
  12. homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart2PDF.py +96 -0
  13. homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart_daily.py +66 -0
  14. homebrewlibra-0.1.0/homebrewlibra/gantt_chart/gantt_chart_m.py +108 -0
  15. homebrewlibra-0.1.0/homebrewlibra/gantt_chart/requirements.txt +3 -0
  16. homebrewlibra-0.1.0/homebrewlibra/helper_console/__init__.py +11 -0
  17. homebrewlibra-0.1.0/homebrewlibra/helper_csv/__init__.py +0 -0
  18. homebrewlibra-0.1.0/homebrewlibra/helper_csv/csv_io_helper.py +58 -0
  19. homebrewlibra-0.1.0/homebrewlibra/helper_csv/load_csv.py +35 -0
  20. homebrewlibra-0.1.0/homebrewlibra/helper_data/__init__.py +0 -0
  21. homebrewlibra-0.1.0/homebrewlibra/helper_data/combine_df.md +834 -0
  22. homebrewlibra-0.1.0/homebrewlibra/helper_data/combine_df.py +836 -0
  23. homebrewlibra-0.1.0/homebrewlibra/helper_data/data_stats.py +49 -0
  24. homebrewlibra-0.1.0/homebrewlibra/helper_data/get_columns.py +28 -0
  25. homebrewlibra-0.1.0/homebrewlibra/helper_data/make_lag.py +25 -0
  26. homebrewlibra-0.1.0/homebrewlibra/helper_data/make_normalize.py +42 -0
  27. homebrewlibra-0.1.0/homebrewlibra/helper_data/set_default_index.py +34 -0
  28. homebrewlibra-0.1.0/homebrewlibra/helper_date/__init__.py +1 -0
  29. homebrewlibra-0.1.0/homebrewlibra/helper_date/monthly_date_ranges.py +96 -0
  30. homebrewlibra-0.1.0/homebrewlibra/helper_exchange/__init__.py +0 -0
  31. homebrewlibra-0.1.0/homebrewlibra/helper_exchange/exchange_io_helper.py +210 -0
  32. homebrewlibra-0.1.0/homebrewlibra/helper_exchange/exchange_io_kraken.py +155 -0
  33. homebrewlibra-0.1.0/homebrewlibra/helper_feather/__init__.py +0 -0
  34. homebrewlibra-0.1.0/homebrewlibra/helper_feather/feather_io_helper.py +62 -0
  35. homebrewlibra-0.1.0/homebrewlibra/helper_io/__init__.py +0 -0
  36. homebrewlibra-0.1.0/homebrewlibra/helper_io/select_files.py +22 -0
  37. homebrewlibra-0.1.0/homebrewlibra/helper_io/step_execution.py +65 -0
  38. homebrewlibra-0.1.0/homebrewlibra/helper_json/__init__.py +2 -0
  39. homebrewlibra-0.1.0/homebrewlibra/helper_json/json_io_helper.py +93 -0
  40. homebrewlibra-0.1.0/homebrewlibra/helper_market/__init__.py +0 -0
  41. homebrewlibra-0.1.0/homebrewlibra/helper_market/convert_timestamp_date.py +4 -0
  42. homebrewlibra-0.1.0/homebrewlibra/helper_market/convert_trades_to_OHLCV.py +321 -0
  43. homebrewlibra-0.1.0/homebrewlibra/helper_market/grouping_trades.py +505 -0
  44. homebrewlibra-0.1.0/homebrewlibra/helper_market/market_sides.py +105 -0
  45. homebrewlibra-0.1.0/homebrewlibra/helper_market/split_trades_to_sides.py +179 -0
  46. homebrewlibra-0.1.0/homebrewlibra/helper_market/trades_fields.py +4 -0
  47. homebrewlibra-0.1.0/homebrewlibra/helper_plot/__init__.py +0 -0
  48. homebrewlibra-0.1.0/homebrewlibra/helper_plot/plot_tools.py +202 -0
  49. homebrewlibra-0.1.0/homebrewlibra/requirements.txt +11 -0
  50. 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
+