quantplay 2.0.53__tar.gz → 2.0.55__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.53 → quantplay-2.0.55}/PKG-INFO +1 -1
- quantplay-2.0.55/quantplay/broker/broker_factory.py +478 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay.egg-info/PKG-INFO +1 -1
- {quantplay-2.0.53 → quantplay-2.0.55}/setup.py +1 -1
- quantplay-2.0.53/quantplay/broker/broker_factory.py +0 -282
- {quantplay-2.0.53 → quantplay-2.0.55}/README.md +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/pyproject.toml +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/aliceblue.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/angelone.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/auto_login/aliceblue.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/dhan.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/five_paisa.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/ft_utils/ft_noren.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/generics/broker.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/kotak.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/motilal.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/noren.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/upstox.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/xts.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/xts_utils/Connect.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/broker/zerodha.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/exception/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/model/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/model/broker.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/model/generics.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/model/instrument_data.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/model/order_event.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/py.typed +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/caching.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/constant.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/exchange.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/pickle_utils.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/utils/selenium_utils.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/wrapper/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay/wrapper/aws/s3.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay.egg-info/SOURCES.txt +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay.egg-info/requires.txt +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/setup.cfg +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/tests/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/tests/conftest.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/tests/wrapper/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.53 → quantplay-2.0.55}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import codecs
|
|
2
|
+
import os
|
|
3
|
+
import pickle
|
|
4
|
+
import traceback
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
|
|
8
|
+
from quantplay.broker.aliceblue import Aliceblue
|
|
9
|
+
from quantplay.broker.angelone import AngelOne
|
|
10
|
+
from quantplay.broker.dhan import Dhan
|
|
11
|
+
from quantplay.broker.five_paisa import FivePaisa
|
|
12
|
+
from quantplay.broker.flattrade import FlatTrade
|
|
13
|
+
from quantplay.broker.iifl_xts import IIFL as IIFL_XTS
|
|
14
|
+
from quantplay.broker.kotak import Kotak
|
|
15
|
+
from quantplay.broker.motilal import Motilal
|
|
16
|
+
from quantplay.broker.shoonya import FinvAsia
|
|
17
|
+
from quantplay.broker.upstox import Upstox
|
|
18
|
+
from quantplay.broker.zerodha import Zerodha
|
|
19
|
+
from quantplay.exception.exceptions import InvalidArgumentException
|
|
20
|
+
from quantplay.utils.caching import InstrumentCache
|
|
21
|
+
from quantplay.utils.pickle_utils import PickleUtils
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
BrokerType = (
|
|
25
|
+
Aliceblue
|
|
26
|
+
| AngelOne
|
|
27
|
+
| FlatTrade
|
|
28
|
+
| Motilal
|
|
29
|
+
| FinvAsia
|
|
30
|
+
| Upstox
|
|
31
|
+
| Zerodha
|
|
32
|
+
| IIFL_XTS
|
|
33
|
+
| FivePaisa
|
|
34
|
+
| Kotak
|
|
35
|
+
| Dhan
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
instrument_cache = InstrumentCache()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Broker:
|
|
43
|
+
ZERODHA = "Zerodha"
|
|
44
|
+
UPSTOX = "Upstox"
|
|
45
|
+
ALICEBLUE = "Aliceblue"
|
|
46
|
+
FIVEPAISA_OPENAPI = "5Paisa_OpenAPI"
|
|
47
|
+
FINVASIA = "Finvasia"
|
|
48
|
+
FLATTRADE = "Flattrade"
|
|
49
|
+
IIFL_XTS = "IIFL_XTS"
|
|
50
|
+
MOTILAL = "Motilal"
|
|
51
|
+
ANGELONE = "Angelone"
|
|
52
|
+
KOTAK = "Kotak"
|
|
53
|
+
DHAN = "Dhan"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
broker_instruments_map = {
|
|
57
|
+
Broker.ZERODHA: "zerodha_instruments",
|
|
58
|
+
Broker.FINVASIA: "shoonya_instruments",
|
|
59
|
+
Broker.FLATTRADE: "shoonya_instruments",
|
|
60
|
+
Broker.IIFL_XTS: "xts_instruments",
|
|
61
|
+
Broker.MOTILAL: "motilal_instruments",
|
|
62
|
+
Broker.ANGELONE: "angelone_instruments",
|
|
63
|
+
Broker.ALICEBLUE: "aliceblue_instruments",
|
|
64
|
+
Broker.UPSTOX: "upstox_instruments",
|
|
65
|
+
Broker.FIVEPAISA_OPENAPI: "5paisa_instruments",
|
|
66
|
+
Broker.KOTAK: "upstox_instruments",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
broker_required_args = {
|
|
70
|
+
Broker.ZERODHA: set(["user_id", "zerodha_wrapper"]),
|
|
71
|
+
Broker.FINVASIA: set(["user_id", "user_token"]),
|
|
72
|
+
Broker.FLATTRADE: set(["user_id", "user_token"]),
|
|
73
|
+
Broker.IIFL_XTS: set(["user_id", "wrapper", "md_wrapper"]),
|
|
74
|
+
Broker.MOTILAL: set(["user_id", "headers"]),
|
|
75
|
+
Broker.ALICEBLUE: set(["user_id", "client"]),
|
|
76
|
+
Broker.UPSTOX: set(["user_id", "access_token"]),
|
|
77
|
+
Broker.DHAN: set(["user_id", "access_token"]),
|
|
78
|
+
Broker.FIVEPAISA_OPENAPI: set(["user_id", "client"]),
|
|
79
|
+
Broker.KOTAK: set(["user_id", "configuration"]),
|
|
80
|
+
Broker.ANGELONE: set(
|
|
81
|
+
[
|
|
82
|
+
"user_id",
|
|
83
|
+
"api_key",
|
|
84
|
+
"access_token",
|
|
85
|
+
"refresh_token",
|
|
86
|
+
"feed_token",
|
|
87
|
+
]
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
broker_generate_args = {
|
|
92
|
+
Broker.ZERODHA: set(["user_id", "api_key", "api_secret", "password", "totp"]),
|
|
93
|
+
Broker.FLATTRADE: set(["user_id" "api_secret", "password", "totp_key", "api_key"]),
|
|
94
|
+
Broker.IIFL_XTS: set(["user_id", "wrapper", "md_wrapper"]),
|
|
95
|
+
Broker.MOTILAL: set(["user_id", "password", "api_key", "two_fa", "totp"]),
|
|
96
|
+
Broker.ALICEBLUE: set(["user_id", "api_key"]),
|
|
97
|
+
Broker.DHAN: set(["user_id", "access_token"]),
|
|
98
|
+
Broker.FIVEPAISA_OPENAPI: set(["user_id", "client"]),
|
|
99
|
+
Broker.ANGELONE: set(["user_id", "api_key", "mpin", "totp"]),
|
|
100
|
+
Broker.FINVASIA: set(
|
|
101
|
+
["api_secret", "imei", "password", "totp_key", "user_id", "vendor_code"]
|
|
102
|
+
),
|
|
103
|
+
Broker.KOTAK: set(
|
|
104
|
+
["consumer_key", "consumer_secret", "mobilenumber", "password", "mpin"]
|
|
105
|
+
),
|
|
106
|
+
Broker.UPSTOX: set(
|
|
107
|
+
[
|
|
108
|
+
"user_id",
|
|
109
|
+
"api_key",
|
|
110
|
+
"api_secret",
|
|
111
|
+
"totp",
|
|
112
|
+
"mobile_number",
|
|
113
|
+
"account_pin",
|
|
114
|
+
"redirect_url",
|
|
115
|
+
]
|
|
116
|
+
),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class BrokerFactory:
|
|
121
|
+
def __init__(self):
|
|
122
|
+
self.client_broker_data: Dict[str, BrokerType] = {}
|
|
123
|
+
|
|
124
|
+
def get_broker_key(self, username: str, broker_name: str) -> str:
|
|
125
|
+
return f"{username}:{broker_name}"
|
|
126
|
+
|
|
127
|
+
def validate_broker_args(
|
|
128
|
+
self, broker_info: Dict[str, Any], is_generator_args: bool = False
|
|
129
|
+
):
|
|
130
|
+
broker = broker_info["broker"]
|
|
131
|
+
broker_data = broker_info["broker_data"]
|
|
132
|
+
|
|
133
|
+
compare_map = broker_generate_args if is_generator_args else broker_required_args
|
|
134
|
+
|
|
135
|
+
if broker not in compare_map.keys():
|
|
136
|
+
raise InvalidArgumentException(f"Unsupported Broker: '{broker}'")
|
|
137
|
+
|
|
138
|
+
if not compare_map[broker].issubset(broker_data.keys()):
|
|
139
|
+
raise InvalidArgumentException(
|
|
140
|
+
f"Missing Arguments for {broker_info['username']}:{broker_info['nickname']} in broker '{broker}' -> {compare_map[broker].difference(broker_info.keys())}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def generate_token(self, broker_info: Dict[str, Any]):
|
|
144
|
+
broker_client: BrokerType | None = None
|
|
145
|
+
|
|
146
|
+
broker_data = broker_info["broker_data"]
|
|
147
|
+
broker = broker_info["broker"]
|
|
148
|
+
|
|
149
|
+
broker_client: BrokerType | None = None
|
|
150
|
+
self.validate_broker_args(broker_info, is_generator_args=True)
|
|
151
|
+
|
|
152
|
+
if broker == Broker.MOTILAL:
|
|
153
|
+
broker_client = Motilal(
|
|
154
|
+
user_id=broker_data["user_id"],
|
|
155
|
+
password=broker_data["password"],
|
|
156
|
+
api_key=broker_data["api_key"],
|
|
157
|
+
two_fa=broker_data["two_fa"],
|
|
158
|
+
totp=broker_data["totp"],
|
|
159
|
+
load_instrument=False,
|
|
160
|
+
)
|
|
161
|
+
broker_data["headers"] = broker_client.headers
|
|
162
|
+
|
|
163
|
+
elif broker == Broker.DHAN:
|
|
164
|
+
broker_client = Dhan(
|
|
165
|
+
user_id=broker_data["user_id"],
|
|
166
|
+
access_token=broker_data["access_token"],
|
|
167
|
+
load_instrument=False,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
elif broker == Broker.KOTAK:
|
|
171
|
+
broker_client = Kotak(
|
|
172
|
+
consumer_key=broker_data["consumer_key"],
|
|
173
|
+
consumer_secret=broker_data["consumer_secret"],
|
|
174
|
+
mobilenumber=broker_data["mobilenumber"],
|
|
175
|
+
password=broker_data["password"],
|
|
176
|
+
mpin=broker_data["mpin"],
|
|
177
|
+
load_instrument=False,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
broker_data["configuration"] = broker_client.configuration
|
|
181
|
+
|
|
182
|
+
elif broker == Broker.ZERODHA:
|
|
183
|
+
broker_client = Zerodha(
|
|
184
|
+
user_id=broker_data["user_id"],
|
|
185
|
+
api_key=broker_data["api_key"],
|
|
186
|
+
api_secret=broker_data["api_secret"],
|
|
187
|
+
password=broker_data["password"],
|
|
188
|
+
totp=broker_data["totp"],
|
|
189
|
+
load_instrument=False,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
broker_data["zerodha_wrapper"] = codecs.encode(
|
|
193
|
+
pickle.dumps(broker_client.wrapper), "base64"
|
|
194
|
+
).decode()
|
|
195
|
+
|
|
196
|
+
elif broker == Broker.ANGELONE:
|
|
197
|
+
broker_client = AngelOne(
|
|
198
|
+
api_key=broker_data["api_key"],
|
|
199
|
+
user_id=broker_data["user_id"],
|
|
200
|
+
mpin=broker_data["mpin"],
|
|
201
|
+
totp=broker_data["totp"],
|
|
202
|
+
load_instrument=False,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
broker_data["refresh_token"] = broker_client.wrapper.refresh_token # type: ignore
|
|
206
|
+
broker_data["access_token"] = broker_client.wrapper.access_token # type: ignore
|
|
207
|
+
broker_data["feed_token"] = broker_client.wrapper.feed_token # type: ignore
|
|
208
|
+
|
|
209
|
+
elif broker == Broker.ALICEBLUE:
|
|
210
|
+
broker_client = Aliceblue(
|
|
211
|
+
user_id=broker_data["user_id"],
|
|
212
|
+
api_key=broker_data["api_key"],
|
|
213
|
+
load_instrument=False,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
broker_data["client"] = codecs.encode(
|
|
217
|
+
pickle.dumps(broker_client.alice), "base64"
|
|
218
|
+
).decode()
|
|
219
|
+
|
|
220
|
+
elif broker == Broker.UPSTOX:
|
|
221
|
+
broker_client = Upstox(
|
|
222
|
+
user_id=broker_data["user_id"],
|
|
223
|
+
api_key=broker_data["api_key"],
|
|
224
|
+
api_secret=broker_data["api_secret"],
|
|
225
|
+
totp=broker_data["totp"],
|
|
226
|
+
mobile_number=broker_data["mobile_number"],
|
|
227
|
+
account_pin=broker_data["account_pin"],
|
|
228
|
+
redirect_url=broker_data["redirect_url"],
|
|
229
|
+
load_instrument=False,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
broker_data["access_token"] = broker_client.configuration.access_token
|
|
233
|
+
|
|
234
|
+
elif broker == Broker.FINVASIA:
|
|
235
|
+
broker_client = FinvAsia(
|
|
236
|
+
api_secret=broker_data["api_secret"],
|
|
237
|
+
imei=broker_data["imei"],
|
|
238
|
+
password=broker_data["password"],
|
|
239
|
+
totp_key=broker_data["totp_key"],
|
|
240
|
+
user_id=broker_data["user_id"],
|
|
241
|
+
vendor_code=broker_data["vendor_code"],
|
|
242
|
+
load_instrument=False,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
broker_client.api.close_websocket()
|
|
246
|
+
broker_data["user_token"] = broker_client.user_token
|
|
247
|
+
|
|
248
|
+
elif broker == Broker.FLATTRADE:
|
|
249
|
+
broker_client = FlatTrade(
|
|
250
|
+
user_id=broker_data["user_id"],
|
|
251
|
+
api_secret=broker_data["api_secret"],
|
|
252
|
+
password=broker_data["password"],
|
|
253
|
+
totp_key=broker_data["totp_key"],
|
|
254
|
+
api_key=broker_data["api_key"],
|
|
255
|
+
load_instrument=False,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
broker_client.api.close_websocket()
|
|
259
|
+
broker_data["user_token"] = broker_client.user_token
|
|
260
|
+
|
|
261
|
+
elif broker == Broker.FIVEPAISA_OPENAPI:
|
|
262
|
+
broker_client = FivePaisa(
|
|
263
|
+
app_source=broker_data["app_source"],
|
|
264
|
+
app_user_id=broker_data["app_user_id"],
|
|
265
|
+
app_password=broker_data["app_password"],
|
|
266
|
+
user_key=broker_data["user_key"],
|
|
267
|
+
encryption_key=broker_data["encryption_key"],
|
|
268
|
+
client_id=broker_data["client_id"],
|
|
269
|
+
totp_key=broker_data["totp_key"],
|
|
270
|
+
pin=broker_data["pin"],
|
|
271
|
+
load_instrument=False,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
broker_data["client"] = broker_client.get_client()
|
|
275
|
+
broker_data["user_id"] = broker_client.user_id
|
|
276
|
+
|
|
277
|
+
elif broker == Broker.IIFL_XTS:
|
|
278
|
+
broker_client = IIFL_XTS(
|
|
279
|
+
api_secret=broker_data["api_secret"],
|
|
280
|
+
api_key=broker_data["api_key"],
|
|
281
|
+
md_api_key=broker_data["md_api_key"],
|
|
282
|
+
md_api_secret=broker_data["md_api_secret"],
|
|
283
|
+
client_id=broker_data["user_id"],
|
|
284
|
+
load_instrument=False,
|
|
285
|
+
)
|
|
286
|
+
broker_data["user_id"] = broker_client.ClientID
|
|
287
|
+
|
|
288
|
+
broker_data["wrapper"] = codecs.encode(
|
|
289
|
+
pickle.dumps(broker_client.wrapper), "base64"
|
|
290
|
+
).decode()
|
|
291
|
+
|
|
292
|
+
broker_data["md_wrapper"] = codecs.encode(
|
|
293
|
+
pickle.dumps(broker_client.md_wrapper), "base64"
|
|
294
|
+
).decode()
|
|
295
|
+
|
|
296
|
+
else:
|
|
297
|
+
raise InvalidArgumentException(f"Broker '{broker}' not supported")
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
broker_client,
|
|
301
|
+
broker_data,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
def store_broker_client(
|
|
305
|
+
self, broker_info: Dict[str, Any], load_instrument: bool = True
|
|
306
|
+
) -> BrokerType | None:
|
|
307
|
+
username = broker_info["username"]
|
|
308
|
+
nickname = broker_info["nickname"]
|
|
309
|
+
|
|
310
|
+
self.validate_broker_args(broker_info)
|
|
311
|
+
|
|
312
|
+
broker_key = self.get_broker_key(username, nickname)
|
|
313
|
+
|
|
314
|
+
broker_data = broker_info["broker_data"]
|
|
315
|
+
broker = broker_info["broker"]
|
|
316
|
+
|
|
317
|
+
broker_client: BrokerType | None = None
|
|
318
|
+
|
|
319
|
+
if broker == Broker.MOTILAL:
|
|
320
|
+
broker_client = Motilal(
|
|
321
|
+
headers=broker_data["headers"],
|
|
322
|
+
load_instrument=load_instrument,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
elif broker == Broker.DHAN:
|
|
326
|
+
broker_client = Dhan(
|
|
327
|
+
user_id=broker_data["user_id"],
|
|
328
|
+
access_token=broker_data["access_token"],
|
|
329
|
+
load_instrument=load_instrument,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
elif broker == Broker.KOTAK:
|
|
333
|
+
broker_client = Kotak(
|
|
334
|
+
configuration=broker_data["configuration"],
|
|
335
|
+
load_instrument=load_instrument,
|
|
336
|
+
)
|
|
337
|
+
elif broker == Broker.ZERODHA:
|
|
338
|
+
broker_client = Zerodha(
|
|
339
|
+
wrapper=broker_data["zerodha_wrapper"],
|
|
340
|
+
load_instrument=load_instrument,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
elif broker == Broker.ANGELONE:
|
|
344
|
+
broker_client = AngelOne(
|
|
345
|
+
user_id=broker_data["user_id"],
|
|
346
|
+
api_key=broker_data["api_key"],
|
|
347
|
+
access_token=broker_data["access_token"],
|
|
348
|
+
refresh_token=broker_data["refresh_token"],
|
|
349
|
+
feed_token=broker_data["feed_token"],
|
|
350
|
+
load_instrument=load_instrument,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
elif broker == Broker.ALICEBLUE:
|
|
354
|
+
broker_client = Aliceblue(
|
|
355
|
+
client=broker_data["client"],
|
|
356
|
+
load_instrument=load_instrument,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
elif broker == Broker.UPSTOX:
|
|
360
|
+
broker_client = Upstox(
|
|
361
|
+
access_token=broker_data["access_token"],
|
|
362
|
+
user_id=broker_data["user_id"],
|
|
363
|
+
load_instrument=load_instrument,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
elif broker == Broker.FINVASIA:
|
|
367
|
+
broker_client = FinvAsia(
|
|
368
|
+
user_id=broker_data["user_id"],
|
|
369
|
+
user_token=broker_data["user_token"],
|
|
370
|
+
load_instrument=load_instrument,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
elif broker == Broker.FLATTRADE:
|
|
374
|
+
broker_client = FlatTrade(
|
|
375
|
+
user_id=broker_data["user_id"],
|
|
376
|
+
user_token=broker_data["user_token"],
|
|
377
|
+
load_instrument=load_instrument,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
elif broker == Broker.FIVEPAISA_OPENAPI:
|
|
381
|
+
broker_client = FivePaisa(
|
|
382
|
+
client=broker_data["client"],
|
|
383
|
+
load_instrument=load_instrument,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
elif broker == Broker.IIFL_XTS:
|
|
387
|
+
broker_client = IIFL_XTS(
|
|
388
|
+
wrapper=broker_data["wrapper"],
|
|
389
|
+
md_wrapper=broker_data["md_wrapper"],
|
|
390
|
+
client_id=broker_data["user_id"],
|
|
391
|
+
load_instrument=load_instrument,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
else:
|
|
395
|
+
raise InvalidArgumentException(f"Broker '{broker}' not supported")
|
|
396
|
+
|
|
397
|
+
if not load_instrument:
|
|
398
|
+
broker_client = self.set_broker_instruments(
|
|
399
|
+
broker_name=broker, broker=broker_client
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
broker_client.username = broker_info["username"]
|
|
403
|
+
broker_client.nickname = broker_info["nickname"]
|
|
404
|
+
broker_client.broker_name = broker_info["broker"]
|
|
405
|
+
broker_client.user_id = broker_data["user_id"]
|
|
406
|
+
|
|
407
|
+
self.client_broker_data[broker_key] = broker_client
|
|
408
|
+
|
|
409
|
+
return broker_client
|
|
410
|
+
|
|
411
|
+
def get_broker_client(self, broker_info: Dict[str, Any]) -> BrokerType:
|
|
412
|
+
username = broker_info["username"]
|
|
413
|
+
nickname = broker_info["nickname"]
|
|
414
|
+
|
|
415
|
+
broker_key = self.get_broker_key(username, nickname)
|
|
416
|
+
|
|
417
|
+
if broker_key in self.client_broker_data:
|
|
418
|
+
return self.client_broker_data[broker_key]
|
|
419
|
+
|
|
420
|
+
broker_client = self.store_broker_client(broker_info, load_instrument=False)
|
|
421
|
+
|
|
422
|
+
if broker_client is not None:
|
|
423
|
+
return broker_client
|
|
424
|
+
else:
|
|
425
|
+
raise InvalidArgumentException("Invalid broker API configuration")
|
|
426
|
+
|
|
427
|
+
def set_broker_instruments(self, broker_name: str, broker: BrokerType) -> BrokerType:
|
|
428
|
+
symbol_data_key = f"{broker_name}_instruments"
|
|
429
|
+
quantplay_symbol_key = f"{broker_name}_qplay_symbols"
|
|
430
|
+
broker_symbol_key = f"{broker_name}_broker_symbols"
|
|
431
|
+
|
|
432
|
+
symbol_data = instrument_cache.get(symbol_data_key)
|
|
433
|
+
quantplay_symbol_map = instrument_cache.get(quantplay_symbol_key)
|
|
434
|
+
broker_symbol_map = instrument_cache.get(broker_symbol_key)
|
|
435
|
+
|
|
436
|
+
if symbol_data is not None:
|
|
437
|
+
broker.symbol_data = symbol_data
|
|
438
|
+
|
|
439
|
+
if broker_name != "Zerodha":
|
|
440
|
+
if quantplay_symbol_map is not None and broker_symbol_map is not None:
|
|
441
|
+
broker.quantplay_symbol_map = quantplay_symbol_map
|
|
442
|
+
broker.broker_symbol_map = broker_symbol_map
|
|
443
|
+
|
|
444
|
+
else:
|
|
445
|
+
broker.initialize_broker_symbol_map()
|
|
446
|
+
instrument_cache.set(
|
|
447
|
+
quantplay_symbol_key, broker.quantplay_symbol_map
|
|
448
|
+
)
|
|
449
|
+
instrument_cache.set(broker_symbol_key, broker.broker_symbol_map)
|
|
450
|
+
|
|
451
|
+
return broker
|
|
452
|
+
|
|
453
|
+
try:
|
|
454
|
+
symbol_data = PickleUtils.load_data(broker_instruments_map[broker_name])
|
|
455
|
+
broker.symbol_data = symbol_data
|
|
456
|
+
|
|
457
|
+
if broker_name != "Zerodha":
|
|
458
|
+
broker.initialize_broker_symbol_map()
|
|
459
|
+
instrument_cache.set(quantplay_symbol_key, broker.quantplay_symbol_map)
|
|
460
|
+
|
|
461
|
+
instrument_cache.set(symbol_data_key, symbol_data)
|
|
462
|
+
|
|
463
|
+
except Exception:
|
|
464
|
+
traceback.print_exc()
|
|
465
|
+
|
|
466
|
+
if broker_name != "Zerodha":
|
|
467
|
+
broker.load_instrument(broker_instruments_map[broker_name])
|
|
468
|
+
else:
|
|
469
|
+
broker.initialize_symbol_data()
|
|
470
|
+
|
|
471
|
+
return broker
|
|
472
|
+
|
|
473
|
+
def clear_instrument_cache(self, broker: str) -> None:
|
|
474
|
+
symbol_data_key = f"{broker}_instruments"
|
|
475
|
+
instrument_cache.delete(symbol_data_key)
|
|
476
|
+
|
|
477
|
+
file_name = broker_instruments_map[broker]
|
|
478
|
+
os.system(f"rm /tmp/{file_name}*")
|
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import traceback
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, Dict
|
|
5
|
-
|
|
6
|
-
from quantplay.broker.aliceblue import Aliceblue
|
|
7
|
-
from quantplay.broker.angelone import AngelOne
|
|
8
|
-
from quantplay.broker.dhan import Dhan
|
|
9
|
-
from quantplay.broker.five_paisa import FivePaisa
|
|
10
|
-
from quantplay.broker.flattrade import FlatTrade
|
|
11
|
-
from quantplay.broker.iifl_xts import IIFL as IIFL_XTS
|
|
12
|
-
from quantplay.broker.kotak import Kotak
|
|
13
|
-
from quantplay.broker.motilal import Motilal
|
|
14
|
-
from quantplay.broker.shoonya import FinvAsia
|
|
15
|
-
from quantplay.broker.upstox import Upstox
|
|
16
|
-
from quantplay.broker.zerodha import Zerodha
|
|
17
|
-
from quantplay.exception.exceptions import InvalidArgumentException
|
|
18
|
-
from quantplay.utils.caching import InstrumentCache
|
|
19
|
-
from quantplay.utils.pickle_utils import PickleUtils
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
BrokerType = (
|
|
23
|
-
Aliceblue
|
|
24
|
-
| AngelOne
|
|
25
|
-
| FlatTrade
|
|
26
|
-
| Motilal
|
|
27
|
-
| FinvAsia
|
|
28
|
-
| Upstox
|
|
29
|
-
| Zerodha
|
|
30
|
-
| IIFL_XTS
|
|
31
|
-
| FivePaisa
|
|
32
|
-
| Kotak
|
|
33
|
-
| Dhan
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
instrument_cache = InstrumentCache()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@dataclass
|
|
40
|
-
class Broker:
|
|
41
|
-
ZERODHA = "Zerodha"
|
|
42
|
-
UPSTOX = "Upstox"
|
|
43
|
-
ALICEBLUE = "Aliceblue"
|
|
44
|
-
FIVEPAISA_OPENAPI = "5Paisa_OpenAPI"
|
|
45
|
-
FINVASIA = "Finvasia"
|
|
46
|
-
FLATTRADE = "Flattrade"
|
|
47
|
-
IIFL_XTS = "IIFL_XTS"
|
|
48
|
-
MOTILAL = "Motilal"
|
|
49
|
-
ANGELONE = "Angelone"
|
|
50
|
-
KOTAK = "Kotak"
|
|
51
|
-
DHAN = "Dhan"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class BrokerFactory:
|
|
55
|
-
broker_instruments_map = {
|
|
56
|
-
Broker.ZERODHA: "zerodha_instruments",
|
|
57
|
-
Broker.FINVASIA: "shoonya_instruments",
|
|
58
|
-
Broker.FLATTRADE: "shoonya_instruments",
|
|
59
|
-
Broker.IIFL_XTS: "xts_instruments",
|
|
60
|
-
Broker.MOTILAL: "motilal_instruments",
|
|
61
|
-
Broker.ANGELONE: "angelone_instruments",
|
|
62
|
-
Broker.ALICEBLUE: "aliceblue_instruments",
|
|
63
|
-
Broker.UPSTOX: "upstox_instruments",
|
|
64
|
-
Broker.FIVEPAISA_OPENAPI: "5paisa_instruments",
|
|
65
|
-
Broker.KOTAK: "upstox_instruments",
|
|
66
|
-
}
|
|
67
|
-
broker_required_args = {
|
|
68
|
-
Broker.ZERODHA: set(["user_id", "zerodha_wrapper"]),
|
|
69
|
-
Broker.FINVASIA: set(["user_id", "user_token"]),
|
|
70
|
-
Broker.FLATTRADE: set(["user_id", "user_token"]),
|
|
71
|
-
Broker.IIFL_XTS: set(["user_id", "wrapper", "md_wrapper"]),
|
|
72
|
-
Broker.MOTILAL: set(["user_id", "headers"]),
|
|
73
|
-
Broker.ALICEBLUE: set(["user_id", "client"]),
|
|
74
|
-
Broker.UPSTOX: set(["user_id", "access_token"]),
|
|
75
|
-
Broker.DHAN: set(["user_id", "access_token"]),
|
|
76
|
-
Broker.FIVEPAISA_OPENAPI: set(["user_id", "client"]),
|
|
77
|
-
Broker.ANGELONE: set(
|
|
78
|
-
[
|
|
79
|
-
"user_id",
|
|
80
|
-
"api_key",
|
|
81
|
-
"access_token",
|
|
82
|
-
"refresh_token",
|
|
83
|
-
"feed_token",
|
|
84
|
-
]
|
|
85
|
-
),
|
|
86
|
-
Broker.KOTAK: set(["user_id", "configuration"]),
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
def __init__(self):
|
|
90
|
-
self.client_broker_data: Dict[str, BrokerType] = {}
|
|
91
|
-
|
|
92
|
-
def get_broker_key(self, username: str, broker_name: str) -> str:
|
|
93
|
-
return f"{username}:{broker_name}"
|
|
94
|
-
|
|
95
|
-
def validate_broker_args(self, broker_info: Dict[str, Any]):
|
|
96
|
-
broker = broker_info["broker"]
|
|
97
|
-
broker_data = broker_info["broker_data"]
|
|
98
|
-
|
|
99
|
-
if broker not in self.broker_required_args.keys():
|
|
100
|
-
raise InvalidArgumentException(f"Unsupported Broker: '{broker}'")
|
|
101
|
-
|
|
102
|
-
if not self.broker_required_args[broker].issubset(broker_data.keys()):
|
|
103
|
-
raise InvalidArgumentException(
|
|
104
|
-
f"Missing Arguments for {broker_info['username']}:{broker_info['nickname']} in broker '{broker}' -> {self.broker_required_args[broker].difference(broker_info.keys())}"
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
def store_broker_client(
|
|
108
|
-
self, broker_info: Dict[str, Any], load_instrument: bool = True
|
|
109
|
-
) -> BrokerType | None:
|
|
110
|
-
username = broker_info["username"]
|
|
111
|
-
nickname = broker_info["nickname"]
|
|
112
|
-
|
|
113
|
-
broker_key = self.get_broker_key(username, nickname)
|
|
114
|
-
|
|
115
|
-
broker_data = broker_info["broker_data"]
|
|
116
|
-
broker = broker_info["broker"]
|
|
117
|
-
|
|
118
|
-
broker_client: BrokerType | None = None
|
|
119
|
-
|
|
120
|
-
if broker == Broker.MOTILAL:
|
|
121
|
-
broker_client = Motilal(
|
|
122
|
-
headers=broker_data["headers"],
|
|
123
|
-
load_instrument=load_instrument,
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
if broker == Broker.DHAN:
|
|
127
|
-
broker_client = Dhan(
|
|
128
|
-
user_id=broker_data["user_id"],
|
|
129
|
-
access_token=broker_data["access_token"],
|
|
130
|
-
load_instrument=load_instrument,
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
if broker == Broker.KOTAK:
|
|
134
|
-
broker_client = Kotak(
|
|
135
|
-
configuration=broker_data["configuration"],
|
|
136
|
-
load_instrument=load_instrument,
|
|
137
|
-
)
|
|
138
|
-
elif broker == Broker.ZERODHA:
|
|
139
|
-
broker_client = Zerodha(
|
|
140
|
-
wrapper=broker_data["zerodha_wrapper"],
|
|
141
|
-
load_instrument=load_instrument,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
elif broker == Broker.ANGELONE:
|
|
145
|
-
broker_client = AngelOne(
|
|
146
|
-
user_id=broker_data["user_id"],
|
|
147
|
-
api_key=broker_data["api_key"],
|
|
148
|
-
access_token=broker_data["access_token"],
|
|
149
|
-
refresh_token=broker_data["refresh_token"],
|
|
150
|
-
feed_token=broker_data["feed_token"],
|
|
151
|
-
load_instrument=load_instrument,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
elif broker == Broker.ALICEBLUE:
|
|
155
|
-
broker_client = Aliceblue(
|
|
156
|
-
client=broker_data["client"],
|
|
157
|
-
load_instrument=load_instrument,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
elif broker == Broker.UPSTOX:
|
|
161
|
-
broker_client = Upstox(
|
|
162
|
-
access_token=broker_data["access_token"],
|
|
163
|
-
user_id=broker_data["user_id"],
|
|
164
|
-
load_instrument=load_instrument,
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
elif broker == Broker.FINVASIA:
|
|
168
|
-
broker_client = FinvAsia(
|
|
169
|
-
user_id=broker_data["user_id"],
|
|
170
|
-
user_token=broker_data["user_token"],
|
|
171
|
-
load_instrument=load_instrument,
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
elif broker == Broker.FLATTRADE:
|
|
175
|
-
broker_client = FlatTrade(
|
|
176
|
-
user_id=broker_data["user_id"],
|
|
177
|
-
user_token=broker_data["user_token"],
|
|
178
|
-
load_instrument=load_instrument,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
elif broker == Broker.FIVEPAISA_OPENAPI:
|
|
182
|
-
broker_client = FivePaisa(
|
|
183
|
-
client=broker_data["client"],
|
|
184
|
-
load_instrument=load_instrument,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
elif broker == Broker.IIFL_XTS:
|
|
188
|
-
broker_client = IIFL_XTS(
|
|
189
|
-
wrapper=broker_data["wrapper"],
|
|
190
|
-
md_wrapper=broker_data["md_wrapper"],
|
|
191
|
-
client_id=broker_data["user_id"],
|
|
192
|
-
load_instrument=load_instrument,
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
else:
|
|
196
|
-
raise InvalidArgumentException(f"Broker '{broker}' not supported")
|
|
197
|
-
|
|
198
|
-
if not load_instrument:
|
|
199
|
-
broker_client = self.set_broker_instruments(
|
|
200
|
-
broker_name=broker, broker=broker_client
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
broker_client.username = broker_info["username"]
|
|
204
|
-
broker_client.nickname = broker_info["nickname"]
|
|
205
|
-
broker_client.broker_name = broker_info["broker"]
|
|
206
|
-
broker_client.user_id = broker_data["user_id"]
|
|
207
|
-
|
|
208
|
-
self.client_broker_data[broker_key] = broker_client
|
|
209
|
-
|
|
210
|
-
return broker_client
|
|
211
|
-
|
|
212
|
-
def get_broker_client(self, broker_info: Dict[str, Any]) -> BrokerType:
|
|
213
|
-
username = broker_info["username"]
|
|
214
|
-
nickname = broker_info["nickname"]
|
|
215
|
-
|
|
216
|
-
broker_key = self.get_broker_key(username, nickname)
|
|
217
|
-
|
|
218
|
-
if broker_key in self.client_broker_data:
|
|
219
|
-
return self.client_broker_data[broker_key]
|
|
220
|
-
|
|
221
|
-
self.validate_broker_args(broker_info)
|
|
222
|
-
broker_client = self.store_broker_client(broker_info, load_instrument=False)
|
|
223
|
-
|
|
224
|
-
if broker_client is not None:
|
|
225
|
-
return broker_client
|
|
226
|
-
else:
|
|
227
|
-
raise InvalidArgumentException("Invalid broker API configuration")
|
|
228
|
-
|
|
229
|
-
def set_broker_instruments(self, broker_name: str, broker: BrokerType) -> BrokerType:
|
|
230
|
-
symbol_data_key = f"{broker_name}_instruments"
|
|
231
|
-
quantplay_symbol_key = f"{broker_name}_qplay_symbols"
|
|
232
|
-
broker_symbol_key = f"{broker_name}_broker_symbols"
|
|
233
|
-
|
|
234
|
-
symbol_data = instrument_cache.get(symbol_data_key)
|
|
235
|
-
quantplay_symbol_map = instrument_cache.get(quantplay_symbol_key)
|
|
236
|
-
broker_symbol_map = instrument_cache.get(broker_symbol_key)
|
|
237
|
-
|
|
238
|
-
if symbol_data is not None:
|
|
239
|
-
broker.symbol_data = symbol_data
|
|
240
|
-
|
|
241
|
-
if broker_name != "Zerodha":
|
|
242
|
-
if quantplay_symbol_map is not None and broker_symbol_map is not None:
|
|
243
|
-
broker.quantplay_symbol_map = quantplay_symbol_map
|
|
244
|
-
broker.broker_symbol_map = broker_symbol_map
|
|
245
|
-
|
|
246
|
-
else:
|
|
247
|
-
broker.initialize_broker_symbol_map()
|
|
248
|
-
instrument_cache.set(
|
|
249
|
-
quantplay_symbol_key, broker.quantplay_symbol_map
|
|
250
|
-
)
|
|
251
|
-
instrument_cache.set(broker_symbol_key, broker.broker_symbol_map)
|
|
252
|
-
|
|
253
|
-
return broker
|
|
254
|
-
|
|
255
|
-
try:
|
|
256
|
-
symbol_data = PickleUtils.load_data(
|
|
257
|
-
BrokerFactory.broker_instruments_map[broker_name]
|
|
258
|
-
)
|
|
259
|
-
broker.symbol_data = symbol_data
|
|
260
|
-
|
|
261
|
-
if broker_name != "Zerodha":
|
|
262
|
-
broker.initialize_broker_symbol_map()
|
|
263
|
-
instrument_cache.set(quantplay_symbol_key, broker.quantplay_symbol_map)
|
|
264
|
-
|
|
265
|
-
instrument_cache.set(symbol_data_key, symbol_data)
|
|
266
|
-
|
|
267
|
-
except Exception:
|
|
268
|
-
traceback.print_exc()
|
|
269
|
-
|
|
270
|
-
if broker_name != "Zerodha":
|
|
271
|
-
broker.load_instrument(BrokerFactory.broker_instruments_map[broker_name])
|
|
272
|
-
else:
|
|
273
|
-
broker.initialize_symbol_data()
|
|
274
|
-
|
|
275
|
-
return broker
|
|
276
|
-
|
|
277
|
-
def clear_instrument_cache(self, broker: str) -> None:
|
|
278
|
-
symbol_data_key = f"{broker}_instruments"
|
|
279
|
-
instrument_cache.delete(symbol_data_key)
|
|
280
|
-
|
|
281
|
-
file_name = self.broker_instruments_map[broker]
|
|
282
|
-
os.system(f"rm /tmp/{file_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
|
|
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
|
|
File without changes
|