tradingapi 0.3.2__tar.gz → 0.3.4__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.
Files changed (31) hide show
  1. {tradingapi-0.3.2 → tradingapi-0.3.4}/PKG-INFO +1 -1
  2. {tradingapi-0.3.2 → tradingapi-0.3.4}/pyproject.toml +1 -1
  3. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/fivepaisa.py +47 -14
  4. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/utils.py +3 -20
  5. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi.egg-info/PKG-INFO +1 -1
  6. {tradingapi-0.3.2 → tradingapi-0.3.4}/README.md +0 -0
  7. {tradingapi-0.3.2 → tradingapi-0.3.4}/setup.cfg +0 -0
  8. {tradingapi-0.3.2 → tradingapi-0.3.4}/tests/test_find_option_with_delta.py +0 -0
  9. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/__init__.py +0 -0
  10. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/allocation.py +0 -0
  11. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/attribution.py +0 -0
  12. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/broker_base.py +0 -0
  13. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/config/commissions_20241216.yaml +0 -0
  14. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/config/config_sample.yaml +0 -0
  15. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/config.py +0 -0
  16. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/dhan.py +0 -0
  17. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/error_handling.py +0 -0
  18. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/exceptions.py +0 -0
  19. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/flattrade.py +0 -0
  20. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/globals.py +0 -0
  21. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/icicidirect.py +0 -0
  22. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/icicidirect_generate_session.py +0 -0
  23. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/market_data_exchanges.py +0 -0
  24. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/proxy_utils.py +0 -0
  25. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/shoonya.py +0 -0
  26. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi/span.py +0 -0
  27. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi.egg-info/SOURCES.txt +0 -0
  28. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi.egg-info/dependency_links.txt +0 -0
  29. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi.egg-info/entry_points.txt +0 -0
  30. {tradingapi-0.3.2 → tradingapi-0.3.4}/tradingapi.egg-info/requires.txt +0 -0
  31. {tradingapi-0.3.2 → tradingapi-0.3.4}/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.2
3
+ Version: 0.3.4
4
4
  Summary: Trade integration with brokers
5
5
  Author-email: Pankaj Sharma <sharma.pankaj.kumar@gmail.com>
6
6
  License-Expression: MIT
@@ -28,7 +28,7 @@ packages = ["tradingapi"]
28
28
 
29
29
  [project]
30
30
  name = "tradingapi"
31
- version = "0.3.2"
31
+ version = "0.3.4"
32
32
  description = "Trade integration with brokers"
33
33
  readme = "README.md"
34
34
  license = "MIT"
@@ -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 to WebSocket (used by error_data callback)."""
3668
- trading_logger.log_info("Attempting to reconnect...")
3669
- time.sleep(5) # Wait for a few seconds before reconnecting
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
- req_list = expand_symbols_to_request(self.subscribed_symbols)
3672
- if not req_list:
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)
@@ -3247,7 +3247,7 @@ def find_option_with_delta(
3247
3247
  valid_delta_count = 0
3248
3248
  nan_delta_count = 0
3249
3249
  nan_delta_strikes = [] # track strikes where delta could not be calculated
3250
- # Cache so seeding sweep + binary search + linear fallback never recompute the same strike.
3250
+ # Cache so seeding sweep + binary search never recompute the same strike.
3251
3251
  delta_cache: dict[int, float] = {}
3252
3252
 
3253
3253
  def _delta_at(idx: int) -> float:
@@ -3296,15 +3296,9 @@ def find_option_with_delta(
3296
3296
  _consider(idx, ad)
3297
3297
 
3298
3298
  if len(seed_samples) < 2:
3299
- # Too few seeds; fall through to linear scan over the whole chain.
3300
- for idx in range(n):
3301
- d = _delta_at(idx)
3302
- if math.isnan(d):
3303
- continue
3304
- _consider(idx, abs(d))
3305
3299
  if best_index < 0:
3306
3300
  trading_logger.log_warning(
3307
- f"find_option_with_delta: no valid option strike found (linear fallback after seed failure). "
3301
+ f"find_option_with_delta: no valid option strike found (seed failure). "
3308
3302
  f"price_f={price_f} target_delta={target_delta} return_lower_delta={return_lower_delta} "
3309
3303
  f"chain_len={n} strike_range=[{strike_lo}, {strike_hi}] "
3310
3304
  f"valid_delta_count={valid_delta_count} nan_delta_count={nan_delta_count} "
@@ -3333,7 +3327,7 @@ def find_option_with_delta(
3333
3327
  if math.isnan(d):
3334
3328
  alt = _nearest_valid(mid, left, right)
3335
3329
  if alt < 0:
3336
- # Wide NaN band bail to linear fallback below.
3330
+ # Wide NaN band: stop here instead of expanding into a full-chain scan.
3337
3331
  break
3338
3332
  eff_idx = alt
3339
3333
  d = delta_cache[alt]
@@ -3351,17 +3345,6 @@ def find_option_with_delta(
3351
3345
  else:
3352
3346
  right = eff_idx - 1
3353
3347
 
3354
- # --- Change 3: linear-scan fallback if binary search failed to land a best ---
3355
- if best_index < 0:
3356
- for idx in range(n):
3357
- if idx in delta_cache:
3358
- d = delta_cache[idx]
3359
- else:
3360
- d = _delta_at(idx)
3361
- if math.isnan(d):
3362
- continue
3363
- _consider(idx, abs(d))
3364
-
3365
3348
  if best_index < 0:
3366
3349
  trading_logger.log_warning(
3367
3350
  f"find_option_with_delta: no valid option strike found. "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tradingapi
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Trade integration with brokers
5
5
  Author-email: Pankaj Sharma <sharma.pankaj.kumar@gmail.com>
6
6
  License-Expression: MIT
File without changes
File without changes