tradingapi 0.3.8__tar.gz → 0.3.10__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.8 → tradingapi-0.3.10}/PKG-INFO +1 -1
- {tradingapi-0.3.8 → tradingapi-0.3.10}/pyproject.toml +1 -1
- tradingapi-0.3.10/tests/test_broker_side_terminal_order.py +30 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/broker_base.py +130 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/config/config_sample.yaml +7 -1
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/dhan.py +48 -17
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/fivepaisa.py +227 -100
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/flattrade.py +24 -3
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/icicidirect.py +34 -4
- tradingapi-0.3.10/tradingapi/proxy_utils.py +294 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/shoonya.py +22 -3
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/utils.py +129 -14
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi.egg-info/PKG-INFO +1 -1
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi.egg-info/SOURCES.txt +1 -0
- tradingapi-0.3.8/tradingapi/proxy_utils.py +0 -130
- {tradingapi-0.3.8 → tradingapi-0.3.10}/README.md +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/setup.cfg +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tests/test_calculate_delta_realtime_quotes.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tests/test_find_option_with_delta.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/__init__.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/allocation.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/attribution.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/config/commissions_20241216.yaml +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/config.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/error_handling.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/exceptions.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/globals.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/icicidirect_generate_session.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/market_data_exchanges.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi/span.py +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi.egg-info/dependency_links.txt +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi.egg-info/entry_points.txt +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi.egg-info/requires.txt +0 -0
- {tradingapi-0.3.8 → tradingapi-0.3.10}/tradingapi.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from tradingapi.broker_base import (
|
|
2
|
+
is_broker_side_terminal_message,
|
|
3
|
+
is_broker_side_terminal_order,
|
|
4
|
+
is_missing_exchange_order_id,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_is_missing_exchange_order_id():
|
|
9
|
+
assert is_missing_exchange_order_id("0")
|
|
10
|
+
assert is_missing_exchange_order_id(0)
|
|
11
|
+
assert is_missing_exchange_order_id(None)
|
|
12
|
+
assert is_missing_exchange_order_id("")
|
|
13
|
+
assert not is_missing_exchange_order_id("2700000201461104")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_is_broker_side_terminal_message():
|
|
17
|
+
assert is_broker_side_terminal_message("Trading not allowed in illiquid contract")
|
|
18
|
+
assert is_broker_side_terminal_message("Order rejected by RMS")
|
|
19
|
+
assert is_broker_side_terminal_message(
|
|
20
|
+
"RMS:23226063020407:You have insufficient funds. Please add Rs.71301.37 to trade."
|
|
21
|
+
)
|
|
22
|
+
assert is_broker_side_terminal_message("Cancelled by user")
|
|
23
|
+
assert not is_broker_side_terminal_message("")
|
|
24
|
+
assert not is_broker_side_terminal_message("Success")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_is_broker_side_terminal_order_requires_missing_exchange_id():
|
|
28
|
+
msg = "Trading not allowed in illiquid contract"
|
|
29
|
+
assert is_broker_side_terminal_order("0", msg)
|
|
30
|
+
assert not is_broker_side_terminal_order("12345", msg)
|
|
@@ -95,6 +95,49 @@ class OrderStatus(Enum):
|
|
|
95
95
|
CANCELLED = 7 # Cancelled by Exchange
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
_BROKER_SIDE_TERMINAL_KEYWORDS = (
|
|
99
|
+
"illiquid",
|
|
100
|
+
"not allowed",
|
|
101
|
+
"not permitted",
|
|
102
|
+
"barred",
|
|
103
|
+
"freeze",
|
|
104
|
+
"frozen",
|
|
105
|
+
"restricted",
|
|
106
|
+
"invalid contract",
|
|
107
|
+
"invalid scrip",
|
|
108
|
+
"security not available",
|
|
109
|
+
"order rejected",
|
|
110
|
+
"rejected by",
|
|
111
|
+
"cannot place",
|
|
112
|
+
"unable to place",
|
|
113
|
+
"trading disabled",
|
|
114
|
+
"not tradable",
|
|
115
|
+
"not tradeable",
|
|
116
|
+
"margin shortfall",
|
|
117
|
+
"insufficient margin",
|
|
118
|
+
"insufficient funds",
|
|
119
|
+
"rms reject",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def is_missing_exchange_order_id(exch_order_id) -> bool:
|
|
124
|
+
value = str(exch_order_id or "").strip()
|
|
125
|
+
return value in {"", "0", "None", "NONE", "null", "nan", "NaN"}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def is_broker_side_terminal_message(message: str) -> bool:
|
|
129
|
+
msg = str(message or "").strip().lower()
|
|
130
|
+
if not msg:
|
|
131
|
+
return False
|
|
132
|
+
if "reject" in msg or "cancel" in msg:
|
|
133
|
+
return True
|
|
134
|
+
return any(keyword in msg for keyword in _BROKER_SIDE_TERMINAL_KEYWORDS)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def is_broker_side_terminal_order(exch_order_id, message: str) -> bool:
|
|
138
|
+
return is_missing_exchange_order_id(exch_order_id) and is_broker_side_terminal_message(message)
|
|
139
|
+
|
|
140
|
+
|
|
98
141
|
def _validate_quantity(quantity):
|
|
99
142
|
"""
|
|
100
143
|
Validate quantity parameter that can be int, float, or string representation.
|
|
@@ -528,6 +571,7 @@ class Price:
|
|
|
528
571
|
high: float = float("nan"),
|
|
529
572
|
low: float = float("nan"),
|
|
530
573
|
volume: int = 0,
|
|
574
|
+
oi: int = 0,
|
|
531
575
|
symbol: str = "",
|
|
532
576
|
exchange: str = "",
|
|
533
577
|
src: str = "",
|
|
@@ -546,6 +590,7 @@ class Price:
|
|
|
546
590
|
high: High price
|
|
547
591
|
low: Low price
|
|
548
592
|
volume: Total volume
|
|
593
|
+
oi: Open interest
|
|
549
594
|
symbol: Trading symbol
|
|
550
595
|
exchange: Exchange name
|
|
551
596
|
src: Source of the price data
|
|
@@ -563,6 +608,7 @@ class Price:
|
|
|
563
608
|
self.high = high
|
|
564
609
|
self.low = low
|
|
565
610
|
self.volume = volume
|
|
611
|
+
self.oi = oi
|
|
566
612
|
self.symbol = symbol
|
|
567
613
|
self.exchange = exchange
|
|
568
614
|
self.src = src
|
|
@@ -590,6 +636,7 @@ class Price:
|
|
|
590
636
|
high=safe_add(self.high, other.high),
|
|
591
637
|
low=safe_add(self.low, other.low),
|
|
592
638
|
volume=safe_add_volume(self.volume, other.volume),
|
|
639
|
+
oi=safe_add_volume(self.oi, other.oi),
|
|
593
640
|
)
|
|
594
641
|
# dont change symbol
|
|
595
642
|
|
|
@@ -603,6 +650,7 @@ class Price:
|
|
|
603
650
|
self.high = other.high * size if other.high * size is not float("nan") else self.high
|
|
604
651
|
self.low = other.low * size if other.low * size is not float("nan") else self.low
|
|
605
652
|
self.volume = other.volume if other.volume is not float("nan") else self.volume
|
|
653
|
+
self.oi = other.oi if other.oi is not float("nan") else self.oi
|
|
606
654
|
self.symbol = other.symbol
|
|
607
655
|
self.exchange = other.exchange
|
|
608
656
|
self.src = other.src
|
|
@@ -619,6 +667,7 @@ class Price:
|
|
|
619
667
|
"high": self.high,
|
|
620
668
|
"low": self.low,
|
|
621
669
|
"volume": self.volume,
|
|
670
|
+
"oi": self.oi,
|
|
622
671
|
"symbol": self.symbol,
|
|
623
672
|
"exchange": self.exchange,
|
|
624
673
|
"src": self.src,
|
|
@@ -637,6 +686,7 @@ class Price:
|
|
|
637
686
|
high=data.get("high", float("nan")),
|
|
638
687
|
low=data.get("low", float("nan")),
|
|
639
688
|
volume=data.get("volume", 0),
|
|
689
|
+
oi=data.get("oi", 0),
|
|
640
690
|
symbol=data.get("symbol", ""),
|
|
641
691
|
exchange=data.get("exchange", ""),
|
|
642
692
|
src=data.get("src", ""),
|
|
@@ -756,6 +806,86 @@ class BrokerBase(ABC):
|
|
|
756
806
|
"symbol_map_reversed": {},
|
|
757
807
|
}
|
|
758
808
|
|
|
809
|
+
def _request_cancel_broker_side_terminal_order(self, order: "Order") -> bool:
|
|
810
|
+
"""Send an explicit broker cancel when the order may still be live."""
|
|
811
|
+
broker_order_id = str(getattr(order, "broker_order_id", "") or "").strip()
|
|
812
|
+
if not broker_order_id or broker_order_id == "0" or broker_order_id.upper().endswith("P"):
|
|
813
|
+
return False
|
|
814
|
+
if getattr(order, "paper", False) in (True, "True", "true"):
|
|
815
|
+
return False
|
|
816
|
+
try:
|
|
817
|
+
self.cancel_order(broker_order_id=broker_order_id, resolve_terminal=False)
|
|
818
|
+
return True
|
|
819
|
+
except Exception as e:
|
|
820
|
+
_get_trading_logger().log_warning(
|
|
821
|
+
"Cancel request failed for broker-side terminal order",
|
|
822
|
+
{"broker_order_id": broker_order_id, "error": str(e)},
|
|
823
|
+
)
|
|
824
|
+
return False
|
|
825
|
+
|
|
826
|
+
def _resolve_broker_side_terminal_order(
|
|
827
|
+
self,
|
|
828
|
+
order: "Order",
|
|
829
|
+
message: str,
|
|
830
|
+
*,
|
|
831
|
+
order_size=None,
|
|
832
|
+
order_price=None,
|
|
833
|
+
) -> OrderInfo:
|
|
834
|
+
"""Cancel if possible and return a zero-fill CANCELLED OrderInfo."""
|
|
835
|
+
broker_order_id = str(getattr(order, "broker_order_id", "") or "").strip()
|
|
836
|
+
if message:
|
|
837
|
+
order.message = message
|
|
838
|
+
if broker_order_id:
|
|
839
|
+
try:
|
|
840
|
+
self.redis_o.hset(broker_order_id, "message", message)
|
|
841
|
+
except Exception:
|
|
842
|
+
pass
|
|
843
|
+
|
|
844
|
+
cancel_requested = self._request_cancel_broker_side_terminal_order(order)
|
|
845
|
+
order.status = OrderStatus.CANCELLED
|
|
846
|
+
if broker_order_id:
|
|
847
|
+
try:
|
|
848
|
+
self.redis_o.hset(broker_order_id, "status", OrderStatus.CANCELLED.name)
|
|
849
|
+
except Exception:
|
|
850
|
+
pass
|
|
851
|
+
|
|
852
|
+
_get_trading_logger().log_info(
|
|
853
|
+
"Broker-side terminal order resolved as cancelled",
|
|
854
|
+
{
|
|
855
|
+
"broker_order_id": broker_order_id,
|
|
856
|
+
"long_symbol": getattr(order, "long_symbol", ""),
|
|
857
|
+
"message": message,
|
|
858
|
+
"cancel_requested": cancel_requested,
|
|
859
|
+
},
|
|
860
|
+
)
|
|
861
|
+
return OrderInfo(
|
|
862
|
+
order_size=order_size if order_size is not None else order.quantity,
|
|
863
|
+
order_price=order_price if order_price is not None else order.price,
|
|
864
|
+
fill_size=0,
|
|
865
|
+
fill_price=0,
|
|
866
|
+
status=OrderStatus.CANCELLED,
|
|
867
|
+
broker_order_id=broker_order_id,
|
|
868
|
+
exchange_order_id=getattr(order, "exch_order_id", ""),
|
|
869
|
+
broker=getattr(order, "broker", None) or self.broker,
|
|
870
|
+
)
|
|
871
|
+
|
|
872
|
+
def _apply_broker_side_terminal_resolution(
|
|
873
|
+
self,
|
|
874
|
+
order: "Order",
|
|
875
|
+
order_info: OrderInfo,
|
|
876
|
+
message: Optional[str] = None,
|
|
877
|
+
) -> OrderInfo:
|
|
878
|
+
msg = str(message if message is not None else getattr(order, "message", "") or "").strip()
|
|
879
|
+
exch_id = order_info.exchange_order_id or getattr(order, "exch_order_id", "")
|
|
880
|
+
if not is_broker_side_terminal_order(exch_id, msg):
|
|
881
|
+
return order_info
|
|
882
|
+
return self._resolve_broker_side_terminal_order(
|
|
883
|
+
order,
|
|
884
|
+
msg,
|
|
885
|
+
order_size=order_info.order_size,
|
|
886
|
+
order_price=order_info.order_price,
|
|
887
|
+
)
|
|
888
|
+
|
|
759
889
|
@abstractmethod
|
|
760
890
|
def update_symbology(self, **kwargs):
|
|
761
891
|
"""
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
bhavcopy_folder : /home/psharma/onedrive/rfiles/data/bhavcopy
|
|
3
3
|
|
|
4
4
|
# Optional: proxy for broker HTTP requests (e.g. connect, save_symbol_data).
|
|
5
|
-
#
|
|
5
|
+
# USE_PROXY: trading API calls. USE_PROXY_SYMBOL_DOWNLOAD: symbol master download only.
|
|
6
6
|
proxy:
|
|
7
7
|
source: webshare
|
|
8
8
|
api_key: # Webshare API key (or set WEBSHARE_PROXY_API_KEY env)
|
|
9
9
|
username: # Optional: override per-proxy username from API
|
|
10
10
|
password: # Optional: override per-proxy password from API
|
|
11
11
|
country_code: # Optional: e.g. IN
|
|
12
|
+
max_proxies: 30 # NordVPN servers to try when rotating (symbol download)
|
|
12
13
|
mode: direct
|
|
13
14
|
|
|
14
15
|
commissions:
|
|
@@ -22,6 +23,7 @@ commissions:
|
|
|
22
23
|
FIVEPAISA:
|
|
23
24
|
EXCHANGES: [NSE, BSE, MCX]
|
|
24
25
|
USE_PROXY: false
|
|
26
|
+
USE_PROXY_SYMBOL_DOWNLOAD: false
|
|
25
27
|
APP_NAME:
|
|
26
28
|
APP_SOURCE:
|
|
27
29
|
USER_ID:
|
|
@@ -41,6 +43,7 @@ FIVEPAISA:
|
|
|
41
43
|
SHOONYA:
|
|
42
44
|
EXCHANGES: [NSE, BSE, MCX]
|
|
43
45
|
USE_PROXY: false
|
|
46
|
+
USE_PROXY_SYMBOL_DOWNLOAD: false
|
|
44
47
|
USER:
|
|
45
48
|
PWD:
|
|
46
49
|
VC:
|
|
@@ -56,6 +59,7 @@ SHOONYA:
|
|
|
56
59
|
ICICIDIRECT:
|
|
57
60
|
EXCHANGES: [NSE, BSE, MCX]
|
|
58
61
|
USE_PROXY: false
|
|
62
|
+
USE_PROXY_SYMBOL_DOWNLOAD: false
|
|
59
63
|
API_KEY:
|
|
60
64
|
API_SECRET:
|
|
61
65
|
USER_ID: # Optional: used by icicidirect-generate-session (AUTO_SESSION_TOKEN_CMD)
|
|
@@ -91,6 +95,7 @@ ICICIDIRECT:
|
|
|
91
95
|
DHAN:
|
|
92
96
|
EXCHANGES: [NSE, BSE]
|
|
93
97
|
USE_PROXY: false
|
|
98
|
+
USE_PROXY_SYMBOL_DOWNLOAD: false
|
|
94
99
|
CLIENT_ID:
|
|
95
100
|
ACCESS_TOKEN: # Optional fallback if TOTP/PIN flow is not used
|
|
96
101
|
TOTP_TOKEN: # Optional: used for auto token refresh
|
|
@@ -107,6 +112,7 @@ DHAN:
|
|
|
107
112
|
DHAN_ACCOUNT2:
|
|
108
113
|
EXCHANGES: [NSE, BSE]
|
|
109
114
|
USE_PROXY: false
|
|
115
|
+
USE_PROXY_SYMBOL_DOWNLOAD: false
|
|
110
116
|
CLIENT_ID:
|
|
111
117
|
ACCESS_TOKEN:
|
|
112
118
|
TOTP_TOKEN:
|
|
@@ -287,11 +287,21 @@ def save_symbol_data(saveToFolder: bool = False):
|
|
|
287
287
|
dest_file = f"{bhavcopyfolder}/{dt.datetime.today().strftime('%Y%m%d')}_dhan_codes.csv"
|
|
288
288
|
|
|
289
289
|
headers = {"User-Agent": "Mozilla/5.0", "Accept": "*/*"}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
290
|
+
if os.path.exists(dest_file):
|
|
291
|
+
df = pd.read_csv(dest_file, low_memory=False)
|
|
292
|
+
else:
|
|
293
|
+
from .proxy_utils import request_get_with_broker_proxy
|
|
294
|
+
|
|
295
|
+
response = request_get_with_broker_proxy(
|
|
296
|
+
DHAN_SECURITY_LIST_URL,
|
|
297
|
+
"DHAN",
|
|
298
|
+
purpose="symbol_download",
|
|
299
|
+
headers=headers,
|
|
300
|
+
timeout=(10, 300),
|
|
301
|
+
)
|
|
302
|
+
with open(dest_file, "wb") as f:
|
|
303
|
+
f.write(response.content)
|
|
304
|
+
df = pd.read_csv(dest_file, low_memory=False)
|
|
295
305
|
df.columns = [col.strip() for col in df.columns]
|
|
296
306
|
object_cols = df.select_dtypes(include=["object"]).columns
|
|
297
307
|
for col in object_cols:
|
|
@@ -1476,7 +1486,11 @@ class Dhan(BrokerBase):
|
|
|
1476
1486
|
|
|
1477
1487
|
fills = None
|
|
1478
1488
|
try:
|
|
1479
|
-
fills = self.get_order_info(
|
|
1489
|
+
fills = self.get_order_info(
|
|
1490
|
+
broker_order_id=broker_order_id,
|
|
1491
|
+
order=order,
|
|
1492
|
+
resolve_terminal=kwargs.get("resolve_terminal", False),
|
|
1493
|
+
)
|
|
1480
1494
|
except Exception:
|
|
1481
1495
|
pass
|
|
1482
1496
|
|
|
@@ -1617,15 +1631,7 @@ class Dhan(BrokerBase):
|
|
|
1617
1631
|
if order is not None and error_message:
|
|
1618
1632
|
order.message = error_message
|
|
1619
1633
|
|
|
1620
|
-
|
|
1621
|
-
try:
|
|
1622
|
-
internal_order_id = self.redis_o.hget(broker_order_id, "orderRef")
|
|
1623
|
-
if internal_order_id:
|
|
1624
|
-
delete_broker_order_id(self, internal_order_id, broker_order_id)
|
|
1625
|
-
except Exception:
|
|
1626
|
-
pass
|
|
1627
|
-
|
|
1628
|
-
return OrderInfo(
|
|
1634
|
+
order_info = OrderInfo(
|
|
1629
1635
|
order_size=order_qty,
|
|
1630
1636
|
order_price=order_price,
|
|
1631
1637
|
fill_size=fill_size,
|
|
@@ -1635,6 +1641,20 @@ class Dhan(BrokerBase):
|
|
|
1635
1641
|
exchange_order_id=exch_order_id,
|
|
1636
1642
|
broker=self.broker,
|
|
1637
1643
|
)
|
|
1644
|
+
if kwargs.get("resolve_terminal", True):
|
|
1645
|
+
order_info = self._apply_broker_side_terminal_resolution(
|
|
1646
|
+
order, order_info, message=error_message or order.message
|
|
1647
|
+
)
|
|
1648
|
+
|
|
1649
|
+
if order_info.status == OrderStatus.REJECTED:
|
|
1650
|
+
try:
|
|
1651
|
+
internal_order_id = self.redis_o.hget(broker_order_id, "orderRef")
|
|
1652
|
+
if internal_order_id:
|
|
1653
|
+
delete_broker_order_id(self, internal_order_id, broker_order_id)
|
|
1654
|
+
except Exception:
|
|
1655
|
+
pass
|
|
1656
|
+
|
|
1657
|
+
return order_info
|
|
1638
1658
|
|
|
1639
1659
|
except (ValidationError, OrderError, BrokerConnectionError):
|
|
1640
1660
|
raise
|
|
@@ -1975,6 +1995,11 @@ class Dhan(BrokerBase):
|
|
|
1975
1995
|
market_feed.low = float(ohlc.get("low", data.get("dayLow", float("nan"))) or float("nan"))
|
|
1976
1996
|
market_feed.prior_close = float(ohlc.get("close", data.get("previousClosePrice", float("nan"))) or float("nan"))
|
|
1977
1997
|
market_feed.volume = int(data.get("volume", data.get("totalTradedVolume", 0)) or 0)
|
|
1998
|
+
raw_oi = data.get("OI", data.get("oi", data.get("open_interest", data.get("openInterest", 0))))
|
|
1999
|
+
try:
|
|
2000
|
+
market_feed.oi = int(str(raw_oi).strip() or 0)
|
|
2001
|
+
except (TypeError, ValueError):
|
|
2002
|
+
market_feed.oi = int(float(raw_oi or 0))
|
|
1978
2003
|
depth = data.get("depth", {})
|
|
1979
2004
|
buy_qty = depth.get("buy", [{}])
|
|
1980
2005
|
sell_qty = depth.get("sell", [{}])
|
|
@@ -2330,9 +2355,12 @@ class Dhan(BrokerBase):
|
|
|
2330
2355
|
if value in (None, "", "nan"):
|
|
2331
2356
|
return 0
|
|
2332
2357
|
try:
|
|
2333
|
-
return int(
|
|
2358
|
+
return int(str(value).strip())
|
|
2334
2359
|
except (TypeError, ValueError):
|
|
2335
|
-
|
|
2360
|
+
try:
|
|
2361
|
+
return int(float(value))
|
|
2362
|
+
except (TypeError, ValueError):
|
|
2363
|
+
return 0
|
|
2336
2364
|
|
|
2337
2365
|
def _apply_if_valid(current: float, value: Any) -> float:
|
|
2338
2366
|
parsed = _to_float(value)
|
|
@@ -2381,6 +2409,9 @@ class Dhan(BrokerBase):
|
|
|
2381
2409
|
volume = _to_int(response.get("volume"))
|
|
2382
2410
|
if volume:
|
|
2383
2411
|
price.volume = volume
|
|
2412
|
+
oi = _to_int(response.get("OI") or response.get("oi") or response.get("open_interest") or response.get("openInterest"))
|
|
2413
|
+
if oi:
|
|
2414
|
+
price.oi = oi
|
|
2384
2415
|
price.bid = _apply_if_valid(price.bid, response.get("bidPrice"))
|
|
2385
2416
|
price.ask = _apply_if_valid(price.ask, response.get("askPrice"))
|
|
2386
2417
|
bid_qty = _to_int(response.get("bidQty"))
|