tradingapi 0.1.7__tar.gz → 0.2.1__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.7 → tradingapi-0.2.1}/PKG-INFO +14 -1
- {tradingapi-0.1.7 → tradingapi-0.2.1}/README.md +13 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/pyproject.toml +1 -1
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/__init__.py +27 -5
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/broker_base.py +58 -40
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/config/config_sample.yaml +45 -0
- tradingapi-0.2.1/tradingapi/dhan.py +2578 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/fivepaisa.py +400 -139
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/flattrade.py +349 -61
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/icicidirect.py +905 -210
- tradingapi-0.2.1/tradingapi/market_data_exchanges.py +57 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/proxy_utils.py +9 -10
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/shoonya.py +620 -121
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/utils.py +590 -207
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi.egg-info/PKG-INFO +14 -1
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi.egg-info/SOURCES.txt +1 -1
- tradingapi-0.1.7/tests/test_shoonya_symbol_parser.py +0 -0
- tradingapi-0.1.7/tradingapi/dhan.py +0 -1686
- {tradingapi-0.1.7 → tradingapi-0.2.1}/setup.cfg +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/attribution.py +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/config/commissions_20241216.yaml +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/config.py +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/error_handling.py +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/exceptions.py +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/globals.py +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi/icicidirect_generate_session.py +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi.egg-info/dependency_links.txt +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi.egg-info/entry_points.txt +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/tradingapi.egg-info/requires.txt +0 -0
- {tradingapi-0.1.7 → tradingapi-0.2.1}/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.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Trade integration with brokers
|
|
5
5
|
Author-email: Pankaj Sharma <sharma.pankaj.kumar@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -259,6 +259,19 @@ ICICIDIRECT:
|
|
|
259
259
|
USERTOKEN_MAX_AGE_HOURS: 20
|
|
260
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
|
|
261
261
|
SYMBOLCODES: "/path/to/icicidirect/symbols"
|
|
262
|
+
|
|
263
|
+
# Dhan Broker Configuration
|
|
264
|
+
DHAN:
|
|
265
|
+
CLIENT_ID: "your_client_id"
|
|
266
|
+
ACCESS_TOKEN: "your_access_token" # Optional fallback if TOTP/PIN flow is not used
|
|
267
|
+
TOTP_TOKEN: "your_totp_token" # Optional: used for auto token refresh
|
|
268
|
+
PIN: "your_pin" # Optional: used for auto token refresh
|
|
269
|
+
USERTOKEN: "/path/to/dhan_token.txt" # Optional token cache file
|
|
270
|
+
SYMBOLCODES: "/path/to/dhan/symbols"
|
|
271
|
+
QUOTE_RATE_LIMIT_REDIS_DB: 4 # Optional: dedicated Redis DB for global quote limiter
|
|
272
|
+
QUOTE_RATE_LIMIT_RPS: 1 # Optional: quote API limit in requests/second
|
|
273
|
+
HISTORICAL_RATE_LIMIT_RPS: 10 # Optional: historical API limit in requests/second
|
|
274
|
+
REQUEST_STREAMING_DATA_RATE_LIMIT_RPS: 10 # Optional: streaming subscription request rate in requests/second
|
|
262
275
|
```
|
|
263
276
|
|
|
264
277
|
#### ICICIDirect fully automated session-token refresh (no copy/paste)
|
|
@@ -240,6 +240,19 @@ ICICIDIRECT:
|
|
|
240
240
|
USERTOKEN_MAX_AGE_HOURS: 20
|
|
241
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
|
|
242
242
|
SYMBOLCODES: "/path/to/icicidirect/symbols"
|
|
243
|
+
|
|
244
|
+
# Dhan Broker Configuration
|
|
245
|
+
DHAN:
|
|
246
|
+
CLIENT_ID: "your_client_id"
|
|
247
|
+
ACCESS_TOKEN: "your_access_token" # Optional fallback if TOTP/PIN flow is not used
|
|
248
|
+
TOTP_TOKEN: "your_totp_token" # Optional: used for auto token refresh
|
|
249
|
+
PIN: "your_pin" # Optional: used for auto token refresh
|
|
250
|
+
USERTOKEN: "/path/to/dhan_token.txt" # Optional token cache file
|
|
251
|
+
SYMBOLCODES: "/path/to/dhan/symbols"
|
|
252
|
+
QUOTE_RATE_LIMIT_REDIS_DB: 4 # Optional: dedicated Redis DB for global quote limiter
|
|
253
|
+
QUOTE_RATE_LIMIT_RPS: 1 # Optional: quote API limit in requests/second
|
|
254
|
+
HISTORICAL_RATE_LIMIT_RPS: 10 # Optional: historical API limit in requests/second
|
|
255
|
+
REQUEST_STREAMING_DATA_RATE_LIMIT_RPS: 10 # Optional: streaming subscription request rate in requests/second
|
|
243
256
|
```
|
|
244
257
|
|
|
245
258
|
#### ICICIDirect fully automated session-token refresh (no copy/paste)
|
|
@@ -144,6 +144,13 @@ class TradingAPILogger:
|
|
|
144
144
|
for handler in handlers:
|
|
145
145
|
self.logger.addHandler(handler)
|
|
146
146
|
|
|
147
|
+
# Child loggers (e.g. tradingapi.utils) propagate here; without this, the same
|
|
148
|
+
# record also reaches root and duplicates when root has its own handlers.
|
|
149
|
+
if handlers:
|
|
150
|
+
self.logger.propagate = False
|
|
151
|
+
else:
|
|
152
|
+
self.logger.propagate = True
|
|
153
|
+
|
|
147
154
|
self._configured = True
|
|
148
155
|
|
|
149
156
|
# Log configuration
|
|
@@ -180,13 +187,26 @@ class TradingAPILogger:
|
|
|
180
187
|
pass
|
|
181
188
|
return {}
|
|
182
189
|
|
|
190
|
+
def _sanitize_extra(self, context: Optional[dict] = None) -> dict:
|
|
191
|
+
"""Rename reserved LogRecord keys in extra context to avoid logging failures."""
|
|
192
|
+
extra = dict(context or {})
|
|
193
|
+
reserved_keys = set(logging.makeLogRecord({}).__dict__.keys()) | {
|
|
194
|
+
"message",
|
|
195
|
+
"asctime",
|
|
196
|
+
}
|
|
197
|
+
sanitized = {}
|
|
198
|
+
for key, value in extra.items():
|
|
199
|
+
new_key = f"context_{key}" if key in reserved_keys else key
|
|
200
|
+
sanitized[new_key] = value
|
|
201
|
+
return sanitized
|
|
202
|
+
|
|
183
203
|
def log_error(
|
|
184
204
|
self, message: str, error: Optional[Exception] = None, context: Optional[dict] = None, exc_info: bool = True
|
|
185
205
|
):
|
|
186
206
|
"""Log an error with structured context. Extra values are sanitized to single-line so
|
|
187
207
|
parse_log_errors.py can reliably match the ERROR line (same format as other tradingapi logs).
|
|
188
208
|
"""
|
|
189
|
-
extra = context
|
|
209
|
+
extra = self._sanitize_extra(context)
|
|
190
210
|
if error:
|
|
191
211
|
extra["error_type"] = type(error).__name__
|
|
192
212
|
# Keep error_message single-line so the log line matches parse_log_errors structured format
|
|
@@ -206,7 +226,7 @@ class TradingAPILogger:
|
|
|
206
226
|
|
|
207
227
|
def log_warning(self, message: str, context: Optional[dict] = None):
|
|
208
228
|
"""Log a warning with structured context."""
|
|
209
|
-
extra = context
|
|
229
|
+
extra = self._sanitize_extra(context)
|
|
210
230
|
|
|
211
231
|
# Add caller information
|
|
212
232
|
caller_info = self._get_caller_info()
|
|
@@ -216,7 +236,7 @@ class TradingAPILogger:
|
|
|
216
236
|
|
|
217
237
|
def log_info(self, message: str, context: Optional[dict] = None):
|
|
218
238
|
"""Log an info message with structured context."""
|
|
219
|
-
extra = context
|
|
239
|
+
extra = self._sanitize_extra(context)
|
|
220
240
|
|
|
221
241
|
# Add caller information
|
|
222
242
|
caller_info = self._get_caller_info()
|
|
@@ -226,7 +246,7 @@ class TradingAPILogger:
|
|
|
226
246
|
|
|
227
247
|
def log_debug(self, message: str, context: Optional[dict] = None):
|
|
228
248
|
"""Log a debug message with structured context."""
|
|
229
|
-
extra = context
|
|
249
|
+
extra = self._sanitize_extra(context)
|
|
230
250
|
|
|
231
251
|
# Add caller information
|
|
232
252
|
caller_info = self._get_caller_info()
|
|
@@ -262,7 +282,9 @@ def configure_logging(
|
|
|
262
282
|
backup_count: Number of log files to keep.
|
|
263
283
|
format_string: Custom format string for log messages.
|
|
264
284
|
enable_structured_logging: Enable structured logging with additional context.
|
|
265
|
-
configure_root_logger:
|
|
285
|
+
configure_root_logger: If True, attach the same handlers to the root logger (for third-party
|
|
286
|
+
loggers). The ``tradingapi`` logger sets propagate=False when it has handlers, so package
|
|
287
|
+
messages are not written twice when root is also configured.
|
|
266
288
|
"""
|
|
267
289
|
trading_logger.configure(
|
|
268
290
|
level=level,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
2
|
import json
|
|
3
|
-
import logging
|
|
4
3
|
import math
|
|
5
4
|
from abc import ABC, abstractmethod
|
|
6
5
|
from dataclasses import asdict, dataclass
|
|
@@ -11,10 +10,6 @@ import pandas as pd
|
|
|
11
10
|
import pytz
|
|
12
11
|
import redis
|
|
13
12
|
|
|
14
|
-
from chameli.dateutils import parse_datetime
|
|
15
|
-
|
|
16
|
-
logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
13
|
from .exceptions import (
|
|
19
14
|
TradingAPIError,
|
|
20
15
|
BrokerConnectionError,
|
|
@@ -26,8 +21,12 @@ from .exceptions import (
|
|
|
26
21
|
create_error_context,
|
|
27
22
|
)
|
|
28
23
|
from .error_handling import retry_on_error, safe_execute, log_execution_time, handle_broker_errors, validate_inputs
|
|
24
|
+
from chameli.dateutils import parse_datetime
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
|
|
27
|
+
def _get_trading_logger():
|
|
28
|
+
from tradingapi import trading_logger
|
|
29
|
+
return trading_logger
|
|
31
30
|
|
|
32
31
|
NEXT_DAY_TIMESTAMP = int((get_tradingapi_now() + dt.timedelta(days=1)).timestamp())
|
|
33
32
|
|
|
@@ -292,9 +291,9 @@ class Order:
|
|
|
292
291
|
try:
|
|
293
292
|
# Handle None values
|
|
294
293
|
if value is None:
|
|
295
|
-
|
|
294
|
+
_get_trading_logger().log_warning(
|
|
296
295
|
"Received None value, returning 0",
|
|
297
|
-
|
|
296
|
+
context={"value": value, "default": 0, "method": "_convert_to_int", "argument_name": argument_name},
|
|
298
297
|
)
|
|
299
298
|
return 0
|
|
300
299
|
|
|
@@ -311,9 +310,9 @@ class Order:
|
|
|
311
310
|
# Strip whitespace and handle empty strings
|
|
312
311
|
value = value.strip()
|
|
313
312
|
if not value:
|
|
314
|
-
|
|
313
|
+
_get_trading_logger().log_warning(
|
|
315
314
|
"Empty string value, returning 0",
|
|
316
|
-
|
|
315
|
+
context={
|
|
317
316
|
"value": repr(value),
|
|
318
317
|
"default": 0,
|
|
319
318
|
"method": "_convert_to_int",
|
|
@@ -326,9 +325,9 @@ class Order:
|
|
|
326
325
|
# Try to convert string to float first, then to int
|
|
327
326
|
return int(float(value))
|
|
328
327
|
except ValueError:
|
|
329
|
-
|
|
328
|
+
_get_trading_logger().log_warning(
|
|
330
329
|
"Failed to convert string to int",
|
|
331
|
-
|
|
330
|
+
context={
|
|
332
331
|
"value": repr(value),
|
|
333
332
|
"default": 0,
|
|
334
333
|
"method": "_convert_to_int",
|
|
@@ -343,9 +342,9 @@ class Order:
|
|
|
343
342
|
# Convert object to string and then to number
|
|
344
343
|
str_value = str(value).strip()
|
|
345
344
|
if not str_value:
|
|
346
|
-
|
|
345
|
+
_get_trading_logger().log_warning(
|
|
347
346
|
"Object converted to empty string, returning 0",
|
|
348
|
-
|
|
347
|
+
context={
|
|
349
348
|
"value": repr(value),
|
|
350
349
|
"value_type": type(value).__name__,
|
|
351
350
|
"default": 0,
|
|
@@ -357,9 +356,9 @@ class Order:
|
|
|
357
356
|
|
|
358
357
|
return int(float(str_value))
|
|
359
358
|
except (ValueError, TypeError):
|
|
360
|
-
|
|
359
|
+
_get_trading_logger().log_warning(
|
|
361
360
|
"Failed to convert object to int",
|
|
362
|
-
|
|
361
|
+
context={
|
|
363
362
|
"value": repr(value),
|
|
364
363
|
"value_type": type(value).__name__,
|
|
365
364
|
"default": 0,
|
|
@@ -370,10 +369,10 @@ class Order:
|
|
|
370
369
|
return 0
|
|
371
370
|
|
|
372
371
|
except Exception as e:
|
|
373
|
-
|
|
372
|
+
_get_trading_logger().log_error(
|
|
374
373
|
"Error converting value to int",
|
|
375
|
-
|
|
376
|
-
|
|
374
|
+
e,
|
|
375
|
+
context={
|
|
377
376
|
"value": repr(value),
|
|
378
377
|
"value_type": type(value).__name__,
|
|
379
378
|
"method": "_convert_to_int",
|
|
@@ -390,9 +389,9 @@ class Order:
|
|
|
390
389
|
try:
|
|
391
390
|
# Handle None values
|
|
392
391
|
if value is None:
|
|
393
|
-
|
|
392
|
+
_get_trading_logger().log_warning(
|
|
394
393
|
"Received None value, returning NaN",
|
|
395
|
-
|
|
394
|
+
context={
|
|
396
395
|
"value": value,
|
|
397
396
|
"default": float("nan"),
|
|
398
397
|
"method": "_convert_to_float",
|
|
@@ -410,9 +409,9 @@ class Order:
|
|
|
410
409
|
# Strip whitespace and handle empty strings
|
|
411
410
|
value = value.strip()
|
|
412
411
|
if not value:
|
|
413
|
-
|
|
412
|
+
_get_trading_logger().log_warning(
|
|
414
413
|
"Empty string value, returning NaN",
|
|
415
|
-
|
|
414
|
+
context={
|
|
416
415
|
"value": repr(value),
|
|
417
416
|
"default": float("nan"),
|
|
418
417
|
"method": "_convert_to_float",
|
|
@@ -424,9 +423,9 @@ class Order:
|
|
|
424
423
|
try:
|
|
425
424
|
return float(value)
|
|
426
425
|
except ValueError:
|
|
427
|
-
|
|
426
|
+
_get_trading_logger().log_warning(
|
|
428
427
|
"Failed to convert string to float",
|
|
429
|
-
|
|
428
|
+
context={
|
|
430
429
|
"value": repr(value),
|
|
431
430
|
"default": float("nan"),
|
|
432
431
|
"method": "_convert_to_float",
|
|
@@ -441,9 +440,9 @@ class Order:
|
|
|
441
440
|
# Convert object to string and then to number
|
|
442
441
|
str_value = str(value).strip()
|
|
443
442
|
if not str_value:
|
|
444
|
-
|
|
443
|
+
_get_trading_logger().log_warning(
|
|
445
444
|
"Object converted to empty string, returning NaN",
|
|
446
|
-
|
|
445
|
+
context={
|
|
447
446
|
"value": repr(value),
|
|
448
447
|
"value_type": type(value).__name__,
|
|
449
448
|
"default": float("nan"),
|
|
@@ -455,9 +454,9 @@ class Order:
|
|
|
455
454
|
|
|
456
455
|
return float(str_value)
|
|
457
456
|
except (ValueError, TypeError):
|
|
458
|
-
|
|
457
|
+
_get_trading_logger().log_warning(
|
|
459
458
|
"Failed to convert object to float",
|
|
460
|
-
|
|
459
|
+
context={
|
|
461
460
|
"value": repr(value),
|
|
462
461
|
"value_type": type(value).__name__,
|
|
463
462
|
"default": float("nan"),
|
|
@@ -468,10 +467,10 @@ class Order:
|
|
|
468
467
|
return float("nan")
|
|
469
468
|
|
|
470
469
|
except Exception as e:
|
|
471
|
-
|
|
470
|
+
_get_trading_logger().log_error(
|
|
472
471
|
"Error converting value to float",
|
|
473
|
-
|
|
474
|
-
|
|
472
|
+
e,
|
|
473
|
+
context={
|
|
475
474
|
"value": repr(value),
|
|
476
475
|
"value_type": type(value).__name__,
|
|
477
476
|
"method": "_convert_to_float",
|
|
@@ -494,10 +493,10 @@ class Order:
|
|
|
494
493
|
else:
|
|
495
494
|
return False
|
|
496
495
|
except Exception as e:
|
|
497
|
-
|
|
496
|
+
_get_trading_logger().log_error(
|
|
498
497
|
"Error converting value to bool",
|
|
499
|
-
|
|
500
|
-
|
|
498
|
+
e,
|
|
499
|
+
context={
|
|
501
500
|
"value": value,
|
|
502
501
|
"value_type": type(value).__name__,
|
|
503
502
|
"method": "_convert_to_bool",
|
|
@@ -722,9 +721,9 @@ class BrokerBase(ABC):
|
|
|
722
721
|
try:
|
|
723
722
|
self._validate_config(kwargs)
|
|
724
723
|
self._initialize_broker(kwargs)
|
|
725
|
-
|
|
724
|
+
_get_trading_logger().log_info(
|
|
726
725
|
"Broker initialized successfully",
|
|
727
|
-
|
|
726
|
+
context={"broker_type": self.__class__.__name__, "config_keys": list(kwargs.keys())},
|
|
728
727
|
)
|
|
729
728
|
except Exception as e:
|
|
730
729
|
context = create_error_context(
|
|
@@ -738,9 +737,9 @@ class BrokerBase(ABC):
|
|
|
738
737
|
raise ValidationError("Configuration must be a dictionary")
|
|
739
738
|
|
|
740
739
|
# Add specific validation logic for each broker type
|
|
741
|
-
|
|
740
|
+
_get_trading_logger().log_debug(
|
|
742
741
|
"Validating broker configuration",
|
|
743
|
-
|
|
742
|
+
context={"broker_type": self.__class__.__name__, "config_keys": list(config.keys())},
|
|
744
743
|
)
|
|
745
744
|
|
|
746
745
|
def _initialize_broker(self, config: dict):
|
|
@@ -835,7 +834,11 @@ class BrokerBase(ABC):
|
|
|
835
834
|
Modify an existing order.
|
|
836
835
|
|
|
837
836
|
Args:
|
|
838
|
-
**kwargs:
|
|
837
|
+
**kwargs:
|
|
838
|
+
broker_order_id (str): Broker order ID to modify.
|
|
839
|
+
new_price (float): New limit price (0 for market).
|
|
840
|
+
new_quantity (int): New total quantity.
|
|
841
|
+
order (Order, optional): Order object to bootstrap Redis state if not cached.
|
|
839
842
|
|
|
840
843
|
Returns:
|
|
841
844
|
Order: Updated order object
|
|
@@ -1071,3 +1074,18 @@ class BrokerBase(ABC):
|
|
|
1071
1074
|
MarketDataError: If balance retrieval fails
|
|
1072
1075
|
"""
|
|
1073
1076
|
pass
|
|
1077
|
+
|
|
1078
|
+
def get_margin_requirement(
|
|
1079
|
+
self, combo_symbol: str, order_size: int, exchange: str = "NSE", mds: Optional[str] = None
|
|
1080
|
+
) -> Optional[float]:
|
|
1081
|
+
"""Default broker margin hook. Brokers can override if supported."""
|
|
1082
|
+
_get_trading_logger().log_warning(
|
|
1083
|
+
"get_margin_requirement not implemented for broker",
|
|
1084
|
+
context={
|
|
1085
|
+
"broker_type": self.__class__.__name__,
|
|
1086
|
+
"combo_symbol": combo_symbol,
|
|
1087
|
+
"order_size": order_size,
|
|
1088
|
+
"exchange": exchange,
|
|
1089
|
+
},
|
|
1090
|
+
)
|
|
1091
|
+
return None
|
|
@@ -20,6 +20,7 @@ commissions:
|
|
|
20
20
|
file: "commissions_20241216.yaml"
|
|
21
21
|
|
|
22
22
|
FIVEPAISA:
|
|
23
|
+
EXCHANGES: [NSE, BSE, MCX]
|
|
23
24
|
USE_PROXY: false
|
|
24
25
|
APP_NAME:
|
|
25
26
|
APP_SOURCE:
|
|
@@ -33,7 +34,12 @@ FIVEPAISA:
|
|
|
33
34
|
SYMBOLCODES:
|
|
34
35
|
USERTOKEN:
|
|
35
36
|
|
|
37
|
+
# Additional FivePaisa accounts (optional). Pass as account= to FivePaisa().
|
|
38
|
+
# FIVEPAISA_ACCOUNT2:
|
|
39
|
+
# ... (same keys as FIVEPAISA above)
|
|
40
|
+
|
|
36
41
|
SHOONYA:
|
|
42
|
+
EXCHANGES: [NSE, BSE, MCX]
|
|
37
43
|
USE_PROXY: false
|
|
38
44
|
USER:
|
|
39
45
|
PWD:
|
|
@@ -43,7 +49,12 @@ SHOONYA:
|
|
|
43
49
|
SYMBOLCODES: "/home/psharma/onedrive/rfiles/data/static/shoonya_symbols"
|
|
44
50
|
USERTOKEN:
|
|
45
51
|
|
|
52
|
+
# Additional Shoonya accounts (optional). Pass as account= to Shoonya().
|
|
53
|
+
# SHOONYA_ACCOUNT2:
|
|
54
|
+
# ... (same keys as SHOONYA above)
|
|
55
|
+
|
|
46
56
|
ICICIDIRECT:
|
|
57
|
+
EXCHANGES: [NSE, BSE, MCX]
|
|
47
58
|
USE_PROXY: false
|
|
48
59
|
API_KEY:
|
|
49
60
|
API_SECRET:
|
|
@@ -72,3 +83,37 @@ ICICIDIRECT:
|
|
|
72
83
|
# --totp-token "${ICICI_TOTP_TOKEN}"
|
|
73
84
|
SYMBOL_MASTER_URL: # Optional: symbol master zip url
|
|
74
85
|
SYMBOLCODES:
|
|
86
|
+
|
|
87
|
+
# Additional ICICIDirect accounts (optional). Pass as account= to IciciDirect().
|
|
88
|
+
# ICICIDIRECT_ACCOUNT2:
|
|
89
|
+
# ... (same keys as ICICIDIRECT above)
|
|
90
|
+
|
|
91
|
+
DHAN:
|
|
92
|
+
EXCHANGES: [NSE, BSE]
|
|
93
|
+
USE_PROXY: false
|
|
94
|
+
CLIENT_ID:
|
|
95
|
+
ACCESS_TOKEN: # Optional fallback if TOTP/PIN flow is not used
|
|
96
|
+
TOTP_TOKEN: # Optional: used for auto token refresh
|
|
97
|
+
PIN: # Optional: used for auto token refresh
|
|
98
|
+
USERTOKEN: # Optional: token cache file path
|
|
99
|
+
SYMBOLCODES:
|
|
100
|
+
QUOTE_RATE_LIMIT_REDIS_DB: 4 # Optional: dedicated Redis DB for global quote limiter
|
|
101
|
+
QUOTE_RATE_LIMIT_RPS: 1 # Optional: quote API limit in requests/second
|
|
102
|
+
HISTORICAL_RATE_LIMIT_RPS: 10 # Optional: historical API limit in requests/second
|
|
103
|
+
REQUEST_STREAMING_DATA_RATE_LIMIT_RPS: 10 # Optional: streaming subscription request rate in requests/second
|
|
104
|
+
|
|
105
|
+
# Additional DHAN accounts (optional). Name can be anything; pass as account= to Dhan().
|
|
106
|
+
# Example: dh2 = Dhan(account="DHAN_ACCOUNT2"); dh2.connect(redis_db=5)
|
|
107
|
+
DHAN_ACCOUNT2:
|
|
108
|
+
EXCHANGES: [NSE, BSE]
|
|
109
|
+
USE_PROXY: false
|
|
110
|
+
CLIENT_ID:
|
|
111
|
+
ACCESS_TOKEN:
|
|
112
|
+
TOTP_TOKEN:
|
|
113
|
+
PIN:
|
|
114
|
+
USERTOKEN:
|
|
115
|
+
SYMBOLCODES:
|
|
116
|
+
QUOTE_RATE_LIMIT_REDIS_DB: 5
|
|
117
|
+
QUOTE_RATE_LIMIT_RPS: 1
|
|
118
|
+
HISTORICAL_RATE_LIMIT_RPS: 10
|
|
119
|
+
REQUEST_STREAMING_DATA_RATE_LIMIT_RPS: 10
|