quantplay 2.0.25__tar.gz → 2.0.27__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.
- {quantplay-2.0.25 → quantplay-2.0.27}/PKG-INFO +1 -1
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/aliceblue.py +14 -9
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/angelone.py +19 -66
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/five_paisa.py +3 -1
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/generics/broker.py +16 -9
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/motilal.py +39 -49
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/noren.py +7 -3
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/upstox.py +4 -2
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts.py +3 -1
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/zerodha.py +8 -3
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/PKG-INFO +1 -1
- {quantplay-2.0.25 → quantplay-2.0.27}/setup.py +1 -1
- {quantplay-2.0.25 → quantplay-2.0.27}/README.md +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/pyproject.toml +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/auto_login/aliceblue.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/ft_utils/ft_noren.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/Connect.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/broker.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/generics.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/py.typed +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/equities/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/equities/intraday/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/equities/overnight/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/futures/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/futures/overnight/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/ladder.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/musk.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/short_straddle.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/pickle_utils.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/utils/selenium_utils.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/wrapper/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay/wrapper/aws/s3.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/SOURCES.txt +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/setup.cfg +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/tests/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/tests/conftest.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/tests/wrapper/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.25 → quantplay-2.0.27}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -213,8 +213,10 @@ class Aliceblue(Broker):
|
|
|
213
213
|
exception_message = f"Order placement failed [{str(e)}]"
|
|
214
214
|
raise QuantplayOrderPlacementException(exception_message)
|
|
215
215
|
|
|
216
|
-
def
|
|
217
|
-
inst = self.
|
|
216
|
+
def ltp(self, exchange, tradingsymbol: str):
|
|
217
|
+
inst = self.invoke_aliceblue_api(
|
|
218
|
+
self.alice.get_instrument_by_symbol, exchange=exchange, symbol=tradingsymbol
|
|
219
|
+
)
|
|
218
220
|
info = self.invoke_aliceblue_api(self.alice.get_scrip_info, instrument=inst)
|
|
219
221
|
|
|
220
222
|
return float(info["LTP"])
|
|
@@ -327,7 +329,7 @@ class Aliceblue(Broker):
|
|
|
327
329
|
holdings_df = holdings_df.with_columns(
|
|
328
330
|
pl.struct(["exchange", "tradingsymbol"])
|
|
329
331
|
.map_elements(
|
|
330
|
-
lambda x: int(self.
|
|
332
|
+
lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
|
|
331
333
|
return_dtype=pl.Float64,
|
|
332
334
|
)
|
|
333
335
|
.alias("price")
|
|
@@ -453,6 +455,8 @@ class Aliceblue(Broker):
|
|
|
453
455
|
orders_df = orders_df.join(
|
|
454
456
|
positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
|
|
455
457
|
)
|
|
458
|
+
else:
|
|
459
|
+
orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
456
460
|
|
|
457
461
|
if "rorgqty" in orders_df.columns:
|
|
458
462
|
orders_df = orders_df.with_columns(
|
|
@@ -547,10 +551,6 @@ class Aliceblue(Broker):
|
|
|
547
551
|
def margins(self):
|
|
548
552
|
margins = self.invoke_aliceblue_api(self.alice.get_balance)
|
|
549
553
|
|
|
550
|
-
if "emsg" in margins and "expired" in margins["emsg"].lower():
|
|
551
|
-
raise TokenException("Session expired")
|
|
552
|
-
if "stat" in margins and "not_ok" in margins["stat"].lower():
|
|
553
|
-
raise TokenException(f"Aliceblue broker error: {margins['emsg']}")
|
|
554
554
|
margins = [a for a in margins if a["segment"] == "ALL"][0]
|
|
555
555
|
|
|
556
556
|
response = {
|
|
@@ -570,6 +570,11 @@ class Aliceblue(Broker):
|
|
|
570
570
|
def invoke_aliceblue_api(self, fn: Callable, *args, **kwargs) -> Any:
|
|
571
571
|
try:
|
|
572
572
|
response = fn(*args, **kwargs)
|
|
573
|
+
|
|
574
|
+
if "emsg" in response and "expired" in response["emsg"].lower():
|
|
575
|
+
raise TokenException("Session expired")
|
|
576
|
+
if "stat" in response and response["stat"].lower() == "not_ok":
|
|
577
|
+
raise InvalidArgumentException(response["emsg"])
|
|
573
578
|
if response is None:
|
|
574
579
|
raise InvalidArgumentException(
|
|
575
580
|
"Invalid data response. Aliceblue sent incorrect data, Please check."
|
|
@@ -577,8 +582,8 @@ class Aliceblue(Broker):
|
|
|
577
582
|
|
|
578
583
|
return response
|
|
579
584
|
|
|
580
|
-
except TokenException:
|
|
581
|
-
raise
|
|
585
|
+
except (TokenException, InvalidArgumentException):
|
|
586
|
+
raise
|
|
582
587
|
|
|
583
588
|
except Exception:
|
|
584
589
|
traceback.print_exc()
|
|
@@ -23,7 +23,6 @@ from quantplay.exception.exceptions import (
|
|
|
23
23
|
TokenException,
|
|
24
24
|
retry_exception,
|
|
25
25
|
)
|
|
26
|
-
from quantplay.exception.exceptions import TokenException as QuantplayTokenException
|
|
27
26
|
from quantplay.model.broker import ModifyOrderRequest, UserBrokerProfileResponse
|
|
28
27
|
from quantplay.model.order_event import OrderUpdateEvent
|
|
29
28
|
from quantplay.utils.constant import Constants, OrderType
|
|
@@ -162,12 +161,7 @@ class AngelOne(Broker):
|
|
|
162
161
|
"Product {} not supported for trading".format(product)
|
|
163
162
|
)
|
|
164
163
|
|
|
165
|
-
|
|
166
|
-
wait_exponential_multiplier=3000,
|
|
167
|
-
wait_exponential_max=10000,
|
|
168
|
-
stop_max_attempt_number=3,
|
|
169
|
-
)
|
|
170
|
-
def get_ltp(self, exchange, tradingsymbol: str) -> float:
|
|
164
|
+
def ltp(self, exchange, tradingsymbol: str) -> float:
|
|
171
165
|
if tradingsymbol in MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP:
|
|
172
166
|
tradingsymbol = MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP[
|
|
173
167
|
tradingsymbol
|
|
@@ -185,11 +179,6 @@ class AngelOne(Broker):
|
|
|
185
179
|
symboltoken=symboltoken,
|
|
186
180
|
)
|
|
187
181
|
|
|
188
|
-
if "status" in response and response["status"] is False:
|
|
189
|
-
raise InvalidArgumentException(
|
|
190
|
-
"Failed to fetch ltp broker error {}".format(response)
|
|
191
|
-
)
|
|
192
|
-
|
|
193
182
|
return response["data"]["ltp"]
|
|
194
183
|
|
|
195
184
|
def place_order(
|
|
@@ -310,18 +299,8 @@ class AngelOne(Broker):
|
|
|
310
299
|
def cancel_order(self, order_id, variety="NORMAL"):
|
|
311
300
|
self.wrapper.cancelOrder(order_id=order_id, variety=variety)
|
|
312
301
|
|
|
313
|
-
@retry(
|
|
314
|
-
wait_exponential_multiplier=3000,
|
|
315
|
-
wait_exponential_max=15000,
|
|
316
|
-
stop_max_attempt_number=5,
|
|
317
|
-
retry_on_exception=retry_exception,
|
|
318
|
-
)
|
|
319
302
|
def holdings(self):
|
|
320
|
-
|
|
321
|
-
holdings = self.invoke_angelone_api(self.wrapper.holding)
|
|
322
|
-
except Exception:
|
|
323
|
-
raise RetryableException("Access Denied retrying")
|
|
324
|
-
self.handle_exception(holdings)
|
|
303
|
+
holdings = self.invoke_angelone_api(self.wrapper.holding)
|
|
325
304
|
|
|
326
305
|
if holdings["data"] is None or len(holdings["data"]) == 0:
|
|
327
306
|
return pl.DataFrame(schema=self.holidings_schema)
|
|
@@ -350,15 +329,11 @@ class AngelOne(Broker):
|
|
|
350
329
|
@retry(
|
|
351
330
|
wait_exponential_multiplier=3000,
|
|
352
331
|
wait_exponential_max=15000,
|
|
353
|
-
stop_max_attempt_number=
|
|
332
|
+
stop_max_attempt_number=2,
|
|
354
333
|
retry_on_exception=retry_exception,
|
|
355
334
|
)
|
|
356
335
|
def positions(self, drop_cnc: bool = True):
|
|
357
|
-
|
|
358
|
-
positions = self.invoke_angelone_api(self.wrapper.position)
|
|
359
|
-
except Exception:
|
|
360
|
-
raise RetryableException("Access Denied retrying")
|
|
361
|
-
self.handle_exception(positions)
|
|
336
|
+
positions = self.invoke_angelone_api(self.wrapper.position)
|
|
362
337
|
|
|
363
338
|
if positions["data"] is None:
|
|
364
339
|
return pl.DataFrame(schema=self.positions_schema)
|
|
@@ -427,18 +402,8 @@ class AngelOne(Broker):
|
|
|
427
402
|
self.positions_schema
|
|
428
403
|
)
|
|
429
404
|
|
|
430
|
-
@retry(
|
|
431
|
-
wait_exponential_multiplier=3000,
|
|
432
|
-
wait_exponential_max=10000,
|
|
433
|
-
stop_max_attempt_number=3,
|
|
434
|
-
retry_on_exception=retry_exception,
|
|
435
|
-
)
|
|
436
405
|
def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
|
|
437
|
-
|
|
438
|
-
order_book = self.invoke_angelone_api(self.wrapper.orderBook)
|
|
439
|
-
except Exception:
|
|
440
|
-
raise RetryableException("Access Denied retrying")
|
|
441
|
-
self.handle_exception(order_book)
|
|
406
|
+
order_book = self.invoke_angelone_api(self.wrapper.orderBook)
|
|
442
407
|
|
|
443
408
|
if order_book["data"]:
|
|
444
409
|
orders_df = pl.DataFrame(order_book["data"])
|
|
@@ -534,21 +499,10 @@ class AngelOne(Broker):
|
|
|
534
499
|
traceback.print_exc()
|
|
535
500
|
raise ServiceException("Unknown error while fetching order book [{}]")
|
|
536
501
|
|
|
537
|
-
@retry(
|
|
538
|
-
wait_exponential_multiplier=3000,
|
|
539
|
-
wait_exponential_max=10000,
|
|
540
|
-
stop_max_attempt_number=3,
|
|
541
|
-
retry_on_exception=retry_exception,
|
|
542
|
-
)
|
|
543
502
|
def profile(self):
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
)
|
|
548
|
-
except Exception:
|
|
549
|
-
raise RetryableException("Access Denied retrying")
|
|
550
|
-
|
|
551
|
-
self.handle_exception(profile_data)
|
|
503
|
+
profile_data = self.invoke_angelone_api(
|
|
504
|
+
self.wrapper.getProfile, refreshToken=self.refresh_token
|
|
505
|
+
)
|
|
552
506
|
|
|
553
507
|
profile_data = profile_data["data"]
|
|
554
508
|
response: UserBrokerProfileResponse = {
|
|
@@ -559,21 +513,14 @@ class AngelOne(Broker):
|
|
|
559
513
|
|
|
560
514
|
return response
|
|
561
515
|
|
|
562
|
-
@retry(
|
|
563
|
-
wait_exponential_multiplier=3000,
|
|
564
|
-
wait_exponential_max=10000,
|
|
565
|
-
stop_max_attempt_number=3,
|
|
566
|
-
retry_on_exception=retry_exception,
|
|
567
|
-
)
|
|
568
516
|
def margins(self):
|
|
569
517
|
api_margins = self.invoke_angelone_api(self.wrapper.rmsLimit)
|
|
570
|
-
self.handle_exception(api_margins)
|
|
571
518
|
|
|
572
519
|
if "data" in api_margins and api_margins["data"] is None:
|
|
573
520
|
if "errorcode" in api_margins and api_margins["errorcode"] == "AB1004":
|
|
574
521
|
raise TokenException("Angelone server not not responding")
|
|
575
522
|
|
|
576
|
-
return {"margin_used": 0, "margin_available": 0, "total_balance": 0}
|
|
523
|
+
return {"margin_used": 0.0, "margin_available": 0.0, "total_balance": 0.0}
|
|
577
524
|
|
|
578
525
|
api_margins = api_margins["data"]
|
|
579
526
|
|
|
@@ -610,19 +557,25 @@ class AngelOne(Broker):
|
|
|
610
557
|
|
|
611
558
|
return response
|
|
612
559
|
|
|
560
|
+
@retry(
|
|
561
|
+
wait_exponential_multiplier=3000,
|
|
562
|
+
wait_exponential_max=10000,
|
|
563
|
+
stop_max_attempt_number=3,
|
|
564
|
+
retry_on_exception=retry_exception,
|
|
565
|
+
)
|
|
613
566
|
def invoke_angelone_api(self, fn: Callable, *args, **kwargs) -> Dict:
|
|
614
567
|
try:
|
|
615
568
|
response = fn(*args, **kwargs)
|
|
569
|
+
if "errorCode" in response and response["errorCode"] == "AG8001":
|
|
570
|
+
raise TokenException(f"{self.user_id}: Invalid Token")
|
|
616
571
|
if isinstance(response, bytes):
|
|
617
572
|
raise InvalidArgumentException(
|
|
618
573
|
"Invalid data response. AngelOne sent incorrect data, Please check."
|
|
619
574
|
)
|
|
620
575
|
|
|
621
576
|
return response
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
raise QuantplayTokenException("Token Expired")
|
|
625
|
-
|
|
577
|
+
except (TokenException, InvalidArgumentException):
|
|
578
|
+
raise
|
|
626
579
|
except Exception:
|
|
627
580
|
traceback.print_exc()
|
|
628
581
|
raise RetryableException("Failed to fetch user profile")
|
|
@@ -134,7 +134,7 @@ class FivePaisa(Broker):
|
|
|
134
134
|
stop_max_attempt_number=3,
|
|
135
135
|
retry_on_exception=retry_exception,
|
|
136
136
|
)
|
|
137
|
-
def
|
|
137
|
+
def ltp(self, exchange, tradingsymbol: str) -> float:
|
|
138
138
|
tradingsymbol = self.get_symbol(tradingsymbol, exchange)
|
|
139
139
|
exchange_name = self.get_exchange(exchange)
|
|
140
140
|
exchange_type = self.get_exchange_type(exchange)
|
|
@@ -413,6 +413,8 @@ class FivePaisa(Broker):
|
|
|
413
413
|
.then(-pl.col("pnl"))
|
|
414
414
|
.otherwise(pl.col("pnl"))
|
|
415
415
|
)
|
|
416
|
+
else:
|
|
417
|
+
orders = orders.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
416
418
|
|
|
417
419
|
return orders
|
|
418
420
|
|
|
@@ -456,7 +456,7 @@ class Broker:
|
|
|
456
456
|
|
|
457
457
|
def live_data(self, exchange, tradingsymbol):
|
|
458
458
|
return {
|
|
459
|
-
"ltp": self.
|
|
459
|
+
"ltp": self.ltp(exchange, tradingsymbol),
|
|
460
460
|
"upper_circuit": None,
|
|
461
461
|
"lower_circuit": None,
|
|
462
462
|
}
|
|
@@ -606,7 +606,7 @@ class Broker:
|
|
|
606
606
|
exchange = stoploss_order["exchange"]
|
|
607
607
|
tradingsymbol = stoploss_order["tradingsymbol"]
|
|
608
608
|
|
|
609
|
-
ltp = self.
|
|
609
|
+
ltp = self.ltp(exchange, tradingsymbol)
|
|
610
610
|
stoploss_order["order_type"] = "LIMIT"
|
|
611
611
|
stoploss_order["price"] = self.round_to_tick(ltp)
|
|
612
612
|
stoploss_order["trigger_price"] = None
|
|
@@ -713,7 +713,7 @@ class Broker:
|
|
|
713
713
|
positions = positions.with_columns(
|
|
714
714
|
pl.struct(["exchange", "tradingsymbol"])
|
|
715
715
|
.map_elements(
|
|
716
|
-
lambda x: int(self.
|
|
716
|
+
lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
|
|
717
717
|
return_dtype=pl.Float64,
|
|
718
718
|
)
|
|
719
719
|
.alias("price")
|
|
@@ -895,7 +895,7 @@ class Broker:
|
|
|
895
895
|
elif "SENSEX" in symbol and exchange == "BFO":
|
|
896
896
|
return self.underlying_config("SENSEX")["max_lots"]
|
|
897
897
|
elif exchange == "NSE":
|
|
898
|
-
max_qty = int(500000 / self.
|
|
898
|
+
max_qty = int(500000 / self.ltp(exchange, symbol))
|
|
899
899
|
if max_qty == 0:
|
|
900
900
|
return 1
|
|
901
901
|
return max_qty
|
|
@@ -995,9 +995,7 @@ class Broker:
|
|
|
995
995
|
all_symbols = list(orders.exchange_symbol.unique())
|
|
996
996
|
symbol_ltp = {}
|
|
997
997
|
for exchange_symbol in all_symbols:
|
|
998
|
-
ltp = self.
|
|
999
|
-
exchange_symbol.split(":")[0], exchange_symbol.split(":")[1]
|
|
1000
|
-
)
|
|
998
|
+
ltp = self.ltp(exchange_symbol.split(":")[0], exchange_symbol.split(":")[1])
|
|
1001
999
|
symbol_ltp[exchange_symbol] = ltp
|
|
1002
1000
|
orders.loc[:, "ltp"] = orders["exchange_symbol"].map(symbol_ltp)
|
|
1003
1001
|
|
|
@@ -1130,7 +1128,7 @@ class Broker:
|
|
|
1130
1128
|
else:
|
|
1131
1129
|
modification_count[order_id] += 1
|
|
1132
1130
|
|
|
1133
|
-
ltp = self.
|
|
1131
|
+
ltp = self.ltp(order["exchange"], order["tradingsymbol"])
|
|
1134
1132
|
|
|
1135
1133
|
market_protection = 0.02
|
|
1136
1134
|
if modification_count[order_id] > 5:
|
|
@@ -1237,7 +1235,7 @@ class Broker:
|
|
|
1237
1235
|
"""
|
|
1238
1236
|
raise NotImplementedError
|
|
1239
1237
|
|
|
1240
|
-
def
|
|
1238
|
+
def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
|
|
1241
1239
|
"""_summary_
|
|
1242
1240
|
|
|
1243
1241
|
Args:
|
|
@@ -1284,3 +1282,12 @@ class Broker:
|
|
|
1284
1282
|
UserBrokerProfile: contains exchange user_id full_name and email
|
|
1285
1283
|
"""
|
|
1286
1284
|
raise NotImplementedError
|
|
1285
|
+
|
|
1286
|
+
def margins(self) -> Dict[str, float]:
|
|
1287
|
+
"""Returns User Margin Summary
|
|
1288
|
+
|
|
1289
|
+
Raises:
|
|
1290
|
+
NotImplementedError: if Function Not Implemented by Sub class
|
|
1291
|
+
|
|
1292
|
+
"""
|
|
1293
|
+
raise NotImplementedError
|
|
@@ -19,6 +19,7 @@ import binascii
|
|
|
19
19
|
from quantplay.utils.pickle_utils import InstrumentData
|
|
20
20
|
from quantplay.exception.exceptions import (
|
|
21
21
|
QuantplayOrderPlacementException,
|
|
22
|
+
TokenException,
|
|
22
23
|
retry_exception,
|
|
23
24
|
RetryableException,
|
|
24
25
|
)
|
|
@@ -88,7 +89,7 @@ class Motilal(Broker):
|
|
|
88
89
|
self.verify_otp_url = (
|
|
89
90
|
"https://{}openapi.motilaloswal.com/rest/login/v3/verifyotp".format(uat)
|
|
90
91
|
)
|
|
91
|
-
self.
|
|
92
|
+
self.ltp_url = (
|
|
92
93
|
"https://{}openapi.motilaloswal.com/rest/report/v1/getltpdata".format(uat)
|
|
93
94
|
)
|
|
94
95
|
self.place_order_url = (
|
|
@@ -133,8 +134,7 @@ class Motilal(Broker):
|
|
|
133
134
|
else:
|
|
134
135
|
raise Exception("Missing Arguments")
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
self.user_id = profile["user_id"]
|
|
137
|
+
self.user_id = self.headers["vendorinfo"]
|
|
138
138
|
except binascii.Error:
|
|
139
139
|
raise InvalidArgumentException("Invalid TOTP key provided")
|
|
140
140
|
except InvalidArgumentException:
|
|
@@ -263,12 +263,7 @@ class Motilal(Broker):
|
|
|
263
263
|
Constants.logger.info(response)
|
|
264
264
|
return response
|
|
265
265
|
|
|
266
|
-
|
|
267
|
-
wait_exponential_multiplier=3000,
|
|
268
|
-
wait_exponential_max=10000,
|
|
269
|
-
stop_max_attempt_number=3,
|
|
270
|
-
)
|
|
271
|
-
def get_ltp(self, exchange, tradingsymbol: str) -> float:
|
|
266
|
+
def ltp(self, exchange, tradingsymbol: str) -> float:
|
|
272
267
|
tradingsymbol = self.get_symbol(tradingsymbol)
|
|
273
268
|
token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
|
|
274
269
|
exchange = self.get_exchange(exchange)
|
|
@@ -279,9 +274,7 @@ class Motilal(Broker):
|
|
|
279
274
|
}
|
|
280
275
|
|
|
281
276
|
Constants.logger.info("[GET_LTP_REQUEST] response {}".format(data))
|
|
282
|
-
response =
|
|
283
|
-
self.ltp_utl, headers=self.headers, data=json.dumps(data)
|
|
284
|
-
)
|
|
277
|
+
response = self.__post_request(self.ltp_url, json.dumps(data))
|
|
285
278
|
Constants.logger.info("[GET_LTP_RESPONSE] response {}".format(response.json()))
|
|
286
279
|
return response.json()["data"]["ltp"] / 100.0
|
|
287
280
|
|
|
@@ -383,36 +376,15 @@ class Motilal(Broker):
|
|
|
383
376
|
)
|
|
384
377
|
Constants.logger.error(exception_message)
|
|
385
378
|
|
|
386
|
-
def get_profile(self):
|
|
387
|
-
response = requests.post(
|
|
388
|
-
self.get_profile_url,
|
|
389
|
-
headers=self.headers,
|
|
390
|
-
data=json.dumps({"Clientcode": self.headers["vendorinfo"]}),
|
|
391
|
-
).json()
|
|
392
|
-
if response["status"] == "ERROR":
|
|
393
|
-
raise Exception(response["message"])
|
|
394
|
-
|
|
395
|
-
return response["data"]
|
|
396
|
-
|
|
397
|
-
@retry(
|
|
398
|
-
wait_exponential_multiplier=3000,
|
|
399
|
-
wait_exponential_max=10000,
|
|
400
|
-
stop_max_attempt_number=3,
|
|
401
|
-
)
|
|
402
379
|
def margins(self):
|
|
403
|
-
response =
|
|
404
|
-
self.margin_summary_url,
|
|
405
|
-
headers=self.headers,
|
|
406
|
-
data=json.dumps({"Clientcode": self.headers["vendorinfo"]}),
|
|
407
|
-
).json()
|
|
408
|
-
if response["status"] == "ERROR":
|
|
409
|
-
raise Exception(response["message"])
|
|
380
|
+
response = self.__post_request(self.margin_summary_url, {})
|
|
410
381
|
margin_summary = response["data"]
|
|
411
382
|
|
|
412
|
-
margins = {"margin_used": 0, "margin_available": 0}
|
|
383
|
+
margins = {"margin_used": 0.0, "margin_available": 0.0}
|
|
413
384
|
for margin_particular in margin_summary:
|
|
414
385
|
if margin_particular["srno"] in [103]:
|
|
415
386
|
margins["margin_available"] += margin_particular["amount"]
|
|
387
|
+
|
|
416
388
|
if margin_particular["srno"] in [301, 321, 340, 360]:
|
|
417
389
|
margins["margin_used"] += margin_particular["amount"]
|
|
418
390
|
|
|
@@ -554,14 +526,35 @@ class Motilal(Broker):
|
|
|
554
526
|
|
|
555
527
|
return response
|
|
556
528
|
|
|
557
|
-
|
|
529
|
+
@retry(
|
|
530
|
+
wait_exponential_multiplier=3000,
|
|
531
|
+
wait_exponential_max=10000,
|
|
532
|
+
stop_max_attempt_number=3,
|
|
533
|
+
retry_on_exception=retry_exception,
|
|
534
|
+
)
|
|
535
|
+
def __post_request(self, url, data):
|
|
558
536
|
api_response = requests.post(
|
|
559
|
-
|
|
537
|
+
url,
|
|
560
538
|
headers=self.headers,
|
|
561
539
|
data=json.dumps({"Clientcode": self.headers["vendorinfo"]}),
|
|
562
540
|
).json()
|
|
563
|
-
|
|
564
|
-
|
|
541
|
+
|
|
542
|
+
if api_response["status"] == "ERROR" and api_response["errorcode"] in [
|
|
543
|
+
"MO8001",
|
|
544
|
+
"MO8002",
|
|
545
|
+
"MO8003",
|
|
546
|
+
"MO8050",
|
|
547
|
+
"MO8051",
|
|
548
|
+
"MO8052",
|
|
549
|
+
]:
|
|
550
|
+
raise TokenException("Motilal token expired")
|
|
551
|
+
elif api_response["status"] == "ERROR":
|
|
552
|
+
raise RetryableException(api_response["message"])
|
|
553
|
+
|
|
554
|
+
return api_response
|
|
555
|
+
|
|
556
|
+
def profile(self) -> UserBrokerProfileResponse:
|
|
557
|
+
api_response = self.__post_request(self.get_profile_url, {})
|
|
565
558
|
|
|
566
559
|
api_response = api_response["data"]
|
|
567
560
|
response: UserBrokerProfileResponse = {
|
|
@@ -578,7 +571,7 @@ class Motilal(Broker):
|
|
|
578
571
|
stop_max_attempt_number=3,
|
|
579
572
|
)
|
|
580
573
|
def holdings(self):
|
|
581
|
-
response =
|
|
574
|
+
response = self.__post_request(self.holdings_url, {})
|
|
582
575
|
if response["status"] == "ERROR":
|
|
583
576
|
Constants.logger.info(
|
|
584
577
|
"Error while fetching order book [{}]".format(response["message"])
|
|
@@ -605,7 +598,7 @@ class Motilal(Broker):
|
|
|
605
598
|
holdings_df = holdings_df.with_columns(
|
|
606
599
|
pl.struct(["exchange", "tradingsymbol"])
|
|
607
600
|
.map_elements(
|
|
608
|
-
lambda x: int(self.
|
|
601
|
+
lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
|
|
609
602
|
return_dtype=pl.Float64,
|
|
610
603
|
)
|
|
611
604
|
.alias("price")
|
|
@@ -624,13 +617,8 @@ class Motilal(Broker):
|
|
|
624
617
|
|
|
625
618
|
return holdings_df[list(self.holidings_schema.keys())].cast(self.holidings_schema)
|
|
626
619
|
|
|
627
|
-
@retry(
|
|
628
|
-
wait_exponential_multiplier=3000,
|
|
629
|
-
wait_exponential_max=10000,
|
|
630
|
-
stop_max_attempt_number=3,
|
|
631
|
-
)
|
|
632
620
|
def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
|
|
633
|
-
response =
|
|
621
|
+
response = self.__post_request(self.positions_url, {})
|
|
634
622
|
if response["status"] == "ERROR":
|
|
635
623
|
Constants.logger.info(
|
|
636
624
|
"Error while fetching positions [{}]".format(response["message"])
|
|
@@ -699,7 +687,7 @@ class Motilal(Broker):
|
|
|
699
687
|
)
|
|
700
688
|
|
|
701
689
|
def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
|
|
702
|
-
response =
|
|
690
|
+
response = self.__post_request(self.order_book_url, {})
|
|
703
691
|
if response["status"] == "ERROR":
|
|
704
692
|
Constants.logger.info(
|
|
705
693
|
"Error while fetching order book [{}]".format(response["message"])
|
|
@@ -742,6 +730,8 @@ class Motilal(Broker):
|
|
|
742
730
|
orders_df = orders_df.join(
|
|
743
731
|
positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
|
|
744
732
|
)
|
|
733
|
+
else:
|
|
734
|
+
orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
745
735
|
|
|
746
736
|
orders_df = orders_df.with_columns(
|
|
747
737
|
pl.when(pl.col("product") == "VALUEPLUS")
|
|
@@ -75,6 +75,8 @@ class Noren(Broker):
|
|
|
75
75
|
def get_symbol(self, symbol: str, exchange: ExchangeType | None = None):
|
|
76
76
|
if symbol not in self.quantplay_symbol_map:
|
|
77
77
|
return symbol
|
|
78
|
+
if exchange == "NSE" and "-EQ" not in symbol:
|
|
79
|
+
return f"{symbol}-EQ"
|
|
78
80
|
|
|
79
81
|
return self.quantplay_symbol_map[symbol]
|
|
80
82
|
|
|
@@ -246,8 +248,8 @@ class Noren(Broker):
|
|
|
246
248
|
wait_exponential_max=10000,
|
|
247
249
|
stop_max_attempt_number=3,
|
|
248
250
|
)
|
|
249
|
-
def
|
|
250
|
-
tradingsymbol = self.get_symbol(tradingsymbol)
|
|
251
|
+
def ltp(self, exchange, tradingsymbol):
|
|
252
|
+
tradingsymbol = self.get_symbol(tradingsymbol, exchange)
|
|
251
253
|
|
|
252
254
|
token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
|
|
253
255
|
quote = self.api.get_quotes(exchange, str(token))
|
|
@@ -387,7 +389,7 @@ class Noren(Broker):
|
|
|
387
389
|
holdings_df = holdings_df.with_columns(
|
|
388
390
|
pl.struct(["exchange", "tradingsymbol"])
|
|
389
391
|
.map_elements(
|
|
390
|
-
lambda x: int(self.
|
|
392
|
+
lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
|
|
391
393
|
return_dtype=pl.Float64,
|
|
392
394
|
)
|
|
393
395
|
.alias("price")
|
|
@@ -523,6 +525,8 @@ class Noren(Broker):
|
|
|
523
525
|
orders_df = orders_df.join(
|
|
524
526
|
positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
|
|
525
527
|
)
|
|
528
|
+
else:
|
|
529
|
+
orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
526
530
|
|
|
527
531
|
orders_df = orders_df.with_columns(pl.lit(None).alias("variety"))
|
|
528
532
|
|
|
@@ -126,7 +126,7 @@ class Upstox(Broker):
|
|
|
126
126
|
stop_max_attempt_number=3,
|
|
127
127
|
retry_on_exception=retry_exception,
|
|
128
128
|
)
|
|
129
|
-
def
|
|
129
|
+
def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
|
|
130
130
|
api_instance = upstox_client.MarketQuoteApi(self.api_client)
|
|
131
131
|
|
|
132
132
|
ltp = 0
|
|
@@ -315,7 +315,7 @@ class Upstox(Broker):
|
|
|
315
315
|
holdings_df = holdings_df.with_columns(
|
|
316
316
|
pl.struct(["exchange", "tradingsymbol"])
|
|
317
317
|
.map_elements(
|
|
318
|
-
lambda x: int(self.
|
|
318
|
+
lambda x: int(self.ltp(x["exchange"], x["tradingsymbol"])),
|
|
319
319
|
return_dtype=pl.Float64,
|
|
320
320
|
)
|
|
321
321
|
.alias("price")
|
|
@@ -465,6 +465,8 @@ class Upstox(Broker):
|
|
|
465
465
|
orders_df = orders_df.join(
|
|
466
466
|
positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
|
|
467
467
|
)
|
|
468
|
+
else:
|
|
469
|
+
orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
468
470
|
orders_df = orders_df.rename({"placed_by": "user_id"})
|
|
469
471
|
|
|
470
472
|
orders_df = orders_df.with_columns(
|
|
@@ -275,6 +275,8 @@ class XTS(Broker):
|
|
|
275
275
|
orders_df = orders_df.join(
|
|
276
276
|
positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
|
|
277
277
|
)
|
|
278
|
+
else:
|
|
279
|
+
orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
278
280
|
|
|
279
281
|
orders_df = orders_df.with_columns(
|
|
280
282
|
pl.col("filled_quantity").cast(pl.Int64).alias("filled_quantity"),
|
|
@@ -538,7 +540,7 @@ class XTS(Broker):
|
|
|
538
540
|
|
|
539
541
|
return exchange_code_map[exchange]
|
|
540
542
|
|
|
541
|
-
def
|
|
543
|
+
def ltp(self, exchange=None, tradingsymbol=None) -> float:
|
|
542
544
|
exchange_code = self.get_exchange_code(exchange)
|
|
543
545
|
exchange_token = self.symbol_data[f"{exchange}:{tradingsymbol}"]["exchange_token"]
|
|
544
546
|
|
|
@@ -132,8 +132,9 @@ class Zerodha(Broker):
|
|
|
132
132
|
wait_exponential_multiplier=3000,
|
|
133
133
|
wait_exponential_max=10000,
|
|
134
134
|
stop_max_attempt_number=3,
|
|
135
|
+
retry_on_exception=retry_exception,
|
|
135
136
|
)
|
|
136
|
-
def
|
|
137
|
+
def ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
|
|
137
138
|
try:
|
|
138
139
|
key = "{}:".format(exchange) + tradingsymbol
|
|
139
140
|
response = self.wrapper.ltp([key])
|
|
@@ -149,11 +150,13 @@ class Zerodha(Broker):
|
|
|
149
150
|
)
|
|
150
151
|
|
|
151
152
|
return api_response[key]["last_price"]
|
|
153
|
+
except TokenException:
|
|
154
|
+
raise QuantplayTokenException("Zerodha token expired")
|
|
152
155
|
except Exception as e:
|
|
153
156
|
exception_message = "GetLtp call failed for [{}] with error [{}]".format(
|
|
154
157
|
tradingsymbol, str(e)
|
|
155
158
|
)
|
|
156
|
-
raise
|
|
159
|
+
raise RetryableException(exception_message)
|
|
157
160
|
|
|
158
161
|
@retry(
|
|
159
162
|
wait_exponential_multiplier=3000,
|
|
@@ -211,7 +214,7 @@ class Zerodha(Broker):
|
|
|
211
214
|
exchange = order["exchange"]
|
|
212
215
|
tradingsymbol = order["tradingsymbol"]
|
|
213
216
|
|
|
214
|
-
return self.
|
|
217
|
+
return self.ltp(exchange, tradingsymbol)
|
|
215
218
|
|
|
216
219
|
@retry(
|
|
217
220
|
wait_exponential_multiplier=3000,
|
|
@@ -492,6 +495,8 @@ class Zerodha(Broker):
|
|
|
492
495
|
orders_df = orders_df.join(
|
|
493
496
|
positions.select(["tradingsymbol", "ltp"]), on="tradingsymbol", how="left"
|
|
494
497
|
)
|
|
498
|
+
else:
|
|
499
|
+
orders_df = orders_df.with_columns(pl.lit(None).cast(pl.Float64).alias("ltp"))
|
|
495
500
|
|
|
496
501
|
orders_df = orders_df.with_columns(
|
|
497
502
|
(
|
|
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
|
|
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
|
{quantplay-2.0.25 → quantplay-2.0.27}/quantplay/strategies/options/intraday/short_straddle.py
RENAMED
|
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
|