rangebar 11.6.1__cp313-cp313-macosx_11_0_arm64.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 (54) hide show
  1. rangebar/CLAUDE.md +327 -0
  2. rangebar/__init__.py +227 -0
  3. rangebar/__init__.pyi +1089 -0
  4. rangebar/_core.cpython-313-darwin.so +0 -0
  5. rangebar/checkpoint.py +472 -0
  6. rangebar/cli.py +298 -0
  7. rangebar/clickhouse/CLAUDE.md +139 -0
  8. rangebar/clickhouse/__init__.py +100 -0
  9. rangebar/clickhouse/bulk_operations.py +309 -0
  10. rangebar/clickhouse/cache.py +734 -0
  11. rangebar/clickhouse/client.py +121 -0
  12. rangebar/clickhouse/config.py +141 -0
  13. rangebar/clickhouse/mixin.py +120 -0
  14. rangebar/clickhouse/preflight.py +504 -0
  15. rangebar/clickhouse/query_operations.py +345 -0
  16. rangebar/clickhouse/schema.sql +187 -0
  17. rangebar/clickhouse/tunnel.py +222 -0
  18. rangebar/constants.py +288 -0
  19. rangebar/conversion.py +177 -0
  20. rangebar/exceptions.py +207 -0
  21. rangebar/exness.py +364 -0
  22. rangebar/hooks.py +311 -0
  23. rangebar/logging.py +171 -0
  24. rangebar/notify/__init__.py +15 -0
  25. rangebar/notify/pushover.py +155 -0
  26. rangebar/notify/telegram.py +271 -0
  27. rangebar/orchestration/__init__.py +20 -0
  28. rangebar/orchestration/count_bounded.py +797 -0
  29. rangebar/orchestration/helpers.py +412 -0
  30. rangebar/orchestration/models.py +76 -0
  31. rangebar/orchestration/precompute.py +498 -0
  32. rangebar/orchestration/range_bars.py +736 -0
  33. rangebar/orchestration/tick_fetcher.py +226 -0
  34. rangebar/ouroboros.py +454 -0
  35. rangebar/processors/__init__.py +22 -0
  36. rangebar/processors/api.py +383 -0
  37. rangebar/processors/core.py +522 -0
  38. rangebar/resource_guard.py +567 -0
  39. rangebar/storage/__init__.py +22 -0
  40. rangebar/storage/checksum_registry.py +218 -0
  41. rangebar/storage/parquet.py +728 -0
  42. rangebar/streaming.py +300 -0
  43. rangebar/validation/__init__.py +69 -0
  44. rangebar/validation/cache_staleness.py +277 -0
  45. rangebar/validation/continuity.py +664 -0
  46. rangebar/validation/gap_classification.py +294 -0
  47. rangebar/validation/post_storage.py +317 -0
  48. rangebar/validation/tier1.py +175 -0
  49. rangebar/validation/tier2.py +261 -0
  50. rangebar-11.6.1.dist-info/METADATA +308 -0
  51. rangebar-11.6.1.dist-info/RECORD +54 -0
  52. rangebar-11.6.1.dist-info/WHEEL +4 -0
  53. rangebar-11.6.1.dist-info/entry_points.txt +2 -0
  54. rangebar-11.6.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,121 @@
1
+ """ClickHouse client utilities for rangebar cache.
2
+
3
+ This module provides client creation and exception handling.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ if TYPE_CHECKING:
11
+ import clickhouse_connect
12
+
13
+
14
+ class ClickHouseUnavailableError(RuntimeError):
15
+ """ClickHouse not available at specified host:port.
16
+
17
+ Raised when connection to ClickHouse fails. Includes actionable guidance.
18
+ """
19
+
20
+
21
+ class ClickHouseQueryError(RuntimeError):
22
+ """Query execution failed.
23
+
24
+ Raised when a ClickHouse query fails to execute.
25
+ """
26
+
27
+
28
+ def get_client(
29
+ host: str = "localhost",
30
+ port: int = 8123,
31
+ database: str = "default",
32
+ **kwargs: Any,
33
+ ) -> clickhouse_connect.driver.Client:
34
+ """Get a ClickHouse client connection.
35
+
36
+ Parameters
37
+ ----------
38
+ host : str
39
+ Host to connect to (default: localhost)
40
+ port : int
41
+ HTTP port (default: 8123)
42
+ database : str
43
+ Database to use (default: default)
44
+ **kwargs
45
+ Additional arguments passed to clickhouse_connect.get_client()
46
+
47
+ Returns
48
+ -------
49
+ clickhouse_connect.driver.Client
50
+ Connected ClickHouse client
51
+
52
+ Raises
53
+ ------
54
+ ClickHouseUnavailableError
55
+ If connection fails
56
+
57
+ Examples
58
+ --------
59
+ >>> client = get_client()
60
+ >>> version = client.command("SELECT version()")
61
+ >>> client.close()
62
+ """
63
+ try:
64
+ import clickhouse_connect
65
+
66
+ client = clickhouse_connect.get_client(
67
+ host=host,
68
+ port=port,
69
+ database=database,
70
+ **kwargs,
71
+ )
72
+
73
+ # Verify connection with a simple query
74
+ client.command("SELECT 1")
75
+ return client
76
+
77
+ except ImportError as e:
78
+ msg = (
79
+ "clickhouse-connect not installed. "
80
+ "Install with: pip install clickhouse-connect"
81
+ )
82
+ raise ClickHouseUnavailableError(msg) from e
83
+
84
+ except Exception as e:
85
+ msg = f"Failed to connect to ClickHouse at {host}:{port}: {e}"
86
+ raise ClickHouseUnavailableError(msg) from e
87
+
88
+
89
+ def execute_query(
90
+ client: clickhouse_connect.driver.Client,
91
+ query: str,
92
+ parameters: dict[str, Any] | None = None,
93
+ ) -> Any:
94
+ """Execute a ClickHouse query with error handling.
95
+
96
+ Parameters
97
+ ----------
98
+ client : Client
99
+ ClickHouse client
100
+ query : str
101
+ SQL query to execute
102
+ parameters : dict, optional
103
+ Query parameters
104
+
105
+ Returns
106
+ -------
107
+ Any
108
+ Query result
109
+
110
+ Raises
111
+ ------
112
+ ClickHouseQueryError
113
+ If query execution fails
114
+ """
115
+ try:
116
+ if parameters:
117
+ return client.command(query, parameters=parameters)
118
+ return client.command(query)
119
+ except Exception as e:
120
+ msg = f"Query failed: {e}\nQuery: {query[:200]}..."
121
+ raise ClickHouseQueryError(msg) from e
@@ -0,0 +1,141 @@
1
+ """ClickHouse configuration for rangebar cache.
2
+
3
+ This module provides configuration management following the mise SSoT pattern.
4
+ All host information comes from environment variables or ~/.ssh/config aliases,
5
+ never hardcoded in the package.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ from dataclasses import dataclass, field
12
+ from enum import Enum
13
+
14
+
15
+ class ClickHouseConfigError(ValueError):
16
+ """Configuration error for ClickHouse connection."""
17
+
18
+
19
+ class ConnectionMode(str, Enum):
20
+ """Connection mode for ClickHouse cache.
21
+
22
+ Controls how rangebar-py connects to ClickHouse:
23
+ - LOCAL: Force localhost:8123 only (no SSH aliases)
24
+ - CLOUD: Require CLICKHOUSE_HOST environment variable
25
+ - AUTO: Auto-detect (try localhost first, then SSH aliases)
26
+ """
27
+
28
+ LOCAL = "local"
29
+ CLOUD = "cloud"
30
+ AUTO = "auto"
31
+
32
+
33
+ def get_connection_mode() -> ConnectionMode:
34
+ """Get the connection mode from environment.
35
+
36
+ Returns
37
+ -------
38
+ ConnectionMode
39
+ Current connection mode based on RANGEBAR_MODE env var.
40
+ Defaults to AUTO if not set.
41
+
42
+ Examples
43
+ --------
44
+ >>> import os
45
+ >>> os.environ["RANGEBAR_MODE"] = "local"
46
+ >>> get_connection_mode()
47
+ <ConnectionMode.LOCAL: 'local'>
48
+ """
49
+ mode_str = os.getenv("RANGEBAR_MODE", "auto").lower()
50
+ try:
51
+ return ConnectionMode(mode_str)
52
+ except ValueError:
53
+ # Invalid mode, default to AUTO
54
+ return ConnectionMode.AUTO
55
+
56
+
57
+ @dataclass
58
+ class ClickHouseConfig:
59
+ """Configuration for ClickHouse connection.
60
+
61
+ Follows the mise SSoT pattern - all values come from environment variables.
62
+ Host aliases reference ~/.ssh/config entries, never actual IPs/hostnames.
63
+
64
+ Parameters
65
+ ----------
66
+ host : str
67
+ Host to connect to (default: localhost)
68
+ port : int
69
+ Port number (default: 8123)
70
+ database : str
71
+ Database name (default: rangebar_cache)
72
+ mode : ConnectionMode
73
+ Connection mode (default: AUTO)
74
+
75
+ Environment Variables
76
+ ---------------------
77
+ RANGEBAR_MODE : str
78
+ Connection mode: "local", "cloud", or "auto" (default: auto)
79
+ RANGEBAR_CH_HOSTS : str
80
+ Comma-separated list of SSH aliases from ~/.ssh/config
81
+ RANGEBAR_CH_PRIMARY : str
82
+ Primary host alias to prefer
83
+ CLICKHOUSE_HOST : str
84
+ Direct host override (for localhost only)
85
+ CLICKHOUSE_PORT : str
86
+ Port override
87
+ """
88
+
89
+ host: str = field(default_factory=lambda: os.getenv("CLICKHOUSE_HOST", "localhost"))
90
+ port: int = field(default_factory=lambda: int(os.getenv("CLICKHOUSE_PORT", "8123")))
91
+ database: str = "rangebar_cache"
92
+ mode: ConnectionMode = field(default_factory=get_connection_mode)
93
+
94
+ @classmethod
95
+ def from_env(cls) -> ClickHouseConfig:
96
+ """Create configuration from environment variables.
97
+
98
+ Returns
99
+ -------
100
+ ClickHouseConfig
101
+ Configuration instance
102
+
103
+ Examples
104
+ --------
105
+ >>> config = ClickHouseConfig.from_env()
106
+ >>> print(config.host, config.port)
107
+ localhost 8123
108
+ """
109
+ return cls(
110
+ host=os.getenv("CLICKHOUSE_HOST", "localhost"),
111
+ port=int(os.getenv("CLICKHOUSE_PORT", "8123")),
112
+ database=os.getenv("RANGEBAR_CH_DATABASE", "rangebar_cache"),
113
+ mode=get_connection_mode(),
114
+ )
115
+
116
+ def validate(self) -> None:
117
+ """Validate configuration.
118
+
119
+ Raises
120
+ ------
121
+ ClickHouseConfigError
122
+ If configuration is invalid
123
+ """
124
+ if self.port < 1 or self.port > 65535:
125
+ msg = f"Invalid port: {self.port}"
126
+ raise ClickHouseConfigError(msg)
127
+
128
+ if not self.database:
129
+ msg = "Database name cannot be empty"
130
+ raise ClickHouseConfigError(msg)
131
+
132
+ @property
133
+ def connection_string(self) -> str:
134
+ """Get connection string for display/logging.
135
+
136
+ Returns
137
+ -------
138
+ str
139
+ Connection string (host:port/database)
140
+ """
141
+ return f"{self.host}:{self.port}/{self.database}"
@@ -0,0 +1,120 @@
1
+ """ClickHouse client mixin for rangebar cache classes.
2
+
3
+ This module provides a mixin for managing ClickHouse client lifecycle,
4
+ following the pattern from exness-data-preprocess.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ from .client import get_client
12
+
13
+ if TYPE_CHECKING:
14
+ import clickhouse_connect
15
+
16
+
17
+ class ClickHouseClientMixin:
18
+ """Mixin for classes that need ClickHouse client access.
19
+
20
+ Provides client lifecycle management with ownership tracking.
21
+ If a client is provided externally, the class doesn't own it.
22
+ If created internally, the class owns it and will close it.
23
+
24
+ Attributes
25
+ ----------
26
+ _client : Client | None
27
+ ClickHouse client instance
28
+ _owns_client : bool
29
+ Whether this instance owns the client (should close it)
30
+
31
+ Examples
32
+ --------
33
+ >>> class MyCache(ClickHouseClientMixin):
34
+ ... def __init__(self, client=None):
35
+ ... self._init_client(client)
36
+ ...
37
+ >>> cache = MyCache() # Creates own client
38
+ >>> cache.client.command("SELECT 1")
39
+ '1'
40
+ >>> cache.close() # Closes client
41
+ """
42
+
43
+ _client: clickhouse_connect.driver.Client | None = None
44
+ _owns_client: bool = False
45
+
46
+ def _init_client(
47
+ self,
48
+ client: clickhouse_connect.driver.Client | None = None,
49
+ host: str = "localhost",
50
+ port: int = 8123,
51
+ ) -> None:
52
+ """Initialize the client.
53
+
54
+ Parameters
55
+ ----------
56
+ client : Client | None
57
+ External client to use. If None, creates a new client.
58
+ host : str
59
+ Host for new client (default: localhost)
60
+ port : int
61
+ Port for new client (default: 8123)
62
+ """
63
+ if client is not None:
64
+ self._client = client
65
+ self._owns_client = False
66
+ else:
67
+ self._client = None # Lazy initialization
68
+ self._owns_client = True
69
+ self._client_host = host
70
+ self._client_port = port
71
+
72
+ @property
73
+ def client(self) -> clickhouse_connect.driver.Client:
74
+ """Get the ClickHouse client.
75
+
76
+ Creates a new client if one doesn't exist and this instance
77
+ owns its client.
78
+
79
+ Returns
80
+ -------
81
+ Client
82
+ ClickHouse client
83
+
84
+ Raises
85
+ ------
86
+ ClickHouseUnavailableError
87
+ If client creation fails
88
+ """
89
+ if self._client is None:
90
+ if self._owns_client:
91
+ self._client = get_client(
92
+ host=getattr(self, "_client_host", "localhost"),
93
+ port=getattr(self, "_client_port", 8123),
94
+ )
95
+ else:
96
+ msg = "No client available and this instance doesn't own one"
97
+ raise RuntimeError(msg)
98
+ return self._client
99
+
100
+ def close(self) -> None:
101
+ """Close the client if owned by this instance.
102
+
103
+ Safe to call multiple times. Only closes client if this
104
+ instance created it.
105
+ """
106
+ if self._owns_client and self._client is not None:
107
+ try:
108
+ self._client.close()
109
+ except Exception:
110
+ pass # Ignore close errors
111
+ finally:
112
+ self._client = None
113
+
114
+ def __enter__(self) -> ClickHouseClientMixin:
115
+ """Context manager entry."""
116
+ return self
117
+
118
+ def __exit__(self, *args: object) -> None:
119
+ """Context manager exit - closes client."""
120
+ self.close()