├── .gitignore
├── .idea
├── .gitignore
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── vcs.xml
└── xts-pythonclient-api-sdk.iml
├── Connect.py
├── Example.py
├── Exception.py
├── InteractiveSocketClient.py
├── InteractiveSocketExample.py
├── MarketDataSocketClient.py
├── MarketdataSocketExample.py
├── README.md
├── __init__.py
├── __version__.py
├── config.ini
└── requirements.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | /venv/
2 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/xts-pythonclient-api-sdk.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Connect.py:
--------------------------------------------------------------------------------
1 | """
2 | Connect.py
3 |
4 | API wrapper for XTS Connect REST APIs.
5 |
6 | :copyright:
7 | :license: see LICENSE for details.
8 | """
9 | import configparser
10 | import json
11 | import logging
12 | import requests
13 | from urllib import parse
14 | import Exception as ex
15 | log = logging.getLogger(__name__)
16 |
17 |
18 | class XTSCommon:
19 | """
20 | Base variables class
21 | """
22 |
23 | def __init__(self, token=None, userID=None, isInvestorClient=None):
24 | """Initialize the common variables."""
25 | self.token = token
26 | self.userID = userID
27 | self.isInvestorClient = isInvestorClient
28 |
29 |
30 | class XTSConnect(XTSCommon):
31 | """
32 | The XTS Connect API wrapper class.
33 | In production, you may initialise a single instance of this class per `api_key`.
34 | """
35 | """Get the configurations from config.ini"""
36 | cfg = configparser.ConfigParser()
37 | cfg.read('config.ini')
38 |
39 | # Default root API endpoint. It's possible to
40 | # override this by passing the `root` parameter during initialisation.
41 | _default_root_uri = cfg.get('root_url', 'root')
42 | _default_login_uri = _default_root_uri + "/user/session"
43 | _default_timeout = 7 # In seconds
44 |
45 | # SSL Flag
46 | _ssl_flag = cfg.get('SSL', 'disable_ssl')
47 |
48 | # Constants
49 | # Products
50 | PRODUCT_MIS = "MIS"
51 | PRODUCT_NRML = "NRML"
52 |
53 | # Order types
54 | ORDER_TYPE_MARKET = "MARKET"
55 | ORDER_TYPE_LIMIT = "LIMIT"
56 | ORDER_TYPE_STOPMARKET = "STOPMARKET"
57 | ORDER_TYPE_STOPLIMIT = "STOPLIMIT"
58 |
59 | # Transaction type
60 | TRANSACTION_TYPE_BUY = "BUY"
61 | TRANSACTION_TYPE_SELL = "SELL"
62 |
63 | # Squareoff mode
64 | SQUAREOFF_DAYWISE = "DayWise"
65 | SQUAREOFF_NETWISE = "Netwise"
66 |
67 | # Squareoff position quantity types
68 | SQUAREOFFQUANTITY_EXACTQUANTITY = "ExactQty"
69 | SQUAREOFFQUANTITY_PERCENTAGE = "Percentage"
70 |
71 | # Validity
72 | VALIDITY_DAY = "DAY"
73 |
74 | # Exchange Segments
75 | EXCHANGE_NSECM = "NSECM"
76 | EXCHANGE_NSEFO = "NSEFO"
77 | EXCHANGE_NSECD = "NSECD"
78 | EXCHANGE_MCXFO = "MCXFO"
79 | EXCHANGE_BSECM = "BSECM"
80 | EXCHANGE_BSEFO = "BSEFO"
81 |
82 | # URIs to various calls
83 | _routes = {
84 | # Interactive API endpoints
85 | "interactive.prefix": "interactive",
86 | "user.login": "/interactive/user/session",
87 | "user.logout": "/interactive/user/session",
88 | "user.profile": "/interactive/user/profile",
89 | "user.balance": "/interactive/user/balance",
90 |
91 | "orders": "/interactive/orders",
92 | "trades": "/interactive/orders/trades",
93 | "order.status": "/interactive/orders",
94 | "order.place": "/interactive/orders",
95 | "bracketorder.place": "/interactive/orders/bracket",
96 | "bracketorder.modify": "/interactive/orders/bracket",
97 | "bracketorder.cancel": "/interactive/orders/bracket",
98 | "order.place.cover": "/interactive/orders/cover",
99 | "order.exit.cover": "/interactive/orders/cover",
100 | "order.modify": "/interactive/orders",
101 | "order.cancel": "/interactive/orders",
102 | "order.cancelall": "/interactive/orders/cancelall",
103 | "order.history": "/interactive/orders",
104 |
105 | "portfolio.positions": "/interactive/portfolio/positions",
106 | "portfolio.holdings": "/interactive/portfolio/holdings",
107 | "portfolio.positions.convert": "/interactive/portfolio/positions/convert",
108 | "portfolio.squareoff": "/interactive/portfolio/squareoff",
109 | "portfolio.dealerpositions": "interactive/portfolio/dealerpositions",
110 | "order.dealer.status": "/interactive/orders/dealerorderbook",
111 | "dealer.trades": "/interactive/orders/dealertradebook",
112 |
113 |
114 |
115 |
116 | # Market API endpoints
117 | "marketdata.prefix": "apimarketdata",
118 | "market.login": "/apimarketdata/auth/login",
119 | "market.logout": "/apimarketdata/auth/logout",
120 |
121 | "market.config": "/apimarketdata/config/clientConfig",
122 |
123 | "market.instruments.master": "/apimarketdata/instruments/master",
124 | "market.instruments.subscription": "/apimarketdata/instruments/subscription",
125 | "market.instruments.unsubscription": "/apimarketdata/instruments/subscription",
126 | "market.instruments.ohlc": "/apimarketdata/instruments/ohlc",
127 | "market.instruments.indexlist": "/apimarketdata/instruments/indexlist",
128 | "market.instruments.quotes": "/apimarketdata/instruments/quotes",
129 |
130 | "market.search.instrumentsbyid": '/apimarketdata/search/instrumentsbyid',
131 | "market.search.instrumentsbystring": '/apimarketdata/search/instruments',
132 |
133 | "market.instruments.instrument.series": "/apimarketdata/instruments/instrument/series",
134 | "market.instruments.instrument.equitysymbol": "/apimarketdata/instruments/instrument/symbol",
135 | "market.instruments.instrument.futuresymbol": "/apimarketdata/instruments/instrument/futureSymbol",
136 | "market.instruments.instrument.optionsymbol": "/apimarketdata/instruments/instrument/optionsymbol",
137 | "market.instruments.instrument.optiontype": "/apimarketdata/instruments/instrument/optionType",
138 | "market.instruments.instrument.expirydate": "/apimarketdata/instruments/instrument/expiryDate"
139 | }
140 |
141 | def __init__(self,
142 | apiKey,
143 | secretKey,
144 | source,
145 | root=None,
146 | debug=False,
147 | timeout=None,
148 | pool=None,
149 | disable_ssl=_ssl_flag):
150 | """
151 | Initialise a new XTS Connect client instance.
152 |
153 | - `api_key` is the key issued to you
154 | - `token` is the token obtained after the login flow. Pre-login, this will default to None,
155 | but once you have obtained it, you should persist it in a database or session to pass
156 | to the XTS Connect class initialisation for subsequent requests.
157 | - `root` is the API end point root. Unless you explicitly
158 | want to send API requests to a non-default endpoint, this
159 | can be ignored.
160 | - `debug`, if set to True, will serialise and print requests
161 | and responses to stdout.
162 | - `timeout` is the time (seconds) for which the API client will wait for
163 | a request to complete before it fails. Defaults to 7 seconds
164 | - `pool` is manages request pools. It takes a dict of params accepted by HTTPAdapter
165 | - `disable_ssl` disables the SSL verification while making a request.
166 | If set requests won't throw SSLError if its set to custom `root` url without SSL.
167 | """
168 | self.debug = debug
169 | self.apiKey = apiKey
170 | self.secretKey = secretKey
171 | self.source = source
172 | self.disable_ssl = disable_ssl
173 | self.root = root or self._default_root_uri
174 | self.timeout = timeout or self._default_timeout
175 |
176 | super().__init__()
177 |
178 | # Create requests session only if pool exists. Reuse session
179 | # for every request. Otherwise create session for each request
180 | if pool:
181 | self.reqsession = requests.Session()
182 | reqadapter = requests.adapters.HTTPAdapter(**pool)
183 | self.reqsession.mount("https://", reqadapter)
184 | else:
185 | self.reqsession = requests
186 |
187 | # disable requests SSL warning
188 | requests.packages.urllib3.disable_warnings()
189 |
190 | def _set_common_variables(self, access_token,userID, isInvestorClient):
191 | """Set the `access_token` received after a successful authentication."""
192 | super().__init__(access_token,userID, isInvestorClient)
193 |
194 | def _login_url(self):
195 | """Get the remote login url to which a user should be redirected to initiate the login flow."""
196 | return self._default_login_uri
197 |
198 | def interactive_login(self):
199 | """Send the login url to which a user should receive the token."""
200 | try:
201 | params = {
202 | "appKey": self.apiKey,
203 | "secretKey": self.secretKey,
204 | "source": self.source
205 | }
206 | response = self._post("user.login", params)
207 |
208 | if "token" in response['result']:
209 | self._set_common_variables(response['result']['token'], response['result']['userID'],
210 | response['result']['isInvestorClient'])
211 | return response
212 | except Exception as e:
213 | return response['description']
214 |
215 | def get_order_book(self, clientID=None):
216 | """Request Order book gives states of all the orders placed by an user"""
217 | try:
218 | params = {}
219 | if not self.isInvestorClient:
220 | params['clientID'] = clientID
221 | response = self._get("order.status", params)
222 | return response
223 | except Exception as e:
224 | return response['description']
225 |
226 | def get_dealer_orderbook(self, clientID=None):
227 | """Request Order book gives states of all the orders placed by an user"""
228 | try:
229 | params = {}
230 | if not self.isInvestorClient:
231 | params['clientID'] = clientID
232 | response = self._get("order.dealer.status", params)
233 | return response
234 | except Exception as e:
235 | return response['description']
236 |
237 |
238 | def place_order(self,
239 | exchangeSegment,
240 | exchangeInstrumentID,
241 | productType,
242 | orderType,
243 | orderSide,
244 | timeInForce,
245 | disclosedQuantity,
246 | orderQuantity,
247 | limitPrice,
248 | stopPrice,
249 | orderUniqueIdentifier,
250 | apiOrderSource,
251 | clientID=None
252 | ):
253 | """To place an order"""
254 | try:
255 |
256 | params = {
257 | "exchangeSegment": exchangeSegment,
258 | "exchangeInstrumentID": exchangeInstrumentID,
259 | "productType": productType,
260 | "orderType": orderType,
261 | "orderSide": orderSide,
262 | "timeInForce": timeInForce,
263 | "disclosedQuantity": disclosedQuantity,
264 | "orderQuantity": orderQuantity,
265 | "limitPrice": limitPrice,
266 | "stopPrice": stopPrice,
267 | "apiOrderSource":apiOrderSource,
268 | "orderUniqueIdentifier": orderUniqueIdentifier
269 | }
270 |
271 | if not self.isInvestorClient:
272 | params['clientID'] = clientID
273 |
274 | response = self._post('order.place', json.dumps(params))
275 | return response
276 | except Exception as e:
277 | return response['description']
278 |
279 | def modify_order(self,
280 | appOrderID,
281 | modifiedProductType,
282 | modifiedOrderType,
283 | modifiedOrderQuantity,
284 | modifiedDisclosedQuantity,
285 | modifiedLimitPrice,
286 | modifiedStopPrice,
287 | modifiedTimeInForce,
288 | orderUniqueIdentifier,
289 | clientID=None
290 | ):
291 | """The facility to modify your open orders by allowing you to change limit order to market or vice versa,
292 | change Price or Quantity of the limit open order, change disclosed quantity or stop-loss of any
293 | open stop loss order. """
294 | try:
295 | appOrderID = int(appOrderID)
296 | params = {
297 | 'appOrderID': appOrderID,
298 | 'modifiedProductType': modifiedProductType,
299 | 'modifiedOrderType': modifiedOrderType,
300 | 'modifiedOrderQuantity': modifiedOrderQuantity,
301 | 'modifiedDisclosedQuantity': modifiedDisclosedQuantity,
302 | 'modifiedLimitPrice': modifiedLimitPrice,
303 | 'modifiedStopPrice': modifiedStopPrice,
304 | 'modifiedTimeInForce': modifiedTimeInForce,
305 | 'orderUniqueIdentifier': orderUniqueIdentifier
306 | }
307 |
308 | if not self.isInvestorClient:
309 | params['clientID'] = clientID
310 |
311 | response = self._put('order.modify', json.dumps(params))
312 | return response
313 | except Exception as e:
314 | return response['description']
315 |
316 |
317 |
318 | def place_bracketorder(self,
319 | exchangeSegment,
320 | exchangeInstrumentID,
321 | orderType,
322 | orderSide,
323 | disclosedQuantity,
324 | orderQuantity,
325 | limitPrice,
326 | squarOff,
327 | stopLossPrice,
328 | trailingStoploss,
329 | isProOrder,
330 | apiOrderSource,
331 | orderUniqueIdentifier,
332 | ):
333 | """To place a bracketorder"""
334 | try:
335 |
336 | params = {
337 | "exchangeSegment": exchangeSegment,
338 | "exchangeInstrumentID": exchangeInstrumentID,
339 | "orderType": orderType,
340 | "orderSide": orderSide,
341 | "disclosedQuantity": disclosedQuantity,
342 | "orderQuantity": orderQuantity,
343 | "limitPrice": limitPrice,
344 | "squarOff": squarOff,
345 | "stopLossPrice": stopLossPrice,
346 | "trailingStoploss": trailingStoploss,
347 | "isProOrder": isProOrder,
348 | "apiOrderSource":apiOrderSource,
349 | "orderUniqueIdentifier": orderUniqueIdentifier
350 | }
351 | response = self._post('bracketorder.place', json.dumps(params))
352 | print(response)
353 | return response
354 | except Exception as e:
355 | return response['description']
356 |
357 | def bracketorder_cancel(self, appOrderID, clientID=None):
358 | """This API can be called to cancel any open order of the user by providing correct appOrderID matching with
359 | the chosen open order to cancel. """
360 | try:
361 | params = {'boEntryOrderId': int(appOrderID)}
362 | if not self.isInvestorClient:
363 | params['clientID'] = clientID
364 | response = self._delete('bracketorder.cancel', params)
365 | return response
366 | except Exception as e:
367 | return response['description']
368 |
369 | def modify_bracketorder(self,
370 | appOrderID,
371 | orderQuantity,
372 | limitPrice,
373 | stopPrice,
374 | clientID=None
375 | ):
376 | try:
377 | appOrderID = int(appOrderID)
378 | params = {
379 | 'appOrderID': appOrderID,
380 | 'bracketorder.modify': orderQuantity,
381 | 'limitPrice': limitPrice,
382 | 'stopPrice': stopPrice
383 | }
384 |
385 | if not self.isInvestorClient:
386 | params['clientID'] = clientID
387 |
388 | response = self._put('bracketorder.modify', json.dumps(params))
389 | return response
390 | except Exception as e:
391 | return response['description']
392 |
393 |
394 | def place_cover_order(self,
395 | exchangeSegment,
396 | exchangeInstrumentID,
397 | orderSide,orderType,
398 | orderQuantity,
399 | disclosedQuantity,
400 | limitPrice,
401 | stopPrice,
402 | apiOrderSource,
403 | orderUniqueIdentifier,
404 | clientID=None):
405 | """A Cover Order is an advance intraday order that is accompanied by a compulsory Stop Loss Order. This helps
406 | users to minimize their losses by safeguarding themselves from unexpected market movements. A Cover Order
407 | offers high leverage and is available in Equity Cash, Equity F&O, Commodity F&O and Currency F&O segments. It
408 | has 2 orders embedded in itself, they are Limit/Market Order Stop Loss Order """
409 | try:
410 |
411 | params = {'exchangeSegment': exchangeSegment,
412 | 'exchangeInstrumentID': exchangeInstrumentID,
413 | 'orderSide': orderSide,
414 | "orderType": orderType,
415 | 'orderQuantity': orderQuantity,
416 | 'disclosedQuantity': disclosedQuantity,
417 | 'limitPrice': limitPrice,
418 | 'stopPrice': stopPrice,
419 | 'apiOrderSource': apiOrderSource,
420 | 'orderUniqueIdentifier': orderUniqueIdentifier
421 | }
422 | if not self.isInvestorClient:
423 | params['clientID'] = clientID
424 | response = self._post('order.place.cover', json.dumps(params))
425 | return response
426 | except Exception as e:
427 | return response['description']
428 |
429 | def exit_cover_order(self, appOrderID, clientID=None):
430 | """Exit Cover API is a functionality to enable user to easily exit an open stoploss order by converting it
431 | into Exit order. """
432 | try:
433 |
434 | params = {'appOrderID': appOrderID}
435 | if not self.isInvestorClient:
436 | params['clientID'] = clientID
437 | response = self._put('order.exit.cover', json.dumps(params))
438 | return response
439 | except Exception as e:
440 | return response['description']
441 |
442 |
443 |
444 | def get_profile(self, clientID=None):
445 | """Using session token user can access his profile stored with the broker, it's possible to retrieve it any
446 | point of time with the http: //ip:port/interactive/user/profile API. """
447 | try:
448 | params = {}
449 | if not self.isInvestorClient:
450 | params['clientID'] = clientID
451 |
452 | response = self._get('user.profile', params)
453 | return response
454 | except Exception as e:
455 | return response['description']
456 |
457 | def get_balance(self, clientID=None):
458 | """Get Balance API call grouped under this category information related to limits on equities, derivative,
459 | upfront margin, available exposure and other RMS related balances available to the user."""
460 | if self.isInvestorClient:
461 | try:
462 | params = {}
463 | if not self.isInvestorClient:
464 | params['clientID'] = clientID
465 | response = self._get('user.balance', params)
466 | return response
467 | except Exception as e:
468 | return response['description']
469 | else:
470 | print("Balance : Balance API available for retail API users only, dealers can watch the same on dealer "
471 | "terminal")
472 |
473 |
474 | def get_trade(self, clientID=None):
475 | """Trade book returns a list of all trades executed on a particular day , that were placed by the user . The
476 | trade book will display all filled and partially filled orders. """
477 | try:
478 | params = {}
479 | if not self.isInvestorClient:
480 | params['clientID'] = clientID
481 | response = self._get('trades', params)
482 | return response
483 | except Exception as e:
484 | return response['description']
485 |
486 | def get_dealer_tradebook(self, clientID=None):
487 | """Trade book returns a list of all trades executed on a particular day , that were placed by the user . The
488 | trade book will display all filled and partially filled orders. """
489 | try:
490 | params = {}
491 | if not self.isInvestorClient:
492 | params['clientID'] = clientID
493 | response = self._get('dealer.trades', params)
494 | return response
495 | except Exception as e:
496 | return response['description']
497 |
498 | def get_holding(self, clientID=None):
499 | """Holdings API call enable users to check their long term holdings with the broker."""
500 | try:
501 | params = {}
502 | if not self.isInvestorClient:
503 | params['clientID'] = clientID
504 |
505 | response = self._get('portfolio.holdings', params)
506 | return response
507 | except Exception as e:
508 | return response['description']
509 |
510 |
511 | def get_dealerposition_netwise(self, clientID=None):
512 | """The positions API positions by net. Net is the actual, current net position portfolio."""
513 | try:
514 | params = {'dayOrNet': 'NetWise'}
515 | if not self.isInvestorClient:
516 | params['clientID'] = clientID
517 | response = self._get('portfolio.dealerpositions', params)
518 | return response
519 | except Exception as e:
520 | return response['description']
521 |
522 |
523 |
524 | def get_dealerposition_daywise(self, clientID=None):
525 | """The positions API returns positions by day, which is a snapshot of the buying and selling activity for
526 | that particular day."""
527 | try:
528 | params = {'dayOrNet': 'DayWise'}
529 | if not self.isInvestorClient:
530 | params['clientID'] = clientID
531 |
532 | response = self._get('portfolio.dealerpositions', params)
533 | return response
534 | except Exception as e:
535 | return response['description']
536 |
537 | def get_position_daywise(self, clientID=None):
538 |
539 | """The positions API returns positions by day, which is a snapshot of the buying and selling activity for
540 | that particular day."""
541 | try:
542 | params = {'dayOrNet': 'DayWise'}
543 | if not self.isInvestorClient:
544 | params['clientID'] = clientID
545 |
546 | response = self._get('portfolio.positions', params)
547 | return response
548 | except Exception as e:
549 | return response['description']
550 |
551 | def get_position_netwise(self, clientID=None):
552 | """The positions API positions by net. Net is the actual, current net position portfolio."""
553 | try:
554 | params = {'dayOrNet': 'NetWise'}
555 | if not self.isInvestorClient:
556 | params['clientID'] = clientID
557 | response = self._get('portfolio.positions', params)
558 | return response
559 | except Exception as e:
560 | return response['description']
561 |
562 | def convert_position(self, exchangeSegment, exchangeInstrumentID, targetQty, isDayWise, oldProductType,
563 | newProductType, clientID=None):
564 | """Convert position API, enable users to convert their open positions from NRML intra-day to Short term MIS or
565 | vice versa, provided that there is sufficient margin or funds in the account to effect such conversion """
566 | try:
567 | params = {
568 | 'exchangeSegment': exchangeSegment,
569 | 'exchangeInstrumentID': exchangeInstrumentID,
570 | 'targetQty': targetQty,
571 | 'isDayWise': isDayWise,
572 | 'oldProductType': oldProductType,
573 | 'newProductType': newProductType
574 | }
575 | if not self.isInvestorClient:
576 | params['clientID'] = clientID
577 | response = self._put('portfolio.positions.convert', json.dumps(params))
578 | return response
579 | except Exception as e:
580 | return response['description']
581 |
582 | def cancel_order(self, appOrderID, orderUniqueIdentifier, clientID=None):
583 | """This API can be called to cancel any open order of the user by providing correct appOrderID matching with
584 | the chosen open order to cancel. """
585 | try:
586 | params = {'appOrderID': int(appOrderID), 'orderUniqueIdentifier': orderUniqueIdentifier}
587 | if not self.isInvestorClient:
588 | params['clientID'] = clientID
589 | response = self._delete('order.cancel', params)
590 | return response
591 | except Exception as e:
592 | return response['description']
593 |
594 | def cancelall_order(self, exchangeSegment, exchangeInstrumentID):
595 | """This API can be called to cancel all open order of the user by providing exchange segment and exchange instrument ID """
596 | try:
597 | params = {"exchangeSegment": exchangeSegment, "exchangeInstrumentID": exchangeInstrumentID}
598 | if not self.isInvestorClient:
599 | params['clientID'] = self.userID
600 | response = self._post('order.cancelall', json.dumps(params))
601 | return response
602 | except Exception as e:
603 | return response['description']
604 |
605 |
606 | def squareoff_position(self, exchangeSegment, exchangeInstrumentID, productType, squareoffMode,
607 | positionSquareOffQuantityType, squareOffQtyValue, blockOrderSending, cancelOrders,
608 | clientID=None):
609 | """User can request square off to close all his positions in Equities, Futures and Option. Users are advised
610 | to use this request with caution if one has short term holdings. """
611 | try:
612 |
613 | params = {'exchangeSegment': exchangeSegment, 'exchangeInstrumentID': exchangeInstrumentID,
614 | 'productType': productType, 'squareoffMode': squareoffMode,
615 | 'positionSquareOffQuantityType': positionSquareOffQuantityType,
616 | 'squareOffQtyValue': squareOffQtyValue, 'blockOrderSending': blockOrderSending,
617 | 'cancelOrders': cancelOrders
618 | }
619 | if not self.isInvestorClient:
620 | params['clientID'] = clientID
621 | response = self._put('portfolio.squareoff', json.dumps(params))
622 | return response
623 | except Exception as e:
624 | return response['description']
625 |
626 | def get_order_history(self, appOrderID, clientID=None):
627 | """Order history will provide particular order trail chain. This indicate the particular order & its state
628 | changes. i.e.Pending New to New, New to PartiallyFilled, PartiallyFilled, PartiallyFilled & PartiallyFilled
629 | to Filled etc """
630 | try:
631 | params = {'appOrderID': appOrderID}
632 | if not self.isInvestorClient:
633 | params['clientID'] = clientID
634 | response = self._get('order.history', params)
635 | return response
636 | except Exception as e:
637 | return response['description']
638 |
639 | def interactive_logout(self, clientID=None):
640 | """This call invalidates the session token and destroys the API session. After this, the user should go
641 | through login flow again and extract session token from login response before further activities. """
642 | try:
643 | params = {}
644 | if not self.isInvestorClient:
645 | params['clientID'] = clientID
646 | response = self._delete('user.logout', params)
647 | return response
648 | except Exception as e:
649 | return response['description']
650 |
651 | ########################################################################################################
652 | # Market data API
653 | ########################################################################################################
654 |
655 | def marketdata_login(self):
656 | try:
657 | params = {
658 | "appKey": self.apiKey,
659 | "secretKey": self.secretKey,
660 | "source": self.source
661 | }
662 | response = self._post("market.login", params)
663 |
664 | if "token" in response['result']:
665 | self._set_common_variables(response['result']['token'], response['result']['userID'],False)
666 | return response
667 | except Exception as e:
668 | return response['description']
669 |
670 | def get_config(self):
671 | try:
672 | params = {}
673 | response = self._get('market.config', params)
674 | return response
675 | except Exception as e:
676 | return response['description']
677 |
678 | def get_quote(self, Instruments, xtsMessageCode, publishFormat):
679 | try:
680 |
681 | params = {'instruments': Instruments, 'xtsMessageCode': xtsMessageCode, 'publishFormat': publishFormat}
682 | response = self._post('market.instruments.quotes', json.dumps(params))
683 | return response
684 | except Exception as e:
685 | return response['description']
686 |
687 | def send_subscription(self, Instruments, xtsMessageCode):
688 | try:
689 | params = {'instruments': Instruments, 'xtsMessageCode': xtsMessageCode}
690 | response = self._post('market.instruments.subscription', json.dumps(params))
691 | return response
692 | except Exception as e:
693 | return response['description']
694 |
695 | def send_unsubscription(self, Instruments, xtsMessageCode):
696 | try:
697 | params = {'instruments': Instruments, 'xtsMessageCode': xtsMessageCode}
698 | response = self._put('market.instruments.unsubscription', json.dumps(params))
699 | return response
700 | except Exception as e:
701 | return response['description']
702 |
703 | def get_master(self, exchangeSegmentList):
704 | try:
705 | params = {"exchangeSegmentList": exchangeSegmentList}
706 | response = self._post('market.instruments.master', json.dumps(params))
707 | return response
708 | except Exception as e:
709 | return response['description']
710 |
711 | def get_ohlc(self, exchangeSegment, exchangeInstrumentID, startTime, endTime, compressionValue):
712 | try:
713 | params = {
714 | 'exchangeSegment': exchangeSegment,
715 | 'exchangeInstrumentID': exchangeInstrumentID,
716 | 'startTime': startTime,
717 | 'endTime': endTime,
718 | 'compressionValue': compressionValue}
719 | response = self._get('market.instruments.ohlc', params)
720 | return response
721 | except Exception as e:
722 | return response['description']
723 |
724 | def get_series(self, exchangeSegment):
725 | try:
726 | params = {'exchangeSegment': exchangeSegment}
727 | response = self._get('market.instruments.instrument.series', params)
728 | return response
729 | except Exception as e:
730 | return response['description']
731 |
732 | def get_equity_symbol(self, exchangeSegment, series, symbol):
733 | try:
734 |
735 | params = {'exchangeSegment': exchangeSegment, 'series': series, 'symbol': symbol}
736 | response = self._get('market.instruments.instrument.equitysymbol', params)
737 | return response
738 | except Exception as e:
739 | return response['description']
740 |
741 | def get_expiry_date(self, exchangeSegment, series, symbol):
742 | try:
743 | params = {'exchangeSegment': exchangeSegment, 'series': series, 'symbol': symbol}
744 | response = self._get('market.instruments.instrument.expirydate', params)
745 | return response
746 | except Exception as e:
747 | return response['description']
748 |
749 | def get_future_symbol(self, exchangeSegment, series, symbol, expiryDate):
750 | try:
751 | params = {'exchangeSegment': exchangeSegment, 'series': series, 'symbol': symbol, 'expiryDate': expiryDate}
752 | response = self._get('market.instruments.instrument.futuresymbol', params)
753 | return response
754 | except Exception as e:
755 | return response['description']
756 |
757 | def get_option_symbol(self, exchangeSegment, series, symbol, expiryDate, optionType, strikePrice):
758 | try:
759 | params = {'exchangeSegment': exchangeSegment, 'series': series, 'symbol': symbol, 'expiryDate': expiryDate,
760 | 'optionType': optionType, 'strikePrice': strikePrice}
761 | response = self._get('market.instruments.instrument.optionsymbol', params)
762 | return response
763 | except Exception as e:
764 | return response['description']
765 |
766 | def get_option_type(self, exchangeSegment, series, symbol, expiryDate):
767 | try:
768 | params = {'exchangeSegment': exchangeSegment, 'series': series, 'symbol': symbol, 'expiryDate': expiryDate}
769 | response = self._get('market.instruments.instrument.optiontype', params)
770 | return response
771 | except Exception as e:
772 | return response['description']
773 |
774 | def get_index_list(self, exchangeSegment):
775 | try:
776 | params = {'exchangeSegment': exchangeSegment}
777 | response = self._get('market.instruments.indexlist', params)
778 | return response
779 | except Exception as e:
780 | return response['description']
781 |
782 | def search_by_instrumentid(self, Instruments):
783 | try:
784 | params = {'source': self.source, 'instruments': Instruments}
785 | response = self._post('market.search.instrumentsbyid', json.dumps(params))
786 | return response
787 | except Exception as e:
788 | return response['description']
789 |
790 | def search_by_scriptname(self, searchString):
791 | try:
792 | params = {'searchString': searchString}
793 | response = self._get('market.search.instrumentsbystring', params)
794 | return response
795 | except Exception as e:
796 | return response['description']
797 |
798 | def marketdata_logout(self):
799 | try:
800 | params = {}
801 | response = self._delete('market.logout', params)
802 | return response
803 | except Exception as e:
804 | return response['description']
805 |
806 | ########################################################################################################
807 | # Common Methods
808 | ########################################################################################################
809 |
810 | def _get(self, route, params=None):
811 | """Alias for sending a GET request."""
812 | return self._request(route, "GET", params)
813 |
814 | def _post(self, route, params=None):
815 | """Alias for sending a POST request."""
816 | return self._request(route, "POST", params)
817 |
818 | def _put(self, route, params=None):
819 | """Alias for sending a PUT request."""
820 | return self._request(route, "PUT", params)
821 |
822 | def _delete(self, route, params=None):
823 | """Alias for sending a DELETE request."""
824 | return self._request(route, "DELETE", params)
825 |
826 | def _request(self, route, method, parameters=None):
827 | """Make an HTTP request."""
828 | params = parameters if parameters else {}
829 |
830 | # Form a restful URL
831 | uri = self._routes[route].format(params)
832 | url = parse.urljoin(self.root, uri)
833 | headers = {}
834 |
835 | if self.token:
836 | # set authorization header
837 | headers.update({'Content-Type': 'application/json', 'Authorization': self.token})
838 |
839 | try:
840 | r = self.reqsession.request(method,
841 | url,
842 | data=params if method in ["POST", "PUT"] else None,
843 | params=params if method in ["GET", "DELETE"] else None,
844 | headers=headers,
845 | verify=not self.disable_ssl)
846 |
847 | except Exception as e:
848 | raise e
849 |
850 | if self.debug:
851 | log.debug("Response: {code} {content}".format(code=r.status_code, content=r.content))
852 |
853 | # Validate the content type.
854 | if "json" in r.headers["content-type"]:
855 | try:
856 | data = json.loads(r.content.decode("utf8"))
857 | except ValueError:
858 | raise ex.XTSDataException("Couldn't parse the JSON response received from the server: {content}".format(
859 | content=r.content))
860 |
861 | # api error
862 | if data.get("type"):
863 |
864 | if r.status_code == 400 and data["type"] == "error" and data["description"] == "Invalid Token":
865 | raise ex.XTSTokenException(data["description"])
866 |
867 | if r.status_code == 400 and data["type"] == "error" and data["description"] == "Bad Request":
868 | message = "Description: " + data["description"] + " errors: " + str(data['result']["errors"])
869 | raise ex.XTSInputException(str(message))
870 |
871 | return data
872 | else:
873 | raise ex.XTSDataException("Unknown Content-Type ({content_type}) with response: ({content})".format(
874 | content_type=r.headers["content-type"],
875 | content=r.content))
876 |
--------------------------------------------------------------------------------
/Example.py:
--------------------------------------------------------------------------------
1 | # from XTConnect import XTSConnect
2 | from Connect import XTSConnect
3 |
4 |
5 | # ----------------------------------------------------------------------------------------------------------------------
6 | # Interactive
7 | # ----------------------------------------------------------------------------------------------------------------------
8 |
9 | # Interactive API Credentials
10 | # API_KEY = "YOUR_API_KEY_HERE"
11 | # API_SECRET = "YOUR_API_SECRET_HERE"
12 | # clientID = "YOUR_CLIENT_ID_HERE"
13 | # userID = "YOUR_USER_ID_HERE"
14 | # XTS_API_BASE_URL = "https://xts-api.trading"
15 | # source = "WEBAPI"
16 |
17 | "Note : For dealer credentials add the clientID and for investor client leave the clientID blank"
18 |
19 | """Dealer credentials"""
20 | API_KEY = "096328d98d30f4c551c503"
21 | API_SECRET = "Letp625#gV"
22 | clientID = "ATHARVA1"
23 | userID = ""
24 | XTS_API_BASE_URL = "https://developers.symphonyfintech.in"
25 | source = "WEBAPI"
26 |
27 | """Investor client credentials"""
28 | # API_KEY = "431c75e076e238bdff8176"
29 | # API_SECRET = "Ieil074#FH"
30 | # clientID = "RUCHA"
31 | # XTS_API_BASE_URL = "https://developers.symphonyfintech.in"
32 | # source = "WEBAPI"
33 |
34 | """Make XTSConnect object by passing your interactive API appKey, secretKey and source"""
35 | xt = XTSConnect(API_KEY, API_SECRET, source)
36 |
37 | """Using the xt object we created call the interactive login Request"""
38 | response = xt.interactive_login()
39 | print("Login: ", response)
40 |
41 |
42 | """Place Order Request"""
43 | response = xt.place_order(
44 | exchangeSegment=xt.EXCHANGE_NSECM,
45 | exchangeInstrumentID=2885,
46 | productType=xt.PRODUCT_MIS,
47 | orderType=xt.ORDER_TYPE_MARKET,
48 | orderSide=xt.TRANSACTION_TYPE_BUY,
49 | timeInForce=xt.VALIDITY_DAY,
50 | disclosedQuantity=0,
51 | orderQuantity=10,
52 | limitPrice=0,
53 | stopPrice=0,
54 | apiOrderSource="",
55 | orderUniqueIdentifier="454845",
56 | clientID=clientID)
57 |
58 | print("Place Order: ", response)
59 |
60 |
61 | clientID = "ATHARVA1"
62 | """Order book Request"""
63 | response = xt.get_order_book(clientID)
64 | print("Order Book: ", response)
65 |
66 | # extracting the order id from response
67 | if response['type'] != 'error':
68 | OrderID = response['result']['AppOrderID']
69 |
70 | """Get Order History Request"""
71 | response = xt.get_order_history(appOrderID=OrderID,clientID=clientID)
72 | print("Order History: ", response)
73 |
74 | """Modify Order Request"""
75 | response = xt.modify_order(
76 | appOrderID=OrderID,
77 | modifiedProductType=xt.PRODUCT_NRML,
78 | modifiedOrderType=xt.ORDER_TYPE_LIMIT,
79 | modifiedOrderQuantity=8,
80 | modifiedDisclosedQuantity=0,
81 | modifiedLimitPrice=1405,
82 | modifiedStopPrice=0,
83 | modifiedTimeInForce=xt.VALIDITY_DAY,
84 | orderUniqueIdentifier="454845",
85 | clientID=clientID
86 | )
87 | print("Modify Order: ", response)
88 |
89 | """Cancel Orders Request"""
90 | response = xt.cancel_order(
91 | appOrderID=OrderID,
92 | orderUniqueIdentifier='454845',
93 | clientID=clientID)
94 | print("Cancel Order: ", response)
95 |
96 | """Get Order History Request"""
97 | response = xt.get_order_history(appOrderID=OrderID,clientID=clientID)
98 | print("Order History: ", response)
99 |
100 |
101 | """Place BracketOrder Request"""
102 | response = xt.place_bracketorder(
103 | exchangeSegment=xt.EXCHANGE_NSECM,
104 | exchangeInstrumentID=2885,
105 | orderType=xt.ORDER_TYPE_MARKET,
106 | orderSide=xt.TRANSACTION_TYPE_BUY,
107 | disclosedQuantity=0,
108 | orderQuantity=10,
109 | limitPrice=59,
110 | squarOff=1,
111 | stopLossPrice=1,
112 | trailingStoploss=1,
113 | isProOrder=False,
114 | apiOrderSource="",
115 | orderUniqueIdentifier="454845"
116 | )
117 | print("Bracket Order: ", response)
118 |
119 | # extracting the order id from response
120 | if response['type'] != 'error':
121 | OrderID = response['result']['AppOrderID']
122 |
123 | """Cancel BracketOrder Request"""
124 | res = xt.bracketorder_cancel(OrderID)
125 | print("Bracket Cancel: ", response)
126 |
127 | """Modify BracketOrder Request"""
128 | response = xt.modify_order(
129 | appOrderID=OrderID,
130 | orderQuantity=8,
131 | limitPrice=1405,
132 | stopPrice=0,
133 | clientID=clientID
134 | )
135 | print("Modify BracketOrder: ", response)
136 |
137 |
138 | """Get Profile Request"""
139 | response = xt.get_profile(clientID=userID)
140 | print("Profile: ", response)
141 |
142 | """Get Balance Request"""
143 | response = xt.get_balance(clientID=clientID)
144 | print("Balance: ", response)
145 |
146 | """Get Trade Book Request"""
147 | response = xt.get_trade(clientID=clientID)
148 | print("Trade Book: ", response)
149 |
150 | """Get Holdings Request"""
151 | response = xt.get_holding(clientID=clientID)
152 | print("Holdings: ", response)
153 |
154 | """Get Position by DAY Request"""
155 | response = xt.get_position_daywise(clientID=clientID)
156 | print("Position by Day: ", response)
157 |
158 | """Get Position by NET Request"""
159 | response = xt.get_position_netwise(clientID=clientID)
160 | print("Position by Net: ", response)
161 |
162 | """Get Dealer Position by NET Request"""
163 | response = xt.get_dealerposition_daywise(clientID=clientID)
164 | print("Dealer Position by Net: ", response)
165 |
166 | """Get Dealer Position by DAY Request"""
167 | response = xt.get_dealerposition_netwise(clientID=clientID)
168 | print("Dealer Position by Day: ", response)
169 |
170 |
171 | """Dealer Order book Request"""
172 | response = xt.get_dealer_orderbook(clientID)
173 | print("Dealer Order Book: ", response)
174 |
175 | """Get Dealer Trade Book Request"""
176 | response = xt.get_dealer_tradebook(clientID=clientID)
177 | print("Dealer Trade Book: ", response)
178 |
179 | """Position Convert Request"""
180 | response = xt.convert_position(
181 | exchangeSegment=xt.EXCHANGE_NSECM,
182 | exchangeInstrumentID=2885,
183 | targetQty=10,
184 | isDayWise=True,
185 | oldProductType=xt.PRODUCT_MIS,
186 | newProductType=xt.PRODUCT_NRML,
187 | clientID=clientID)
188 | print("Position Convert: ", response)
189 |
190 | """Place Cover Order Request"""
191 | response = xt.place_cover_order(
192 | exchangeSegment=xt.EXCHANGE_NSECM,
193 | exchangeInstrumentID=2885,
194 | orderSide=xt.TRANSACTION_TYPE_BUY,
195 | orderType=xt.ORDER_TYPE_LIMIT,
196 | orderQuantity=2,
197 | disclosedQuantity=0,
198 | limitPrice=1802,
199 | stopPrice=1899,
200 | apiOrderSource="",
201 | orderUniqueIdentifier="454845",
202 | clientID=clientID)
203 | print("Cover Order:", response)
204 |
205 | # extracting the order id from response
206 | if response['type'] != 'error':
207 | OrderID = response['result']['ExitAppOrderID']
208 |
209 | """Exit Cover Order Request"""
210 | response = xt.exit_cover_order(appOrderID=OrderID, clientID=clientID)
211 | print("Exit Cover Order:", response)
212 |
213 | """Cancel all Orders Request"""
214 | response = xt.cancelall_order(exchangeInstrumentID=22,exchangeSegment=xt.EXCHANGE_NSECM)
215 | print("Cancel all Orders: ", response)
216 |
217 | """Interactive logout Request"""
218 | response = xt.interactive_logout(clientID=clientID)
219 | print("Interactive Logout: ", response)
220 |
221 | exit()
222 |
223 | # ----------------------------------------------------------------------------------------------------------------------
224 | # Marketdata
225 | # ----------------------------------------------------------------------------------------------------------------------
226 |
227 | # Marketdata API Credentials
228 | API_KEY = "YOUR_API_KEY_HERE"
229 | API_SECRET = "YOUR_API_SECRET_HERE"
230 | XTS_API_BASE_URL = "https://xts-api.trading"
231 | source = "WEBAPI"
232 |
233 | """Dealer login credentials"""
234 | API_KEY = "22f6f9dad2fe3982419756"
235 | API_SECRET = "Bxtj027$Dr"
236 | XTS_API_BASE_URL = "https://developers.symphonyfintech.in"
237 | source = "WEBAPI"
238 |
239 | """Investor client login credentials"""
240 | API_KEY = "76179cbae91810ddda7774"
241 | API_SECRET = "Bylg203#fv"
242 | XTS_API_BASE_URL = "https://developers.symphonyfintech.in"
243 | source = "WEBAPI"
244 |
245 | """Make the XTSConnect Object with Marketdata API appKey, secretKey and source"""
246 | xt = XTSConnect(API_KEY, API_SECRET, source)
247 |
248 | """Using the object we call the login function Request"""
249 | response = xt.marketdata_login()
250 | print("MarketData Login: ", response)
251 |
252 | """Get Config Request"""
253 | response = xt.get_config()
254 | print('Config :', response)
255 |
256 | """instruments list"""
257 | instruments = [
258 | {'exchangeSegment': 1, 'exchangeInstrumentID': 2885},
259 | {'exchangeSegment': 1, 'exchangeInstrumentID': 22}]
260 |
261 | """Get Quote Request"""
262 | response = xt.get_quote(
263 | Instruments=instruments,
264 | xtsMessageCode=1502,
265 | publishFormat='JSON')
266 | print('Quote :', response)
267 |
268 | """Send Subscription Request"""
269 | response = xt.send_subscription(
270 | Instruments=instruments,
271 | xtsMessageCode=1502)
272 | print('Subscribe :', response)
273 |
274 | """Send Unsubscription Request"""
275 | response = xt.send_unsubscription(
276 | Instruments=instruments,
277 | xtsMessageCode=1502)
278 | print('Unsubscribe :', response)
279 |
280 | """Get Master Instruments Request"""
281 | exchangesegments = [xt.EXCHANGE_NSECM, xt.EXCHANGE_NSEFO]
282 | response = xt.get_master(exchangeSegmentList=exchangesegments)
283 | print("Master: " + str(response))
284 |
285 | """Get OHLC Request"""
286 | response = xt.get_ohlc(
287 | exchangeSegment=xt.EXCHANGE_NSECM,
288 | exchangeInstrumentID=22,
289 | startTime='Jan 04 2025 090000',
290 | endTime='Jan 04 2019 150000',
291 | compressionValue='60')
292 | print("OHLC: " + str(response))
293 |
294 | """Get Series Request"""
295 | response = xt.get_series(exchangeSegment=1)
296 | print('Series:', str(response))
297 |
298 | """Get Equity Symbol Request"""
299 | response = xt.get_equity_symbol(
300 | exchangeSegment=1,
301 | series='EQ',
302 | symbol='Acc')
303 | print('Equity Symbol:', str(response))
304 |
305 | """Get Expiry Date Request"""
306 | response = xt.get_expiry_date(
307 | exchangeSegment=2,
308 | series='FUTIDX',
309 | symbol='NIFTY')
310 | print('Expiry Date:', str(response))
311 |
312 | """Get Future Symbol Request"""
313 | response = xt.get_future_symbol(
314 | exchangeSegment=2,
315 | series='FUTIDX',
316 | symbol='NIFTY',
317 | expiryDate='28MAY25JUN')
318 | print('Future Symbol:', str(response))
319 |
320 | """Get Option Symbol Request"""
321 | response = xt.get_option_symbol(
322 | exchangeSegment=2,
323 | series='OPTIDX',
324 | symbol='NIFTY',
325 | expiryDate='26Mar2020',
326 | optionType='CE',
327 | strikePrice=10000)
328 | print('Option Symbol:', str(response))
329 |
330 | """Get Option Type Request"""
331 | response = xt.get_option_type(
332 | exchangeSegment=2,
333 | series='OPTIDX',
334 | symbol='NIFTY',
335 | expiryDate='26Mar2020')
336 | print('Option Type:', str(response))
337 |
338 | """Get Index List Request"""
339 | response = xt.get_index_list(exchangeSegment=xt.EXCHANGE_NSECM)
340 | print('Index List:', str(response))
341 |
342 | """Search Instrument by ID Request"""
343 | response = xt.search_by_instrumentid(Instruments=instruments)
344 | print('Search By Instrument ID:', str(response))
345 |
346 | """Search Instrument by Scriptname Request"""
347 | response = xt.search_by_scriptname(searchString='REL')
348 | print('Search By Symbol :', str(response))
349 |
350 | """Marketdata Logout Request"""
351 | response = xt.marketdata_logout()
352 | print('Marketdata Logout :', str(response))
353 |
--------------------------------------------------------------------------------
/Exception.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | from requests import exceptions
4 | from requests.exceptions import HTTPError
5 | from requests import ConnectTimeout, HTTPError, Timeout, ConnectionError
6 |
7 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
8 | Here we have declared all the exception and responses
9 | If there is any exception occurred we have this code to convey the messages
10 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
11 |
12 |
13 | class XTSException(Exception):
14 | """
15 | Base exception class representing a XTS client exception.
16 |
17 | Every specific XTS client exception is a subclass of this
18 | and exposes two instance variables `.code` (HTTP error code)
19 | and `.message` (error text).
20 | """
21 |
22 | def __init__(self, message, code=500):
23 | """Initialize the exception."""
24 | super(XTSException, self).__init__(message)
25 | self.code = code
26 |
27 |
28 | class XTSGeneralException(XTSException):
29 | """An unclassified, general error. Default code is 500."""
30 |
31 | def __init__(self, message, code=500):
32 | """Initialize the exception."""
33 | super(XTSGeneralException, self).__init__(message, code)
34 |
35 |
36 | class XTSTokenException(XTSException):
37 | """Represents all token and authentication related errors. Default code is 400."""
38 |
39 | def __init__(self, message, code=400):
40 | """Initialize the exception."""
41 | super(XTSTokenException, self).__init__(message, code)
42 |
43 |
44 | class XTSPermissionException(XTSException):
45 | """Represents permission denied exceptions for certain calls. Default code is 400."""
46 |
47 | def __init__(self, message, code=400):
48 | """Initialize the exception."""
49 | super(XTSPermissionException, self).__init__(message, code)
50 |
51 |
52 | class XTSOrderException(XTSException):
53 | """Represents all order placement and manipulation errors. Default code is 500."""
54 |
55 | def __init__(self, message, code=400):
56 | """Initialize the exception."""
57 | super(XTSOrderException, self).__init__(message, code)
58 |
59 |
60 | class XTSInputException(XTSException):
61 | """Represents user input errors such as missing and invalid parameters. Default code is 400."""
62 |
63 | def __init__(self, message, code=400):
64 | """Initialize the exception."""
65 | super(XTSInputException, self).__init__(message, code)
66 |
67 |
68 | class XTSDataException(XTSException):
69 | """Represents a bad response from the backend Order Management System (OMS). Default code is 500."""
70 |
71 | def __init__(self, message, code=500):
72 | """Initialize the exception."""
73 | super(XTSDataException, self).__init__(message, code)
74 |
75 |
76 | class XTSNetworkException(XTSException):
77 | """Represents a network issue between XTS and the backend Order Management System (OMS). Default code is 500."""
78 |
79 | def __init__(self, message, code=500):
80 | """Initialize the exception."""
81 | super(XTSNetworkException, self).__init__(message, code)
82 |
--------------------------------------------------------------------------------
/InteractiveSocketClient.py:
--------------------------------------------------------------------------------
1 | import configparser
2 | import os
3 |
4 | import socketio
5 |
6 |
7 | class OrderSocket_io(socketio.Client):
8 | """A Socket.IO client.
9 | This class implements a fully compliant Socket.IO web client with support
10 | for websocket and long-polling transports.
11 | :param reconnection: 'True'. if the client should automatically attempt to
12 | reconnect to the server after an interruption, or
13 | 'False' to not reconnect. The default is 'True'.
14 | :param reconnection_attempts: How many reconnection attempts to issue
15 | before giving up, or 0 for infinity attempts.
16 | The default is 0.
17 | :param reconnection_delay: How long to wait in seconds before the first
18 | reconnection attempt. Each successive attempt
19 | doubles this delay.
20 | :param reconnection_delay_max: The maximum delay between reconnection
21 | attempts.
22 | :param randomization_factor: Randomization amount for each delay between
23 | reconnection attempts. The default is 0.5,
24 | which means that each delay is randomly
25 | adjusted by +/- 50%.
26 | :param logger: To enable logging set to 'True' or pass a logger object to
27 | use. To disable logging set to 'False'. The default is
28 | 'False'.
29 | :param binary: 'True' to support binary payloads, 'False' to treat all
30 | payloads as text. On Python 2, if this is set to 'True',
31 | 'unicode' values are treated as text, and 'str' and
32 | 'bytes' values are treated as binary. This option has no
33 | effect on Python 3, where text and binary payloads are
34 | always automatically discovered.
35 | :param json: An alternative json module to use for encoding and decoding
36 | packets. Custom json modules must have 'dumps' and 'loads'
37 | functions that are compatible with the standard library
38 | versions.
39 | """
40 |
41 | def __init__(self, token, userID, reconnection=True, reconnection_attempts=0, reconnection_delay=1,
42 | reconnection_delay_max=50000, randomization_factor=0.5, logger=False, binary=False, json=None,
43 | **kwargs):
44 | self.sid = socketio.Client(logger=True, engineio_logger=True)
45 | self.eventlistener = self.sid
46 | self.sid.on('connect', self.on_connect)
47 | self.sid.on('message', self.on_message)
48 | self.sid.on('joined', self.on_joined)
49 | self.sid.on('error', self.on_error)
50 | self.sid.on('order', self.on_order)
51 | self.sid.on('trade', self.on_trade)
52 | self.sid.on('position', self.on_position)
53 | self.sid.on('tradeConversion', self.on_tradeconversion)
54 | self.sid.on('logout', self.on_messagelogout)
55 | self.sid.on('disconnect', self.on_disconnect)
56 |
57 | self.userID = userID
58 | self.token = token
59 |
60 | """Get root url from config file"""
61 | currDirMain = os.getcwd()
62 | configParser = configparser.RawConfigParser()
63 | configFilePath = os.path.join(currDirMain, 'config.ini')
64 | configParser.read(configFilePath)
65 | self.port = configParser.get('root_url', 'root').strip()
66 |
67 | port = f'{self.port}/?token='
68 |
69 | self.connection_url = port + self.token + '&userID=' + self.userID + "&apiType=INTERACTIVE"
70 |
71 | def connect(self, headers={}, transports='websocket', namespaces=None, socketio_path='/interactive/socket.io',
72 | verify=False):
73 | """Connect to a Socket.IO server.
74 | :param url: The URL of the Socket.IO server. It can include custom
75 | query string parameters if required by the server.
76 | :param headers: A dictionary with custom headers to send with the
77 | connection request.
78 | :param transports: The list of allowed transports. Valid transports
79 | are 'polling' and 'websocket'. If not
80 | given, the polling transport is connected first,
81 | then an upgrade to websocket is attempted.
82 | :param namespaces: The list of custom namespaces to connect, in
83 | addition to the default namespace. If not given,
84 | the namespace list is obtained from the registered
85 | event handlers.
86 | :param socketio_path: The endpoint where the Socket.IO server is
87 | installed. The default value is appropriate for
88 | most cases.
89 |
90 | """
91 | """Connect to the socket."""
92 | url = self.connection_url
93 |
94 | """Connected to the socket."""
95 | self.sid.connect(url, headers, transports, namespaces, socketio_path)
96 | self.sid.wait()
97 | """Disconnect from the socket."""
98 | # self.sid.disconnect()
99 |
100 | def on_connect(self):
101 | """Connect from the socket"""
102 | print('Interactive socket connected successfully!')
103 |
104 | def on_message(self):
105 | """On message from socket"""
106 | print('I received a message!')
107 |
108 | def on_joined(self, data):
109 | """On socket joined"""
110 | print('Interactive socket joined successfully!' + data)
111 |
112 | def on_error(self, data):
113 | """On receiving error from socket"""
114 | print('Interactive socket error!' + data)
115 |
116 | def on_order(self, data):
117 | """On receiving order placed data from socket"""
118 | print("Order placed!" + data)
119 |
120 | def on_trade(self, data):
121 | """On receiving trade data from socket"""
122 | print("Trade Received!" + data)
123 |
124 | def on_position(self, data):
125 | """On receiving position data from socket"""
126 | print("Position Retrieved!" + data)
127 |
128 | def on_tradeconversion(self, data):
129 | """On receiving trade conversion data from socket"""
130 | print("Trade Conversion Received!" + data)
131 |
132 | def on_messagelogout(self, data):
133 | """On receiving user logout message"""
134 | print("User logged out!" + data)
135 |
136 | def on_disconnect(self):
137 | """On receiving disconnection from socket"""
138 | print('Interactive Socket disconnected!')
139 |
140 | def get_emitter(self):
141 | """For getting event listener"""
142 | return self.eventlistener
143 |
--------------------------------------------------------------------------------
/InteractiveSocketExample.py:
--------------------------------------------------------------------------------
1 | from Connect import XTSConnect
2 | from InteractiveSocketClient import OrderSocket_io
3 |
4 | # Interactive API Credentials
5 | API_KEY = "cccc0397aefe029ce61392"
6 | API_SECRET = "Aoxf623#aa"
7 | source = "WEBAPI"
8 |
9 | # Initialise
10 | xt = XTSConnect(API_KEY, API_SECRET, source)
11 |
12 | # Login for authorization token
13 | response = xt.interactive_login()
14 |
15 | # Store the token and userid
16 | set_interactiveToken = response['result']['token']
17 | set_iuserID = response['result']['userID']
18 | print("Login: ", response)
19 |
20 | # Connecting to Interactive socket
21 | soc = OrderSocket_io(set_interactiveToken, set_iuserID)
22 |
23 |
24 | # Callback for connection
25 | def on_connect():
26 | """Connect from the socket."""
27 | print('Interactive socket connected successfully!')
28 |
29 |
30 | # Callback for receiving message
31 | def on_message():
32 | print('I received a message!')
33 |
34 |
35 | # Callback for joined event
36 | def on_joined(data):
37 | print('Interactive socket joined successfully!' + data)
38 |
39 |
40 | # Callback for error
41 | def on_error(data):
42 | print('Interactive socket error!' + data)
43 |
44 |
45 | # Callback for order
46 | def on_order(data):
47 | print("Order placed!" + data)
48 |
49 |
50 | # Callback for trade
51 | def on_trade(data):
52 | print("Trade Received!" + data)
53 |
54 |
55 | # Callback for position
56 | def on_position(data):
57 | print("Position Retrieved!" + data)
58 |
59 |
60 | # Callback for trade conversion event
61 | def on_tradeconversion(data):
62 | print("Trade Conversion Received!" + data)
63 |
64 |
65 | # Callback for message logout
66 | def on_messagelogout(data):
67 | print("User logged out!" + data)
68 |
69 |
70 | # Callback for disconnection
71 | def on_disconnect():
72 | print('Interactive Socket disconnected!')
73 |
74 |
75 | # Assign the callbacks.
76 | soc.on_connect = on_connect
77 | soc.on_message = on_message
78 | soc.on_joined = on_joined
79 | soc.on_error = on_error
80 | soc.on_order = on_order
81 | soc.on_trade = on_trade
82 | soc.on_position = on_position
83 | soc.on_tradeconversion = on_tradeconversion
84 | soc.on_messagelogout = on_messagelogout
85 | soc.on_disconnect = on_disconnect
86 |
87 | # Event listener
88 | el = soc.get_emitter()
89 | el.on('connect', on_connect)
90 | el.on('order', on_order)
91 | el.on('trade', on_trade)
92 | el.on('position', on_position)
93 | el.on('tradeConversion', on_tradeconversion)
94 |
95 | # Infinite loop on the main thread. Nothing after this will run.
96 | # You have to use the pre-defined callbacks to manage subscriptions.
97 | soc.connect()
98 |
--------------------------------------------------------------------------------
/MarketDataSocketClient.py:
--------------------------------------------------------------------------------
1 | import configparser
2 | import os
3 | from datetime import datetime
4 |
5 | import socketio
6 |
7 |
8 | class MDSocket_io(socketio.Client):
9 | """A Socket.IO client.
10 | This class implements a fully compliant Socket.IO web client with support
11 | for websocket and long-polling transports.
12 | :param reconnection: 'True'. if the client should automatically attempt to
13 | reconnect to the server after an interruption, or
14 | 'False' to not reconnect. The default is 'True'.
15 | :param reconnection_attempts: How many reconnection attempts to issue
16 | before giving up, or 0 for infinity attempts.
17 | The default is 0.
18 | :param reconnection_delay: How long to wait in seconds before the first
19 | reconnection attempt. Each successive attempt
20 | doubles this delay.
21 | :param reconnection_delay_max: The maximum delay between reconnection
22 | attempts.
23 | :param randomization_factor: Randomization amount for each delay between
24 | reconnection attempts. The default is 0.5,
25 | which means that each delay is randomly
26 | adjusted by +/- 50%.
27 | :param logger: To enable logging set to 'True' or pass a logger object to
28 | use. To disable logging set to 'False'. The default is
29 | 'False'.
30 | :param binary: 'True' to support binary payloads, 'False' to treat all
31 | payloads as text. On Python 2, if this is set to 'True',
32 | 'unicode' values are treated as text, and 'str' and
33 | 'bytes' values are treated as binary. This option has no
34 | effect on Python 3, where text and binary payloads are
35 | always automatically discovered.
36 | :param json: An alternative json module to use for encoding and decoding
37 | packets. Custom json modules must have 'dumps' and 'loads'
38 | functions that are compatible with the standard library
39 | versions.
40 | """
41 |
42 | def __init__(self, token, userID, reconnection=True, reconnection_attempts=0, reconnection_delay=1,
43 | reconnection_delay_max=50000, randomization_factor=0.5, logger=False, binary=False, json=None,
44 | **kwargs):
45 | self.sid = socketio.Client(logger=False, engineio_logger=False)
46 | self.eventlistener = self.sid
47 |
48 | self.sid.on('connect', self.on_connect)
49 | self.sid.on('message', self.on_message)
50 |
51 | """Similarly implement partial json full and binary json full."""
52 | self.sid.on('1501-json-full', self.on_message1501_json_full)
53 | self.sid.on('1501-json-partial', self.on_message1501_json_partial)
54 |
55 | self.sid.on('1502-json-full', self.on_message1502_json_full)
56 | self.sid.on('1502-json-partial', self.on_message1502_json_partial)
57 |
58 | self.sid.on('1505-json-full', self.on_message1505_json_full)
59 | self.sid.on('1505-json-partial', self.on_message1505_json_partial)
60 |
61 |
62 | self.sid.on('1510-json-full', self.on_message1510_json_full)
63 | self.sid.on('1510-json-partial', self.on_message1510_json_partial)
64 |
65 | self.sid.on('1512-json-full', self.on_message1512_json_full)
66 | self.sid.on('1512-json-partial', self.on_message1512_json_partial)
67 |
68 | self.sid.on('disconnect', self.on_disconnect)
69 |
70 | """Get the root url from config file"""
71 | currDirMain = os.getcwd()
72 | configParser = configparser.ConfigParser()
73 | configFilePath = os.path.join(currDirMain, 'config.ini')
74 | configParser.read(configFilePath)
75 |
76 | self.port = configParser.get('root_url', 'root')
77 | self.userID = userID
78 | publishFormat = 'JSON'
79 | self.broadcastMode = configParser.get('root_url', 'broadcastMode')
80 | self.token = token
81 |
82 | port = f'{self.port}/?token='
83 |
84 | self.connection_url = port + token + '&userID=' + self.userID + '&publishFormat=' + publishFormat + '&broadcastMode=' + self.broadcastMode
85 |
86 | def connect(self, headers={}, transports='websocket', namespaces=None, socketio_path='/apimarketdata/socket.io',
87 | verify=False):
88 | """Connect to a Socket.IO server.
89 | :param verify: Verify SSL
90 | :param url: The URL of the Socket.IO server. It can include custom
91 | query string parameters if required by the server.
92 | :param headers: A dictionary with custom headers to send with the
93 | connection request.
94 | :param transports: The list of allowed transports. Valid transports
95 | are 'polling' and 'websocket'. If not
96 | given, the polling transport is connected first,
97 | then an upgrade to websocket is attempted.
98 | :param namespaces: The list of custom namespaces to connect, in
99 | addition to the default namespace. If not given,
100 | the namespace list is obtained from the registered
101 | event handlers.
102 | :param socketio_path: The endpoint where the Socket.IO server is
103 | installed. The default value is appropriate for
104 | most cases.
105 |
106 | self.url = self.connection_url
107 | self.connection_headers = headers
108 | self.connection_transports = transports
109 | self.connection_namespaces = namespaces
110 | self.socketio_path = socketio_path
111 |
112 | Connect to the socket.
113 | """
114 | url = self.connection_url
115 | """Connected to the socket."""
116 | self.sid.connect(url, headers, transports, namespaces, socketio_path)
117 | self.sid.wait()
118 | """Disconnected from the socket."""
119 | # self.sid.disconnect()
120 |
121 | def on_connect(self):
122 | """Connect from the socket."""
123 | print('Market Data Socket connected successfully!')
124 |
125 | def on_message(self, data):
126 | """On receiving message"""
127 | print('I received a message!' + data)
128 |
129 | def on_message1502_json_full(self, data):
130 | """On receiving message code 1502 full"""
131 | print('I received a 1502 Market depth message!' + data)
132 |
133 | def on_message1512_json_full(self, data):
134 | """On receiving message code 1512 full"""
135 | print('I received a 1512 LTP message!' + data)
136 |
137 | def on_message1505_json_full(self, data):
138 | """On receiving message code 1505 full"""
139 | print('I received a 1505 Candle data message!' + data)
140 |
141 | def on_message1510_json_full(self, data):
142 | """On receiving message code 1510 full"""
143 | print('I received a 1510 Open interest message!' + data)
144 |
145 | def on_message1501_json_full(self, data):
146 | """On receiving message code 1501 full"""
147 | print('I received a 1501 Level1,Touchline message!' + data)
148 |
149 | def on_message1502_json_partial(self, data):
150 | """On receiving message code 1502 partial"""
151 | print('I received a 1502 partial message!' + data)
152 |
153 | def on_message1512_json_partial(self, data):
154 | """On receiving message code 1512 partial"""
155 | print('I received a 1512 LTP message!' + data)
156 |
157 | def on_message1505_json_partial(self, data):
158 | """On receiving message code 1505 partial"""
159 | print('I received a 1505 Candle data message!' + data)
160 |
161 | def on_message1510_json_partial(self, data):
162 | """On receiving message code 1510 partial"""
163 | print('I received a 1510 Open interest message!' + data)
164 |
165 | def on_message1501_json_partial(self, data):
166 | """On receiving message code 1501 partial"""
167 | now = datetime.now()
168 | today = now.strftime("%H:%M:%S")
169 | print(today, 'in main 1501 partial Level1,Touchline message!' + data + ' \n')
170 |
171 | def on_disconnect(self):
172 | """Disconnected from the socket"""
173 | print('Market Data Socket disconnected!')
174 |
175 | def on_error(self, data):
176 | """Error from the socket"""
177 | print('Market Data Error', data)
178 |
179 | def get_emitter(self):
180 | """For getting the event listener"""
181 | return self.eventlistener
182 |
--------------------------------------------------------------------------------
/MarketdataSocketExample.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from Connect import XTSConnect
4 | from MarketDataSocketClient import MDSocket_io
5 |
6 | # MarketData API Credentials
7 | API_KEY = "a27e231381266f63c62157"
8 | API_SECRET = "Akko754#C3"
9 | source = "WEBAPI"
10 |
11 | # Initialise
12 | xt = XTSConnect(API_KEY, API_SECRET, source)
13 |
14 | # Login for authorization token
15 | response = xt.marketdata_login()
16 |
17 | # Store the token and userid
18 | set_marketDataToken = response['result']['token']
19 | set_muserID = response['result']['userID']
20 | print("Login: ", response)
21 |
22 | # Connecting to Marketdata socket
23 | soc = MDSocket_io(set_marketDataToken, set_muserID)
24 |
25 | # Instruments for subscribing
26 | Instruments = [
27 | {'exchangeSegment': 1, 'exchangeInstrumentID': 2885},
28 | {'exchangeSegment': 1, 'exchangeInstrumentID': 26000},
29 | {'exchangeSegment': 2, 'exchangeInstrumentID': 35013}
30 | ]
31 |
32 | # Callback for connection
33 | def on_connect():
34 | """Connect from the socket."""
35 | print('Market Data Socket connected successfully!')
36 |
37 | # # Subscribe to instruments
38 | print('Sending subscription request for Instruments - \n' + str(Instruments))
39 | response = xt.send_subscription(Instruments, 1501)
40 | print('Sent Subscription request!')
41 | print("Subscription response: ", response)
42 |
43 | # Callback on receiving message
44 | def on_message(data):
45 | print('I received a message!')
46 |
47 | # Callback for message code 1501 FULL
48 | def on_message1501_json_full(data):
49 | print('I received a 1501 Touchline message!' + data)
50 |
51 | # Callback for message code 1502 FULL
52 | def on_message1502_json_full(data):
53 | print('I received a 1502 Market depth message!' + data)
54 |
55 | # Callback for message code 1505 FULL
56 | def on_message1505_json_full(data):
57 | print('I received a 1505 Candle data message!' + data)
58 |
59 | # Callback for message code 1510 FULL
60 | def on_message1510_json_full(data):
61 | print('I received a 1510 Open interest message!' + data)
62 |
63 | # Callback for message code 1512 FULL
64 | def on_message1512_json_full(data):
65 | print('I received a 1512 Level1,LTP message!' + data)
66 |
67 | # Callback for message code 1501 PARTIAL
68 | def on_message1501_json_partial(data):
69 | print('I received a 1501, Touchline Event message!' + data)
70 |
71 | # Callback for message code 1502 PARTIAL
72 | def on_message1502_json_partial(data):
73 | print('I received a 1502 Market depth message!' + data)
74 |
75 | # Callback for message code 1505 PARTIAL
76 | def on_message1505_json_partial(data):
77 | print('I received a 1505 Candle data message!' + data)
78 |
79 | # Callback for message code 1510 PARTIAL
80 | def on_message1510_json_partial(data):
81 | print('I received a 1510 Open interest message!' + data)
82 |
83 | # Callback for message code 1512 PARTIAL
84 | def on_message1512_json_partial(data):
85 | print('I received a 1512, LTP Event message!' + data)
86 |
87 | # Callback for disconnection
88 | def on_disconnect():
89 | print('Market Data Socket disconnected!')
90 |
91 |
92 | # Callback for error
93 | def on_error(data):
94 | """Error from the socket."""
95 | print('Market Data Error', data)
96 |
97 |
98 | # Assign the callbacks.
99 | soc.on_connect = on_connect
100 | soc.on_message = on_message
101 | soc.on_message1502_json_full = on_message1502_json_full
102 | soc.on_message1505_json_full = on_message1505_json_full
103 | soc.on_message1510_json_full = on_message1510_json_full
104 | soc.on_message1501_json_full = on_message1501_json_full
105 | soc.on_message1512_json_full = on_message1512_json_full
106 | soc.on_message1502_json_partial = on_message1502_json_partial
107 | soc.on_message1505_json_partial = on_message1505_json_partial
108 | soc.on_message1510_json_partial = on_message1510_json_partial
109 | soc.on_message1501_json_partial = on_message1501_json_partial
110 | soc.on_message1512_json_partial = on_message1512_json_partial
111 | soc.on_disconnect = on_disconnect
112 | soc.on_error = on_error
113 |
114 |
115 | # Event listener
116 | el = soc.get_emitter()
117 | el.on('connect', on_connect)
118 | el.on('1501-json-full', on_message1501_json_full)
119 | el.on('1502-json-full', on_message1502_json_full)
120 | el.on('1505-json-full', on_message1505_json_full)
121 | el.on('1510-json-full', on_message1510_json_full)
122 | el.on('1512-json-full', on_message1512_json_full)
123 | el.on('1501-json-partial', on_message1501_json_partial)
124 | el.on('1502-json-partial', on_message1502_json_partial)
125 | el.on('1505-json-partial', on_message1505_json_partial)
126 | el.on('1510-json-partial', on_message1510_json_partial)
127 | el.on('1512-json-partial', on_message1512_json_partial)
128 |
129 |
130 | # Infinite loop on the main thread. Nothing after this will run.
131 | # You have to use the pre-defined callbacks to manage subscriptions.
132 | soc.connect()
133 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # XTS-SDK-Client Python
2 |
3 | This is the XTS Python API Client library , which has both Marketdata and Interactive services.
4 | API Documentation for XTS-MarketData API and XTS-Trading API can be found in the below link.
5 |
6 | https://symphonyfintech.com/xts-market-data-front-end-api/
7 |
8 | https://symphonyfintech.com/xts-trading-front-end-api-v2/
9 |
10 | ## Installation
11 |
12 | ### Prerequisites
13 |
14 | Python 3.8 or above.
15 | Internet Access.
16 |
17 | Execute below command:
18 | pip install -r requirements.txt
19 |
20 | ### Usage
21 | Check the config.ini file, need to add the root url keep source as WEBAPI and disable_ssl as true
22 | ```
23 | [user]
24 | source=WEBAPI
25 |
26 | [SSL]
27 | disable_ssl=True
28 |
29 | [root_url]
30 | root=https://developers.symphonyfintech.in
31 | broadcastMode=Full
32 | ```
33 |
34 | #### Create XT Connect Object
35 |
36 | ```js
37 | """API Credentials"""
38 | API_KEY = "YOUR_API_KEY_HERE"
39 | API_SECRET = "YOUR_API_SECRET_HERE"
40 | XTS_API_BASE_URL = "https://xts-api.trading"
41 | source = "WEBAPI"
42 |
43 | """Make XTSConnect object by passing your interactive API appKey, secretKey and source"""
44 | xt = XTSConnect(API_KEY, API_SECRET, source)
45 | ```
46 |
47 | #### Login
48 | To login into API call the login service which will return a token. This token will help you to access other services throughout the session.
49 | ```js
50 | """Marketdata Login"""
51 | response = xt.marketdata_login()
52 |
53 | """Interactive Login"""
54 | response = xt.interactive_login()
55 |
56 | ```
57 |
58 | #### Subscribe
59 | To Subscribe to symbol use marketdata API. It returns Subscribe Response object which will contain the tick data like LTP, Open, High etc
60 | ```js
61 | """instruments list"""
62 | instruments = [{'exchangeSegment': 1, 'exchangeInstrumentID': 2885},{'exchangeSegment': 1, 'exchangeInstrumentID': 22}]
63 |
64 | """Send Subscription Request"""
65 | response = xt.send_subscription(
66 | Instruments=instruments,
67 | xtsMessageCode=1502)
68 | ```
69 |
70 | #### Quotes
71 | Quote service returns Asks, Bids and Touchline
72 | ```js
73 | """instruments list"""
74 | instruments = [
75 | {'exchangeSegment': 1, 'exchangeInstrumentID': 2885},
76 | {'exchangeSegment': 1, 'exchangeInstrumentID': 22}]
77 |
78 | """Get Quote Request"""
79 | response = xt.get_quote(
80 | Instruments=instruments,
81 | xtsMessageCode=1504,
82 | publishFormat='JSON')
83 | ```
84 | #### PlaceOrder
85 | To Place an order you need to use Interactive API. Response will contain an orderid.
86 | ```js
87 | """Place Order Request"""
88 | response = xt.place_order(
89 | exchangeSegment=xt.EXCHANGE_NSECM,
90 | exchangeInstrumentID=2885,
91 | productType=xt.PRODUCT_MIS,
92 | orderType=xt.ORDER_TYPE_MARKET,
93 | orderSide=xt.TRANSACTION_TYPE_BUY,
94 | timeInForce=xt.VALIDITY_DAY,
95 | disclosedQuantity=0,
96 | orderQuantity=10,
97 | limitPrice=0,
98 | stopPrice=0,
99 | orderUniqueIdentifier="454845")
100 | ```
101 |
102 | #### CancelOrder
103 | To Cancel an order you need to user Interactive api and In response you will get orderid.
104 | ```js
105 | """Cancel Orders Request"""
106 | response = xt.cancel_order(
107 | appOrderID=OrderID,
108 | orderUniqueIdentifier='454845')
109 | ```
110 |
111 | #### Streams and Events
112 | Events such as TouchLine, MarketData, CandleData, OpenInterest and Index are received from socket.To get those events XTSAPIMarketdataEvents interface needs to be implemented.
113 | Event will be received in the respective overridden methods.
114 | ```js
115 | # Callback for connection
116 | def on_connect():
117 | """Connect from the socket."""
118 | print('Market Data Socket connected successfully!')
119 |
120 | # Subscribe to instruments
121 | response = xt.send_subscription(Instruments, 1501)
122 | print("res: ", response)
123 |
124 |
125 | # Callback on receiving message
126 | def on_message(data):
127 | print('I received a message!')
128 |
129 | # Callback for message code 1502 FULL
130 | def on_message1502_json_full(data):
131 | print('I received a 1502 Market depth message!' + data)
132 | message = "1502 >> #{id} >> {data}".format(id=2885, data=data)
133 | sock.send(message)
134 |
135 | # Callback for message code 1505 FULL
136 | def on_message1505_json_full(data):
137 | print('I received a 1505 Candle data message!' + data)
138 |
139 | # Callback for message code 1510 FULL
140 | def on_message1510_json_full(data):
141 | print('I received a 1510 Open interest message!' + data)
142 |
143 | # Callback for message code 1501 FULL
144 | def on_message1501_json_full(data):
145 | print('I received a 1510 Level1,Touchline message!' + data)
146 | message = "1501 >> #{id} >> {data}".format(id=2885, data=data)
147 | sock.send(message)
148 |
149 | # Callback for message code 1502 PARTIAL
150 | def on_message1502_json_partial(data):
151 | print('I received a 1502 partial message!' + data)
152 |
153 | # Callback for message code 1505 PARTIAL
154 | def on_message1505_json_partial(data):
155 | print('I received a 1505 Candle data message!' + data)
156 |
157 | # Callback for message code 1510 PARTIAL
158 | def on_message1510_json_partial(data):
159 | print('I received a 1510 Open interest message!' + data)
160 |
161 | # Callback for message code 1501 PARTIAL
162 | def on_message1501_json_partial(data):
163 | now = datetime.now()
164 | today = now.strftime("%H:%M:%S")
165 | print(today, 'in main 1501 partial Level1,Touchline message!' + data + ' \n')
166 | print('I received a 1510 Level1,Touchline message!' + data)
167 |
168 | # Callback for disconnection
169 | def on_disconnect():
170 | print('Market Data Socket disconnected!')
171 |
172 | # Callback for error
173 | def on_error(data):
174 | """Error from the socket."""
175 | print('Market Data Error', data)
176 | ```
177 |
178 | ### Examples
179 | Example code demonstrating how to use XTS Api can be found in xts-python-api-sdk
180 |
181 | Example.py : Examples of all the API calls for Interactive as well as Marketdata APIs
182 |
183 | InteractiveSocketExample.py : Interactive Socket Streaming Example
184 |
185 | MarketdataSocketExample.py : Marketdata Socket Streaming Example
186 |
187 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | XTS Connect API client for Python.
4 |
5 | Symphony Fintech Pvt. Ltd.
6 |
7 | License
8 | -------
9 | XTSConnect Python library is licensed.
10 |
11 | The library
12 | -----------
13 | XTS Connect is a set of RESTful APIs that expose
14 | many capabilities required to build a complete
15 | investment and trading platform. Execute orders in
16 | real time, manage user portfolio, stream live market
17 | data (WebSockets), and more, with the simple HTTP API collection
18 |
19 | This module provides an easy to use abstraction over the HTTP APIs.
20 | The HTTP calls have been converted to methods and their JSON responses
21 | are returned as native Python structures, for example, dicts, lists, bools etc.
22 | See the **[XTS Connect API documentation]**
23 | for the complete list of APIs, supported parameters and values, and response formats.
24 |
25 | """
26 |
27 | # from __future__ import unicode_literals, absolute_import
28 |
29 | from XTConnect import Exception
30 | from XTConnect.Connect import XTSConnect
31 |
32 | __all__ = ["XTConnect", "Exception"]
33 |
--------------------------------------------------------------------------------
/__version__.py:
--------------------------------------------------------------------------------
1 | __title__ = "XTS Connect"
2 | __description__ = "The official Python client for the XTS Connect Interactive Trading and Market Data API"
3 | __url__ = "https://www.symphonyfintech.com"
4 | __download_url__ = ""
5 | __version__ = "1.0.0"
6 | __author__ = "Symphony Fintech Pvt ltd."
7 | __author_email__ = ""
8 | __license__ = ""
--------------------------------------------------------------------------------
/config.ini:
--------------------------------------------------------------------------------
1 | [user]
2 | source=WEBAPI
3 |
4 | [SSL]
5 | disable_ssl=True
6 |
7 | [root_url]
8 | root=https://developers.symphonyfintech.in
9 | ;root=http://103.69.170.14:10332
10 | broadcastMode=Full
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | bidict==0.21.2
2 | certifi==2020.12.5
3 | chardet==4.0.0
4 | idna==2.10
5 | python-engineio==3.13.0
6 | python-socketio==4.6.0
7 | requests==2.25.1
8 | six==1.15.0
9 | urllib3==1.26.4
10 | websocket-client==0.57.0
11 |
--------------------------------------------------------------------------------