tradingapi 0.3.3__tar.gz → 0.3.5__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.3.3 → tradingapi-0.3.5}/PKG-INFO +1 -1
- {tradingapi-0.3.3 → tradingapi-0.3.5}/pyproject.toml +1 -1
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/allocation.py +18 -4
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/fivepaisa.py +50 -20
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/utils.py +1 -1
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi.egg-info/PKG-INFO +1 -1
- {tradingapi-0.3.3 → tradingapi-0.3.5}/README.md +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/setup.cfg +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tests/test_find_option_with_delta.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/__init__.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/attribution.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/broker_base.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/config/commissions_20241216.yaml +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/config/config_sample.yaml +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/config.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/dhan.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/error_handling.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/exceptions.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/flattrade.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/globals.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/icicidirect.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/icicidirect_generate_session.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/market_data_exchanges.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/proxy_utils.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/shoonya.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi/span.py +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi.egg-info/SOURCES.txt +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi.egg-info/dependency_links.txt +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi.egg-info/entry_points.txt +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi.egg-info/requires.txt +0 -0
- {tradingapi-0.3.3 → tradingapi-0.3.5}/tradingapi.egg-info/top_level.txt +0 -0
|
@@ -21,13 +21,15 @@ _ALLOCATIONS_DIR = Path(os.path.expanduser("~/onedrive/.tradingapi/allocations")
|
|
|
21
21
|
_TODAY_SYMLINK = _ALLOCATIONS_DIR / "master_allocation_today.yaml"
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
"""Return the
|
|
24
|
+
def load_today_allocation_entry(broker_name: str, strategy_name: str) -> dict:
|
|
25
|
+
"""Return the full allocation entry dict for strategy_name on broker_name today.
|
|
26
|
+
|
|
27
|
+
Example return value: {'pct': 0.275, 'redis_db': 8}
|
|
28
|
+
SCALPING strategies include a 'redis_db' key; others have only 'pct'.
|
|
26
29
|
|
|
27
30
|
Reads master_allocation_today.yaml (a symlink to today's dated file).
|
|
28
31
|
Raises FileNotFoundError if the symlink/file is missing.
|
|
29
32
|
Raises ValueError if the date in the file does not match today.
|
|
30
|
-
Returns 0.0 if the strategy is listed with pct == 0 (inactive today).
|
|
31
33
|
Raises KeyError if the broker/strategy is not found in the file at all.
|
|
32
34
|
"""
|
|
33
35
|
if not _TODAY_SYMLINK.exists():
|
|
@@ -63,7 +65,19 @@ def load_today_allocation(broker_name: str, strategy_name: str) -> float:
|
|
|
63
65
|
f"Available strategies: {list(strategies.keys())}"
|
|
64
66
|
)
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
return dict(strategies[strategy_key])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def load_today_allocation(broker_name: str, strategy_name: str) -> float:
|
|
72
|
+
"""Return the pct (0.0–1.0) allocated to strategy_name on broker_name today.
|
|
73
|
+
|
|
74
|
+
Reads master_allocation_today.yaml (a symlink to today's dated file).
|
|
75
|
+
Raises FileNotFoundError if the symlink/file is missing.
|
|
76
|
+
Raises ValueError if the date in the file does not match today.
|
|
77
|
+
Returns 0.0 if the strategy is listed with pct == 0 (inactive today).
|
|
78
|
+
Raises KeyError if the broker/strategy is not found in the file at all.
|
|
79
|
+
"""
|
|
80
|
+
entry = load_today_allocation_entry(broker_name, strategy_name)
|
|
67
81
|
return float(entry.get("pct", 0.0))
|
|
68
82
|
|
|
69
83
|
|
|
@@ -10,7 +10,7 @@ import sys
|
|
|
10
10
|
import threading
|
|
11
11
|
import time
|
|
12
12
|
import traceback
|
|
13
|
-
from typing import Any, Dict, List, Optional, Union, cast
|
|
13
|
+
from typing import Any, Callable, Dict, List, Optional, Union, cast
|
|
14
14
|
from uuid import uuid4
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
@@ -332,6 +332,10 @@ class FivePaisa(BrokerBase):
|
|
|
332
332
|
self._last_market_data_api_call_ts = 0.0
|
|
333
333
|
self._global_market_data_rate_limit_key = "fivepaisa:market_data:last_call_ts"
|
|
334
334
|
self._global_market_data_rate_limit_lock_key = "fivepaisa:market_data:lock"
|
|
335
|
+
# Populated in connect() for start_quotes_streaming.reconnect_stream (out of connect() locals).
|
|
336
|
+
self._fp_susertoken_path: Optional[str] = None
|
|
337
|
+
self._fp_restore_session_from_token: Optional[Callable[[str], bool]] = None
|
|
338
|
+
self._fp_fresh_login: Optional[Callable[[str], None]] = None
|
|
335
339
|
|
|
336
340
|
trading_logger.log_info(
|
|
337
341
|
"FivePaisa broker initialized", {"broker_type": "FivePaisa", "config_keys": list(kwargs.keys())}
|
|
@@ -747,6 +751,9 @@ class FivePaisa(BrokerBase):
|
|
|
747
751
|
context = create_error_context(susertoken_path=susertoken_path, error=str(e))
|
|
748
752
|
raise AuthenticationError(f"Error in _fresh_login: {str(e)}", context)
|
|
749
753
|
|
|
754
|
+
self._fp_restore_session_from_token = _restore_session_from_token
|
|
755
|
+
self._fp_fresh_login = _fresh_login
|
|
756
|
+
|
|
750
757
|
def get_connected():
|
|
751
758
|
"""Main connection logic with robust session management."""
|
|
752
759
|
susertoken_path = config.get(f"{self.account_key}.USERTOKEN")
|
|
@@ -755,6 +762,8 @@ class FivePaisa(BrokerBase):
|
|
|
755
762
|
context = create_error_context(broker_name=self.broker.name, config_keys=list(config.configs.keys()))
|
|
756
763
|
raise BrokerConnectionError("USERTOKEN path not configured", context)
|
|
757
764
|
|
|
765
|
+
self._fp_susertoken_path = susertoken_path
|
|
766
|
+
|
|
758
767
|
# Check if we can use existing token
|
|
759
768
|
if os.path.exists(susertoken_path):
|
|
760
769
|
try:
|
|
@@ -3663,23 +3672,27 @@ class FivePaisa(BrokerBase):
|
|
|
3663
3672
|
except Exception as e:
|
|
3664
3673
|
trading_logger.log_error("Error handling WebSocket error", e, {"original_error": str(err)})
|
|
3665
3674
|
|
|
3675
|
+
_ws_reconnect_state: Dict[str, int] = {"failures": 0}
|
|
3676
|
+
_ws_reconnect_max = 10
|
|
3677
|
+
|
|
3666
3678
|
def reconnect():
|
|
3667
|
-
"""Reconnect
|
|
3668
|
-
trading_logger.log_info("Attempting to reconnect...")
|
|
3669
|
-
time.sleep(5) # Wait
|
|
3679
|
+
"""Reconnect after WebSocket errors — same path as send_stream_request on closed socket."""
|
|
3680
|
+
trading_logger.log_info("Attempting to reconnect after WebSocket error...")
|
|
3681
|
+
time.sleep(5) # Wait before reconnecting to avoid hammering the broker
|
|
3670
3682
|
try:
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
trading_logger.log_warning(
|
|
3674
|
-
"No symbols to reconnect", {"subscribed_symbols": self.subscribed_symbols}
|
|
3675
|
-
)
|
|
3676
|
-
return
|
|
3677
|
-
if self.api is None:
|
|
3678
|
-
raise BrokerConnectionError("API client not initialized")
|
|
3679
|
-
req_data = self.api.Request_Feed("mf", "s", req_list)
|
|
3680
|
-
connect_and_receive(req_data)
|
|
3683
|
+
reconnect_stream()
|
|
3684
|
+
_ws_reconnect_state["failures"] = 0
|
|
3681
3685
|
except Exception as e:
|
|
3686
|
+
_ws_reconnect_state["failures"] += 1
|
|
3682
3687
|
trading_logger.log_error("Reconnection failed", e, {"subscribed_symbols": self.subscribed_symbols})
|
|
3688
|
+
if _ws_reconnect_state["failures"] >= _ws_reconnect_max:
|
|
3689
|
+
trading_logger.log_error(
|
|
3690
|
+
"WebSocket reconnect giving up after repeated failures",
|
|
3691
|
+
None,
|
|
3692
|
+
{"failures": _ws_reconnect_state["failures"], "subscribed_symbols": self.subscribed_symbols},
|
|
3693
|
+
)
|
|
3694
|
+
_ws_reconnect_state["failures"] = 0
|
|
3695
|
+
return
|
|
3683
3696
|
reconnect()
|
|
3684
3697
|
|
|
3685
3698
|
def connect_and_receive(req_data):
|
|
@@ -3711,6 +3724,26 @@ class FivePaisa(BrokerBase):
|
|
|
3711
3724
|
except Exception:
|
|
3712
3725
|
pass
|
|
3713
3726
|
self.subscribe_thread = None
|
|
3727
|
+
# Re-authenticate at broker level before reconnecting the websocket.
|
|
3728
|
+
# fivepaisa's server requires a fresh session after a connection drop;
|
|
3729
|
+
# reusing the old api object without re-auth results in a silent connection
|
|
3730
|
+
# that accepts subscriptions but never streams data.
|
|
3731
|
+
trading_logger.log_info("Re-authenticating before stream reconnect", {"broker": self.broker.name})
|
|
3732
|
+
token_path = self._fp_susertoken_path or config.get(f"{self.account_key}.USERTOKEN")
|
|
3733
|
+
if not token_path:
|
|
3734
|
+
raise BrokerConnectionError("USERTOKEN path not configured")
|
|
3735
|
+
restore_fn = self._fp_restore_session_from_token
|
|
3736
|
+
fresh_fn = self._fp_fresh_login
|
|
3737
|
+
if restore_fn is None or fresh_fn is None:
|
|
3738
|
+
raise BrokerConnectionError(
|
|
3739
|
+
"FivePaisa stream session helpers not initialized; connect() must succeed before streaming"
|
|
3740
|
+
)
|
|
3741
|
+
if not restore_fn(token_path):
|
|
3742
|
+
trading_logger.log_warning(
|
|
3743
|
+
"Token restore failed during reconnect, attempting fresh login",
|
|
3744
|
+
{"broker": self.broker.name},
|
|
3745
|
+
)
|
|
3746
|
+
fresh_fn(token_path)
|
|
3714
3747
|
req_list_full = expand_symbols_to_request(self.subscribed_symbols)
|
|
3715
3748
|
if not req_list_full:
|
|
3716
3749
|
context = create_error_context(operation=operation, symbols=symbols, exchange=exchange)
|
|
@@ -3827,12 +3860,9 @@ class FivePaisa(BrokerBase):
|
|
|
3827
3860
|
|
|
3828
3861
|
# Start the connection and receiving data in a separate thread
|
|
3829
3862
|
if not has_live_stream():
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
)
|
|
3833
|
-
self.subscribe_thread.start()
|
|
3834
|
-
time.sleep(2)
|
|
3835
|
-
trading_logger.log_info("Streaming thread started", {"thread_name": "MarketDataStreamer"})
|
|
3863
|
+
# WS is dead — full reconnect restoring all subscribed_symbols (already updated above).
|
|
3864
|
+
# Connecting with just the current op's req_data would lose all prior subscriptions.
|
|
3865
|
+
reconnect_stream()
|
|
3836
3866
|
else:
|
|
3837
3867
|
trading_logger.log_info(
|
|
3838
3868
|
"Requesting streaming for existing connection", {"req_data": json.dumps(req_data)}
|
|
@@ -93,7 +93,7 @@ empty_trades = pd.DataFrame(
|
|
|
93
93
|
"exit_time": pd.Series(dtype="string"),
|
|
94
94
|
"exit_quantity": pd.Series(dtype="float64"),
|
|
95
95
|
"exit_price": pd.Series(dtype="float64"),
|
|
96
|
-
"commission": pd.Series(dtype="
|
|
96
|
+
"commission": pd.Series(dtype="float64"),
|
|
97
97
|
"int_order_id": pd.Series(dtype="object"),
|
|
98
98
|
"entry_keys": pd.Series(dtype="object"),
|
|
99
99
|
"exit_keys": pd.Series(dtype="object"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|