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.
- rangebar/CLAUDE.md +327 -0
- rangebar/__init__.py +227 -0
- rangebar/__init__.pyi +1089 -0
- rangebar/_core.cpython-313-darwin.so +0 -0
- rangebar/checkpoint.py +472 -0
- rangebar/cli.py +298 -0
- rangebar/clickhouse/CLAUDE.md +139 -0
- rangebar/clickhouse/__init__.py +100 -0
- rangebar/clickhouse/bulk_operations.py +309 -0
- rangebar/clickhouse/cache.py +734 -0
- rangebar/clickhouse/client.py +121 -0
- rangebar/clickhouse/config.py +141 -0
- rangebar/clickhouse/mixin.py +120 -0
- rangebar/clickhouse/preflight.py +504 -0
- rangebar/clickhouse/query_operations.py +345 -0
- rangebar/clickhouse/schema.sql +187 -0
- rangebar/clickhouse/tunnel.py +222 -0
- rangebar/constants.py +288 -0
- rangebar/conversion.py +177 -0
- rangebar/exceptions.py +207 -0
- rangebar/exness.py +364 -0
- rangebar/hooks.py +311 -0
- rangebar/logging.py +171 -0
- rangebar/notify/__init__.py +15 -0
- rangebar/notify/pushover.py +155 -0
- rangebar/notify/telegram.py +271 -0
- rangebar/orchestration/__init__.py +20 -0
- rangebar/orchestration/count_bounded.py +797 -0
- rangebar/orchestration/helpers.py +412 -0
- rangebar/orchestration/models.py +76 -0
- rangebar/orchestration/precompute.py +498 -0
- rangebar/orchestration/range_bars.py +736 -0
- rangebar/orchestration/tick_fetcher.py +226 -0
- rangebar/ouroboros.py +454 -0
- rangebar/processors/__init__.py +22 -0
- rangebar/processors/api.py +383 -0
- rangebar/processors/core.py +522 -0
- rangebar/resource_guard.py +567 -0
- rangebar/storage/__init__.py +22 -0
- rangebar/storage/checksum_registry.py +218 -0
- rangebar/storage/parquet.py +728 -0
- rangebar/streaming.py +300 -0
- rangebar/validation/__init__.py +69 -0
- rangebar/validation/cache_staleness.py +277 -0
- rangebar/validation/continuity.py +664 -0
- rangebar/validation/gap_classification.py +294 -0
- rangebar/validation/post_storage.py +317 -0
- rangebar/validation/tier1.py +175 -0
- rangebar/validation/tier2.py +261 -0
- rangebar-11.6.1.dist-info/METADATA +308 -0
- rangebar-11.6.1.dist-info/RECORD +54 -0
- rangebar-11.6.1.dist-info/WHEEL +4 -0
- rangebar-11.6.1.dist-info/entry_points.txt +2 -0
- rangebar-11.6.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"""Telegram notification integration for rangebar-py.
|
|
2
|
+
|
|
3
|
+
This module provides Telegram notifications for hook events, enabling
|
|
4
|
+
"loud" alerts for failures and successes during cache operations.
|
|
5
|
+
|
|
6
|
+
Configuration
|
|
7
|
+
-------------
|
|
8
|
+
Set environment variables (via Doppler or .env):
|
|
9
|
+
|
|
10
|
+
RANGEBAR_TELEGRAM_TOKEN - Bot token from @BotFather
|
|
11
|
+
RANGEBAR_TELEGRAM_CHAT_ID - Your chat ID (send /start to bot to get it)
|
|
12
|
+
|
|
13
|
+
Or use Doppler:
|
|
14
|
+
doppler secrets set RANGEBAR_TELEGRAM_TOKEN --project rangebar --config prd
|
|
15
|
+
doppler secrets set RANGEBAR_TELEGRAM_CHAT_ID --project rangebar --config prd
|
|
16
|
+
|
|
17
|
+
Usage
|
|
18
|
+
-----
|
|
19
|
+
>>> from rangebar.notify.telegram import enable_telegram_notifications
|
|
20
|
+
>>> enable_telegram_notifications()
|
|
21
|
+
>>> # All hook events will now be sent to Telegram
|
|
22
|
+
|
|
23
|
+
Or selective registration for failures only:
|
|
24
|
+
>>> from rangebar.notify.telegram import telegram_notify
|
|
25
|
+
>>> from rangebar.hooks import register_for_failures
|
|
26
|
+
>>> register_for_failures(telegram_notify)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import json
|
|
32
|
+
import logging
|
|
33
|
+
import os
|
|
34
|
+
from functools import lru_cache
|
|
35
|
+
from typing import TYPE_CHECKING
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from ..hooks import HookPayload
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
# Default chat ID (Terry Li @EonLabsOperations) - can be overridden via env
|
|
43
|
+
# SSoT-OK: This is a Telegram chat ID, not a version number
|
|
44
|
+
_DEFAULT_CHAT_ID = "90417581"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@lru_cache(maxsize=1)
|
|
48
|
+
def get_telegram_config() -> dict[str, str | None]:
|
|
49
|
+
"""Load Telegram configuration from environment.
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
dict
|
|
54
|
+
Configuration with 'token' and 'chat_id' keys.
|
|
55
|
+
Values may be None if not configured.
|
|
56
|
+
|
|
57
|
+
Notes
|
|
58
|
+
-----
|
|
59
|
+
Configuration sources (in priority order):
|
|
60
|
+
1. Environment variables (RANGEBAR_TELEGRAM_TOKEN, RANGEBAR_TELEGRAM_CHAT_ID)
|
|
61
|
+
2. Default chat ID (for internal use)
|
|
62
|
+
"""
|
|
63
|
+
return {
|
|
64
|
+
"token": os.environ.get("RANGEBAR_TELEGRAM_TOKEN"),
|
|
65
|
+
"chat_id": os.environ.get("RANGEBAR_TELEGRAM_CHAT_ID", _DEFAULT_CHAT_ID),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_configured() -> bool:
|
|
70
|
+
"""Check if Telegram notifications are configured.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
bool
|
|
75
|
+
True if both token and chat_id are available.
|
|
76
|
+
"""
|
|
77
|
+
config = get_telegram_config()
|
|
78
|
+
return bool(config.get("token") and config.get("chat_id"))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def send_telegram(
|
|
82
|
+
message: str,
|
|
83
|
+
*,
|
|
84
|
+
parse_mode: str = "HTML",
|
|
85
|
+
disable_notification: bool = False,
|
|
86
|
+
) -> bool:
|
|
87
|
+
"""Send a message via Telegram bot.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
message : str
|
|
92
|
+
Message text (supports HTML formatting).
|
|
93
|
+
parse_mode : str
|
|
94
|
+
Telegram parse mode ("HTML" or "Markdown").
|
|
95
|
+
disable_notification : bool
|
|
96
|
+
If True, send silently without notification sound.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
bool
|
|
101
|
+
True if message was sent successfully, False otherwise.
|
|
102
|
+
|
|
103
|
+
Examples
|
|
104
|
+
--------
|
|
105
|
+
>>> send_telegram("<b>Alert:</b> Cache write failed for BTCUSDT")
|
|
106
|
+
True
|
|
107
|
+
"""
|
|
108
|
+
config = get_telegram_config()
|
|
109
|
+
token = config.get("token")
|
|
110
|
+
chat_id = config.get("chat_id")
|
|
111
|
+
|
|
112
|
+
if not token:
|
|
113
|
+
logger.warning("Telegram not configured: RANGEBAR_TELEGRAM_TOKEN not set")
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
if not chat_id:
|
|
117
|
+
logger.warning("Telegram not configured: RANGEBAR_TELEGRAM_CHAT_ID not set")
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
# Import requests only when needed (optional dependency)
|
|
121
|
+
try:
|
|
122
|
+
import requests
|
|
123
|
+
except ImportError:
|
|
124
|
+
logger.warning("Telegram notifications require 'requests' package")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
url = f"https://api.telegram.org/bot{token}/sendMessage"
|
|
128
|
+
payload = {
|
|
129
|
+
"chat_id": chat_id,
|
|
130
|
+
"text": message,
|
|
131
|
+
"parse_mode": parse_mode,
|
|
132
|
+
"disable_notification": disable_notification,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
response = requests.post(url, json=payload, timeout=10)
|
|
137
|
+
response.raise_for_status()
|
|
138
|
+
logger.debug("Telegram message sent successfully")
|
|
139
|
+
return True
|
|
140
|
+
except requests.exceptions.Timeout:
|
|
141
|
+
logger.warning("Telegram notification timed out")
|
|
142
|
+
return False
|
|
143
|
+
except requests.exceptions.RequestException as e:
|
|
144
|
+
logger.warning("Telegram notification failed: %s", e)
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def telegram_notify(payload: HookPayload) -> None:
|
|
149
|
+
"""Hook callback that sends notifications to Telegram.
|
|
150
|
+
|
|
151
|
+
This function is designed to be registered as a hook callback.
|
|
152
|
+
It formats the payload into a human-readable message and sends
|
|
153
|
+
it to Telegram.
|
|
154
|
+
|
|
155
|
+
Parameters
|
|
156
|
+
----------
|
|
157
|
+
payload : HookPayload
|
|
158
|
+
Event payload from the hooks system.
|
|
159
|
+
|
|
160
|
+
Examples
|
|
161
|
+
--------
|
|
162
|
+
>>> from rangebar.hooks import register_hook, HookEvent
|
|
163
|
+
>>> register_hook(HookEvent.CACHE_WRITE_FAILED, telegram_notify)
|
|
164
|
+
"""
|
|
165
|
+
emoji = "\u274c" if payload.is_failure else "\u2705" # ❌ or ✅
|
|
166
|
+
status = "FAILED" if payload.is_failure else "SUCCESS"
|
|
167
|
+
|
|
168
|
+
# Format details as readable text
|
|
169
|
+
details_text = ""
|
|
170
|
+
if payload.details:
|
|
171
|
+
details_lines = []
|
|
172
|
+
for key, value in payload.details.items():
|
|
173
|
+
# Handle nested dicts/lists
|
|
174
|
+
if isinstance(value, dict | list):
|
|
175
|
+
display_value = json.dumps(value, indent=2)
|
|
176
|
+
else:
|
|
177
|
+
display_value = value
|
|
178
|
+
details_lines.append(f" {key}: {display_value}")
|
|
179
|
+
details_text = "\n".join(details_lines)
|
|
180
|
+
|
|
181
|
+
message = f"""{emoji} <b>rangebar-py {status}</b>
|
|
182
|
+
|
|
183
|
+
<b>Event:</b> {payload.event.value}
|
|
184
|
+
<b>Symbol:</b> {payload.symbol}
|
|
185
|
+
<b>Time:</b> {payload.timestamp.strftime("%Y-%m-%d %H:%M:%S UTC")}"""
|
|
186
|
+
|
|
187
|
+
if details_text:
|
|
188
|
+
message += f"""
|
|
189
|
+
|
|
190
|
+
<b>Details:</b>
|
|
191
|
+
<pre>{details_text}</pre>"""
|
|
192
|
+
|
|
193
|
+
# Send silently for success, loudly for failures
|
|
194
|
+
send_telegram(
|
|
195
|
+
message,
|
|
196
|
+
disable_notification=not payload.is_failure,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def enable_telegram_notifications() -> bool:
|
|
201
|
+
"""Enable Telegram notifications for all hook events.
|
|
202
|
+
|
|
203
|
+
Registers telegram_notify as a callback for all HookEvent types.
|
|
204
|
+
|
|
205
|
+
Returns
|
|
206
|
+
-------
|
|
207
|
+
bool
|
|
208
|
+
True if Telegram is configured and hooks were registered,
|
|
209
|
+
False if Telegram is not configured.
|
|
210
|
+
|
|
211
|
+
Examples
|
|
212
|
+
--------
|
|
213
|
+
>>> if enable_telegram_notifications():
|
|
214
|
+
... print("Telegram notifications enabled")
|
|
215
|
+
... else:
|
|
216
|
+
... print("Telegram not configured - set RANGEBAR_TELEGRAM_TOKEN")
|
|
217
|
+
"""
|
|
218
|
+
if not is_configured():
|
|
219
|
+
logger.warning(
|
|
220
|
+
"Telegram not configured. Set RANGEBAR_TELEGRAM_TOKEN environment variable."
|
|
221
|
+
)
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
from ..hooks import HookEvent, register_hook
|
|
225
|
+
|
|
226
|
+
for event in HookEvent:
|
|
227
|
+
register_hook(event, telegram_notify)
|
|
228
|
+
|
|
229
|
+
logger.info("Telegram notifications enabled for all hook events")
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def enable_failure_notifications() -> bool:
|
|
234
|
+
"""Enable Telegram notifications for failure events only.
|
|
235
|
+
|
|
236
|
+
Registers telegram_notify as a callback for all *_FAILED events.
|
|
237
|
+
This is useful when you only want to be alerted about problems.
|
|
238
|
+
|
|
239
|
+
Returns
|
|
240
|
+
-------
|
|
241
|
+
bool
|
|
242
|
+
True if Telegram is configured and hooks were registered,
|
|
243
|
+
False if Telegram is not configured.
|
|
244
|
+
|
|
245
|
+
Examples
|
|
246
|
+
--------
|
|
247
|
+
>>> enable_failure_notifications()
|
|
248
|
+
True
|
|
249
|
+
"""
|
|
250
|
+
if not is_configured():
|
|
251
|
+
logger.warning(
|
|
252
|
+
"Telegram not configured. Set RANGEBAR_TELEGRAM_TOKEN environment variable."
|
|
253
|
+
)
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
from ..hooks import register_for_failures
|
|
257
|
+
|
|
258
|
+
register_for_failures(telegram_notify)
|
|
259
|
+
|
|
260
|
+
logger.info("Telegram notifications enabled for failure events")
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
__all__ = [
|
|
265
|
+
"enable_failure_notifications",
|
|
266
|
+
"enable_telegram_notifications",
|
|
267
|
+
"get_telegram_config",
|
|
268
|
+
"is_configured",
|
|
269
|
+
"send_telegram",
|
|
270
|
+
"telegram_notify",
|
|
271
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Issue #46: Modularization M4 - Orchestration subpackage
|
|
2
|
+
"""Orchestration subpackage for range bar retrieval and precomputation.
|
|
3
|
+
|
|
4
|
+
Re-exports public symbols for backward compatibility:
|
|
5
|
+
from rangebar.orchestration import get_range_bars, precompute_range_bars
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .count_bounded import get_n_range_bars
|
|
9
|
+
from .models import PrecomputeProgress, PrecomputeResult
|
|
10
|
+
from .precompute import precompute_range_bars
|
|
11
|
+
from .range_bars import get_range_bars, get_range_bars_pandas
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"PrecomputeProgress",
|
|
15
|
+
"PrecomputeResult",
|
|
16
|
+
"get_n_range_bars",
|
|
17
|
+
"get_range_bars",
|
|
18
|
+
"get_range_bars_pandas",
|
|
19
|
+
"precompute_range_bars",
|
|
20
|
+
]
|