quantplay 2.0.41__tar.gz → 2.0.43__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.41 → quantplay-2.0.43}/PKG-INFO +26 -1
- {quantplay-2.0.41 → quantplay-2.0.43}/pyproject.toml +1 -1
- quantplay-2.0.43/quantplay/broker/__init__.py +25 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/aliceblue.py +13 -4
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/angelone.py +6 -3
- {quantplay-2.0.41/quantplay/broker/generics → quantplay-2.0.43/quantplay/broker}/broker_factory.py +107 -69
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/dhan.py +2 -35
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/finvasia_utils/fa_noren.py +3 -2
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/five_paisa.py +11 -15
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/flattrade.py +3 -3
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/ft_utils/ft_noren.py +3 -2
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/motilal.py +16 -15
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/noren.py +7 -3
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/upstox.py +3 -3
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/xts.py +1 -1
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/xts_utils/Connect.py +1 -2
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/xts_utils/InteractiveSocketClient.py +1 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/zerodha.py +1 -1
- quantplay-2.0.43/quantplay/exception/exceptions.py +100 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/model/instrument_data.py +2 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/wrapper/aws/s3.py +4 -3
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay.egg-info/PKG-INFO +26 -1
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay.egg-info/SOURCES.txt +1 -1
- {quantplay-2.0.41 → quantplay-2.0.43}/setup.py +1 -1
- quantplay-2.0.41/quantplay/exception/exceptions.py +0 -114
- quantplay-2.0.41/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/README.md +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/__init__.py +0 -0
- {quantplay-2.0.41/quantplay/broker → quantplay-2.0.43/quantplay/broker/auto_login}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/auto_login/aliceblue.py +1 -1
- {quantplay-2.0.41/quantplay/broker/auto_login → quantplay-2.0.43/quantplay/broker/finvasia_utils}/__init__.py +0 -0
- {quantplay-2.0.41/quantplay/broker/finvasia_utils → quantplay-2.0.43/quantplay/broker/ft_utils}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.41/quantplay/broker/ft_utils → quantplay-2.0.43/quantplay/broker/generics}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/generics/broker.py +2 -2
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.41/quantplay/broker/generics → quantplay-2.0.43/quantplay/broker/uplink}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.41/quantplay/broker/uplink → quantplay-2.0.43/quantplay/broker/xts_utils}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.41/quantplay/broker/xts_utils → quantplay-2.0.43/quantplay/model}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/model/broker.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/model/generics.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/py.typed +0 -0
- {quantplay-2.0.41/quantplay/model → quantplay-2.0.43/quantplay/utils}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/utils/caching.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/utils/pickle_utils.py +1 -1
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay/utils/selenium_utils.py +0 -0
- {quantplay-2.0.41/quantplay/utils → quantplay-2.0.43/quantplay/wrapper}/__init__.py +0 -0
- {quantplay-2.0.41/quantplay/wrapper → quantplay-2.0.43/quantplay/wrapper/aws}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/setup.cfg +0 -0
- {quantplay-2.0.41/quantplay/wrapper/aws → quantplay-2.0.43/tests}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/tests/conftest.py +0 -0
- {quantplay-2.0.41/tests → quantplay-2.0.43/tests/wrapper}/__init__.py +0 -0
- {quantplay-2.0.41/tests/wrapper → quantplay-2.0.43/tests/wrapper/aws}/__init__.py +0 -0
- {quantplay-2.0.41 → quantplay-2.0.43}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: quantplay
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.43
|
|
4
4
|
Summary: This python package will be stored in AWS CodeArtifact
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
7
7
|
Author-email:
|
|
8
8
|
License: MIT
|
|
9
|
+
Requires-Dist: setuptools
|
|
10
|
+
Requires-Dist: path
|
|
11
|
+
Requires-Dist: pyotp
|
|
12
|
+
Requires-Dist: retrying
|
|
13
|
+
Requires-Dist: boto3
|
|
14
|
+
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: websocket-client
|
|
16
|
+
Requires-Dist: smartapi-python
|
|
17
|
+
Requires-Dist: logzero
|
|
18
|
+
Requires-Dist: selenium
|
|
19
|
+
Requires-Dist: requests
|
|
20
|
+
Requires-Dist: pandas
|
|
21
|
+
Requires-Dist: pyarrow
|
|
22
|
+
Requires-Dist: polars
|
|
23
|
+
Requires-Dist: kiteconnect
|
|
24
|
+
Requires-Dist: pya3
|
|
25
|
+
Requires-Dist: py5paisa
|
|
26
|
+
Requires-Dist: upstox-python-sdk
|
|
27
|
+
Requires-Dist: undetected-chromedriver
|
|
28
|
+
Requires-Dist: cachetools
|
|
29
|
+
Requires-Dist: py_vollib
|
|
30
|
+
Requires-Dist: python-engineio
|
|
31
|
+
Requires-Dist: python-socketio
|
|
32
|
+
Requires-Dist: six
|
|
33
|
+
Requires-Dist: dhanhq
|
|
9
34
|
|
|
10
35
|
# Quantplay Alpha playground
|
|
11
36
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .aliceblue import Aliceblue
|
|
2
|
+
from .angelone import AngelOne
|
|
3
|
+
from .broker_factory import BrokerFactory
|
|
4
|
+
from .dhan import Dhan
|
|
5
|
+
from .five_paisa import FivePaisa
|
|
6
|
+
from .flattrade import FlatTrade
|
|
7
|
+
from .iifl_xts import IIFL
|
|
8
|
+
from .motilal import Motilal
|
|
9
|
+
from .shoonya import FinvAsia
|
|
10
|
+
from .upstox import Upstox
|
|
11
|
+
from .zerodha import Zerodha
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Aliceblue",
|
|
15
|
+
"AngelOne",
|
|
16
|
+
"BrokerFactory",
|
|
17
|
+
"Dhan",
|
|
18
|
+
"FivePaisa",
|
|
19
|
+
"FlatTrade",
|
|
20
|
+
"FinvAsia",
|
|
21
|
+
"Motilal",
|
|
22
|
+
"Zerodha",
|
|
23
|
+
"Upstox",
|
|
24
|
+
"IIFL",
|
|
25
|
+
]
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
import copy
|
|
3
3
|
import pickle
|
|
4
|
-
from queue import Queue
|
|
5
4
|
import traceback
|
|
5
|
+
from queue import Queue
|
|
6
6
|
from typing import Any, Dict, Literal
|
|
7
7
|
|
|
8
8
|
import polars as pl
|
|
9
9
|
from pya3 import (
|
|
10
10
|
Aliceblue as Alice,
|
|
11
|
+
)
|
|
12
|
+
from pya3 import (
|
|
11
13
|
OrderType as AliceOrderType,
|
|
12
|
-
|
|
14
|
+
)
|
|
15
|
+
from pya3 import (
|
|
13
16
|
ProductType as AliceProductType,
|
|
14
17
|
)
|
|
18
|
+
from pya3 import (
|
|
19
|
+
TransactionType as AliceTransactionType,
|
|
20
|
+
)
|
|
15
21
|
from retrying import retry # type: ignore
|
|
16
22
|
|
|
17
23
|
from quantplay.broker.generics.broker import Broker
|
|
@@ -50,8 +56,9 @@ class Aliceblue(Broker):
|
|
|
50
56
|
api_key: str | None = None,
|
|
51
57
|
order_updates: Queue[OrderUpdateEvent] | None = None,
|
|
52
58
|
client: str | None = None,
|
|
59
|
+
load_instrument: bool = True,
|
|
53
60
|
):
|
|
54
|
-
super(
|
|
61
|
+
super().__init__()
|
|
55
62
|
self.order_updates = order_updates
|
|
56
63
|
|
|
57
64
|
try:
|
|
@@ -82,7 +89,9 @@ class Aliceblue(Broker):
|
|
|
82
89
|
raise RetryableException(str(e))
|
|
83
90
|
|
|
84
91
|
self.user_id = self.alice.user_id
|
|
85
|
-
|
|
92
|
+
|
|
93
|
+
if load_instrument:
|
|
94
|
+
self.load_instrument()
|
|
86
95
|
|
|
87
96
|
def set_client(self, serialized_client: str):
|
|
88
97
|
try:
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import binascii
|
|
2
2
|
import copy
|
|
3
3
|
import json
|
|
4
|
-
from queue import Queue
|
|
5
4
|
import traceback
|
|
5
|
+
from queue import Queue
|
|
6
6
|
from typing import Any, Dict
|
|
7
7
|
|
|
8
|
-
from SmartApi.smartExceptions import DataException # type: ignore
|
|
9
8
|
import polars as pl
|
|
10
9
|
import pyotp
|
|
11
10
|
from requests.exceptions import ConnectionError, ConnectTimeout
|
|
12
11
|
from retrying import retry # type: ignore
|
|
13
12
|
from SmartApi import SmartConnect # type: ignore
|
|
13
|
+
from SmartApi.smartExceptions import DataException # type: ignore
|
|
14
14
|
|
|
15
15
|
from quantplay.broker.generics.broker import Broker
|
|
16
16
|
from quantplay.exception.exceptions import (
|
|
@@ -58,7 +58,7 @@ class AngelOne(Broker):
|
|
|
58
58
|
access_token: str | None = None,
|
|
59
59
|
load_instrument: bool = True,
|
|
60
60
|
):
|
|
61
|
-
super(
|
|
61
|
+
super().__init__()
|
|
62
62
|
self.order_updates = order_updates
|
|
63
63
|
|
|
64
64
|
try:
|
|
@@ -73,6 +73,7 @@ class AngelOne(Broker):
|
|
|
73
73
|
else:
|
|
74
74
|
if totp is None:
|
|
75
75
|
raise InvalidArgumentException("TOTP Key is Missing")
|
|
76
|
+
|
|
76
77
|
self.wrapper = SmartConnect(api_key=api_key)
|
|
77
78
|
response = self.invoke_angelone_api(
|
|
78
79
|
self.wrapper.generateSession, # type: ignore
|
|
@@ -80,10 +81,12 @@ class AngelOne(Broker):
|
|
|
80
81
|
password=mpin,
|
|
81
82
|
totp=pyotp.TOTP(totp).now(),
|
|
82
83
|
)
|
|
84
|
+
|
|
83
85
|
if response["status"] is False:
|
|
84
86
|
if "message" in response:
|
|
85
87
|
raise InvalidArgumentException(response["message"])
|
|
86
88
|
raise InvalidArgumentException("Invalid API credentials")
|
|
89
|
+
|
|
87
90
|
token_data = self.invoke_angelone_api(
|
|
88
91
|
self.wrapper.generateToken, # type: ignore
|
|
89
92
|
refresh_token=self.wrapper.refresh_token, # type: ignore
|
{quantplay-2.0.41/quantplay/broker/generics → quantplay-2.0.43/quantplay/broker}/broker_factory.py
RENAMED
|
@@ -5,7 +5,6 @@ from typing import Any, Dict
|
|
|
5
5
|
|
|
6
6
|
from quantplay.broker.aliceblue import Aliceblue
|
|
7
7
|
from quantplay.broker.angelone import AngelOne
|
|
8
|
-
|
|
9
8
|
from quantplay.broker.five_paisa import FivePaisa
|
|
10
9
|
from quantplay.broker.flattrade import FlatTrade
|
|
11
10
|
from quantplay.broker.iifl_xts import IIFL as IIFL_XTS
|
|
@@ -14,9 +13,8 @@ from quantplay.broker.shoonya import FinvAsia
|
|
|
14
13
|
from quantplay.broker.upstox import Upstox
|
|
15
14
|
from quantplay.broker.zerodha import Zerodha
|
|
16
15
|
from quantplay.exception.exceptions import InvalidArgumentException
|
|
17
|
-
|
|
18
|
-
from quantplay.utils.pickle_utils import PickleUtils
|
|
19
16
|
from quantplay.utils.caching import InstrumentCache
|
|
17
|
+
from quantplay.utils.pickle_utils import PickleUtils
|
|
20
18
|
|
|
21
19
|
BrokerType = (
|
|
22
20
|
Aliceblue
|
|
@@ -58,62 +56,47 @@ class BrokerFactory:
|
|
|
58
56
|
Broker.UPSTOX: "upstox_instruments",
|
|
59
57
|
Broker.FIVEPAISA_OPENAPI: "5paisa_instruments",
|
|
60
58
|
}
|
|
59
|
+
broker_required_args = {
|
|
60
|
+
Broker.ZERODHA: set(["user_id", "zerodha_wrapper"]),
|
|
61
|
+
Broker.FINVASIA: set(["user_id", "user_token"]),
|
|
62
|
+
Broker.FLATTRADE: set(["user_id", "user_token"]),
|
|
63
|
+
Broker.IIFL_XTS: set(["user_id", "wrapper", "md_wrapper"]),
|
|
64
|
+
Broker.MOTILAL: set(["user_id", "headers"]),
|
|
65
|
+
Broker.ALICEBLUE: set(["user_id", "client"]),
|
|
66
|
+
Broker.UPSTOX: set(["user_id", "access_token"]),
|
|
67
|
+
Broker.FIVEPAISA_OPENAPI: set(["user_id", "client"]),
|
|
68
|
+
Broker.ANGELONE: set(
|
|
69
|
+
[
|
|
70
|
+
"user_id",
|
|
71
|
+
"api_key",
|
|
72
|
+
"access_token",
|
|
73
|
+
"refresh_token",
|
|
74
|
+
"feed_token",
|
|
75
|
+
]
|
|
76
|
+
),
|
|
77
|
+
}
|
|
61
78
|
|
|
62
79
|
def __init__(self):
|
|
63
80
|
self.client_broker_data: Dict[str, BrokerType] = {}
|
|
64
81
|
|
|
65
82
|
def get_broker_key(self, username: str, broker_name: str) -> str:
|
|
66
|
-
return "{}:{}"
|
|
67
|
-
|
|
68
|
-
def set_broker_instruments(self, broker_name: str, broker: BrokerType) -> None:
|
|
69
|
-
symbol_data_key = f"{broker_name}_instruments"
|
|
70
|
-
quantplay_symbol_key = f"{broker_name}_qplay_symbols"
|
|
71
|
-
broker_symbol_key = f"{broker_name}_broker_symbols"
|
|
83
|
+
return f"{username}:{broker_name}"
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if symbol_data is not None:
|
|
78
|
-
broker.symbol_data = symbol_data
|
|
79
|
-
|
|
80
|
-
if broker_name != "Zerodha":
|
|
81
|
-
if quantplay_symbol_map is not None and broker_symbol_map is not None:
|
|
82
|
-
broker.quantplay_symbol_map = quantplay_symbol_map
|
|
83
|
-
broker.broker_symbol_map = broker_symbol_map
|
|
84
|
-
|
|
85
|
-
else:
|
|
86
|
-
broker.initialize_broker_symbol_map()
|
|
87
|
-
instrument_cache.set(
|
|
88
|
-
quantplay_symbol_key, broker.quantplay_symbol_map
|
|
89
|
-
)
|
|
90
|
-
instrument_cache.set(broker_symbol_key, broker.broker_symbol_map)
|
|
85
|
+
def validate_broker_args(self, broker_info: Dict[str, Any]):
|
|
86
|
+
broker = broker_info["broker"]
|
|
87
|
+
broker_data = broker_info["broker_data"]
|
|
91
88
|
|
|
92
|
-
|
|
89
|
+
if broker not in self.broker_required_args.keys():
|
|
90
|
+
raise InvalidArgumentException(f"Unsupported Broker: '{broker}'")
|
|
93
91
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
if not self.broker_required_args[broker].issubset(broker_data.keys()):
|
|
93
|
+
raise InvalidArgumentException(
|
|
94
|
+
f"Missing Arguments for {broker_info['username']}:{broker_info['nickname']} in broker '{broker}' -> {self.broker_required_args[broker].difference(broker_info.keys())}"
|
|
97
95
|
)
|
|
98
|
-
broker.symbol_data = symbol_data
|
|
99
|
-
|
|
100
|
-
if broker_name != "Zerodha":
|
|
101
|
-
broker.initialize_broker_symbol_map()
|
|
102
|
-
instrument_cache.set(quantplay_symbol_key, broker.quantplay_symbol_map)
|
|
103
|
-
|
|
104
|
-
instrument_cache.set(symbol_data_key, symbol_data)
|
|
105
|
-
|
|
106
|
-
except Exception:
|
|
107
|
-
traceback.print_exc()
|
|
108
|
-
|
|
109
|
-
if broker_name != "Zerodha":
|
|
110
|
-
broker.load_instrument(BrokerFactory.broker_instruments_map[broker_name])
|
|
111
|
-
else:
|
|
112
|
-
broker.initialize_symbol_data()
|
|
113
96
|
|
|
114
97
|
def store_broker_client(
|
|
115
98
|
self, broker_info: Dict[str, Any], load_instrument: bool = True
|
|
116
|
-
) -> None:
|
|
99
|
+
) -> BrokerType | None:
|
|
117
100
|
username = broker_info["username"]
|
|
118
101
|
nickname = broker_info["nickname"]
|
|
119
102
|
|
|
@@ -125,9 +108,9 @@ class BrokerFactory:
|
|
|
125
108
|
broker_client: BrokerType | None = None
|
|
126
109
|
|
|
127
110
|
if broker == "Motilal":
|
|
128
|
-
motial_headers = broker_data["headers"]
|
|
129
111
|
broker_client = Motilal(
|
|
130
|
-
headers=
|
|
112
|
+
headers=broker_data["headers"],
|
|
113
|
+
load_instrument=load_instrument,
|
|
131
114
|
)
|
|
132
115
|
|
|
133
116
|
elif broker == "Zerodha":
|
|
@@ -147,31 +130,29 @@ class BrokerFactory:
|
|
|
147
130
|
)
|
|
148
131
|
|
|
149
132
|
elif broker == Broker.ALICEBLUE:
|
|
150
|
-
broker_client = Aliceblue(
|
|
133
|
+
broker_client = Aliceblue(
|
|
134
|
+
client=broker_data["client"],
|
|
135
|
+
load_instrument=load_instrument,
|
|
136
|
+
)
|
|
151
137
|
|
|
152
138
|
elif broker == Broker.UPSTOX:
|
|
153
139
|
broker_client = Upstox(
|
|
154
140
|
access_token=broker_data["access_token"],
|
|
155
141
|
user_id=broker_data["user_id"],
|
|
142
|
+
load_instrument=load_instrument,
|
|
156
143
|
)
|
|
157
144
|
|
|
158
145
|
elif broker == Broker.FINVASIA:
|
|
159
|
-
finvasia_data = broker_data
|
|
160
146
|
broker_client = FinvAsia(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
password=finvasia_data["password"],
|
|
164
|
-
user_token=finvasia_data["user_token"],
|
|
147
|
+
user_id=broker_data["user_id"],
|
|
148
|
+
user_token=broker_data["user_token"],
|
|
165
149
|
load_instrument=load_instrument,
|
|
166
150
|
)
|
|
167
151
|
|
|
168
152
|
elif broker == Broker.FLATTRADE:
|
|
169
|
-
flattrade_data = broker_data
|
|
170
153
|
broker_client = FlatTrade(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
password=flattrade_data["password"],
|
|
174
|
-
user_token=flattrade_data["user_token"],
|
|
154
|
+
user_id=broker_data["user_id"],
|
|
155
|
+
user_token=broker_data["user_token"],
|
|
175
156
|
load_instrument=load_instrument,
|
|
176
157
|
)
|
|
177
158
|
|
|
@@ -181,22 +162,30 @@ class BrokerFactory:
|
|
|
181
162
|
load_instrument=load_instrument,
|
|
182
163
|
)
|
|
183
164
|
|
|
184
|
-
self.client_broker_data[broker_key] = broker_client
|
|
185
|
-
|
|
186
165
|
elif broker == Broker.IIFL_XTS:
|
|
187
|
-
iifl_xts_data = broker_data
|
|
188
166
|
broker_client = IIFL_XTS(
|
|
189
167
|
wrapper=broker_data["wrapper"],
|
|
190
168
|
md_wrapper=broker_data["md_wrapper"],
|
|
191
|
-
client_id=
|
|
169
|
+
client_id=broker_data["user_id"],
|
|
192
170
|
load_instrument=load_instrument,
|
|
193
171
|
)
|
|
194
172
|
|
|
195
173
|
else:
|
|
196
|
-
raise InvalidArgumentException(f"Broker {broker} not supported")
|
|
174
|
+
raise InvalidArgumentException(f"Broker '{broker}' not supported")
|
|
175
|
+
|
|
176
|
+
if not load_instrument:
|
|
177
|
+
broker_client = self.set_broker_instruments(
|
|
178
|
+
broker_name=broker, broker=broker_client
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
broker_client.username = broker_info["username"]
|
|
182
|
+
broker_client.nickname = broker_info["nickname"]
|
|
183
|
+
broker_client.broker_name = broker_info["broker"]
|
|
184
|
+
broker_client.user_id = broker_data["user_id"]
|
|
197
185
|
|
|
198
186
|
self.client_broker_data[broker_key] = broker_client
|
|
199
|
-
|
|
187
|
+
|
|
188
|
+
return broker_client
|
|
200
189
|
|
|
201
190
|
def get_broker_client(self, broker_info: Dict[str, Any]) -> BrokerType:
|
|
202
191
|
username = broker_info["username"]
|
|
@@ -207,13 +196,62 @@ class BrokerFactory:
|
|
|
207
196
|
if broker_key in self.client_broker_data:
|
|
208
197
|
return self.client_broker_data[broker_key]
|
|
209
198
|
|
|
210
|
-
self.
|
|
199
|
+
self.validate_broker_args(broker_info)
|
|
200
|
+
broker_client = self.store_broker_client(broker_info, load_instrument=False)
|
|
211
201
|
|
|
212
|
-
if
|
|
213
|
-
return
|
|
202
|
+
if broker_client is not None:
|
|
203
|
+
return broker_client
|
|
214
204
|
else:
|
|
215
205
|
raise InvalidArgumentException("Invalid broker API configuration")
|
|
216
206
|
|
|
207
|
+
def set_broker_instruments(self, broker_name: str, broker: BrokerType) -> BrokerType:
|
|
208
|
+
symbol_data_key = f"{broker_name}_instruments"
|
|
209
|
+
quantplay_symbol_key = f"{broker_name}_qplay_symbols"
|
|
210
|
+
broker_symbol_key = f"{broker_name}_broker_symbols"
|
|
211
|
+
|
|
212
|
+
symbol_data = instrument_cache.get(symbol_data_key)
|
|
213
|
+
quantplay_symbol_map = instrument_cache.get(quantplay_symbol_key)
|
|
214
|
+
broker_symbol_map = instrument_cache.get(broker_symbol_key)
|
|
215
|
+
|
|
216
|
+
if symbol_data is not None:
|
|
217
|
+
broker.symbol_data = symbol_data
|
|
218
|
+
|
|
219
|
+
if broker_name != "Zerodha":
|
|
220
|
+
if quantplay_symbol_map is not None and broker_symbol_map is not None:
|
|
221
|
+
broker.quantplay_symbol_map = quantplay_symbol_map
|
|
222
|
+
broker.broker_symbol_map = broker_symbol_map
|
|
223
|
+
|
|
224
|
+
else:
|
|
225
|
+
broker.initialize_broker_symbol_map()
|
|
226
|
+
instrument_cache.set(
|
|
227
|
+
quantplay_symbol_key, broker.quantplay_symbol_map
|
|
228
|
+
)
|
|
229
|
+
instrument_cache.set(broker_symbol_key, broker.broker_symbol_map)
|
|
230
|
+
|
|
231
|
+
return broker
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
symbol_data = PickleUtils.load_data(
|
|
235
|
+
BrokerFactory.broker_instruments_map[broker_name]
|
|
236
|
+
)
|
|
237
|
+
broker.symbol_data = symbol_data
|
|
238
|
+
|
|
239
|
+
if broker_name != "Zerodha":
|
|
240
|
+
broker.initialize_broker_symbol_map()
|
|
241
|
+
instrument_cache.set(quantplay_symbol_key, broker.quantplay_symbol_map)
|
|
242
|
+
|
|
243
|
+
instrument_cache.set(symbol_data_key, symbol_data)
|
|
244
|
+
|
|
245
|
+
except Exception:
|
|
246
|
+
traceback.print_exc()
|
|
247
|
+
|
|
248
|
+
if broker_name != "Zerodha":
|
|
249
|
+
broker.load_instrument(BrokerFactory.broker_instruments_map[broker_name])
|
|
250
|
+
else:
|
|
251
|
+
broker.initialize_symbol_data()
|
|
252
|
+
|
|
253
|
+
return broker
|
|
254
|
+
|
|
217
255
|
def clear_instrument_cache(self, broker: str) -> None:
|
|
218
256
|
symbol_data_key = f"{broker}_instruments"
|
|
219
257
|
instrument_cache.delete(symbol_data_key)
|
|
@@ -4,8 +4,7 @@ import traceback
|
|
|
4
4
|
from typing import Any, Dict, Hashable, List
|
|
5
5
|
|
|
6
6
|
import polars as pl
|
|
7
|
-
from
|
|
8
|
-
from kiteconnect.exceptions import TokenException # type: ignore
|
|
7
|
+
from dhanhq import dhanhq # type:ignore
|
|
9
8
|
from retrying import retry # type: ignore
|
|
10
9
|
|
|
11
10
|
from quantplay.broker.generics.broker import Broker
|
|
@@ -15,7 +14,6 @@ from quantplay.exception.exceptions import (
|
|
|
15
14
|
RetryableException,
|
|
16
15
|
retry_exception,
|
|
17
16
|
)
|
|
18
|
-
from quantplay.exception.exceptions import TokenException as QuantplayTokenException
|
|
19
17
|
from quantplay.model.broker import (
|
|
20
18
|
ExchangeType,
|
|
21
19
|
)
|
|
@@ -27,11 +25,11 @@ from quantplay.model.generics import (
|
|
|
27
25
|
)
|
|
28
26
|
from quantplay.utils.constant import Constants, OrderType
|
|
29
27
|
from quantplay.utils.pickle_utils import InstrumentData, PickleUtils
|
|
30
|
-
from dhanhq import dhanhq # type:ignore
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class Dhan(Broker):
|
|
34
31
|
def __init__(self):
|
|
32
|
+
super().__init__()
|
|
35
33
|
self.dhan = dhanhq(
|
|
36
34
|
client_id="1102866282",
|
|
37
35
|
access_token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJkaGFuIiwicGFydG5lcklkIjoiIiwiZXhwIjoxNzIzODgxNjgxLCJ0b2tlbkNvbnN1bWVyVHlwZSI6IlNFTEYiLCJ3ZWJob29rVXJsIjoiIiwiZGhhbkNsaWVudElkIjoiMTEwMjg2NjI4MiJ9.0dNR4gOdIQ3KeaAokEwbRt6v_6ESn73r6yOL9-7lzphVCAmP-pahgK6OIOxVUExBJa0SaxX0TNX7Vk0RBQ-lxQ",
|
|
@@ -41,8 +39,6 @@ class Dhan(Broker):
|
|
|
41
39
|
access_token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJkaGFuIiwicGFydG5lcklkIjoiIiwiZXhwIjoxNzIxMzc3ODM5LCJ0b2tlbkNvbnN1bWVyVHlwZSI6IlNFTEYiLCJ3ZWJob29rVXJsIjoiIiwiZGhhbkNsaWVudElkIjoiMTEwMDc5OTExNiJ9.ykd4NYsnl7s1UegkUUY9tbP4OxxjqtskGi8JK0lyC9bRRPQRyxDv022JyDIl6crdFsQXfG7-6BsipPYxOtLR8g",
|
|
42
40
|
)
|
|
43
41
|
|
|
44
|
-
super(Dhan, self).__init__()
|
|
45
|
-
|
|
46
42
|
def set_wrapper(self, serialized_wrapper: str):
|
|
47
43
|
self.wrapper = pickle.loads(codecs.decode(serialized_wrapper.encode(), "base64"))
|
|
48
44
|
|
|
@@ -69,33 +65,6 @@ class Dhan(Broker):
|
|
|
69
65
|
def get_username(self):
|
|
70
66
|
return self.username
|
|
71
67
|
|
|
72
|
-
def on_ticks(self, kws: KiteTicker, ticks: Any):
|
|
73
|
-
"""Callback on live ticks"""
|
|
74
|
-
# logger.info("[TEST_TICK] {}".format(ticks))
|
|
75
|
-
pass
|
|
76
|
-
|
|
77
|
-
def on_order_update(self, kws: KiteTicker, data: Any):
|
|
78
|
-
"""Callback on order update"""
|
|
79
|
-
Constants.logger.info(f"[UPDATE_RECEIVED] {data}")
|
|
80
|
-
|
|
81
|
-
if self.order_updates is None:
|
|
82
|
-
raise Exception("Event Queue Not Initalised")
|
|
83
|
-
|
|
84
|
-
self.order_updates.put(data)
|
|
85
|
-
|
|
86
|
-
def on_connect(self, kws: KiteTicker, response: Any):
|
|
87
|
-
"""Callback on successfull connect"""
|
|
88
|
-
kws.subscribe([256265]) # type: ignore
|
|
89
|
-
kws.set_mode(kws.MODE_FULL, [256265]) # type: ignore
|
|
90
|
-
|
|
91
|
-
def stream_order_data(self):
|
|
92
|
-
kite_ticker = KiteTicker(self.wrapper.api_key, self.wrapper.access_token)
|
|
93
|
-
kite_ticker.on_order_update = self.on_order_update # type:ignore
|
|
94
|
-
kite_ticker.on_ticks = self.on_ticks # type:ignore
|
|
95
|
-
kite_ticker.on_connect = self.on_connect # type:ignore
|
|
96
|
-
|
|
97
|
-
kite_ticker.connect(threaded=True) # type: ignore
|
|
98
|
-
|
|
99
68
|
@retry(
|
|
100
69
|
wait_exponential_multiplier=3000,
|
|
101
70
|
wait_exponential_max=10000,
|
|
@@ -135,8 +104,6 @@ class Dhan(Broker):
|
|
|
135
104
|
)
|
|
136
105
|
|
|
137
106
|
return api_response[key]["last_price"]
|
|
138
|
-
except TokenException:
|
|
139
|
-
raise QuantplayTokenException("Zerodha token expired")
|
|
140
107
|
except Exception as e:
|
|
141
108
|
exception_message = "GetLtp call failed for [{}] with error [{}]".format(
|
|
142
109
|
tradingsymbol, str(e)
|
|
@@ -8,11 +8,12 @@ import urllib.parse
|
|
|
8
8
|
from datetime import datetime as dt
|
|
9
9
|
from time import sleep
|
|
10
10
|
from typing import Any, Callable, Dict, List, Literal, TypedDict
|
|
11
|
-
|
|
12
|
-
from quantplay.model.generics import ExchangeType
|
|
11
|
+
|
|
13
12
|
import requests
|
|
14
13
|
import websocket
|
|
15
14
|
|
|
15
|
+
from quantplay.model.generics import ExchangeType, NorenTypes
|
|
16
|
+
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
@@ -49,6 +49,7 @@ class FivePaisa(Broker):
|
|
|
49
49
|
client: str | None = None,
|
|
50
50
|
load_instrument: bool = True,
|
|
51
51
|
):
|
|
52
|
+
super().__init__()
|
|
52
53
|
self.broker_name = "FivePaisa_OpenAPI"
|
|
53
54
|
try:
|
|
54
55
|
if client:
|
|
@@ -96,7 +97,6 @@ class FivePaisa(Broker):
|
|
|
96
97
|
|
|
97
98
|
if load_instrument:
|
|
98
99
|
self.load_instrument()
|
|
99
|
-
super(FivePaisa, self).__init__()
|
|
100
100
|
|
|
101
101
|
def get_exchange(self, exchange: ExchangeType) -> Any:
|
|
102
102
|
return exchange
|
|
@@ -170,16 +170,14 @@ class FivePaisa(Broker):
|
|
|
170
170
|
def add_exchange(self, data: pl.DataFrame):
|
|
171
171
|
return data.with_columns(
|
|
172
172
|
pl.lit(None).alias("exchange"),
|
|
173
|
+
).with_columns(
|
|
173
174
|
pl.when((pl.col("Exch").eq("N")) & (pl.col("ExchType").eq("D")))
|
|
174
175
|
.then(pl.lit("NFO"))
|
|
175
|
-
.
|
|
176
|
-
pl.when((pl.col("Exch").eq("N")) & (pl.col("ExchType").eq("C")))
|
|
176
|
+
.when((pl.col("Exch").eq("N")) & (pl.col("ExchType").eq("C")))
|
|
177
177
|
.then(pl.lit("NFO"))
|
|
178
|
-
.
|
|
179
|
-
pl.when((pl.col("Exch").eq("B")) & (pl.col("ExchType").eq("D")))
|
|
178
|
+
.when((pl.col("Exch").eq("B")) & (pl.col("ExchType").eq("D")))
|
|
180
179
|
.then(pl.lit("NFO"))
|
|
181
|
-
.
|
|
182
|
-
pl.when((pl.col("Exch").eq("B")) & (pl.col("ExchType").eq("C")))
|
|
180
|
+
.when((pl.col("Exch").eq("B")) & (pl.col("ExchType").eq("C")))
|
|
183
181
|
.then(pl.lit("NFO"))
|
|
184
182
|
.alias("exchange"),
|
|
185
183
|
)
|
|
@@ -280,8 +278,7 @@ class FivePaisa(Broker):
|
|
|
280
278
|
).alias("pnl"),
|
|
281
279
|
)
|
|
282
280
|
|
|
283
|
-
self.add_exchange(positions)
|
|
284
|
-
|
|
281
|
+
positions = self.add_exchange(positions)
|
|
285
282
|
positions = positions.with_columns(
|
|
286
283
|
pl.when(pl.col("exchange").is_in(["NFO", "BFO"]))
|
|
287
284
|
.then(pl.col("tradingsymbol").str.split(" ").list.get(-2))
|
|
@@ -322,8 +319,7 @@ class FivePaisa(Broker):
|
|
|
322
319
|
return pl.DataFrame(schema=self.orders_schema)
|
|
323
320
|
|
|
324
321
|
orders = pl.from_dicts(orders)
|
|
325
|
-
|
|
326
|
-
self.add_exchange(orders)
|
|
322
|
+
orders = self.add_exchange(orders)
|
|
327
323
|
|
|
328
324
|
orders = orders.rename(
|
|
329
325
|
{
|
|
@@ -386,12 +382,12 @@ class FivePaisa(Broker):
|
|
|
386
382
|
)
|
|
387
383
|
.with_columns(
|
|
388
384
|
pl.when(pl.col("status").str.to_lowercase().str.contains("rejected"))
|
|
389
|
-
.then(OrderStatus.rejected)
|
|
385
|
+
.then(pl.lit(OrderStatus.rejected))
|
|
390
386
|
.otherwise(pl.col("status"))
|
|
391
387
|
.alias("status"),
|
|
392
388
|
pl.when(
|
|
393
|
-
pl.col("product")
|
|
394
|
-
|
|
389
|
+
(pl.col("product").eq("CNC"))
|
|
390
|
+
& (pl.col("exchange").is_in(["NFO", "BFO"]))
|
|
395
391
|
)
|
|
396
392
|
.then(pl.lit("NRML"))
|
|
397
393
|
.otherwise(pl.col("product"))
|
|
@@ -399,7 +395,7 @@ class FivePaisa(Broker):
|
|
|
399
395
|
)
|
|
400
396
|
.with_columns(
|
|
401
397
|
pl.when((pl.col("status") == "OPEN") & (pl.col("order_type") == "SL"))
|
|
402
|
-
.then(OrderStatus.trigger_pending)
|
|
398
|
+
.then(pl.lit(OrderStatus.trigger_pending))
|
|
403
399
|
.otherwise(pl.col("status"))
|
|
404
400
|
.alias("status")
|
|
405
401
|
)
|
|
@@ -81,9 +81,9 @@ class FlatTrade(Noren):
|
|
|
81
81
|
"request_code": reqCode,
|
|
82
82
|
"api_secret": hashlib.sha256(secret_code.encode()).hexdigest(),
|
|
83
83
|
}
|
|
84
|
-
|
|
84
|
+
url = "https://authapi.flattrade.in/trade/apitoken"
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
token =
|
|
86
|
+
res = requests.post(url, json=payload)
|
|
87
|
+
token = res.json()["token"]
|
|
88
88
|
|
|
89
89
|
return token
|
|
@@ -8,11 +8,12 @@ import urllib.parse
|
|
|
8
8
|
from datetime import datetime as dt
|
|
9
9
|
from time import sleep
|
|
10
10
|
from typing import Any, Callable, Dict, List, Literal, TypedDict
|
|
11
|
-
|
|
12
|
-
from quantplay.model.generics import ExchangeType
|
|
11
|
+
|
|
13
12
|
import requests
|
|
14
13
|
import websocket
|
|
15
14
|
|
|
15
|
+
from quantplay.model.generics import ExchangeType, NorenTypes
|
|
16
|
+
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|