flowbars 0.1.0__py3-none-any.whl

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 (46) hide show
  1. flowbars/__init__.py +163 -0
  2. flowbars/_version.py +8 -0
  3. flowbars/adapters/__init__.py +29 -0
  4. flowbars/adapters/polars.py +1115 -0
  5. flowbars/bars/__init__.py +3 -0
  6. flowbars/bars/accumulators.py +731 -0
  7. flowbars/bars/constructor.py +1254 -0
  8. flowbars/bars/information/__init__.py +41 -0
  9. flowbars/bars/information/imbalance_dollar_bars.py +200 -0
  10. flowbars/bars/information/imbalance_tick_bars.py +206 -0
  11. flowbars/bars/information/imbalance_volume_bars.py +200 -0
  12. flowbars/bars/information/run_dollar_bars.py +225 -0
  13. flowbars/bars/information/run_tick_bars.py +231 -0
  14. flowbars/bars/information/run_volume_bars.py +225 -0
  15. flowbars/bars/minute/__init__.py +99 -0
  16. flowbars/bars/minute/accumulators.py +303 -0
  17. flowbars/bars/minute/engine.py +244 -0
  18. flowbars/bars/minute/information.py +622 -0
  19. flowbars/bars/minute/numba.py +1043 -0
  20. flowbars/bars/minute/registry.py +22 -0
  21. flowbars/bars/minute/standard.py +261 -0
  22. flowbars/bars/minute/structural.py +474 -0
  23. flowbars/bars/numba_backend.py +1227 -0
  24. flowbars/bars/output.py +106 -0
  25. flowbars/bars/registry.py +47 -0
  26. flowbars/bars/spec.py +367 -0
  27. flowbars/bars/standard/__init__.py +17 -0
  28. flowbars/bars/standard/dollar_bars.py +168 -0
  29. flowbars/bars/standard/tick_bars.py +166 -0
  30. flowbars/bars/standard/time_bars.py +192 -0
  31. flowbars/bars/standard/volume_bars.py +168 -0
  32. flowbars/benchmarks.py +420 -0
  33. flowbars/calendars.py +368 -0
  34. flowbars/core.py +109 -0
  35. flowbars/py.typed +0 -0
  36. flowbars/quality.py +393 -0
  37. flowbars/sample_data/__init__.py +28 -0
  38. flowbars/sample_data/tick_data.csv +501 -0
  39. flowbars/schema.py +476 -0
  40. flowbars/thresholds.py +1167 -0
  41. flowbars/tick_rule.py +89 -0
  42. flowbars-0.1.0.dist-info/METADATA +650 -0
  43. flowbars-0.1.0.dist-info/RECORD +46 -0
  44. flowbars-0.1.0.dist-info/WHEEL +5 -0
  45. flowbars-0.1.0.dist-info/licenses/LICENSE +21 -0
  46. flowbars-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,41 @@
1
+ """Information-driven bar types: imbalance and run (tick, volume, dollar variants)."""
2
+
3
+ from flowbars.bars.information.imbalance_dollar_bars import (
4
+ ImbalanceDollarBarConstructor,
5
+ compute_imbalance_dollar_bars,
6
+ )
7
+ from flowbars.bars.information.imbalance_tick_bars import (
8
+ ImbalanceTickBarConstructor,
9
+ compute_imbalance_tick_bars,
10
+ )
11
+ from flowbars.bars.information.imbalance_volume_bars import (
12
+ ImbalanceVolumeBarConstructor,
13
+ compute_imbalance_volume_bars,
14
+ )
15
+ from flowbars.bars.information.run_dollar_bars import (
16
+ RunDollarBarConstructor,
17
+ compute_run_dollar_bars,
18
+ )
19
+ from flowbars.bars.information.run_tick_bars import (
20
+ RunTickBarConstructor,
21
+ compute_run_tick_bars,
22
+ )
23
+ from flowbars.bars.information.run_volume_bars import (
24
+ RunVolumeBarConstructor,
25
+ compute_run_volume_bars,
26
+ )
27
+
28
+ __all__ = [
29
+ "ImbalanceDollarBarConstructor",
30
+ "ImbalanceTickBarConstructor",
31
+ "ImbalanceVolumeBarConstructor",
32
+ "RunDollarBarConstructor",
33
+ "RunTickBarConstructor",
34
+ "RunVolumeBarConstructor",
35
+ "compute_imbalance_dollar_bars",
36
+ "compute_imbalance_tick_bars",
37
+ "compute_imbalance_volume_bars",
38
+ "compute_run_dollar_bars",
39
+ "compute_run_tick_bars",
40
+ "compute_run_volume_bars",
41
+ ]
@@ -0,0 +1,200 @@
1
+ """Imbalance bars (dollar) — close when absolute signed dollar-imbalance crosses an adaptive threshold.
2
+
3
+ Same two-component EWMA formula as tick-imbalance bars, but the imbalance
4
+ is weighted by notional value (price × volume) instead of tick count.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Literal
10
+
11
+ import pandas as pd
12
+
13
+ from flowbars.bars.accumulators import ImbalanceAccumulator
14
+ from flowbars.bars.constructor import BaseBarConstructor
15
+ from flowbars.bars.registry import BarRegistry
16
+ from flowbars.calendars import TradingCalendar
17
+ from flowbars.schema import SchemaMapping
18
+ from flowbars.thresholds import EWMAThresholdEstimator
19
+
20
+
21
+ class ImbalanceDollarBarConstructor(BaseBarConstructor):
22
+ """Bar constructor for dollar-imbalance bars.
23
+
24
+ A dollar-imbalance bar closes when the absolute signed dollar-imbalance
25
+ reaches or exceeds an **adaptive** threshold estimated by an EWMA.
26
+
27
+ Parameters
28
+ ----------
29
+ span : float, default 20.0
30
+ EWMA span for the two-component threshold formula.
31
+ halflife : float or None, default None
32
+ EWMA halflife. Takes precedence over *span* when given.
33
+ initial_ewa_t : float, default 1.0
34
+ Initial seed for :math:`E[T]` (expected dollar value per bar).
35
+ initial_ewa_proportion : float, default 0.5
36
+ Initial seed for :math:`E[\\theta]` (expected imbalance proportion).
37
+ warmup_bars : int, default 0
38
+ Number of initial bars to discard while the EWMA converges.
39
+ calendar : TradingCalendar, optional
40
+ schema : SchemaMapping, optional
41
+ watermark : str or None, default ``"timestamp"``
42
+ Dedup key for idempotent resume (``"timestamp"``, a column name, or
43
+ ``None`` to disable).
44
+ strict_ordering : bool, default False
45
+ If True, raise ``TickDataError`` when a tick arrives with a timestamp
46
+ earlier than the previous tick's (out-of-order input).
47
+ backend : str, default ``"python"``
48
+ ``"python"`` or ``"numba"``. The numba backend accelerates
49
+ :meth:`batch` only; ``update`` always uses Python.
50
+ stream_id : str, default ``"default"``
51
+ on_bar : callable or None
52
+ on_threshold_update : callable or None
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ span: float = 20.0,
58
+ halflife: float | None = None,
59
+ initial_ewa_t: float = 1.0,
60
+ initial_ewa_proportion: float = 0.5,
61
+ warmup_bars: int = 0,
62
+ warmup_minutes: int | None = None,
63
+ calendar: TradingCalendar | None = None,
64
+ schema: SchemaMapping | None = None,
65
+ watermark: str | None = "timestamp",
66
+ stream_id: str = "default",
67
+ strict_ordering: bool = False,
68
+ backend: Literal["python", "numba"] = "python",
69
+ on_bar: Any = None,
70
+ on_threshold_update: Any = None,
71
+ multiplier: float = 1.0,
72
+ volume_type: str = "base",
73
+ ) -> None:
74
+ accumulator = ImbalanceAccumulator(bar_type="imbalance_dollar", metric="dollar")
75
+ estimator = EWMAThresholdEstimator(
76
+ bar_family="imbalance",
77
+ span=span,
78
+ halflife=halflife,
79
+ initial_ewa_t=initial_ewa_t,
80
+ initial_ewa_proportion=initial_ewa_proportion,
81
+ )
82
+
83
+ super().__init__(
84
+ accumulator=accumulator,
85
+ threshold_estimator=estimator,
86
+ calendar=calendar,
87
+ schema=schema,
88
+ watermark=watermark,
89
+ stream_id=stream_id,
90
+ strict_ordering=strict_ordering,
91
+ warmup_bars=warmup_bars,
92
+ warmup_minutes=warmup_minutes,
93
+ backend=backend,
94
+ on_bar=on_bar,
95
+ on_threshold_update=on_threshold_update,
96
+ multiplier=multiplier,
97
+ volume_type=volume_type,
98
+ )
99
+
100
+ @classmethod
101
+ def from_state( # type: ignore[override]
102
+ cls,
103
+ state: dict[str, Any],
104
+ calendar: TradingCalendar | None = None,
105
+ schema: SchemaMapping | None = None,
106
+ *,
107
+ verify_identity: bool = True,
108
+ ) -> ImbalanceDollarBarConstructor:
109
+ """Reconstruct an ``ImbalanceDollarBarConstructor`` from a saved state dict."""
110
+ acc_state = state["accumulator"]
111
+ acc = ImbalanceAccumulator(
112
+ bar_type=state["bar_type"],
113
+ metric=acc_state["metric"],
114
+ )
115
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
116
+ inst: ImbalanceDollarBarConstructor = super().from_state(
117
+ state, acc, est, calendar, schema, verify_identity=verify_identity
118
+ ) # type: ignore[assignment]
119
+ return inst
120
+
121
+
122
+ def compute_imbalance_dollar_bars(
123
+ ticks_df: pd.DataFrame,
124
+ span: float = 20.0,
125
+ halflife: float | None = None,
126
+ initial_ewa_t: float = 1.0,
127
+ initial_ewa_proportion: float = 0.5,
128
+ warmup_bars: int = 0,
129
+ warmup_minutes: int | None = None,
130
+ schema: SchemaMapping | None = None,
131
+ watermark: str | None = "timestamp",
132
+ strict_ordering: bool = False,
133
+ calendar: TradingCalendar | None = None,
134
+ *,
135
+ backend: Literal["python", "numba"] = "python",
136
+ stream_id: str = "default",
137
+ on_bar: Any = None,
138
+ on_threshold_update: Any = None,
139
+ multiplier: float = 1.0,
140
+ volume_type: str = "base",
141
+ ) -> pd.DataFrame:
142
+ """Build dollar-imbalance bars from a DataFrame of ticks.
143
+
144
+ Parameters
145
+ ----------
146
+ ticks_df : pd.DataFrame
147
+ Tick-level data with user-mapped columns.
148
+ span : float, default 20.0
149
+ EWMA span for the adaptive threshold.
150
+ halflife : float or None, default None
151
+ EWMA halflife (takes precedence over *span*).
152
+ initial_ewa_t : float, default 1.0
153
+ Initial seed for expected dollar value per bar.
154
+ initial_ewa_proportion : float, default 0.5
155
+ Initial seed for expected imbalance proportion.
156
+ warmup_bars : int, default 0
157
+ Number of initial bars to discard.
158
+ schema : SchemaMapping, optional
159
+ watermark : str or None, default ``"timestamp"``
160
+ Dedup key for idempotent resume (``"timestamp"``, a column name, or
161
+ ``None`` to disable).
162
+ strict_ordering : bool, default False
163
+ If True, raise ``TickDataError`` when a tick arrives with a timestamp
164
+ earlier than the previous tick's (out-of-order input).
165
+ calendar : TradingCalendar, optional
166
+ backend : str, default ``"python"``
167
+ ``"python"`` or ``"numba"``.
168
+ stream_id : str, default ``"default"``
169
+ on_bar : callable or None
170
+ on_threshold_update : callable or None
171
+
172
+ Returns
173
+ -------
174
+ pd.DataFrame
175
+ Completed bars as a DataFrame.
176
+ """
177
+ ctor = ImbalanceDollarBarConstructor(
178
+ span=span,
179
+ halflife=halflife,
180
+ initial_ewa_t=initial_ewa_t,
181
+ initial_ewa_proportion=initial_ewa_proportion,
182
+ warmup_bars=warmup_bars,
183
+ warmup_minutes=warmup_minutes,
184
+ calendar=calendar,
185
+ schema=schema,
186
+ watermark=watermark,
187
+ strict_ordering=strict_ordering,
188
+ backend=backend,
189
+ stream_id=stream_id,
190
+ on_bar=on_bar,
191
+ on_threshold_update=on_threshold_update,
192
+ multiplier=multiplier,
193
+ volume_type=volume_type,
194
+ )
195
+ return ctor.batch(ticks_df)
196
+
197
+
198
+ BarRegistry.register(
199
+ "imbalance_dollar", ImbalanceDollarBarConstructor, batch_fn=compute_imbalance_dollar_bars
200
+ )
@@ -0,0 +1,206 @@
1
+ """Imbalance bars (tick) — close when absolute signed tick-imbalance crosses an adaptive threshold.
2
+
3
+ Uses the two-component EWMA formula from AFML §2.4:
4
+
5
+ .. math::
6
+
7
+ T_n = E[T]_n \\times |E[\\theta]_n|
8
+
9
+ where :math:`E[T]_n` is the expected tick count per bar and
10
+ :math:`E[\\theta]_n` is the expected signed-imbalance proportion.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, Literal
16
+
17
+ import pandas as pd
18
+
19
+ from flowbars.bars.accumulators import ImbalanceAccumulator
20
+ from flowbars.bars.constructor import BaseBarConstructor
21
+ from flowbars.bars.registry import BarRegistry
22
+ from flowbars.calendars import TradingCalendar
23
+ from flowbars.schema import SchemaMapping
24
+ from flowbars.thresholds import EWMAThresholdEstimator
25
+
26
+
27
+ class ImbalanceTickBarConstructor(BaseBarConstructor):
28
+ """Bar constructor for tick-imbalance bars.
29
+
30
+ A tick-imbalance bar closes when the absolute signed tick-imbalance
31
+ reaches or exceeds an **adaptive** threshold estimated by an EWMA.
32
+
33
+ Parameters
34
+ ----------
35
+ span : float, default 20.0
36
+ EWMA span for the two-component threshold formula.
37
+ halflife : float or None, default None
38
+ EWMA halflife. Takes precedence over *span* when given.
39
+ initial_ewa_t : float, default 1.0
40
+ Initial seed for :math:`E[T]` (expected tick count per bar).
41
+ initial_ewa_proportion : float, default 0.5
42
+ Initial seed for :math:`E[\\theta]` (expected imbalance proportion).
43
+ warmup_bars : int, default 0
44
+ Number of initial bars to discard while the EWMA converges.
45
+ calendar : TradingCalendar, optional
46
+ schema : SchemaMapping, optional
47
+ watermark : str or None, default ``"timestamp"``
48
+ Dedup key for idempotent resume (``"timestamp"``, a column name, or
49
+ ``None`` to disable).
50
+ strict_ordering : bool, default False
51
+ If True, raise ``TickDataError`` when a tick arrives with a timestamp
52
+ earlier than the previous tick's (out-of-order input).
53
+ backend : str, default ``"python"``
54
+ ``"python"`` or ``"numba"``. The numba backend accelerates
55
+ :meth:`batch` only; ``update`` always uses Python.
56
+ stream_id : str, default ``"default"``
57
+ on_bar : callable or None
58
+ on_threshold_update : callable or None
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ span: float = 20.0,
64
+ halflife: float | None = None,
65
+ initial_ewa_t: float = 1.0,
66
+ initial_ewa_proportion: float = 0.5,
67
+ warmup_bars: int = 0,
68
+ warmup_minutes: int | None = None,
69
+ calendar: TradingCalendar | None = None,
70
+ schema: SchemaMapping | None = None,
71
+ watermark: str | None = "timestamp",
72
+ stream_id: str = "default",
73
+ strict_ordering: bool = False,
74
+ backend: Literal["python", "numba"] = "python",
75
+ on_bar: Any = None,
76
+ on_threshold_update: Any = None,
77
+ multiplier: float = 1.0,
78
+ volume_type: str = "base",
79
+ ) -> None:
80
+ accumulator = ImbalanceAccumulator(bar_type="imbalance_tick", metric="tick")
81
+ estimator = EWMAThresholdEstimator(
82
+ bar_family="imbalance",
83
+ span=span,
84
+ halflife=halflife,
85
+ initial_ewa_t=initial_ewa_t,
86
+ initial_ewa_proportion=initial_ewa_proportion,
87
+ )
88
+
89
+ super().__init__(
90
+ accumulator=accumulator,
91
+ threshold_estimator=estimator,
92
+ calendar=calendar,
93
+ schema=schema,
94
+ watermark=watermark,
95
+ stream_id=stream_id,
96
+ strict_ordering=strict_ordering,
97
+ warmup_bars=warmup_bars,
98
+ warmup_minutes=warmup_minutes,
99
+ backend=backend,
100
+ on_bar=on_bar,
101
+ on_threshold_update=on_threshold_update,
102
+ multiplier=multiplier,
103
+ volume_type=volume_type,
104
+ )
105
+
106
+ @classmethod
107
+ def from_state( # type: ignore[override]
108
+ cls,
109
+ state: dict[str, Any],
110
+ calendar: TradingCalendar | None = None,
111
+ schema: SchemaMapping | None = None,
112
+ *,
113
+ verify_identity: bool = True,
114
+ ) -> ImbalanceTickBarConstructor:
115
+ """Reconstruct an ``ImbalanceTickBarConstructor`` from a saved state dict."""
116
+ acc_state = state["accumulator"]
117
+ acc = ImbalanceAccumulator(
118
+ bar_type=state["bar_type"],
119
+ metric=acc_state["metric"],
120
+ )
121
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
122
+ inst: ImbalanceTickBarConstructor = super().from_state(
123
+ state, acc, est, calendar, schema, verify_identity=verify_identity
124
+ ) # type: ignore[assignment]
125
+ return inst
126
+
127
+
128
+ def compute_imbalance_tick_bars(
129
+ ticks_df: pd.DataFrame,
130
+ span: float = 20.0,
131
+ halflife: float | None = None,
132
+ initial_ewa_t: float = 1.0,
133
+ initial_ewa_proportion: float = 0.5,
134
+ warmup_bars: int = 0,
135
+ warmup_minutes: int | None = None,
136
+ schema: SchemaMapping | None = None,
137
+ watermark: str | None = "timestamp",
138
+ strict_ordering: bool = False,
139
+ calendar: TradingCalendar | None = None,
140
+ *,
141
+ backend: Literal["python", "numba"] = "python",
142
+ stream_id: str = "default",
143
+ on_bar: Any = None,
144
+ on_threshold_update: Any = None,
145
+ multiplier: float = 1.0,
146
+ volume_type: str = "base",
147
+ ) -> pd.DataFrame:
148
+ """Build tick-imbalance bars from a DataFrame of ticks.
149
+
150
+ Parameters
151
+ ----------
152
+ ticks_df : pd.DataFrame
153
+ Tick-level data with user-mapped columns.
154
+ span : float, default 20.0
155
+ EWMA span for the adaptive threshold.
156
+ halflife : float or None, default None
157
+ EWMA halflife (takes precedence over *span*).
158
+ initial_ewa_t : float, default 1.0
159
+ Initial seed for expected tick count per bar.
160
+ initial_ewa_proportion : float, default 0.5
161
+ Initial seed for expected imbalance proportion.
162
+ warmup_bars : int, default 0
163
+ Number of initial bars to discard.
164
+ schema : SchemaMapping, optional
165
+ watermark : str or None, default ``"timestamp"``
166
+ Dedup key for idempotent resume (``"timestamp"``, a column name, or
167
+ ``None`` to disable).
168
+ strict_ordering : bool, default False
169
+ If True, raise ``TickDataError`` when a tick arrives with a timestamp
170
+ earlier than the previous tick's (out-of-order input).
171
+ calendar : TradingCalendar, optional
172
+ backend : str, default ``"python"``
173
+ ``"python"`` or ``"numba"``.
174
+ stream_id : str, default ``"default"``
175
+ on_bar : callable or None
176
+ on_threshold_update : callable or None
177
+
178
+ Returns
179
+ -------
180
+ pd.DataFrame
181
+ Completed bars as a DataFrame.
182
+ """
183
+ ctor = ImbalanceTickBarConstructor(
184
+ span=span,
185
+ halflife=halflife,
186
+ initial_ewa_t=initial_ewa_t,
187
+ initial_ewa_proportion=initial_ewa_proportion,
188
+ warmup_bars=warmup_bars,
189
+ warmup_minutes=warmup_minutes,
190
+ calendar=calendar,
191
+ schema=schema,
192
+ watermark=watermark,
193
+ strict_ordering=strict_ordering,
194
+ backend=backend,
195
+ stream_id=stream_id,
196
+ on_bar=on_bar,
197
+ on_threshold_update=on_threshold_update,
198
+ multiplier=multiplier,
199
+ volume_type=volume_type,
200
+ )
201
+ return ctor.batch(ticks_df)
202
+
203
+
204
+ BarRegistry.register(
205
+ "imbalance_tick", ImbalanceTickBarConstructor, batch_fn=compute_imbalance_tick_bars
206
+ )
@@ -0,0 +1,200 @@
1
+ """Imbalance bars (volume) — close when absolute signed volume-imbalance crosses an adaptive threshold.
2
+
3
+ Same two-component EWMA formula as tick-imbalance bars, but the imbalance
4
+ is weighted by trade volume instead of tick count.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Literal
10
+
11
+ import pandas as pd
12
+
13
+ from flowbars.bars.accumulators import ImbalanceAccumulator
14
+ from flowbars.bars.constructor import BaseBarConstructor
15
+ from flowbars.bars.registry import BarRegistry
16
+ from flowbars.calendars import TradingCalendar
17
+ from flowbars.schema import SchemaMapping
18
+ from flowbars.thresholds import EWMAThresholdEstimator
19
+
20
+
21
+ class ImbalanceVolumeBarConstructor(BaseBarConstructor):
22
+ """Bar constructor for volume-imbalance bars.
23
+
24
+ A volume-imbalance bar closes when the absolute signed volume-imbalance
25
+ reaches or exceeds an **adaptive** threshold estimated by an EWMA.
26
+
27
+ Parameters
28
+ ----------
29
+ span : float, default 20.0
30
+ EWMA span for the two-component threshold formula.
31
+ halflife : float or None, default None
32
+ EWMA halflife. Takes precedence over *span* when given.
33
+ initial_ewa_t : float, default 1.0
34
+ Initial seed for :math:`E[T]` (expected volume per bar).
35
+ initial_ewa_proportion : float, default 0.5
36
+ Initial seed for :math:`E[\\theta]` (expected imbalance proportion).
37
+ warmup_bars : int, default 0
38
+ Number of initial bars to discard while the EWMA converges.
39
+ calendar : TradingCalendar, optional
40
+ schema : SchemaMapping, optional
41
+ watermark : str or None, default ``"timestamp"``
42
+ Dedup key for idempotent resume (``"timestamp"``, a column name, or
43
+ ``None`` to disable).
44
+ strict_ordering : bool, default False
45
+ If True, raise ``TickDataError`` when a tick arrives with a timestamp
46
+ earlier than the previous tick's (out-of-order input).
47
+ backend : str, default ``"python"``
48
+ ``"python"`` or ``"numba"``. The numba backend accelerates
49
+ :meth:`batch` only; ``update`` always uses Python.
50
+ stream_id : str, default ``"default"``
51
+ on_bar : callable or None
52
+ on_threshold_update : callable or None
53
+ """
54
+
55
+ def __init__(
56
+ self,
57
+ span: float = 20.0,
58
+ halflife: float | None = None,
59
+ initial_ewa_t: float = 1.0,
60
+ initial_ewa_proportion: float = 0.5,
61
+ warmup_bars: int = 0,
62
+ warmup_minutes: int | None = None,
63
+ calendar: TradingCalendar | None = None,
64
+ schema: SchemaMapping | None = None,
65
+ watermark: str | None = "timestamp",
66
+ stream_id: str = "default",
67
+ strict_ordering: bool = False,
68
+ backend: Literal["python", "numba"] = "python",
69
+ on_bar: Any = None,
70
+ on_threshold_update: Any = None,
71
+ multiplier: float = 1.0,
72
+ volume_type: str = "base",
73
+ ) -> None:
74
+ accumulator = ImbalanceAccumulator(bar_type="imbalance_volume", metric="volume")
75
+ estimator = EWMAThresholdEstimator(
76
+ bar_family="imbalance",
77
+ span=span,
78
+ halflife=halflife,
79
+ initial_ewa_t=initial_ewa_t,
80
+ initial_ewa_proportion=initial_ewa_proportion,
81
+ )
82
+
83
+ super().__init__(
84
+ accumulator=accumulator,
85
+ threshold_estimator=estimator,
86
+ calendar=calendar,
87
+ schema=schema,
88
+ watermark=watermark,
89
+ stream_id=stream_id,
90
+ strict_ordering=strict_ordering,
91
+ warmup_bars=warmup_bars,
92
+ warmup_minutes=warmup_minutes,
93
+ backend=backend,
94
+ on_bar=on_bar,
95
+ on_threshold_update=on_threshold_update,
96
+ multiplier=multiplier,
97
+ volume_type=volume_type,
98
+ )
99
+
100
+ @classmethod
101
+ def from_state( # type: ignore[override]
102
+ cls,
103
+ state: dict[str, Any],
104
+ calendar: TradingCalendar | None = None,
105
+ schema: SchemaMapping | None = None,
106
+ *,
107
+ verify_identity: bool = True,
108
+ ) -> ImbalanceVolumeBarConstructor:
109
+ """Reconstruct an ``ImbalanceVolumeBarConstructor`` from a saved state dict."""
110
+ acc_state = state["accumulator"]
111
+ acc = ImbalanceAccumulator(
112
+ bar_type=state["bar_type"],
113
+ metric=acc_state["metric"],
114
+ )
115
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
116
+ inst: ImbalanceVolumeBarConstructor = super().from_state(
117
+ state, acc, est, calendar, schema, verify_identity=verify_identity
118
+ ) # type: ignore[assignment]
119
+ return inst
120
+
121
+
122
+ def compute_imbalance_volume_bars(
123
+ ticks_df: pd.DataFrame,
124
+ span: float = 20.0,
125
+ halflife: float | None = None,
126
+ initial_ewa_t: float = 1.0,
127
+ initial_ewa_proportion: float = 0.5,
128
+ warmup_bars: int = 0,
129
+ warmup_minutes: int | None = None,
130
+ schema: SchemaMapping | None = None,
131
+ watermark: str | None = "timestamp",
132
+ strict_ordering: bool = False,
133
+ calendar: TradingCalendar | None = None,
134
+ *,
135
+ backend: Literal["python", "numba"] = "python",
136
+ stream_id: str = "default",
137
+ on_bar: Any = None,
138
+ on_threshold_update: Any = None,
139
+ multiplier: float = 1.0,
140
+ volume_type: str = "base",
141
+ ) -> pd.DataFrame:
142
+ """Build volume-imbalance bars from a DataFrame of ticks.
143
+
144
+ Parameters
145
+ ----------
146
+ ticks_df : pd.DataFrame
147
+ Tick-level data with user-mapped columns.
148
+ span : float, default 20.0
149
+ EWMA span for the adaptive threshold.
150
+ halflife : float or None, default None
151
+ EWMA halflife (takes precedence over *span*).
152
+ initial_ewa_t : float, default 1.0
153
+ Initial seed for expected volume per bar.
154
+ initial_ewa_proportion : float, default 0.5
155
+ Initial seed for expected imbalance proportion.
156
+ warmup_bars : int, default 0
157
+ Number of initial bars to discard.
158
+ schema : SchemaMapping, optional
159
+ watermark : str or None, default ``"timestamp"``
160
+ Dedup key for idempotent resume (``"timestamp"``, a column name, or
161
+ ``None`` to disable).
162
+ strict_ordering : bool, default False
163
+ If True, raise ``TickDataError`` when a tick arrives with a timestamp
164
+ earlier than the previous tick's (out-of-order input).
165
+ calendar : TradingCalendar, optional
166
+ backend : str, default ``"python"``
167
+ ``"python"`` or ``"numba"``.
168
+ stream_id : str, default ``"default"``
169
+ on_bar : callable or None
170
+ on_threshold_update : callable or None
171
+
172
+ Returns
173
+ -------
174
+ pd.DataFrame
175
+ Completed bars as a DataFrame.
176
+ """
177
+ ctor = ImbalanceVolumeBarConstructor(
178
+ span=span,
179
+ halflife=halflife,
180
+ initial_ewa_t=initial_ewa_t,
181
+ initial_ewa_proportion=initial_ewa_proportion,
182
+ warmup_bars=warmup_bars,
183
+ warmup_minutes=warmup_minutes,
184
+ calendar=calendar,
185
+ schema=schema,
186
+ watermark=watermark,
187
+ strict_ordering=strict_ordering,
188
+ backend=backend,
189
+ stream_id=stream_id,
190
+ on_bar=on_bar,
191
+ on_threshold_update=on_threshold_update,
192
+ multiplier=multiplier,
193
+ volume_type=volume_type,
194
+ )
195
+ return ctor.batch(ticks_df)
196
+
197
+
198
+ BarRegistry.register(
199
+ "imbalance_volume", ImbalanceVolumeBarConstructor, batch_fn=compute_imbalance_volume_bars
200
+ )