tradingapi 0.1.6__tar.gz → 0.2.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.
- {tradingapi-0.1.6/tradingapi.egg-info → tradingapi-0.2.0}/PKG-INFO +40 -2
- tradingapi-0.1.6/PKG-INFO → tradingapi-0.2.0/README.md +37 -18
- {tradingapi-0.1.6 → tradingapi-0.2.0}/pyproject.toml +10 -4
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/__init__.py +54 -30
- tradingapi-0.2.0/tradingapi/attribution.py +1393 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/broker_base.py +71 -23
- tradingapi-0.2.0/tradingapi/config/commissions_20241216.yaml +108 -0
- tradingapi-0.2.0/tradingapi/config/config_sample.yaml +74 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/config.py +11 -2
- tradingapi-0.2.0/tradingapi/dhan.py +1686 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/error_handling.py +18 -7
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/fivepaisa.py +759 -279
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/flattrade.py +491 -294
- tradingapi-0.2.0/tradingapi/icicidirect.py +2245 -0
- tradingapi-0.2.0/tradingapi/icicidirect_generate_session.py +225 -0
- tradingapi-0.2.0/tradingapi/proxy_utils.py +131 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/shoonya.py +737 -397
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/utils.py +1079 -515
- tradingapi-0.1.6/README.md → tradingapi-0.2.0/tradingapi.egg-info/PKG-INFO +56 -1
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi.egg-info/SOURCES.txt +8 -2
- tradingapi-0.2.0/tradingapi.egg-info/entry_points.txt +2 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi.egg-info/requires.txt +2 -0
- tradingapi-0.1.6/tradingapi/icicidirect.py +0 -680
- tradingapi-0.1.6/tradingapi/testing.py +0 -143
- {tradingapi-0.1.6 → tradingapi-0.2.0}/setup.cfg +0 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tests/test_shoonya_symbol_parser.py +0 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/exceptions.py +0 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi/globals.py +0 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi.egg-info/dependency_links.txt +0 -0
- {tradingapi-0.1.6 → tradingapi-0.2.0}/tradingapi.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tradingapi
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Trade integration with brokers
|
|
5
5
|
Author-email: Pankaj Sharma <sharma.pankaj.kumar@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -14,6 +14,8 @@ Requires-Dist: redis==7.1.0
|
|
|
14
14
|
Requires-Dist: pyotp==2.9.0
|
|
15
15
|
Requires-Dist: httpx<0.29.0,>=0.25.0
|
|
16
16
|
Requires-Dist: bcrypt<4.0.0
|
|
17
|
+
Requires-Dist: pandas-stubs>=2.0.0
|
|
18
|
+
Requires-Dist: types-redis>=4.6.0
|
|
17
19
|
|
|
18
20
|
# tradingapi
|
|
19
21
|
|
|
@@ -249,10 +251,46 @@ FLATTRADE:
|
|
|
249
251
|
ICICIDIRECT:
|
|
250
252
|
API_KEY: "your_api_key"
|
|
251
253
|
API_SECRET: "your_api_secret"
|
|
252
|
-
|
|
254
|
+
USER_ID: "your_user_id" # Optional: used by auto token script
|
|
255
|
+
PASSWORD: "your_password" # Optional: used by auto token script
|
|
256
|
+
TOTP_TOKEN: "your_totp_seed" # Optional: used by auto token script
|
|
257
|
+
API_SESSION_TOKEN: "your_session_token" # Optional if you provide one directly
|
|
258
|
+
USERTOKEN: "/path/to/icicidirect_token.txt" # Optional cache file
|
|
259
|
+
USERTOKEN_MAX_AGE_HOURS: 20
|
|
260
|
+
AUTO_SESSION_TOKEN_CMD: "icicidirect-generate-session --api-key \"${ICICI_API_KEY}\" --user-id \"${ICICI_USER_ID}\" --password \"${ICICI_PASSWORD}\" --totp-token \"${ICICI_TOTP_TOKEN}\"" # Optional non-interactive token command
|
|
253
261
|
SYMBOLCODES: "/path/to/icicidirect/symbols"
|
|
254
262
|
```
|
|
255
263
|
|
|
264
|
+
#### ICICIDirect fully automated session-token refresh (no copy/paste)
|
|
265
|
+
|
|
266
|
+
When the package is installed, the `icicidirect-generate-session` CLI is available. Use it as your `AUTO_SESSION_TOKEN_CMD`; it automates login, captures the redirect token, and prints only the session token to stdout.
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
export ICICI_API_KEY="your_api_key"
|
|
270
|
+
export ICICI_USER_ID="your_user_id"
|
|
271
|
+
export ICICI_PASSWORD="your_password"
|
|
272
|
+
export ICICI_TOTP_TOKEN="your_totp_seed"
|
|
273
|
+
|
|
274
|
+
icicidirect-generate-session \
|
|
275
|
+
--api-key "$ICICI_API_KEY" \
|
|
276
|
+
--user-id "$ICICI_USER_ID" \
|
|
277
|
+
--password "$ICICI_PASSWORD" \
|
|
278
|
+
--totp-token "$ICICI_TOTP_TOKEN"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Then set:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
ICICIDIRECT:
|
|
285
|
+
AUTO_SESSION_TOKEN_CMD: "icicidirect-generate-session --api-key \"${ICICI_API_KEY}\" --user-id \"${ICICI_USER_ID}\" --password \"${ICICI_PASSWORD}\" --totp-token \"${ICICI_TOTP_TOKEN}\""
|
|
286
|
+
USERTOKEN: "/path/to/icicidirect_token.txt"
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Notes:
|
|
290
|
+
- `connect()` executes `AUTO_SESSION_TOKEN_CMD` automatically when no direct/cached token is available.
|
|
291
|
+
- When the package is installed (e.g. `pip install .`), `icicidirect-generate-session` is on PATH. From a source checkout you can run `python -m tradingapi.icicidirect_generate_session`.
|
|
292
|
+
- If `AUTO_SESSION_TOKEN_CMD` is empty and `ICICIDIRECT.AUTO_LOGIN: true`, `connect()` auto-builds a command using `API_KEY/USER_ID/PASSWORD/TOTP_TOKEN` plus optional selector/webdriver overrides.
|
|
293
|
+
|
|
256
294
|
### Commission Files
|
|
257
295
|
|
|
258
296
|
Commission files define broker-specific commission structures. They are YAML files located in the same directory as your main config file. Example structure:
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: tradingapi
|
|
3
|
-
Version: 0.1.6
|
|
4
|
-
Summary: Trade integration with brokers
|
|
5
|
-
Author-email: Pankaj Sharma <sharma.pankaj.kumar@gmail.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: homepage, https://bitbucket.org/incurrency/tradingpapi2
|
|
8
|
-
Project-URL: repository, https://bitbucket.org/incurrency/tradingapi2
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
Requires-Dist: chameli
|
|
11
|
-
Requires-Dist: NorenRestApi==0.0.32
|
|
12
|
-
Requires-Dist: py5paisa==0.7.20
|
|
13
|
-
Requires-Dist: redis==7.1.0
|
|
14
|
-
Requires-Dist: pyotp==2.9.0
|
|
15
|
-
Requires-Dist: httpx<0.29.0,>=0.25.0
|
|
16
|
-
Requires-Dist: bcrypt<4.0.0
|
|
17
|
-
|
|
18
1
|
# tradingapi
|
|
19
2
|
|
|
20
3
|
Python package for broker integration with unified interfaces for multiple Indian brokers. Provides comprehensive trading functionality including order placement, historical data access, symbology handling, margin calculations, and commission management.
|
|
@@ -249,10 +232,46 @@ FLATTRADE:
|
|
|
249
232
|
ICICIDIRECT:
|
|
250
233
|
API_KEY: "your_api_key"
|
|
251
234
|
API_SECRET: "your_api_secret"
|
|
252
|
-
|
|
235
|
+
USER_ID: "your_user_id" # Optional: used by auto token script
|
|
236
|
+
PASSWORD: "your_password" # Optional: used by auto token script
|
|
237
|
+
TOTP_TOKEN: "your_totp_seed" # Optional: used by auto token script
|
|
238
|
+
API_SESSION_TOKEN: "your_session_token" # Optional if you provide one directly
|
|
239
|
+
USERTOKEN: "/path/to/icicidirect_token.txt" # Optional cache file
|
|
240
|
+
USERTOKEN_MAX_AGE_HOURS: 20
|
|
241
|
+
AUTO_SESSION_TOKEN_CMD: "icicidirect-generate-session --api-key \"${ICICI_API_KEY}\" --user-id \"${ICICI_USER_ID}\" --password \"${ICICI_PASSWORD}\" --totp-token \"${ICICI_TOTP_TOKEN}\"" # Optional non-interactive token command
|
|
253
242
|
SYMBOLCODES: "/path/to/icicidirect/symbols"
|
|
254
243
|
```
|
|
255
244
|
|
|
245
|
+
#### ICICIDirect fully automated session-token refresh (no copy/paste)
|
|
246
|
+
|
|
247
|
+
When the package is installed, the `icicidirect-generate-session` CLI is available. Use it as your `AUTO_SESSION_TOKEN_CMD`; it automates login, captures the redirect token, and prints only the session token to stdout.
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
export ICICI_API_KEY="your_api_key"
|
|
251
|
+
export ICICI_USER_ID="your_user_id"
|
|
252
|
+
export ICICI_PASSWORD="your_password"
|
|
253
|
+
export ICICI_TOTP_TOKEN="your_totp_seed"
|
|
254
|
+
|
|
255
|
+
icicidirect-generate-session \
|
|
256
|
+
--api-key "$ICICI_API_KEY" \
|
|
257
|
+
--user-id "$ICICI_USER_ID" \
|
|
258
|
+
--password "$ICICI_PASSWORD" \
|
|
259
|
+
--totp-token "$ICICI_TOTP_TOKEN"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Then set:
|
|
263
|
+
|
|
264
|
+
```yaml
|
|
265
|
+
ICICIDIRECT:
|
|
266
|
+
AUTO_SESSION_TOKEN_CMD: "icicidirect-generate-session --api-key \"${ICICI_API_KEY}\" --user-id \"${ICICI_USER_ID}\" --password \"${ICICI_PASSWORD}\" --totp-token \"${ICICI_TOTP_TOKEN}\""
|
|
267
|
+
USERTOKEN: "/path/to/icicidirect_token.txt"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Notes:
|
|
271
|
+
- `connect()` executes `AUTO_SESSION_TOKEN_CMD` automatically when no direct/cached token is available.
|
|
272
|
+
- When the package is installed (e.g. `pip install .`), `icicidirect-generate-session` is on PATH. From a source checkout you can run `python -m tradingapi.icicidirect_generate_session`.
|
|
273
|
+
- If `AUTO_SESSION_TOKEN_CMD` is empty and `ICICIDIRECT.AUTO_LOGIN: true`, `connect()` auto-builds a command using `API_KEY/USER_ID/PASSWORD/TOTP_TOKEN` plus optional selector/webdriver overrides.
|
|
274
|
+
|
|
256
275
|
### Commission Files
|
|
257
276
|
|
|
258
277
|
Commission files define broker-specific commission structures. They are YAML files located in the same directory as your main config file. Example structure:
|
|
@@ -12,7 +12,7 @@ module = ["yaml", "pytz","requests"]
|
|
|
12
12
|
ignore_missing_imports = true
|
|
13
13
|
|
|
14
14
|
[[tool.mypy.overrides]]
|
|
15
|
-
module = ["numpy", "pyreadr", "rpy2", "
|
|
15
|
+
module = ["numpy", "pyreadr", "rpy2", "scipy"]
|
|
16
16
|
ignore_missing_imports = true
|
|
17
17
|
|
|
18
18
|
[build-system]
|
|
@@ -21,13 +21,14 @@ build-backend = "setuptools.build_meta"
|
|
|
21
21
|
|
|
22
22
|
[tool.setuptools]
|
|
23
23
|
include-package-data = true
|
|
24
|
+
packages = ["tradingapi"]
|
|
24
25
|
|
|
25
26
|
[tool.setuptools.package-data]
|
|
26
|
-
"
|
|
27
|
+
"tradingapi" = ["config/config_sample.yaml", "config/commissions_20241216.yaml"]
|
|
27
28
|
|
|
28
29
|
[project]
|
|
29
30
|
name = "tradingapi"
|
|
30
|
-
version = "0.
|
|
31
|
+
version = "0.2.0"
|
|
31
32
|
description = "Trade integration with brokers"
|
|
32
33
|
readme = "README.md"
|
|
33
34
|
license = "MIT"
|
|
@@ -41,9 +42,14 @@ dependencies = [
|
|
|
41
42
|
"redis==7.1.0",
|
|
42
43
|
"pyotp==2.9.0",
|
|
43
44
|
"httpx>=0.25.0,<0.29.0",
|
|
44
|
-
"bcrypt<4.0.0"
|
|
45
|
+
"bcrypt<4.0.0",
|
|
46
|
+
"pandas-stubs>=2.0.0",
|
|
47
|
+
"types-redis>=4.6.0"
|
|
45
48
|
]
|
|
46
49
|
|
|
50
|
+
[project.scripts]
|
|
51
|
+
icicidirect-generate-session = "tradingapi.icicidirect_generate_session:main"
|
|
52
|
+
|
|
47
53
|
[project.urls]
|
|
48
54
|
homepage = "https://bitbucket.org/incurrency/tradingpapi2"
|
|
49
55
|
repository = "https://bitbucket.org/incurrency/tradingapi2"
|
|
@@ -157,7 +157,7 @@ class TradingAPILogger:
|
|
|
157
157
|
},
|
|
158
158
|
)
|
|
159
159
|
|
|
160
|
-
def get_logger(self, name: str = None) -> logging.Logger:
|
|
160
|
+
def get_logger(self, name: Optional[str] = None) -> logging.Logger:
|
|
161
161
|
"""Get a logger instance with the specified name."""
|
|
162
162
|
if name:
|
|
163
163
|
return logging.getLogger(f"tradingapi.{name}")
|
|
@@ -167,8 +167,9 @@ class TradingAPILogger:
|
|
|
167
167
|
"""Get information about the calling function."""
|
|
168
168
|
try:
|
|
169
169
|
# Get the caller frame (skip this method and the logging method)
|
|
170
|
-
|
|
171
|
-
if
|
|
170
|
+
current_frame = inspect.currentframe()
|
|
171
|
+
if current_frame and current_frame.f_back and current_frame.f_back.f_back:
|
|
172
|
+
caller_frame = current_frame.f_back.f_back
|
|
172
173
|
info = inspect.getframeinfo(caller_frame)
|
|
173
174
|
return {
|
|
174
175
|
"caller_filename": info.filename,
|
|
@@ -179,22 +180,46 @@ class TradingAPILogger:
|
|
|
179
180
|
pass
|
|
180
181
|
return {}
|
|
181
182
|
|
|
182
|
-
def
|
|
183
|
-
"""
|
|
184
|
-
extra = context or {}
|
|
183
|
+
def _sanitize_extra(self, context: Optional[dict] = None) -> dict:
|
|
184
|
+
"""Rename reserved LogRecord keys in extra context to avoid logging failures."""
|
|
185
|
+
extra = dict(context or {})
|
|
186
|
+
reserved_keys = set(logging.makeLogRecord({}).__dict__.keys()) | {
|
|
187
|
+
"message",
|
|
188
|
+
"asctime",
|
|
189
|
+
}
|
|
190
|
+
sanitized = {}
|
|
191
|
+
for key, value in extra.items():
|
|
192
|
+
new_key = f"context_{key}" if key in reserved_keys else key
|
|
193
|
+
sanitized[new_key] = value
|
|
194
|
+
return sanitized
|
|
195
|
+
|
|
196
|
+
def log_error(
|
|
197
|
+
self, message: str, error: Optional[Exception] = None, context: Optional[dict] = None, exc_info: bool = True
|
|
198
|
+
):
|
|
199
|
+
"""Log an error with structured context. Extra values are sanitized to single-line so
|
|
200
|
+
parse_log_errors.py can reliably match the ERROR line (same format as other tradingapi logs).
|
|
201
|
+
"""
|
|
202
|
+
extra = self._sanitize_extra(context)
|
|
185
203
|
if error:
|
|
186
204
|
extra["error_type"] = type(error).__name__
|
|
187
|
-
|
|
205
|
+
# Keep error_message single-line so the log line matches parse_log_errors structured format
|
|
206
|
+
err_msg = str(error).replace("\n", " ").replace("\r", " ").strip()
|
|
207
|
+
extra["error_message"] = err_msg[:500] if len(err_msg) > 500 else err_msg
|
|
188
208
|
|
|
189
209
|
# Add caller information
|
|
190
210
|
caller_info = self._get_caller_info()
|
|
191
211
|
extra.update(caller_info)
|
|
192
212
|
|
|
213
|
+
# Sanitize any other extra values that might contain newlines (e.g. from create_error_context)
|
|
214
|
+
for key, value in list(extra.items()):
|
|
215
|
+
if isinstance(value, str) and ("\n" in value or "\r" in value):
|
|
216
|
+
extra[key] = value.replace("\n", " ").replace("\r", " ").strip()[:500]
|
|
217
|
+
|
|
193
218
|
self.logger.error(message, extra=extra, exc_info=exc_info)
|
|
194
219
|
|
|
195
|
-
def log_warning(self, message: str, context: dict = None):
|
|
220
|
+
def log_warning(self, message: str, context: Optional[dict] = None):
|
|
196
221
|
"""Log a warning with structured context."""
|
|
197
|
-
extra = context
|
|
222
|
+
extra = self._sanitize_extra(context)
|
|
198
223
|
|
|
199
224
|
# Add caller information
|
|
200
225
|
caller_info = self._get_caller_info()
|
|
@@ -202,9 +227,9 @@ class TradingAPILogger:
|
|
|
202
227
|
|
|
203
228
|
self.logger.warning(message, extra=extra)
|
|
204
229
|
|
|
205
|
-
def log_info(self, message: str, context: dict = None):
|
|
230
|
+
def log_info(self, message: str, context: Optional[dict] = None):
|
|
206
231
|
"""Log an info message with structured context."""
|
|
207
|
-
extra = context
|
|
232
|
+
extra = self._sanitize_extra(context)
|
|
208
233
|
|
|
209
234
|
# Add caller information
|
|
210
235
|
caller_info = self._get_caller_info()
|
|
@@ -212,9 +237,9 @@ class TradingAPILogger:
|
|
|
212
237
|
|
|
213
238
|
self.logger.info(message, extra=extra)
|
|
214
239
|
|
|
215
|
-
def log_debug(self, message: str, context: dict = None):
|
|
240
|
+
def log_debug(self, message: str, context: Optional[dict] = None):
|
|
216
241
|
"""Log a debug message with structured context."""
|
|
217
|
-
extra = context
|
|
242
|
+
extra = self._sanitize_extra(context)
|
|
218
243
|
|
|
219
244
|
# Add caller information
|
|
220
245
|
caller_info = self._get_caller_info()
|
|
@@ -331,23 +356,24 @@ def initialize_config(config_file_path: str, force_reload: bool = True):
|
|
|
331
356
|
raise
|
|
332
357
|
|
|
333
358
|
|
|
334
|
-
initialize_config(get_default_config_path())
|
|
359
|
+
initialize_config(str(get_default_config_path()))
|
|
335
360
|
|
|
336
361
|
|
|
337
362
|
def enable_runtime_log_level_toggle(enable: bool = True):
|
|
338
363
|
"""
|
|
339
364
|
Enable runtime log level toggling via SIGUSR1 signal.
|
|
340
|
-
|
|
365
|
+
|
|
341
366
|
When enabled, sending SIGUSR1 to the process will toggle between
|
|
342
367
|
DEBUG and INFO logging levels.
|
|
343
|
-
|
|
368
|
+
|
|
344
369
|
This implementation supports handler chaining - if another package
|
|
345
370
|
has already registered a SIGUSR1 handler, both handlers will execute
|
|
346
371
|
(this handler runs first, then calls the previous handler).
|
|
347
|
-
|
|
372
|
+
|
|
348
373
|
Args:
|
|
349
374
|
enable: If True, register the signal handler. If False, remove it.
|
|
350
375
|
"""
|
|
376
|
+
|
|
351
377
|
def toggle_debug(signum, frame):
|
|
352
378
|
current_level = trading_logger.logger.level
|
|
353
379
|
if current_level == logging.DEBUG:
|
|
@@ -357,8 +383,7 @@ def enable_runtime_log_level_toggle(enable: bool = True):
|
|
|
357
383
|
for handler in trading_logger.logger.handlers:
|
|
358
384
|
handler.setLevel(logging.INFO)
|
|
359
385
|
trading_logger.log_info(
|
|
360
|
-
"Log level changed to INFO via signal",
|
|
361
|
-
{"signal": "SIGUSR1", "previous_level": "DEBUG"}
|
|
386
|
+
"Log level changed to INFO via signal", {"signal": "SIGUSR1", "previous_level": "DEBUG"}
|
|
362
387
|
)
|
|
363
388
|
else:
|
|
364
389
|
# Change to DEBUG
|
|
@@ -368,16 +393,17 @@ def enable_runtime_log_level_toggle(enable: bool = True):
|
|
|
368
393
|
handler.setLevel(logging.DEBUG)
|
|
369
394
|
trading_logger.log_info(
|
|
370
395
|
"Log level changed to DEBUG via signal",
|
|
371
|
-
{"signal": "SIGUSR1", "previous_level": logging.getLevelName(current_level)}
|
|
396
|
+
{"signal": "SIGUSR1", "previous_level": logging.getLevelName(current_level)},
|
|
372
397
|
)
|
|
373
|
-
|
|
398
|
+
|
|
374
399
|
if enable:
|
|
375
400
|
try:
|
|
376
401
|
# Get the current handler (if any) before registering ours
|
|
377
402
|
current_handler = signal.signal(signal.SIGUSR1, toggle_debug)
|
|
378
|
-
|
|
403
|
+
|
|
379
404
|
# If there was a previous handler, chain it
|
|
380
405
|
if current_handler not in (signal.SIG_DFL, signal.SIG_IGN, None):
|
|
406
|
+
|
|
381
407
|
def chained_handler(signum, frame):
|
|
382
408
|
# Execute our handler first
|
|
383
409
|
toggle_debug(signum, frame)
|
|
@@ -388,10 +414,9 @@ def enable_runtime_log_level_toggle(enable: bool = True):
|
|
|
388
414
|
except Exception as e:
|
|
389
415
|
# Log but don't fail if previous handler has issues
|
|
390
416
|
trading_logger.log_warning(
|
|
391
|
-
"Error in chained SIGUSR1 handler",
|
|
392
|
-
{"error": str(e), "error_type": type(e).__name__}
|
|
417
|
+
"Error in chained SIGUSR1 handler", {"error": str(e), "error_type": type(e).__name__}
|
|
393
418
|
)
|
|
394
|
-
|
|
419
|
+
|
|
395
420
|
# Register the chained handler
|
|
396
421
|
signal.signal(signal.SIGUSR1, chained_handler)
|
|
397
422
|
trading_logger.log_info(
|
|
@@ -399,19 +424,18 @@ def enable_runtime_log_level_toggle(enable: bool = True):
|
|
|
399
424
|
{
|
|
400
425
|
"signal": "SIGUSR1",
|
|
401
426
|
"usage": "kill -SIGUSR1 <pid> to toggle DEBUG/INFO",
|
|
402
|
-
"previous_handler": str(current_handler)
|
|
403
|
-
}
|
|
427
|
+
"previous_handler": str(current_handler),
|
|
428
|
+
},
|
|
404
429
|
)
|
|
405
430
|
else:
|
|
406
431
|
trading_logger.log_info(
|
|
407
432
|
"Runtime log level toggle enabled",
|
|
408
|
-
{"signal": "SIGUSR1", "usage": "kill -SIGUSR1 <pid> to toggle DEBUG/INFO"}
|
|
433
|
+
{"signal": "SIGUSR1", "usage": "kill -SIGUSR1 <pid> to toggle DEBUG/INFO"},
|
|
409
434
|
)
|
|
410
435
|
except (ValueError, OSError) as e:
|
|
411
436
|
# Signal might not be available on all platforms
|
|
412
437
|
trading_logger.log_warning(
|
|
413
|
-
"Could not register signal handler for log level toggle",
|
|
414
|
-
{"error": str(e), "platform": sys.platform}
|
|
438
|
+
"Could not register signal handler for log level toggle", {"error": str(e), "platform": sys.platform}
|
|
415
439
|
)
|
|
416
440
|
else:
|
|
417
441
|
# Restore default handler
|