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,622 @@
1
+ """Information-driven minute bars: imbalance and run (volume, dollar variants).
2
+
3
+ Thresholds are the two-component EWMA from AFML §2.4, updated at bar close
4
+ from whole-minute bar statistics.
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.minute.accumulators import (
14
+ MinuteImbalanceAccumulator,
15
+ MinuteRunAccumulator,
16
+ )
17
+ from flowbars.bars.minute.engine import BaseMinuteBarConstructor
18
+ from flowbars.bars.minute.registry import MinuteBarRegistry
19
+ from flowbars.calendars import TradingCalendar
20
+ from flowbars.core import Bar
21
+ from flowbars.schema import MinuteSchemaMapping
22
+ from flowbars.thresholds import EWMAThresholdEstimator
23
+
24
+
25
+ class _MinuteEWMABarConstructor(BaseMinuteBarConstructor):
26
+ """Shared constructor for minute EWMA-threshold bars."""
27
+
28
+ _bar_family: str = "imbalance"
29
+
30
+ def __init__(
31
+ self,
32
+ accumulator: Any,
33
+ span: float,
34
+ halflife: float | None,
35
+ initial_ewa_t: float,
36
+ initial_ewa_proportion: float,
37
+ warmup_bars: int,
38
+ warmup_minutes: int | None,
39
+ calendar: TradingCalendar | None,
40
+ schema: MinuteSchemaMapping | None,
41
+ watermark: str | None,
42
+ stream_id: str,
43
+ strict_ordering: bool,
44
+ backend: str,
45
+ on_bar: Any,
46
+ on_threshold_update: Any,
47
+ multiplier: float,
48
+ volume_type: str,
49
+ session_policy: Literal["close", "carry", "discard"],
50
+ session_estimator: Literal["persist", "reset"],
51
+ ) -> None:
52
+ estimator = EWMAThresholdEstimator(
53
+ bar_family=self._bar_family,
54
+ span=span,
55
+ halflife=halflife,
56
+ initial_ewa_t=initial_ewa_t,
57
+ initial_ewa_proportion=initial_ewa_proportion,
58
+ )
59
+ super().__init__(
60
+ accumulator=accumulator,
61
+ threshold_estimator=estimator,
62
+ calendar=calendar,
63
+ schema=schema,
64
+ watermark=watermark,
65
+ stream_id=stream_id,
66
+ strict_ordering=strict_ordering,
67
+ warmup_bars=warmup_bars,
68
+ warmup_minutes=warmup_minutes,
69
+ backend=backend,
70
+ on_bar=on_bar,
71
+ on_threshold_update=on_threshold_update,
72
+ multiplier=multiplier,
73
+ volume_type=volume_type,
74
+ session_policy=session_policy,
75
+ session_estimator=session_estimator,
76
+ )
77
+
78
+
79
+ class MinuteImbalanceVolumeBarConstructor(_MinuteEWMABarConstructor):
80
+ """Minute volume-imbalance bars (``sign × volume`` per minute)."""
81
+
82
+ _bar_family = "imbalance"
83
+
84
+ def __init__(
85
+ self,
86
+ span: float = 20.0,
87
+ halflife: float | None = None,
88
+ initial_ewa_t: float = 1.0,
89
+ initial_ewa_proportion: float = 0.5,
90
+ warmup_bars: int = 0,
91
+ warmup_minutes: int | None = None,
92
+ calendar: TradingCalendar | None = None,
93
+ schema: MinuteSchemaMapping | None = None,
94
+ watermark: str | None = "timestamp",
95
+ stream_id: str = "default",
96
+ strict_ordering: bool = False,
97
+ backend: str = "python",
98
+ on_bar: Any = None,
99
+ on_threshold_update: Any = None,
100
+ multiplier: float = 1.0,
101
+ volume_type: str = "base",
102
+ session_policy: Literal["close", "carry", "discard"] = "close",
103
+ session_estimator: Literal["persist", "reset"] = "persist",
104
+ ) -> None:
105
+ super().__init__(
106
+ accumulator=MinuteImbalanceAccumulator(bar_type="imbalance_volume", metric="volume"),
107
+ span=span,
108
+ halflife=halflife,
109
+ initial_ewa_t=initial_ewa_t,
110
+ initial_ewa_proportion=initial_ewa_proportion,
111
+ warmup_bars=warmup_bars,
112
+ warmup_minutes=warmup_minutes,
113
+ calendar=calendar,
114
+ schema=schema,
115
+ watermark=watermark,
116
+ stream_id=stream_id,
117
+ strict_ordering=strict_ordering,
118
+ backend=backend,
119
+ on_bar=on_bar,
120
+ on_threshold_update=on_threshold_update,
121
+ multiplier=multiplier,
122
+ volume_type=volume_type,
123
+ session_policy=session_policy,
124
+ session_estimator=session_estimator,
125
+ )
126
+
127
+ @classmethod
128
+ def from_state( # type: ignore[override]
129
+ cls,
130
+ state: dict[str, Any],
131
+ calendar: TradingCalendar | None = None,
132
+ schema: MinuteSchemaMapping | None = None,
133
+ *,
134
+ verify_identity: bool = True,
135
+ ) -> MinuteImbalanceVolumeBarConstructor:
136
+ acc_state = state["accumulator"]
137
+ acc = MinuteImbalanceAccumulator(bar_type=state["bar_type"], metric=acc_state["metric"])
138
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
139
+ return super().from_state(
140
+ state, acc, est, calendar, schema, verify_identity=verify_identity
141
+ ) # type: ignore[return-value]
142
+
143
+
144
+ class MinuteImbalanceDollarBarConstructor(_MinuteEWMABarConstructor):
145
+ """Minute dollar-imbalance bars (``sign × close × volume`` per minute)."""
146
+
147
+ _bar_family = "imbalance"
148
+ approximate = True
149
+
150
+ def __init__(
151
+ self,
152
+ span: float = 20.0,
153
+ halflife: float | None = None,
154
+ initial_ewa_t: float = 1.0,
155
+ initial_ewa_proportion: float = 0.5,
156
+ warmup_bars: int = 0,
157
+ warmup_minutes: int | None = None,
158
+ calendar: TradingCalendar | None = None,
159
+ schema: MinuteSchemaMapping | None = None,
160
+ watermark: str | None = "timestamp",
161
+ stream_id: str = "default",
162
+ strict_ordering: bool = False,
163
+ backend: str = "python",
164
+ on_bar: Any = None,
165
+ on_threshold_update: Any = None,
166
+ multiplier: float = 1.0,
167
+ volume_type: str = "base",
168
+ session_policy: Literal["close", "carry", "discard"] = "close",
169
+ session_estimator: Literal["persist", "reset"] = "persist",
170
+ ) -> None:
171
+ super().__init__(
172
+ accumulator=MinuteImbalanceAccumulator(bar_type="imbalance_dollar", metric="dollar"),
173
+ span=span,
174
+ halflife=halflife,
175
+ initial_ewa_t=initial_ewa_t,
176
+ initial_ewa_proportion=initial_ewa_proportion,
177
+ warmup_bars=warmup_bars,
178
+ warmup_minutes=warmup_minutes,
179
+ calendar=calendar,
180
+ schema=schema,
181
+ watermark=watermark,
182
+ stream_id=stream_id,
183
+ strict_ordering=strict_ordering,
184
+ backend=backend,
185
+ on_bar=on_bar,
186
+ on_threshold_update=on_threshold_update,
187
+ multiplier=multiplier,
188
+ volume_type=volume_type,
189
+ session_policy=session_policy,
190
+ session_estimator=session_estimator,
191
+ )
192
+
193
+ @classmethod
194
+ def from_state( # type: ignore[override]
195
+ cls,
196
+ state: dict[str, Any],
197
+ calendar: TradingCalendar | None = None,
198
+ schema: MinuteSchemaMapping | None = None,
199
+ *,
200
+ verify_identity: bool = True,
201
+ ) -> MinuteImbalanceDollarBarConstructor:
202
+ acc_state = state["accumulator"]
203
+ acc = MinuteImbalanceAccumulator(bar_type=state["bar_type"], metric=acc_state["metric"])
204
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
205
+ return super().from_state(
206
+ state, acc, est, calendar, schema, verify_identity=verify_identity
207
+ ) # type: ignore[return-value]
208
+
209
+
210
+ class _MinuteRunBarConstructor(_MinuteEWMABarConstructor):
211
+ """Shared constructor for minute run bars with ``min_run_length`` filtering."""
212
+
213
+ _bar_family = "run"
214
+
215
+ def __init__(
216
+ self,
217
+ accumulator: Any,
218
+ span: float,
219
+ halflife: float | None,
220
+ initial_ewa_t: float,
221
+ initial_ewa_proportion: float,
222
+ warmup_bars: int,
223
+ warmup_minutes: int | None,
224
+ min_run_length: int,
225
+ calendar: TradingCalendar | None,
226
+ schema: MinuteSchemaMapping | None,
227
+ watermark: str | None,
228
+ stream_id: str,
229
+ strict_ordering: bool,
230
+ backend: str,
231
+ on_bar: Any,
232
+ on_threshold_update: Any,
233
+ multiplier: float,
234
+ volume_type: str,
235
+ session_policy: Literal["close", "carry", "discard"],
236
+ session_estimator: Literal["persist", "reset"],
237
+ ) -> None:
238
+ if min_run_length < 0:
239
+ raise ValueError(f"min_run_length must be non-negative, got {min_run_length}")
240
+ self._min_run_length = min_run_length
241
+ super().__init__(
242
+ accumulator=accumulator,
243
+ span=span,
244
+ halflife=halflife,
245
+ initial_ewa_t=initial_ewa_t,
246
+ initial_ewa_proportion=initial_ewa_proportion,
247
+ warmup_bars=warmup_bars,
248
+ warmup_minutes=warmup_minutes,
249
+ calendar=calendar,
250
+ schema=schema,
251
+ watermark=watermark,
252
+ stream_id=stream_id,
253
+ strict_ordering=strict_ordering,
254
+ backend=backend,
255
+ on_bar=on_bar,
256
+ on_threshold_update=on_threshold_update,
257
+ multiplier=multiplier,
258
+ volume_type=volume_type,
259
+ session_policy=session_policy,
260
+ session_estimator=session_estimator,
261
+ )
262
+
263
+ def _should_return(self, bar: Bar) -> bool:
264
+ """Filter bars below ``min_run_length`` minutes."""
265
+ return bar.num_ticks >= self._min_run_length
266
+
267
+ def _extra_params(self) -> dict[str, Any]:
268
+ return {"min_run_length": self._min_run_length}
269
+
270
+ def _extra_state(self) -> dict[str, Any]:
271
+ state = super()._extra_state()
272
+ state["min_run_length"] = self._min_run_length
273
+ return state
274
+
275
+ def _load_extra_state(self, state: dict[str, Any]) -> None:
276
+ super()._load_extra_state(state)
277
+ self._min_run_length = int(state.get("min_run_length", 0))
278
+
279
+
280
+ class MinuteRunVolumeBarConstructor(_MinuteRunBarConstructor):
281
+ """Minute volume-run bars."""
282
+
283
+ def __init__(
284
+ self,
285
+ span: float = 20.0,
286
+ halflife: float | None = None,
287
+ initial_ewa_t: float = 1.0,
288
+ initial_ewa_proportion: float = 0.5,
289
+ warmup_bars: int = 0,
290
+ warmup_minutes: int | None = None,
291
+ min_run_length: int = 0,
292
+ calendar: TradingCalendar | None = None,
293
+ schema: MinuteSchemaMapping | None = None,
294
+ watermark: str | None = "timestamp",
295
+ stream_id: str = "default",
296
+ strict_ordering: bool = False,
297
+ backend: str = "python",
298
+ on_bar: Any = None,
299
+ on_threshold_update: Any = None,
300
+ multiplier: float = 1.0,
301
+ volume_type: str = "base",
302
+ session_policy: Literal["close", "carry", "discard"] = "close",
303
+ session_estimator: Literal["persist", "reset"] = "persist",
304
+ ) -> None:
305
+ super().__init__(
306
+ accumulator=MinuteRunAccumulator(bar_type="run_volume", metric="volume"),
307
+ span=span,
308
+ halflife=halflife,
309
+ initial_ewa_t=initial_ewa_t,
310
+ initial_ewa_proportion=initial_ewa_proportion,
311
+ warmup_bars=warmup_bars,
312
+ warmup_minutes=warmup_minutes,
313
+ min_run_length=min_run_length,
314
+ calendar=calendar,
315
+ schema=schema,
316
+ watermark=watermark,
317
+ stream_id=stream_id,
318
+ strict_ordering=strict_ordering,
319
+ backend=backend,
320
+ on_bar=on_bar,
321
+ on_threshold_update=on_threshold_update,
322
+ multiplier=multiplier,
323
+ volume_type=volume_type,
324
+ session_policy=session_policy,
325
+ session_estimator=session_estimator,
326
+ )
327
+
328
+ @classmethod
329
+ def from_state( # type: ignore[override]
330
+ cls,
331
+ state: dict[str, Any],
332
+ calendar: TradingCalendar | None = None,
333
+ schema: MinuteSchemaMapping | None = None,
334
+ *,
335
+ verify_identity: bool = True,
336
+ ) -> MinuteRunVolumeBarConstructor:
337
+ acc_state = state["accumulator"]
338
+ acc = MinuteRunAccumulator(bar_type=state["bar_type"], metric=acc_state["metric"])
339
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
340
+ return super().from_state(
341
+ state, acc, est, calendar, schema, verify_identity=verify_identity
342
+ ) # type: ignore[return-value]
343
+
344
+
345
+ class MinuteRunDollarBarConstructor(_MinuteRunBarConstructor):
346
+ """Minute dollar-run bars."""
347
+
348
+ approximate = True
349
+
350
+ def __init__(
351
+ self,
352
+ span: float = 20.0,
353
+ halflife: float | None = None,
354
+ initial_ewa_t: float = 1.0,
355
+ initial_ewa_proportion: float = 0.5,
356
+ warmup_bars: int = 0,
357
+ warmup_minutes: int | None = None,
358
+ min_run_length: int = 0,
359
+ calendar: TradingCalendar | None = None,
360
+ schema: MinuteSchemaMapping | None = None,
361
+ watermark: str | None = "timestamp",
362
+ stream_id: str = "default",
363
+ strict_ordering: bool = False,
364
+ backend: str = "python",
365
+ on_bar: Any = None,
366
+ on_threshold_update: Any = None,
367
+ multiplier: float = 1.0,
368
+ volume_type: str = "base",
369
+ session_policy: Literal["close", "carry", "discard"] = "close",
370
+ session_estimator: Literal["persist", "reset"] = "persist",
371
+ ) -> None:
372
+ super().__init__(
373
+ accumulator=MinuteRunAccumulator(bar_type="run_dollar", metric="dollar"),
374
+ span=span,
375
+ halflife=halflife,
376
+ initial_ewa_t=initial_ewa_t,
377
+ initial_ewa_proportion=initial_ewa_proportion,
378
+ warmup_bars=warmup_bars,
379
+ warmup_minutes=warmup_minutes,
380
+ min_run_length=min_run_length,
381
+ calendar=calendar,
382
+ schema=schema,
383
+ watermark=watermark,
384
+ stream_id=stream_id,
385
+ strict_ordering=strict_ordering,
386
+ backend=backend,
387
+ on_bar=on_bar,
388
+ on_threshold_update=on_threshold_update,
389
+ multiplier=multiplier,
390
+ volume_type=volume_type,
391
+ session_policy=session_policy,
392
+ session_estimator=session_estimator,
393
+ )
394
+
395
+ @classmethod
396
+ def from_state( # type: ignore[override]
397
+ cls,
398
+ state: dict[str, Any],
399
+ calendar: TradingCalendar | None = None,
400
+ schema: MinuteSchemaMapping | None = None,
401
+ *,
402
+ verify_identity: bool = True,
403
+ ) -> MinuteRunDollarBarConstructor:
404
+ acc_state = state["accumulator"]
405
+ acc = MinuteRunAccumulator(bar_type=state["bar_type"], metric=acc_state["metric"])
406
+ est = EWMAThresholdEstimator.from_state(state["threshold_estimator"])
407
+ return super().from_state(
408
+ state, acc, est, calendar, schema, verify_identity=verify_identity
409
+ ) # type: ignore[return-value]
410
+
411
+
412
+ def compute_imbalance_volume_bars_from_minutes(
413
+ minutes_df: pd.DataFrame,
414
+ span: float = 20.0,
415
+ halflife: float | None = None,
416
+ initial_ewa_t: float = 1.0,
417
+ initial_ewa_proportion: float = 0.5,
418
+ warmup_bars: int = 0,
419
+ warmup_minutes: int | None = None,
420
+ schema: MinuteSchemaMapping | None = None,
421
+ *,
422
+ backend: str = "python",
423
+ watermark: str | None = "timestamp",
424
+ strict_ordering: bool = False,
425
+ calendar: TradingCalendar | None = None,
426
+ stream_id: str = "default",
427
+ on_bar: Any = None,
428
+ on_threshold_update: Any = None,
429
+ multiplier: float = 1.0,
430
+ volume_type: str = "base",
431
+ session_policy: Literal["close", "carry", "discard"] = "close",
432
+ session_estimator: Literal["persist", "reset"] = "persist",
433
+ ) -> pd.DataFrame:
434
+ """Build volume-imbalance bars from minute OHLCV candles."""
435
+ ctor = MinuteImbalanceVolumeBarConstructor(
436
+ span=span,
437
+ halflife=halflife,
438
+ initial_ewa_t=initial_ewa_t,
439
+ initial_ewa_proportion=initial_ewa_proportion,
440
+ warmup_bars=warmup_bars,
441
+ warmup_minutes=warmup_minutes,
442
+ calendar=calendar,
443
+ schema=schema,
444
+ watermark=watermark,
445
+ stream_id=stream_id,
446
+ strict_ordering=strict_ordering,
447
+ backend=backend,
448
+ on_bar=on_bar,
449
+ on_threshold_update=on_threshold_update,
450
+ multiplier=multiplier,
451
+ volume_type=volume_type,
452
+ session_policy=session_policy,
453
+ session_estimator=session_estimator,
454
+ )
455
+ return ctor.batch(minutes_df)
456
+
457
+
458
+ def compute_imbalance_dollar_bars_from_minutes(
459
+ minutes_df: pd.DataFrame,
460
+ span: float = 20.0,
461
+ halflife: float | None = None,
462
+ initial_ewa_t: float = 1.0,
463
+ initial_ewa_proportion: float = 0.5,
464
+ warmup_bars: int = 0,
465
+ warmup_minutes: int | None = None,
466
+ schema: MinuteSchemaMapping | None = None,
467
+ *,
468
+ backend: str = "python",
469
+ watermark: str | None = "timestamp",
470
+ strict_ordering: bool = False,
471
+ calendar: TradingCalendar | None = None,
472
+ stream_id: str = "default",
473
+ on_bar: Any = None,
474
+ on_threshold_update: Any = None,
475
+ multiplier: float = 1.0,
476
+ volume_type: str = "base",
477
+ session_policy: Literal["close", "carry", "discard"] = "close",
478
+ session_estimator: Literal["persist", "reset"] = "persist",
479
+ ) -> pd.DataFrame:
480
+ """Build dollar-imbalance bars from minute OHLCV candles."""
481
+ ctor = MinuteImbalanceDollarBarConstructor(
482
+ span=span,
483
+ halflife=halflife,
484
+ initial_ewa_t=initial_ewa_t,
485
+ initial_ewa_proportion=initial_ewa_proportion,
486
+ warmup_bars=warmup_bars,
487
+ warmup_minutes=warmup_minutes,
488
+ calendar=calendar,
489
+ schema=schema,
490
+ watermark=watermark,
491
+ stream_id=stream_id,
492
+ strict_ordering=strict_ordering,
493
+ backend=backend,
494
+ on_bar=on_bar,
495
+ on_threshold_update=on_threshold_update,
496
+ multiplier=multiplier,
497
+ volume_type=volume_type,
498
+ session_policy=session_policy,
499
+ session_estimator=session_estimator,
500
+ )
501
+ return ctor.batch(minutes_df)
502
+
503
+
504
+ def compute_run_volume_bars_from_minutes(
505
+ minutes_df: pd.DataFrame,
506
+ span: float = 20.0,
507
+ halflife: float | None = None,
508
+ initial_ewa_t: float = 1.0,
509
+ initial_ewa_proportion: float = 0.5,
510
+ warmup_bars: int = 0,
511
+ warmup_minutes: int | None = None,
512
+ min_run_length: int = 0,
513
+ schema: MinuteSchemaMapping | None = None,
514
+ *,
515
+ backend: str = "python",
516
+ watermark: str | None = "timestamp",
517
+ strict_ordering: bool = False,
518
+ calendar: TradingCalendar | None = None,
519
+ stream_id: str = "default",
520
+ on_bar: Any = None,
521
+ on_threshold_update: Any = None,
522
+ multiplier: float = 1.0,
523
+ volume_type: str = "base",
524
+ session_policy: Literal["close", "carry", "discard"] = "close",
525
+ session_estimator: Literal["persist", "reset"] = "persist",
526
+ ) -> pd.DataFrame:
527
+ """Build volume-run bars from minute OHLCV candles.
528
+
529
+ ``min_run_length`` is a minimum number of **minutes** per returned bar.
530
+ """
531
+ ctor = MinuteRunVolumeBarConstructor(
532
+ span=span,
533
+ halflife=halflife,
534
+ initial_ewa_t=initial_ewa_t,
535
+ initial_ewa_proportion=initial_ewa_proportion,
536
+ warmup_bars=warmup_bars,
537
+ warmup_minutes=warmup_minutes,
538
+ min_run_length=min_run_length,
539
+ calendar=calendar,
540
+ schema=schema,
541
+ watermark=watermark,
542
+ stream_id=stream_id,
543
+ strict_ordering=strict_ordering,
544
+ backend=backend,
545
+ on_bar=on_bar,
546
+ on_threshold_update=on_threshold_update,
547
+ multiplier=multiplier,
548
+ volume_type=volume_type,
549
+ session_policy=session_policy,
550
+ session_estimator=session_estimator,
551
+ )
552
+ return ctor.batch(minutes_df)
553
+
554
+
555
+ def compute_run_dollar_bars_from_minutes(
556
+ minutes_df: pd.DataFrame,
557
+ span: float = 20.0,
558
+ halflife: float | None = None,
559
+ initial_ewa_t: float = 1.0,
560
+ initial_ewa_proportion: float = 0.5,
561
+ warmup_bars: int = 0,
562
+ warmup_minutes: int | None = None,
563
+ min_run_length: int = 0,
564
+ schema: MinuteSchemaMapping | None = None,
565
+ *,
566
+ backend: str = "python",
567
+ watermark: str | None = "timestamp",
568
+ strict_ordering: bool = False,
569
+ calendar: TradingCalendar | None = None,
570
+ stream_id: str = "default",
571
+ on_bar: Any = None,
572
+ on_threshold_update: Any = None,
573
+ multiplier: float = 1.0,
574
+ volume_type: str = "base",
575
+ session_policy: Literal["close", "carry", "discard"] = "close",
576
+ session_estimator: Literal["persist", "reset"] = "persist",
577
+ ) -> pd.DataFrame:
578
+ """Build dollar-run bars from minute OHLCV candles."""
579
+ ctor = MinuteRunDollarBarConstructor(
580
+ span=span,
581
+ halflife=halflife,
582
+ initial_ewa_t=initial_ewa_t,
583
+ initial_ewa_proportion=initial_ewa_proportion,
584
+ warmup_bars=warmup_bars,
585
+ warmup_minutes=warmup_minutes,
586
+ min_run_length=min_run_length,
587
+ calendar=calendar,
588
+ schema=schema,
589
+ watermark=watermark,
590
+ stream_id=stream_id,
591
+ strict_ordering=strict_ordering,
592
+ backend=backend,
593
+ on_bar=on_bar,
594
+ on_threshold_update=on_threshold_update,
595
+ multiplier=multiplier,
596
+ volume_type=volume_type,
597
+ session_policy=session_policy,
598
+ session_estimator=session_estimator,
599
+ )
600
+ return ctor.batch(minutes_df)
601
+
602
+
603
+ MinuteBarRegistry.register(
604
+ "minute_imbalance_volume",
605
+ MinuteImbalanceVolumeBarConstructor,
606
+ batch_fn=compute_imbalance_volume_bars_from_minutes,
607
+ )
608
+ MinuteBarRegistry.register(
609
+ "minute_imbalance_dollar",
610
+ MinuteImbalanceDollarBarConstructor,
611
+ batch_fn=compute_imbalance_dollar_bars_from_minutes,
612
+ )
613
+ MinuteBarRegistry.register(
614
+ "minute_run_volume",
615
+ MinuteRunVolumeBarConstructor,
616
+ batch_fn=compute_run_volume_bars_from_minutes,
617
+ )
618
+ MinuteBarRegistry.register(
619
+ "minute_run_dollar",
620
+ MinuteRunDollarBarConstructor,
621
+ batch_fn=compute_run_dollar_bars_from_minutes,
622
+ )