tradingapi 0.3.7__tar.gz → 0.3.9__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.7 → tradingapi-0.3.9}/PKG-INFO +1 -1
- {tradingapi-0.3.7 → tradingapi-0.3.9}/pyproject.toml +1 -1
- tradingapi-0.3.9/tests/test_broker_side_terminal_order.py +30 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/broker_base.py +123 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/dhan.py +20 -10
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/fivepaisa.py +120 -68
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/flattrade.py +5 -1
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/icicidirect.py +24 -1
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/shoonya.py +5 -1
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/utils.py +128 -14
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi.egg-info/PKG-INFO +1 -1
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi.egg-info/SOURCES.txt +1 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/README.md +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/setup.cfg +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tests/test_calculate_delta_realtime_quotes.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tests/test_find_option_with_delta.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/__init__.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/allocation.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/attribution.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/config/commissions_20241216.yaml +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/config/config_sample.yaml +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/config.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/error_handling.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/exceptions.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/globals.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/icicidirect_generate_session.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/market_data_exchanges.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/proxy_utils.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi/span.py +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi.egg-info/dependency_links.txt +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi.egg-info/entry_points.txt +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/tradingapi.egg-info/requires.txt +0 -0
- {tradingapi-0.3.7 → tradingapi-0.3.9}/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.
|
|
@@ -756,6 +799,86 @@ class BrokerBase(ABC):
|
|
|
756
799
|
"symbol_map_reversed": {},
|
|
757
800
|
}
|
|
758
801
|
|
|
802
|
+
def _request_cancel_broker_side_terminal_order(self, order: "Order") -> bool:
|
|
803
|
+
"""Send an explicit broker cancel when the order may still be live."""
|
|
804
|
+
broker_order_id = str(getattr(order, "broker_order_id", "") or "").strip()
|
|
805
|
+
if not broker_order_id or broker_order_id == "0" or broker_order_id.upper().endswith("P"):
|
|
806
|
+
return False
|
|
807
|
+
if getattr(order, "paper", False) in (True, "True", "true"):
|
|
808
|
+
return False
|
|
809
|
+
try:
|
|
810
|
+
self.cancel_order(broker_order_id=broker_order_id, resolve_terminal=False)
|
|
811
|
+
return True
|
|
812
|
+
except Exception as e:
|
|
813
|
+
_get_trading_logger().log_warning(
|
|
814
|
+
"Cancel request failed for broker-side terminal order",
|
|
815
|
+
{"broker_order_id": broker_order_id, "error": str(e)},
|
|
816
|
+
)
|
|
817
|
+
return False
|
|
818
|
+
|
|
819
|
+
def _resolve_broker_side_terminal_order(
|
|
820
|
+
self,
|
|
821
|
+
order: "Order",
|
|
822
|
+
message: str,
|
|
823
|
+
*,
|
|
824
|
+
order_size=None,
|
|
825
|
+
order_price=None,
|
|
826
|
+
) -> OrderInfo:
|
|
827
|
+
"""Cancel if possible and return a zero-fill CANCELLED OrderInfo."""
|
|
828
|
+
broker_order_id = str(getattr(order, "broker_order_id", "") or "").strip()
|
|
829
|
+
if message:
|
|
830
|
+
order.message = message
|
|
831
|
+
if broker_order_id:
|
|
832
|
+
try:
|
|
833
|
+
self.redis_o.hset(broker_order_id, "message", message)
|
|
834
|
+
except Exception:
|
|
835
|
+
pass
|
|
836
|
+
|
|
837
|
+
cancel_requested = self._request_cancel_broker_side_terminal_order(order)
|
|
838
|
+
order.status = OrderStatus.CANCELLED
|
|
839
|
+
if broker_order_id:
|
|
840
|
+
try:
|
|
841
|
+
self.redis_o.hset(broker_order_id, "status", OrderStatus.CANCELLED.name)
|
|
842
|
+
except Exception:
|
|
843
|
+
pass
|
|
844
|
+
|
|
845
|
+
_get_trading_logger().log_info(
|
|
846
|
+
"Broker-side terminal order resolved as cancelled",
|
|
847
|
+
{
|
|
848
|
+
"broker_order_id": broker_order_id,
|
|
849
|
+
"long_symbol": getattr(order, "long_symbol", ""),
|
|
850
|
+
"message": message,
|
|
851
|
+
"cancel_requested": cancel_requested,
|
|
852
|
+
},
|
|
853
|
+
)
|
|
854
|
+
return OrderInfo(
|
|
855
|
+
order_size=order_size if order_size is not None else order.quantity,
|
|
856
|
+
order_price=order_price if order_price is not None else order.price,
|
|
857
|
+
fill_size=0,
|
|
858
|
+
fill_price=0,
|
|
859
|
+
status=OrderStatus.CANCELLED,
|
|
860
|
+
broker_order_id=broker_order_id,
|
|
861
|
+
exchange_order_id=getattr(order, "exch_order_id", ""),
|
|
862
|
+
broker=getattr(order, "broker", None) or self.broker,
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
def _apply_broker_side_terminal_resolution(
|
|
866
|
+
self,
|
|
867
|
+
order: "Order",
|
|
868
|
+
order_info: OrderInfo,
|
|
869
|
+
message: Optional[str] = None,
|
|
870
|
+
) -> OrderInfo:
|
|
871
|
+
msg = str(message if message is not None else getattr(order, "message", "") or "").strip()
|
|
872
|
+
exch_id = order_info.exchange_order_id or getattr(order, "exch_order_id", "")
|
|
873
|
+
if not is_broker_side_terminal_order(exch_id, msg):
|
|
874
|
+
return order_info
|
|
875
|
+
return self._resolve_broker_side_terminal_order(
|
|
876
|
+
order,
|
|
877
|
+
msg,
|
|
878
|
+
order_size=order_info.order_size,
|
|
879
|
+
order_price=order_info.order_price,
|
|
880
|
+
)
|
|
881
|
+
|
|
759
882
|
@abstractmethod
|
|
760
883
|
def update_symbology(self, **kwargs):
|
|
761
884
|
"""
|
|
@@ -1476,7 +1476,11 @@ class Dhan(BrokerBase):
|
|
|
1476
1476
|
|
|
1477
1477
|
fills = None
|
|
1478
1478
|
try:
|
|
1479
|
-
fills = self.get_order_info(
|
|
1479
|
+
fills = self.get_order_info(
|
|
1480
|
+
broker_order_id=broker_order_id,
|
|
1481
|
+
order=order,
|
|
1482
|
+
resolve_terminal=kwargs.get("resolve_terminal", False),
|
|
1483
|
+
)
|
|
1480
1484
|
except Exception:
|
|
1481
1485
|
pass
|
|
1482
1486
|
|
|
@@ -1617,15 +1621,7 @@ class Dhan(BrokerBase):
|
|
|
1617
1621
|
if order is not None and error_message:
|
|
1618
1622
|
order.message = error_message
|
|
1619
1623
|
|
|
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(
|
|
1624
|
+
order_info = OrderInfo(
|
|
1629
1625
|
order_size=order_qty,
|
|
1630
1626
|
order_price=order_price,
|
|
1631
1627
|
fill_size=fill_size,
|
|
@@ -1635,6 +1631,20 @@ class Dhan(BrokerBase):
|
|
|
1635
1631
|
exchange_order_id=exch_order_id,
|
|
1636
1632
|
broker=self.broker,
|
|
1637
1633
|
)
|
|
1634
|
+
if kwargs.get("resolve_terminal", True):
|
|
1635
|
+
order_info = self._apply_broker_side_terminal_resolution(
|
|
1636
|
+
order, order_info, message=error_message or order.message
|
|
1637
|
+
)
|
|
1638
|
+
|
|
1639
|
+
if order_info.status == OrderStatus.REJECTED:
|
|
1640
|
+
try:
|
|
1641
|
+
internal_order_id = self.redis_o.hget(broker_order_id, "orderRef")
|
|
1642
|
+
if internal_order_id:
|
|
1643
|
+
delete_broker_order_id(self, internal_order_id, broker_order_id)
|
|
1644
|
+
except Exception:
|
|
1645
|
+
pass
|
|
1646
|
+
|
|
1647
|
+
return order_info
|
|
1638
1648
|
|
|
1639
1649
|
except (ValidationError, OrderError, BrokerConnectionError):
|
|
1640
1650
|
raise
|
|
@@ -85,7 +85,19 @@ def _patch_py5paisa_for_mom(api) -> None:
|
|
|
85
85
|
api.multi_order_Margin = fixed_multi_order_Margin
|
|
86
86
|
api._tradingapi_patched = True
|
|
87
87
|
|
|
88
|
-
from .broker_base import
|
|
88
|
+
from .broker_base import (
|
|
89
|
+
BrokerBase,
|
|
90
|
+
Brokers,
|
|
91
|
+
HistoricalData,
|
|
92
|
+
Order,
|
|
93
|
+
OrderInfo,
|
|
94
|
+
OrderStatus,
|
|
95
|
+
Price,
|
|
96
|
+
_normalize_as_of_date,
|
|
97
|
+
is_broker_side_terminal_message,
|
|
98
|
+
is_broker_side_terminal_order,
|
|
99
|
+
is_missing_exchange_order_id,
|
|
100
|
+
)
|
|
89
101
|
from .config import get_config
|
|
90
102
|
from .utils import (
|
|
91
103
|
delete_broker_order_id,
|
|
@@ -1914,6 +1926,17 @@ class FivePaisa(BrokerBase):
|
|
|
1914
1926
|
context = create_error_context(kwargs=kwargs, error=str(e))
|
|
1915
1927
|
raise OrderError(f"Unexpected error modifying order: {str(e)}", context)
|
|
1916
1928
|
|
|
1929
|
+
@log_execution_time
|
|
1930
|
+
@validate_inputs(broker_order_id=lambda x: isinstance(x, str) and len(x.strip()) > 0)
|
|
1931
|
+
def _request_cancel_broker_side_terminal_order(self, order: Order) -> bool:
|
|
1932
|
+
if is_missing_exchange_order_id(getattr(order, "exch_order_id", None)):
|
|
1933
|
+
trading_logger.log_info(
|
|
1934
|
+
"FivePaisa order blocked before exchange; marking cancelled locally",
|
|
1935
|
+
{"broker_order_id": order.broker_order_id, "message": order.message},
|
|
1936
|
+
)
|
|
1937
|
+
return False
|
|
1938
|
+
return super()._request_cancel_broker_side_terminal_order(order)
|
|
1939
|
+
|
|
1917
1940
|
@log_execution_time
|
|
1918
1941
|
@validate_inputs(broker_order_id=lambda x: isinstance(x, str) and len(x.strip()) > 0)
|
|
1919
1942
|
def cancel_order(self, **kwargs) -> Order:
|
|
@@ -1968,7 +1991,11 @@ class FivePaisa(BrokerBase):
|
|
|
1968
1991
|
if date_matches:
|
|
1969
1992
|
# Get current order info
|
|
1970
1993
|
try:
|
|
1971
|
-
fills = self.get_order_info(
|
|
1994
|
+
fills = self.get_order_info(
|
|
1995
|
+
broker_order_id=broker_order_id,
|
|
1996
|
+
order=order,
|
|
1997
|
+
resolve_terminal=kwargs.get("resolve_terminal", False),
|
|
1998
|
+
)
|
|
1972
1999
|
except Exception as e:
|
|
1973
2000
|
trading_logger.log_error(
|
|
1974
2001
|
"Failed to get order info for cancellation",
|
|
@@ -2247,6 +2274,7 @@ class FivePaisa(BrokerBase):
|
|
|
2247
2274
|
"""
|
|
2248
2275
|
try:
|
|
2249
2276
|
trading_logger.log_debug("Getting order info", {"broker_order_id": kwargs.get("broker_order_id")})
|
|
2277
|
+
resolve_terminal = kwargs.get("resolve_terminal", True)
|
|
2250
2278
|
|
|
2251
2279
|
def return_db_as_fills(order: Order):
|
|
2252
2280
|
"""Return order info from database for historical orders."""
|
|
@@ -2272,6 +2300,11 @@ class FivePaisa(BrokerBase):
|
|
|
2272
2300
|
trading_logger.log_error("Error in return_db_as_fills", e, {"order": str(order)})
|
|
2273
2301
|
raise
|
|
2274
2302
|
|
|
2303
|
+
def _finalize_order_book_info(order: Order, order_info: OrderInfo, reason: str = "") -> OrderInfo:
|
|
2304
|
+
if not resolve_terminal:
|
|
2305
|
+
return order_info
|
|
2306
|
+
return self._apply_broker_side_terminal_resolution(order, order_info, message=reason or order.message)
|
|
2307
|
+
|
|
2275
2308
|
def get_orderinfo_from_orders(exch_order_id: str, order: Order, broker_order_id: str) -> OrderInfo:
|
|
2276
2309
|
"""Get order info from order book."""
|
|
2277
2310
|
try:
|
|
@@ -2281,21 +2314,18 @@ class FivePaisa(BrokerBase):
|
|
|
2281
2314
|
if len(orders) > 0:
|
|
2282
2315
|
fivepaisa_order = orders[orders.BrokerOrderId.astype(str) == str(broker_order_id)]
|
|
2283
2316
|
if len(fivepaisa_order) == 1:
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
trading_logger.log_warning(
|
|
2297
|
-
"Failed to delete broker order ID",
|
|
2298
|
-
{"broker_order_id": broker_order_id, "error": str(e)},
|
|
2317
|
+
reason = str(fivepaisa_order.Reason.iloc[0] if "Reason" in fivepaisa_order.columns else "")
|
|
2318
|
+
exch_id = fivepaisa_order.ExchOrderID.iloc[0]
|
|
2319
|
+
if (
|
|
2320
|
+
fivepaisa_order.OrderStatus.str.lower().str.contains("rejected").iloc[0] is True
|
|
2321
|
+
or is_broker_side_terminal_order(exch_id, reason or order.message)
|
|
2322
|
+
):
|
|
2323
|
+
if resolve_terminal:
|
|
2324
|
+
return self._resolve_broker_side_terminal_order(
|
|
2325
|
+
order,
|
|
2326
|
+
reason or order.message,
|
|
2327
|
+
order_size=order.quantity,
|
|
2328
|
+
order_price=order.price,
|
|
2299
2329
|
)
|
|
2300
2330
|
return OrderInfo(
|
|
2301
2331
|
order_size=order.quantity,
|
|
@@ -2304,7 +2334,7 @@ class FivePaisa(BrokerBase):
|
|
|
2304
2334
|
fill_price=0,
|
|
2305
2335
|
status=OrderStatus.REJECTED,
|
|
2306
2336
|
broker_order_id=order.broker_order_id,
|
|
2307
|
-
exchange_order_id=
|
|
2337
|
+
exchange_order_id=exch_id,
|
|
2308
2338
|
broker=self.broker,
|
|
2309
2339
|
)
|
|
2310
2340
|
else:
|
|
@@ -2332,15 +2362,19 @@ class FivePaisa(BrokerBase):
|
|
|
2332
2362
|
contract_size = self.exchange_mappings[order.exchange]["contractsize_map"].get(
|
|
2333
2363
|
long_symbol
|
|
2334
2364
|
)
|
|
2335
|
-
return
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2365
|
+
return _finalize_order_book_info(
|
|
2366
|
+
order,
|
|
2367
|
+
OrderInfo(
|
|
2368
|
+
order_size=order.quantity,
|
|
2369
|
+
order_price=order.price,
|
|
2370
|
+
fill_size=fill_size * contract_size,
|
|
2371
|
+
fill_price=fill_price,
|
|
2372
|
+
status=status,
|
|
2373
|
+
broker_order_id=order.broker_order_id,
|
|
2374
|
+
exchange_order_id=fivepaisa_order.ExchOrderID.iloc[0],
|
|
2375
|
+
broker=self.broker,
|
|
2376
|
+
),
|
|
2377
|
+
reason,
|
|
2344
2378
|
)
|
|
2345
2379
|
except Exception as e:
|
|
2346
2380
|
trading_logger.log_error(
|
|
@@ -2350,15 +2384,19 @@ class FivePaisa(BrokerBase):
|
|
|
2350
2384
|
)
|
|
2351
2385
|
raise
|
|
2352
2386
|
else:
|
|
2353
|
-
return
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2387
|
+
return _finalize_order_book_info(
|
|
2388
|
+
order,
|
|
2389
|
+
OrderInfo(
|
|
2390
|
+
order_size=order.quantity,
|
|
2391
|
+
order_price=order.price,
|
|
2392
|
+
fill_size=fill_size,
|
|
2393
|
+
fill_price=fill_price,
|
|
2394
|
+
status=status,
|
|
2395
|
+
broker_order_id=order.broker_order_id,
|
|
2396
|
+
exchange_order_id=fivepaisa_order.ExchOrderID.iloc[0],
|
|
2397
|
+
broker=self.broker,
|
|
2398
|
+
),
|
|
2399
|
+
reason,
|
|
2362
2400
|
)
|
|
2363
2401
|
else:
|
|
2364
2402
|
trading_logger.log_debug(
|
|
@@ -2400,15 +2438,19 @@ class FivePaisa(BrokerBase):
|
|
|
2400
2438
|
contract_size = self.exchange_mappings[order.exchange][
|
|
2401
2439
|
"contractsize_map"
|
|
2402
2440
|
].get(long_symbol)
|
|
2403
|
-
return
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2441
|
+
return _finalize_order_book_info(
|
|
2442
|
+
order,
|
|
2443
|
+
OrderInfo(
|
|
2444
|
+
order_size=order_size,
|
|
2445
|
+
order_price=order_price,
|
|
2446
|
+
fill_size=fill_size * contract_size,
|
|
2447
|
+
fill_price=fill_price,
|
|
2448
|
+
status=status,
|
|
2449
|
+
broker_order_id=broker_order_id,
|
|
2450
|
+
exchange_order_id=row["ExchOrderID"],
|
|
2451
|
+
broker=self.broker,
|
|
2452
|
+
),
|
|
2453
|
+
str(row.get("Reason", "")),
|
|
2412
2454
|
)
|
|
2413
2455
|
except Exception as e:
|
|
2414
2456
|
trading_logger.log_error(
|
|
@@ -2418,15 +2460,19 @@ class FivePaisa(BrokerBase):
|
|
|
2418
2460
|
)
|
|
2419
2461
|
raise
|
|
2420
2462
|
else:
|
|
2421
|
-
return
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2463
|
+
return _finalize_order_book_info(
|
|
2464
|
+
order,
|
|
2465
|
+
OrderInfo(
|
|
2466
|
+
order_size=order_size,
|
|
2467
|
+
order_price=order_price,
|
|
2468
|
+
fill_size=fill_size,
|
|
2469
|
+
fill_price=fill_price,
|
|
2470
|
+
status=status,
|
|
2471
|
+
broker_order_id=broker_order_id,
|
|
2472
|
+
exchange_order_id=row["ExchOrderID"],
|
|
2473
|
+
broker=self.broker,
|
|
2474
|
+
),
|
|
2475
|
+
str(row.get("Reason", "")),
|
|
2430
2476
|
)
|
|
2431
2477
|
return OrderInfo(
|
|
2432
2478
|
order_size=order.quantity,
|
|
@@ -2534,20 +2580,14 @@ class FivePaisa(BrokerBase):
|
|
|
2534
2580
|
)
|
|
2535
2581
|
|
|
2536
2582
|
# Handle orders with no exchange order ID
|
|
2537
|
-
if order.exch_order_id
|
|
2538
|
-
if
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
fills.fill_size = 0
|
|
2546
|
-
fills.broker = self.broker
|
|
2547
|
-
fills.broker_order_id = order.broker_order_id
|
|
2548
|
-
fills.order_price = order.price
|
|
2549
|
-
fills.order_size = order.quantity
|
|
2550
|
-
return fills
|
|
2583
|
+
if is_missing_exchange_order_id(order.exch_order_id):
|
|
2584
|
+
if resolve_terminal and is_broker_side_terminal_message(order.message):
|
|
2585
|
+
return self._resolve_broker_side_terminal_order(
|
|
2586
|
+
order,
|
|
2587
|
+
order.message,
|
|
2588
|
+
order_size=order.quantity,
|
|
2589
|
+
order_price=order.price,
|
|
2590
|
+
)
|
|
2551
2591
|
return get_orderinfo_from_orders("0", order=order, broker_order_id=broker_order_id)
|
|
2552
2592
|
else:
|
|
2553
2593
|
# Handle orders with exchange order ID
|
|
@@ -3625,6 +3665,14 @@ class FivePaisa(BrokerBase):
|
|
|
3625
3665
|
|
|
3626
3666
|
return None, tick_exchange
|
|
3627
3667
|
|
|
3668
|
+
def is_market_feed_payload(data):
|
|
3669
|
+
"""True for MarketFeedV3 tick objects; false for trade/order pushes (ReqType) and subscribe acks."""
|
|
3670
|
+
if not isinstance(data, dict):
|
|
3671
|
+
return False
|
|
3672
|
+
if data.get("ReqType"):
|
|
3673
|
+
return False
|
|
3674
|
+
return any(k in data for k in ("Token", "LastRate", "BidRate", "TickDt"))
|
|
3675
|
+
|
|
3628
3676
|
def map_to_price(json_data):
|
|
3629
3677
|
"""Map JSON data to Price object."""
|
|
3630
3678
|
try:
|
|
@@ -3666,12 +3714,16 @@ class FivePaisa(BrokerBase):
|
|
|
3666
3714
|
json_data = json.loads(data_str)
|
|
3667
3715
|
if isinstance(json_data, list):
|
|
3668
3716
|
for tick_data in json_data:
|
|
3717
|
+
if not is_market_feed_payload(tick_data):
|
|
3718
|
+
continue
|
|
3669
3719
|
price = map_to_price(tick_data)
|
|
3670
3720
|
if price is not None:
|
|
3671
3721
|
self._last_stream_tick_ts = time.time()
|
|
3672
3722
|
if price is not None and ext_callback:
|
|
3673
3723
|
ext_callback(price)
|
|
3674
3724
|
elif isinstance(json_data, dict):
|
|
3725
|
+
if not is_market_feed_payload(json_data):
|
|
3726
|
+
return
|
|
3675
3727
|
price = map_to_price(json_data)
|
|
3676
3728
|
if price is not None:
|
|
3677
3729
|
self._last_stream_tick_ts = time.time()
|
|
@@ -1608,7 +1608,7 @@ class FlatTrade(BrokerBase):
|
|
|
1608
1608
|
except (ValueError, TypeError):
|
|
1609
1609
|
date_matches = False
|
|
1610
1610
|
if date_matches:
|
|
1611
|
-
fills = self.get_order_info(broker_order_id=broker_order_id)
|
|
1611
|
+
fills = self.get_order_info(broker_order_id=broker_order_id, resolve_terminal=kwargs.get("resolve_terminal", False))
|
|
1612
1612
|
if fills.fill_size < round(float(order.quantity)):
|
|
1613
1613
|
trading_logger.log_info(
|
|
1614
1614
|
"Cancelling broker order",
|
|
@@ -1644,6 +1644,7 @@ class FlatTrade(BrokerBase):
|
|
|
1644
1644
|
raise BrokerConnectionError("FlatTrade API is not initialized")
|
|
1645
1645
|
|
|
1646
1646
|
trading_logger.log_debug("Getting order info", {"broker_order_id": kwargs.get("broker_order_id")})
|
|
1647
|
+
resolve_terminal = kwargs.get("resolve_terminal", True)
|
|
1647
1648
|
|
|
1648
1649
|
def return_db_as_fills(order: Order) -> OrderInfo:
|
|
1649
1650
|
order_info = OrderInfo()
|
|
@@ -1728,6 +1729,9 @@ class FlatTrade(BrokerBase):
|
|
|
1728
1729
|
order_info.status = status_mapping[latest_status.get("status")]
|
|
1729
1730
|
else:
|
|
1730
1731
|
order_info.status = OrderStatus.UNDEFINED
|
|
1732
|
+
if resolve_terminal:
|
|
1733
|
+
terminal_message = latest_status.get("rejreason") or latest_status.get("emsg") or order.message
|
|
1734
|
+
order_info = self._apply_broker_side_terminal_resolution(order, order_info, message=terminal_message)
|
|
1731
1735
|
return order_info
|
|
1732
1736
|
|
|
1733
1737
|
# ------------------------------------------------------------------
|
|
@@ -2456,8 +2456,26 @@ class IciciDirect(BrokerBase):
|
|
|
2456
2456
|
)
|
|
2457
2457
|
status_raw = str(rec.get("status", rec.get("order_status", "")))
|
|
2458
2458
|
status = self._normalize_order_status(status_raw, fill_size, order_size, exchange_order_id)
|
|
2459
|
+
terminal_message = str(
|
|
2460
|
+
rec.get("reason", rec.get("remarks", rec.get("message", rec.get("reject_reason", ""))))
|
|
2461
|
+
or ""
|
|
2462
|
+
)
|
|
2459
2463
|
|
|
2460
|
-
|
|
2464
|
+
order = kwargs.get("order")
|
|
2465
|
+
if order is None:
|
|
2466
|
+
order_data = self.redis_o.hgetall(order_id) if hasattr(self, "redis_o") else {}
|
|
2467
|
+
if order_data:
|
|
2468
|
+
order = Order(**order_data)
|
|
2469
|
+
else:
|
|
2470
|
+
order = Order(
|
|
2471
|
+
broker_order_id=order_id,
|
|
2472
|
+
broker=self.broker.name,
|
|
2473
|
+
exch_order_id=exchange_order_id,
|
|
2474
|
+
)
|
|
2475
|
+
if terminal_message:
|
|
2476
|
+
order.message = terminal_message
|
|
2477
|
+
|
|
2478
|
+
order_info = OrderInfo(
|
|
2461
2479
|
order_size=order_size,
|
|
2462
2480
|
order_price=order_price,
|
|
2463
2481
|
fill_size=fill_size,
|
|
@@ -2467,6 +2485,11 @@ class IciciDirect(BrokerBase):
|
|
|
2467
2485
|
exchange_order_id=exchange_order_id,
|
|
2468
2486
|
broker=self.broker,
|
|
2469
2487
|
)
|
|
2488
|
+
if kwargs.get("resolve_terminal", True):
|
|
2489
|
+
order_info = self._apply_broker_side_terminal_resolution(
|
|
2490
|
+
order, order_info, message=terminal_message or order.message
|
|
2491
|
+
)
|
|
2492
|
+
return order_info
|
|
2470
2493
|
except (ValidationError, BrokerConnectionError, OrderError):
|
|
2471
2494
|
raise
|
|
2472
2495
|
except Exception as e:
|
|
@@ -1724,7 +1724,7 @@ class Shoonya(BrokerBase):
|
|
|
1724
1724
|
except (ValueError, TypeError):
|
|
1725
1725
|
date_matches = False
|
|
1726
1726
|
if date_matches:
|
|
1727
|
-
fills = self.get_order_info(broker_order_id=broker_order_id)
|
|
1727
|
+
fills = self.get_order_info(broker_order_id=broker_order_id, resolve_terminal=kwargs.get("resolve_terminal", False))
|
|
1728
1728
|
if fills.fill_size < round(float(order.quantity)):
|
|
1729
1729
|
trading_logger.log_info(
|
|
1730
1730
|
"Cancelling broker order",
|
|
@@ -1760,6 +1760,7 @@ class Shoonya(BrokerBase):
|
|
|
1760
1760
|
raise BrokerConnectionError("Shoonya API is not initialized")
|
|
1761
1761
|
|
|
1762
1762
|
trading_logger.log_debug("Getting order info", {"broker_order_id": kwargs.get("broker_order_id")})
|
|
1763
|
+
resolve_terminal = kwargs.get("resolve_terminal", True)
|
|
1763
1764
|
|
|
1764
1765
|
def return_db_as_fills(order: Order) -> OrderInfo:
|
|
1765
1766
|
order_info = OrderInfo()
|
|
@@ -1844,6 +1845,9 @@ class Shoonya(BrokerBase):
|
|
|
1844
1845
|
order_info.status = status_mapping[latest_status.get("status")]
|
|
1845
1846
|
else:
|
|
1846
1847
|
order_info.status = OrderStatus.UNDEFINED
|
|
1848
|
+
if resolve_terminal:
|
|
1849
|
+
terminal_message = latest_status.get("rejreason") or latest_status.get("emsg") or order.message
|
|
1850
|
+
order_info = self._apply_broker_side_terminal_resolution(order, order_info, message=terminal_message)
|
|
1847
1851
|
return order_info
|
|
1848
1852
|
|
|
1849
1853
|
# ------------------------------------------------------------------
|
|
@@ -27,7 +27,17 @@ from selenium.webdriver.common.keys import Keys
|
|
|
27
27
|
from selenium.webdriver.support import expected_conditions as EC
|
|
28
28
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
29
29
|
|
|
30
|
-
from .broker_base import
|
|
30
|
+
from .broker_base import (
|
|
31
|
+
BrokerBase,
|
|
32
|
+
HistoricalData,
|
|
33
|
+
Order,
|
|
34
|
+
OrderInfo,
|
|
35
|
+
OrderStatus,
|
|
36
|
+
Position,
|
|
37
|
+
Price,
|
|
38
|
+
is_broker_side_terminal_order,
|
|
39
|
+
is_missing_exchange_order_id,
|
|
40
|
+
)
|
|
31
41
|
from .config import get_config, get_fno_freeze_limit
|
|
32
42
|
from .exceptions import (
|
|
33
43
|
SymbolError,
|
|
@@ -692,10 +702,8 @@ def get_pnl_table(
|
|
|
692
702
|
# Refresh status if requested
|
|
693
703
|
if refresh_status:
|
|
694
704
|
try:
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
broker_order_id = order.broker_order_id
|
|
698
|
-
update_order_status(broker, int_order_id, broker_order_id, eod=eod)
|
|
705
|
+
_prune_terminal_entry_legs(broker, int_order_id, eod=eod)
|
|
706
|
+
symbol = cast(str, broker.redis_o.hget(int_order_id, "long_symbol") or symbol)
|
|
699
707
|
except Exception as e:
|
|
700
708
|
trading_logger.log_error(
|
|
701
709
|
f"Error refreshing entry status for {int_order_id}",
|
|
@@ -721,9 +729,9 @@ def get_pnl_table(
|
|
|
721
729
|
for leg_symbol, expected_qty in expected_legs.items()
|
|
722
730
|
)
|
|
723
731
|
if not fully_filled:
|
|
724
|
-
|
|
725
|
-
if
|
|
726
|
-
effective_symbol =
|
|
732
|
+
resolved_symbol = _resolve_effective_combo_symbol(symbol, entry_position)
|
|
733
|
+
if resolved_symbol:
|
|
734
|
+
effective_symbol = resolved_symbol
|
|
727
735
|
if "?" not in effective_symbol and entry_order_type:
|
|
728
736
|
side = entry_order_type
|
|
729
737
|
base_position = parse_combo_symbol(effective_symbol)
|
|
@@ -1300,6 +1308,38 @@ def _build_effective_symbol_from_positions(positions: Dict[str, Position]) -> st
|
|
|
1300
1308
|
return ":".join(f"{sym}?{int(size / norm)}" for sym, size in non_zero)
|
|
1301
1309
|
|
|
1302
1310
|
|
|
1311
|
+
def _resolve_effective_combo_symbol(
|
|
1312
|
+
original_combo: str,
|
|
1313
|
+
entry_position: Dict[str, Position],
|
|
1314
|
+
) -> str:
|
|
1315
|
+
"""Prefer subset of original combo with preserved leg ratios; GCD infer only as fallback."""
|
|
1316
|
+
active_legs = {sym for sym, pos in entry_position.items() if pos.size != 0}
|
|
1317
|
+
if not active_legs:
|
|
1318
|
+
return original_combo
|
|
1319
|
+
if "?" not in original_combo:
|
|
1320
|
+
if original_combo in active_legs and len(active_legs) == 1:
|
|
1321
|
+
return original_combo
|
|
1322
|
+
if len(active_legs) == 1:
|
|
1323
|
+
return next(iter(active_legs))
|
|
1324
|
+
return _build_effective_symbol_from_positions(entry_position)
|
|
1325
|
+
|
|
1326
|
+
try:
|
|
1327
|
+
original_legs = parse_combo_symbol(original_combo)
|
|
1328
|
+
except Exception:
|
|
1329
|
+
return _build_effective_symbol_from_positions(entry_position)
|
|
1330
|
+
|
|
1331
|
+
remaining_parts = [
|
|
1332
|
+
f"{leg_sym}?{ratio}" for leg_sym, ratio in original_legs.items() if leg_sym in active_legs
|
|
1333
|
+
]
|
|
1334
|
+
if not remaining_parts:
|
|
1335
|
+
return _build_effective_symbol_from_positions(entry_position)
|
|
1336
|
+
if len(remaining_parts) == len(original_legs):
|
|
1337
|
+
return original_combo
|
|
1338
|
+
if len(remaining_parts) == 1:
|
|
1339
|
+
return remaining_parts[0]
|
|
1340
|
+
return ":".join(remaining_parts)
|
|
1341
|
+
|
|
1342
|
+
|
|
1303
1343
|
@log_execution_time
|
|
1304
1344
|
@validate_inputs(combo_symbol=lambda x: isinstance(x, str) and len(x.strip()) > 0)
|
|
1305
1345
|
def parse_combo_symbol(combo_symbol):
|
|
@@ -1639,6 +1679,8 @@ def transmit_entry_order(
|
|
|
1639
1679
|
temp_order.price = lmt_price
|
|
1640
1680
|
out = broker.place_order(temp_order)
|
|
1641
1681
|
int_order_id = _process_broker_order_update(broker, out, order.long_symbol)
|
|
1682
|
+
if int_order_id:
|
|
1683
|
+
_prune_terminal_entry_legs(broker, int_order_id)
|
|
1642
1684
|
return int_order_id
|
|
1643
1685
|
|
|
1644
1686
|
|
|
@@ -1878,6 +1920,66 @@ def exit_is_expiration(broker: BrokerBase, internal_order_id: str) -> bool:
|
|
|
1878
1920
|
return out
|
|
1879
1921
|
|
|
1880
1922
|
|
|
1923
|
+
def _order_info_fill_size(fills: OrderInfo) -> float:
|
|
1924
|
+
try:
|
|
1925
|
+
return float(fills.fill_size or 0)
|
|
1926
|
+
except (TypeError, ValueError):
|
|
1927
|
+
return 0.0
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
def should_prune_terminal_zero_fill_leg(
|
|
1931
|
+
fills: OrderInfo,
|
|
1932
|
+
*,
|
|
1933
|
+
redis_order: Optional[Order] = None,
|
|
1934
|
+
) -> bool:
|
|
1935
|
+
"""True when a zero-fill leg is terminal and safe to remove from entry_keys."""
|
|
1936
|
+
if _order_info_fill_size(fills) > 0:
|
|
1937
|
+
return False
|
|
1938
|
+
if fills.status in (OrderStatus.CANCELLED, OrderStatus.REJECTED):
|
|
1939
|
+
return True
|
|
1940
|
+
message = str(getattr(fills, "message", "") or "")
|
|
1941
|
+
exch_order_id = getattr(fills, "exchange_order_id", "")
|
|
1942
|
+
if redis_order is not None:
|
|
1943
|
+
if not message.strip():
|
|
1944
|
+
message = str(redis_order.message or "")
|
|
1945
|
+
if is_missing_exchange_order_id(exch_order_id):
|
|
1946
|
+
exch_order_id = redis_order.exch_order_id
|
|
1947
|
+
return is_broker_side_terminal_order(exch_order_id, message)
|
|
1948
|
+
|
|
1949
|
+
|
|
1950
|
+
def _sync_internal_order_long_symbol(broker: BrokerBase, internal_order_id: str) -> None:
|
|
1951
|
+
"""Rewrite parent long_symbol to match remaining filled entry legs after pruning."""
|
|
1952
|
+
if not internal_order_id or not cast(bool, broker.redis_o.exists(internal_order_id)):
|
|
1953
|
+
return
|
|
1954
|
+
long_symbol = cast(str, broker.redis_o.hget(internal_order_id, "long_symbol") or "")
|
|
1955
|
+
if "?" not in long_symbol:
|
|
1956
|
+
return
|
|
1957
|
+
entry_keys = hget_with_default(broker, internal_order_id, "entry_keys", "").split()
|
|
1958
|
+
if not entry_keys or not any(k.strip() for k in entry_keys):
|
|
1959
|
+
return
|
|
1960
|
+
positions = get_open_position_by_order(broker, internal_order_id, exclude_zero=True, side=["entry"])
|
|
1961
|
+
resolved = _resolve_effective_combo_symbol(long_symbol, positions)
|
|
1962
|
+
if resolved and resolved != long_symbol:
|
|
1963
|
+
cast(bool, broker.redis_o.hset(internal_order_id, "long_symbol", resolved))
|
|
1964
|
+
trading_logger.log_info(
|
|
1965
|
+
"Updated internal order long_symbol after terminal leg prune",
|
|
1966
|
+
{"internal_order_id": internal_order_id, "old_symbol": long_symbol, "new_symbol": resolved},
|
|
1967
|
+
)
|
|
1968
|
+
|
|
1969
|
+
|
|
1970
|
+
def _prune_terminal_entry_legs(broker: BrokerBase, internal_order_id: str, *, eod: bool = False) -> None:
|
|
1971
|
+
"""Refresh entry legs and delete zero-fill terminal legs; sync combo long_symbol."""
|
|
1972
|
+
if not internal_order_id or not cast(bool, broker.redis_o.exists(internal_order_id)):
|
|
1973
|
+
return
|
|
1974
|
+
entry_keys = hget_with_default(broker, internal_order_id, "entry_keys", "").split()
|
|
1975
|
+
for entry_key in list(entry_keys):
|
|
1976
|
+
if not entry_key or not cast(bool, broker.redis_o.exists(entry_key)):
|
|
1977
|
+
continue
|
|
1978
|
+
order = Order(**cast(dict[str, Any], broker.redis_o.hgetall(entry_key)))
|
|
1979
|
+
update_order_status(broker, internal_order_id, str(order.broker_order_id), eod=eod)
|
|
1980
|
+
_sync_internal_order_long_symbol(broker, internal_order_id)
|
|
1981
|
+
|
|
1982
|
+
|
|
1881
1983
|
def delete_broker_order_id(
|
|
1882
1984
|
broker,
|
|
1883
1985
|
internal_order_id: str,
|
|
@@ -1978,8 +2080,20 @@ def update_order_status(
|
|
|
1978
2080
|
if not broker_order_id_str or broker_order_id_str == "0" or broker_order_id_str.upper().endswith("P"):
|
|
1979
2081
|
return None
|
|
1980
2082
|
|
|
2083
|
+
redis_order: Optional[Order] = None
|
|
1981
2084
|
try:
|
|
1982
|
-
|
|
2085
|
+
redis_order_data = cast(dict[str, Any], broker.redis_o.hgetall(broker_order_id_str))
|
|
2086
|
+
if redis_order_data:
|
|
2087
|
+
redis_order = Order(**redis_order_data)
|
|
2088
|
+
except Exception:
|
|
2089
|
+
redis_order = None
|
|
2090
|
+
|
|
2091
|
+
get_order_info_kwargs: dict[str, Any] = {"broker_order_id": broker_order_id_str}
|
|
2092
|
+
if redis_order is not None:
|
|
2093
|
+
get_order_info_kwargs["order"] = redis_order
|
|
2094
|
+
|
|
2095
|
+
try:
|
|
2096
|
+
fills = broker.get_order_info(**get_order_info_kwargs)
|
|
1983
2097
|
except Exception as e:
|
|
1984
2098
|
trading_logger.log_error(
|
|
1985
2099
|
"Error calling get_order_info in update_order_status",
|
|
@@ -2006,22 +2120,22 @@ def update_order_status(
|
|
|
2006
2120
|
)
|
|
2007
2121
|
if attr == "exchange_order_id":
|
|
2008
2122
|
trading_logger.log_info(msg)
|
|
2009
|
-
|
|
2010
|
-
|
|
2123
|
+
continue
|
|
2124
|
+
trading_logger.log_error(msg)
|
|
2011
2125
|
return fills
|
|
2012
2126
|
|
|
2013
2127
|
if broker.broker != fills.broker:
|
|
2014
2128
|
return fills
|
|
2015
2129
|
|
|
2016
|
-
if fills.status == OrderStatus.CANCELLED and fills
|
|
2130
|
+
if fills.status == OrderStatus.CANCELLED and _order_info_fill_size(fills) > 0:
|
|
2017
2131
|
broker.redis_o.hset(broker_order_id, "price", str(fills.fill_price))
|
|
2018
2132
|
broker.redis_o.hset(broker_order_id, "quantity", str(fills.fill_size))
|
|
2019
2133
|
broker.redis_o.hset(broker_order_id, "status", fills.status.name)
|
|
2020
2134
|
broker.redis_o.hset(broker_order_id, "exch_order_id", fills.exchange_order_id)
|
|
2021
|
-
elif (fills
|
|
2135
|
+
elif should_prune_terminal_zero_fill_leg(fills, redis_order=redis_order):
|
|
2022
2136
|
delete_broker_order_id(broker, internal_order_id, broker_order_id)
|
|
2023
2137
|
elif eod:
|
|
2024
|
-
if fills
|
|
2138
|
+
if _order_info_fill_size(fills) > 0:
|
|
2025
2139
|
broker.redis_o.hset(broker_order_id, "price", str(fills.fill_price))
|
|
2026
2140
|
broker.redis_o.hset(broker_order_id, "quantity", str(fills.fill_size))
|
|
2027
2141
|
broker.redis_o.hset(broker_order_id, "status", fills.status.name)
|
|
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
|