vortex_api

Vortex API client for Python -- Visit Api Center. Astha Credit & Securities Pvt. Ltd. (c) 2023

License

Rupeezy's Vortex Python library is licensed under the MIT License

The library

Vortex APIs are meant for clients who want to execute orders based on their own strategy programatically and for partners to build their own applications. These apis provide a fast and secure way to place trades, manage positions and access real time market data.

The python client provides an abstraction over these APIs in order to seamlessly write applications and atrategies without the hassle of managing the apis.

Getting started

#!python
from vortex_api import VortexAPI

client = VortexAPI("your api secret","your application id")

#For client login using SSO
client.login_url(callback_param="hi)

# Place order 

client.place_order(client.EXCHANGE_NSE_EQUITY,22,client.TRANSACTION_TYPE_BUY,client.PRODUCT_DELIVERY,client.VARIETY_REGULAR_LIMIT_ORDER,1,1700,0,0,"DAY",1,True)

#Get order book 
client.orders(limit=20,offset=1)
 1"""
 2Vortex API client for Python -- [Visit Api Center](https://vortex.rupeezy.in).
 3Astha Credit & Securities Pvt. Ltd. (c) 2023
 4
 5License
 6-------
 7Rupeezy's Vortex Python library is licensed under the MIT License
 8
 9The library
10-----------
11Vortex APIs are meant for clients who want to execute orders based on their own strategy programatically and for partners to build their own applications. 
12These apis provide a fast and secure way to place trades, manage positions and access real time market data.
13
14The python client provides an abstraction over these APIs in order to seamlessly write applications and atrategies without 
15the hassle of managing the apis. 
16
17Getting started
18---------------
19    #!python
20    from vortex_api import VortexAPI
21
22    client = VortexAPI("your api secret","your application id")
23
24    #For client login using SSO
25    client.login_url(callback_param="hi)
26
27    # Place order 
28
29    client.place_order(client.EXCHANGE_NSE_EQUITY,22,client.TRANSACTION_TYPE_BUY,client.PRODUCT_DELIVERY,client.VARIETY_REGULAR_LIMIT_ORDER,1,1700,0,0,"DAY",1,True)
30
31    #Get order book 
32    client.orders(limit=20,offset=1)
33
34"""
35from __future__ import unicode_literals, absolute_import
36from vortex_api.api import VortexAPI,Constants
37from vortex_api.vortex_feed import VortexFeed
38__all__ = [VortexAPI,Constants,VortexFeed]
@validate_selected_methods(['login', 'place_order', 'modify_order', 'cancel_order', 'get_order_margin', 'historical_candles', 'quotes'])
class VortexAPI:
152@validate_selected_methods(['login','place_order','modify_order','cancel_order','get_order_margin','historical_candles','quotes'])
153class VortexAPI:
154
155    def __init__(self, api_key: str = None , application_id: str = None, base_url: str = "https://vortex-api.rupeezy.in/v2",enable_logging: bool=False) -> None:
156        """
157        Constructor method for VortexAPI class.
158
159        Args:
160            api_key (str, optional): API key for the Vortex API. You can either pass it as an argument or set it as an environment variable named VORTEX_API_KEY.
161            application_id (str, optional): Application ID for the Vortex API. You can either pass it as an argument or set it as an environment variable named VORTEX_APPLICATION_ID.
162            base_url (str, optional): Base URL for the Vortex API. Defaults to "https://vortex-api.rupeezy.in/v2".
163        """
164        if api_key == None:
165            if os.getenv("VORTEX_API_KEY") != None:
166                api_key = os.getenv("VORTEX_API_KEY")
167            else:
168                raise ValueError("API key must be provided either as an argument or through the VORTEX_API_KEY environment variable.")
169        
170        if application_id == None:
171            if os.getenv("VORTEX_APPLICATION_ID") != None:
172                application_id = os.getenv("VORTEX_APPLICATION_ID")
173            else:
174                raise ValueError("Application ID must be provided either as an argument or through the VORTEX_APPLICATION_ID environment variable.")
175        self.api_key = api_key
176        self.application_id = application_id
177        self.base_url = base_url
178        if os.getenv("VORTEX_ACCESS_TOKEN") != None:
179            self.access_token = os.getenv("VORTEX_ACCESS_TOKEN")
180        else:
181            self.access_token = None
182
183        self.enable_logging = enable_logging
184        if self.enable_logging:
185            logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
186    
187    def _make_api_request(self, method: str, endpoint: str, data: dict = None, params=None) -> dict:
188        """
189        Private method to make HTTP requests to the Vortex API.
190
191        Args:
192            method (str): HTTP method for the request (e.g. "GET", "POST", "PUT", "DELETE").
193            endpoint (str): API endpoint for the request.
194            data (dict, optional): Payload data for the request. Defaults to None.
195
196        Returns:
197            dict: Dictionary containing the response data from the API.
198        """
199        if(self.access_token == None):
200            op = {}
201            op["status"]= "error"
202            op["message"] = "please login first"
203            return op
204        bearer_token = f"Bearer {self.access_token}"
205        headers = {"Content-Type": "application/json", "Authorization": bearer_token}
206        url = self.base_url + endpoint
207        if self.enable_logging:
208            logging.debug(f"Making network call to {url}  , params: {params}, data: {data}, headers: {headers}")
209        response = requests.request(method, url, headers=headers, json=data,params=params)
210        if self.enable_logging:
211            logging.debug(f"Response received from {url}  , body: {response.json()}")
212        response.raise_for_status()
213        return response.json()
214    
215    def _make_unauth_request(self, method: str, endpoint: str, data: dict = None, params: dict = None) -> dict:
216        """
217        Private method to make HTTP requests to the Vortex API.
218
219        Args:
220            method (str): HTTP method for the request (e.g. "GET", "POST", "PUT", "DELETE").
221            endpoint (str): API endpoint for the request.
222            data (dict, optional): Payload data for the request. Defaults to None.
223
224        Returns:
225            dict: Dictionary containing the response data from the API.
226        """
227        headers = {"Content-Type": "application/json", "x-api-key": self.api_key}
228        url = self.base_url + endpoint
229        if self.enable_logging:
230            logging.debug(f"Making network call to {url}  , params: {params}, data: {data}, headers: {headers}")
231        response = requests.request(method, url, headers=headers, json=data)
232        response.raise_for_status()
233        if self.enable_logging:
234            logging.debug(f"Response received from {url}  , body: {response.json()}")
235        return response.json()
236    
237    def login(self, client_code: str, password: str, totp: str)->dict:
238        """
239        Depricating Soon. Use SSO Login instead. Login using password and totp directly
240        
241        Documentation:
242            https://vortex.rupeezy.in/docs/latest/authentication/
243
244        Args:
245            client_code(str): Client Code of the account
246            password(str): Password of the account
247            totp(str): TOTP generated using third party apps like google authenticator etc. 
248
249        Returns:
250            dict: JSON response containing the details of the user
251        """
252        endpoint = "/user/login"
253        data = {
254            "client_code": client_code,
255            "password": password,
256            "totp": totp,
257            "application_id": self.application_id
258        }
259        res = self._make_unauth_request("POST", endpoint= endpoint, data=data)
260        self._setup_client_code(login_object=res)
261        return res
262    
263    def download_master(self) -> dict:
264        """
265        Download list of all available instruments and their details across all exchanges
266
267        Documentation:
268            https://vortex.rupeezy.in/docs/latest/historical/#instrument-list
269
270        Returns:
271            dict: CSV Array of all instruments. The first row contains headers
272        """
273        endpoint = "https://static.rupeezy.in/master.csv"
274        with requests.Session() as s: 
275            download = s.get(url=endpoint)
276            decoded_content = download.content.decode('utf-8')
277            cr = csv.reader(decoded_content.splitlines(), delimiter=',')
278            my_list = list(cr)
279            return my_list
280    
281    def place_order(self,exchange: Constants.ExchangeTypes, token: int, transaction_type: Constants.TransactionSides, product: Constants.ProductTypes, variety: Constants.VarietyTypes, 
282                quantity: int, price: float, trigger_price: float, disclosed_quantity: int, validity: Constants.ValidityTypes) -> dict:
283        """
284        Place an order for a specific security
285
286        Documentation:
287            https://vortex.rupeezy.in/docs/latest/order/#placing-an-order
288
289        Args:
290            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
291            token (int): Security token of the scrip. It can be found in the scripmaster file
292            transaction_type (Constants.TransactionSides): Possible values: [BUY, SELL]
293            product (Constants.ProductTypes): Possible values: [INTRADAY, DELIVERY, MTF]. MTF product can only be used in NSE_EQ exchange.
294            variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. 
295                        MKT means that the trade will happen at market price
296            quantity (int): For exchange NSE_FO, if you want to trade in 2 lots and lot size is 50, you should pass 100. 
297                            In all other exchanges, you should pass just the number of lots. For example, in MCX_FO, 
298                            if you want to trade 5 lots, you should pass just 5.
299            price (float): Price should be an integer multiple of Tick Size. For example, IDEA's tick size is 0.05. 
300                        So the price entered can be 9.5 or 9.65. It cannot be 9.67. 
301                        In case of market orders, you should send the Last Trade Price received from the Quote API or Websocket API
302            trigger_price (float): To be used for Stop loss orders. For BUY side SL orders, trigger_price should be 
303                                lesser than price. for SELL side SL orders, trigger_price should be greater than price.
304            disclosed_quantity (int): Can be any number lesser than or equal to quantity, including 0
305            validity (Constants.ValidityTypes): Can be DAY for orders which are valid throughout the day, or IOC. 
306                            IOC order will be cancelled if it is not traded immediately
307        Returns:
308            dict: JSON response containing the details of the placed order
309
310        Raises:
311            HTTPError: If any HTTP error occurs during the API call
312        """
313
314        endpoint = "/trading/orders/regular"
315        if validity == Constants.ValidityTypes.FULL_DAY: 
316            validity_days = 1 
317            is_amo = False
318        elif validity == Constants.ValidityTypes.IMMEDIATE_OR_CANCEL: 
319            validity_days = 0 
320            is_amo = False
321        else: 
322            validity_days = 1 
323            is_amo = True 
324
325        data = {
326            "exchange": exchange,
327            "token": token,
328            "transaction_type": transaction_type,
329            "product": product,
330            "variety": variety,
331            "quantity": quantity,
332            "price": price,
333            "trigger_price": trigger_price,
334            "disclosed_quantity": disclosed_quantity,
335            "validity": validity,
336            "validity_days": validity_days,
337            "is_amo": is_amo
338        }
339        
340        return self._make_api_request("POST", endpoint, data=data)
341    
342    def modify_order(self, order_id: str, variety: Constants.VarietyTypes, quantity: int, traded_quantity: int, price: float, trigger_price: float, disclosed_quantity: int, validity: Constants.ValidityTypes) -> dict:
343        """
344        Method to modify an order using the Vortex API.
345
346        Documentation:
347            https://vortex.rupeezy.in/docs/latest/order/#modifying-an-order
348
349        Args:
350            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
351            order_id (str): The unique ID of the order to modify.
352            variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. 
353                    MKT means that the trade will happen at market price
354            quantity (int): The new quantity for the order.
355            traded_quantity (int): The quantity of the order that has already been traded.
356            price (float): The new price for the order.
357            trigger_price (float): The new trigger price for the order. Required for SL and SL-M orders.
358            disclosed_quantity (int): The new quantity to be disclosed publicly.
359            validity (Constants.ValidityTypes): The new validity for the order (e.g. DAY, IOC, GTD).
360
361        Returns:
362            dict: Dictionary containing the response data from the API.
363        """
364
365        endpoint = f"/trading/orders/regular/{order_id}"
366        if validity == Constants.ValidityTypes.FULL_DAY: 
367            validity_days = 1 
368        elif validity == Constants.ValidityTypes.IMMEDIATE_OR_CANCEL: 
369            validity_days = 0 
370        else: 
371            validity_days = 1 
372
373        data = {
374            "variety": variety,
375            "quantity": quantity,
376            "traded_quantity": traded_quantity,
377            "price": price,
378            "trigger_price": trigger_price,
379            "disclosed_quantity": disclosed_quantity,
380            "validity": validity,
381            "validity_days": validity_days
382        }
383        return self._make_api_request("PUT", endpoint, data=data)
384    
385    def cancel_order(self, order_id: str) -> dict:
386        """
387        Method to cancel an order using the Vortex API.
388
389        Documentation:
390            https://vortex.rupeezy.in/docs/latest/order/#cancel-an-order
391
392        Args:
393            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
394            order_id (str): The unique ID of the order to cancel.
395
396        Returns:
397            dict: Dictionary containing the response data from the API.
398        """
399        
400        endpoint = f"/trading/orders/regular/{order_id}"
401        return self._make_api_request("DELETE", endpoint)
402
403    def orders(self,limit: int, offset: int) -> dict:
404        """
405        Method to get all orders.
406
407        Documentation:
408            https://vortex.rupeezy.in/docs/latest/order/#fetching-order-book
409
410        Args:
411            limit (int): Limit is the number of orders to be fetched. 
412            offset (int): Offset should atleast be 1 
413
414        Returns:
415            dict: Dictionary containing the response data from the API.
416        """
417        endpoint = f"/trading/orders?limit={limit}&offset={offset}"
418        return self._make_api_request("GET", endpoint)
419    
420    def order_history(self,order_id: str) -> dict:
421        """
422        Method to get the order history of a particular order
423
424        Documentation:
425            https://vortex.rupeezy.in/docs/latest/order/
426
427        Args:
428            order_id (str): Order id for which history has to be fetched
429
430        Returns:
431            dict: Dictionary containing the response data from the API.
432        """
433        endpoint = f"/trading/orders/{order_id}"
434        return self._make_api_request("GET", endpoint)
435
436    def positions(self) -> dict:
437        """
438        Method to get the position book using the Vortex API.
439
440        Documentation:
441            https://vortex.rupeezy.in/docs/latest/positions/#fetch-all-positions
442
443        Returns:
444            dict: Dictionary containing the response data from the API.
445        """
446        endpoint = f"/trading/portfolio/positions"
447        return self._make_api_request("GET", endpoint)
448    
449    def holdings(self) -> dict:
450        """
451        Method to get the holdings of the user using the Vortex API.
452
453        Documentation:    
454            https://vortex.rupeezy.in/docs/latest/holdings/
455
456        Returns:
457            dict: Dictionary containing the response data from the API.
458        """
459        endpoint = "/trading/portfolio/holdings"
460        return self._make_api_request("GET", endpoint)
461    
462    def trades(self) -> dict:
463        """
464        Method to get today's trades of the user using the Vortex API.
465
466        Documentation:    
467            https://vortex.rupeezy.in/docs/latest/positions/#get-trades
468
469        Returns:
470            dict: Dictionary containing the response data from the API.
471        """
472        endpoint = "/trading/trades"
473        return self._make_api_request("GET", endpoint)
474    
475    def funds(self) -> dict:
476        """
477        Method to get the funds of the user using the Vortex API.
478
479        Documentation:    
480            https://vortex.rupeezy.in/docs/latest/user/#available-funds
481
482        Returns:
483            dict: Dictionary containing the response data from the API.
484        """
485        endpoint = "/user/funds"
486        return self._make_api_request("GET", endpoint)
487    
488    def get_order_margin(self, exchange: Constants.ExchangeTypes, token: int, transaction_type: Constants.TransactionSides, product: Constants.ProductTypes, variety: Constants.VarietyTypes, 
489                     quantity: int, price: float,mode: Constants.OrderMarginModes, old_quantity: int = 0 , old_price: float = 0 ) -> dict:
490        """
491        Get the margin required for placing an order for a specific security.
492
493        Documentation:    
494            https://vortex.rupeezy.in/docs/latest/margin/#order-margin
495
496        Args:
497            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
498            token (int): Security token of the scrip. It can be found in the scripmaster file
499            transaction_type (Constants.TransactionSides): Possible values: [BUY, SELL]
500            product (Constants.ProductTypes): Possible values: [INTRADAY, DELIVERY, MTF]. MTF product can only be used in NSE_EQ exchange.
501            variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. 
502                        MKT means that the trade will happen at market price
503            quantity (int): For exchange NSE_FO, if you want to trade in 2 lots and lot size is 50, you should pass 100. 
504                            In all other exchanges, you should pass just the number of lots. For example, in MCX_FO, 
505                            if you want to trade 5 lots, you should pass just 5.
506            price (float): Price should be an integer multiple of Tick Size. For example, IDEA's tick size is 0.05. 
507                        So the price entered can be 9.5 or 9.65. It cannot be 9.67. 
508                        In case of market orders, you should send the Last Trade Price received from the Quote API or Websocket API
509            mode (Constants.OrderMarginModes): Possible values: [NEW, MODIFY] , Whether you are trying to modify an existing order or placing a new order.            
510            old_quantity (int): For NSE_FO segments, old_quantity is lots * lot_size. For all others, enter just the number of lots. Required if mode is MODIFY
511            old_price (float): Old Price in INR. Required if mode is MODIFY
512
513        Returns:
514            dict: JSON response containing the details of the margin required to place the order
515
516        Raises:
517            HTTPError: If any HTTP error occurs during the API call
518        """
519        
520        endpoint = "/margins/order"
521        
522        data = {
523            "exchange": exchange,
524            "token": token,
525            "transaction_type": transaction_type,
526            "product": product,
527            "variety": variety,
528            "quantity": quantity,
529            "price": price,
530            "old_quantity": old_quantity,
531            "old_price": old_price,
532            "mode": mode,
533        }
534        return self._make_api_request("POST", endpoint, data=data)
535    
536    def brokerage_plan(self)-> dict: 
537        """
538        Get brokerage plan details of the user.
539
540        Documentation:    
541            https://vortex.rupeezy.in/docs/latest/user/
542
543        Returns:
544            dict: JSON response containing the details of the brokerage plan of the user
545        """
546        endpoint = "/user/profile/brokerage"
547        return self._make_api_request("GET", endpoint, data=None,params=None)
548    
549    def quotes(self, instruments: list, mode: Constants.QuoteModes)-> dict: 
550        """
551        Gets quotes of up to 1000 instruments at a time. 
552
553        Documentation:    
554            https://vortex.rupeezy.in/docs/latest/historical/#fetch-price-quotes
555
556        Args:
557            instrument(list): List of instruments. The items should be like ( "NSE_EQ-22", "NSE_FO-1234")
558            mode(Constants.QuoteModes): Quote mode. Can be ["ltp","ohlcv", "full"]. LTP quotes just give the last trade price, ohlc give open, high, low, close and volume, full mode also gives depth.
559
560        Returns:
561            dict: JSON response containing quotes. It is possible that not all the symbol identifiers you passed had a quote available. Those inputs will be missing from the response.
562            Also, the order of output might be different than the order of input
563        """
564        endpoint = "/data/quotes"
565        params = {"q": instruments,"mode": mode}
566        return self._make_api_request("GET", endpoint, data=None,params=params)
567    
568    def historical_candles(self, exchange: Constants.ExchangeTypes, token: int, to: datetime.datetime , start: datetime.datetime, resolution: Constants.Resolutions): 
569        """
570        Gets historical candle data of a particular instrument. 
571
572        Documentation:    
573            https://vortex.rupeezy.in/docs/latest/historical/#fetch-historical-candle-data
574
575        Args:
576            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
577            token (int): Security token of the scrip. It can be found in the instruments master file: 
578            to (datetime): datetime up till when you want to receive candles 
579            start (datetime): datetime from when you want to receive candles 
580            resolution (Constants.Resolutions): resoulution of the candle. can be "1", "2", "3", "4", "5", "10", "15", "30", "45", "60", "120", "180", "240", "1D", "1W", "1M"
581        
582        Returns:
583            dict: JSON response containing the historical candles
584        """
585
586        if not isinstance(token, int):
587            raise TypeError("token must be an integer")
588        if not isinstance(to,datetime.datetime): 
589            raise TypeError("to must be a datetime")
590        if not isinstance(start,datetime.datetime): 
591            raise TypeError("start must be a datetime")
592
593
594        endpoint = "/data/history"
595        params = {"exchange": exchange,"token": token , "to": int(to.timestamp()), "from": int(start.timestamp()), "resolution": resolution}
596        return self._make_api_request("GET", endpoint, data=None,params=params)
597
598    def login_url(self, callback_param: str) -> str:
599        """
600        Returns the login URL for the Vortex API.
601
602        Documentation:
603            https://vortex.rupeezy.in/docs/latest/authentication/
604
605        Returns:
606            str: The login URL for the Vortex API.
607        """
608
609        return f"https://flow.rupeezy.in?applicationId={self.application_id}&cb_param={callback_param}"
610    
611    def exchange_token(self,auth_code: str) -> dict:
612        """
613        Exchange the auth code received from the login URL for an access token.
614
615        Documentation:
616            https://vortex.rupeezy.in/docs/latest/authentication/
617
618        Args:
619            auth_code (str): The authorization code received from the login URL.
620
621        Returns:
622            dict: JSON response containing the details of the user
623        """
624
625        endpoint = "/user/session"
626        data = {
627            "token": auth_code,
628            "applicationId": self.application_id, 
629            "checksum": self._sha256_hash(f"{self.application_id}{auth_code}{self.api_key}")
630        }
631        res = self._make_unauth_request("POST", endpoint= endpoint, data=data)
632        self._setup_client_code(login_object=res)
633        return res
634
635    def _sha256_hash(self,text: str) -> str:
636        sha = hashlib.sha256()
637        sha.update(text.encode('utf-8'))
638        return sha.hexdigest()
639
640    def _setup_client_code(self, login_object: dict) -> bool:
641        """
642        Sets up access token after login
643
644        Args:
645            login_object(dict): Login object received
646
647        Returns:
648            (bool): Whether successful or not
649        """
650
651        if (('data' in login_object ) and login_object["data"] != None and login_object["data"]["access_token"] != None):
652            self.access_token = login_object["data"]["access_token"]
653            return True
654
655        return False
656
657    def save_backtest_result(self, stats, name: str, symbol: str = "", description: str = "", tags: list = None) -> dict:
658        """
659        Save backtest results to Rupeezy for viewing on the developer portal.
660
661        Supports multiple backtesting libraries (auto-detected from the result type):
662        - **backtesting.py**: pass the stats object from Backtest.run()
663        - **vectorbt**: pass a vbt.Portfolio object
664        - **backtrader**: pass the strategy from cerebro.run() (i.e. results[0])
665
666        Args:
667            stats: The result object from any supported backtesting library.
668            name (str): A label for this backtest run (e.g. "SMA Crossover v2").
669            symbol (str, optional): Primary instrument symbol.
670            description (str, optional): Notes about this run.
671            tags (list[str], optional): Tags for filtering (e.g. ["intraday", "nifty"]).
672
673        Returns:
674            dict: { "status": "success", "backtest_id": "bt_abc123", "url": "https://..." }
675        """
676        from .backtest import serialize_stats
677        payload = serialize_stats(stats, name, symbol, description, tags or [])
678        endpoint = "/strategies/backtests"
679
680        return self._make_api_request("POST", endpoint, data=payload)
681
682    def save_optimization_result(
683        self,
684        stats,
685        heatmap,
686        name: str,
687        symbol: str = "",
688        description: str = "",
689        maximize="Sharpe Ratio",
690        param_ranges: dict = None,
691    ) -> dict:
692        """
693        Save optimization results to Rupeezy for viewing on the developer portal.
694
695        Supports multiple backtesting libraries (auto-detected from the result type):
696        - **backtesting.py**: pass stats + heatmap from bt.optimize(return_heatmap=True)
697        - **vectorbt**: pass Portfolio + metric Series from multi-param run
698        - **backtrader**: pass results list from cerebro.run() after optstrategy()
699
700        Args:
701            stats: The result object from any supported backtesting library.
702            heatmap: The heatmap/metric Series (backtesting.py/vectorbt) or
703                     metric extraction callable (backtrader).
704            name (str): A label for this optimization run (e.g. "SMA Grid Search").
705            symbol (str, optional): Primary instrument symbol (e.g. "NIFTY").
706            description (str, optional): Notes about this optimization run.
707            maximize: The metric that was optimized. Should match the `maximize`
708                      argument passed to Backtest.optimize(). Can be a string
709                      metric name (e.g. "Sharpe Ratio") or a callable.
710                      Defaults to "Sharpe Ratio".
711            param_ranges (dict, optional): Explicit parameter range definitions.
712                Keys are parameter names, values are range() objects or lists.
713                Example: {"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)}
714                If not provided, ranges are inferred from the heatmap index.
715
716        Returns:
717            dict: {"status": "success", "optimization_id": "opt_xxx", "backtest_id": "bt_xxx"}
718
719        Example::
720
721            stats, heatmap = bt.optimize(
722                sma_fast=range(5, 51, 5),
723                sma_slow=range(20, 201, 10),
724                maximize='Sharpe Ratio',
725                return_heatmap=True,
726            )
727            client.save_optimization_result(
728                stats=stats,
729                heatmap=heatmap,
730                name="SMA Crossover Grid Search",
731                symbol="NIFTY",
732                maximize='Sharpe Ratio',
733                param_ranges={"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)},
734            )
735        """
736        is_maximize = True
737        objective_metric = maximize
738
739        if isinstance(maximize, bool):
740            is_maximize = maximize
741            objective_metric = "Sharpe Ratio"
742
743        from .backtest import serialize_optimization
744        payload = serialize_optimization(
745            result=stats,
746            heatmap=heatmap,
747            name=name,
748            symbol=symbol,
749            description=description,
750            objective_metric=objective_metric,
751            maximize=is_maximize,
752            param_ranges=param_ranges,
753        )
754        endpoint = "/strategies/optimizations"
755        return self._make_api_request("POST", endpoint, data=payload)
VortexAPI( api_key: str = None, application_id: str = None, base_url: str = 'https://vortex-api.rupeezy.in/v2', enable_logging: bool = False)
155    def __init__(self, api_key: str = None , application_id: str = None, base_url: str = "https://vortex-api.rupeezy.in/v2",enable_logging: bool=False) -> None:
156        """
157        Constructor method for VortexAPI class.
158
159        Args:
160            api_key (str, optional): API key for the Vortex API. You can either pass it as an argument or set it as an environment variable named VORTEX_API_KEY.
161            application_id (str, optional): Application ID for the Vortex API. You can either pass it as an argument or set it as an environment variable named VORTEX_APPLICATION_ID.
162            base_url (str, optional): Base URL for the Vortex API. Defaults to "https://vortex-api.rupeezy.in/v2".
163        """
164        if api_key == None:
165            if os.getenv("VORTEX_API_KEY") != None:
166                api_key = os.getenv("VORTEX_API_KEY")
167            else:
168                raise ValueError("API key must be provided either as an argument or through the VORTEX_API_KEY environment variable.")
169        
170        if application_id == None:
171            if os.getenv("VORTEX_APPLICATION_ID") != None:
172                application_id = os.getenv("VORTEX_APPLICATION_ID")
173            else:
174                raise ValueError("Application ID must be provided either as an argument or through the VORTEX_APPLICATION_ID environment variable.")
175        self.api_key = api_key
176        self.application_id = application_id
177        self.base_url = base_url
178        if os.getenv("VORTEX_ACCESS_TOKEN") != None:
179            self.access_token = os.getenv("VORTEX_ACCESS_TOKEN")
180        else:
181            self.access_token = None
182
183        self.enable_logging = enable_logging
184        if self.enable_logging:
185            logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

Constructor method for VortexAPI class.

Arguments:
  • api_key (str, optional): API key for the Vortex API. You can either pass it as an argument or set it as an environment variable named VORTEX_API_KEY.
  • application_id (str, optional): Application ID for the Vortex API. You can either pass it as an argument or set it as an environment variable named VORTEX_APPLICATION_ID.
  • base_url (str, optional): Base URL for the Vortex API. Defaults to "https://vortex-api.rupeezy.in/v2".
def login(self, client_code: str, password: str, totp: str) -> dict:
237    def login(self, client_code: str, password: str, totp: str)->dict:
238        """
239        Depricating Soon. Use SSO Login instead. Login using password and totp directly
240        
241        Documentation:
242            https://vortex.rupeezy.in/docs/latest/authentication/
243
244        Args:
245            client_code(str): Client Code of the account
246            password(str): Password of the account
247            totp(str): TOTP generated using third party apps like google authenticator etc. 
248
249        Returns:
250            dict: JSON response containing the details of the user
251        """
252        endpoint = "/user/login"
253        data = {
254            "client_code": client_code,
255            "password": password,
256            "totp": totp,
257            "application_id": self.application_id
258        }
259        res = self._make_unauth_request("POST", endpoint= endpoint, data=data)
260        self._setup_client_code(login_object=res)
261        return res

Depricating Soon. Use SSO Login instead. Login using password and totp directly

Documentation:

https://vortex.rupeezy.in/docs/latest/authentication/

Arguments:
  • client_code(str): Client Code of the account
  • password(str): Password of the account
  • totp(str): TOTP generated using third party apps like google authenticator etc.
Returns:

dict: JSON response containing the details of the user

def download_master(self) -> dict:
263    def download_master(self) -> dict:
264        """
265        Download list of all available instruments and their details across all exchanges
266
267        Documentation:
268            https://vortex.rupeezy.in/docs/latest/historical/#instrument-list
269
270        Returns:
271            dict: CSV Array of all instruments. The first row contains headers
272        """
273        endpoint = "https://static.rupeezy.in/master.csv"
274        with requests.Session() as s: 
275            download = s.get(url=endpoint)
276            decoded_content = download.content.decode('utf-8')
277            cr = csv.reader(decoded_content.splitlines(), delimiter=',')
278            my_list = list(cr)
279            return my_list

Download list of all available instruments and their details across all exchanges

Documentation:

https://vortex.rupeezy.in/docs/latest/historical/#instrument-list

Returns:

dict: CSV Array of all instruments. The first row contains headers

def place_order( self, exchange: vortex_api.Constants.ExchangeTypes, token: int, transaction_type: vortex_api.Constants.TransactionSides, product: vortex_api.Constants.ProductTypes, variety: vortex_api.Constants.VarietyTypes, quantity: int, price: float, trigger_price: float, disclosed_quantity: int, validity: vortex_api.Constants.ValidityTypes) -> dict:
281    def place_order(self,exchange: Constants.ExchangeTypes, token: int, transaction_type: Constants.TransactionSides, product: Constants.ProductTypes, variety: Constants.VarietyTypes, 
282                quantity: int, price: float, trigger_price: float, disclosed_quantity: int, validity: Constants.ValidityTypes) -> dict:
283        """
284        Place an order for a specific security
285
286        Documentation:
287            https://vortex.rupeezy.in/docs/latest/order/#placing-an-order
288
289        Args:
290            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
291            token (int): Security token of the scrip. It can be found in the scripmaster file
292            transaction_type (Constants.TransactionSides): Possible values: [BUY, SELL]
293            product (Constants.ProductTypes): Possible values: [INTRADAY, DELIVERY, MTF]. MTF product can only be used in NSE_EQ exchange.
294            variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. 
295                        MKT means that the trade will happen at market price
296            quantity (int): For exchange NSE_FO, if you want to trade in 2 lots and lot size is 50, you should pass 100. 
297                            In all other exchanges, you should pass just the number of lots. For example, in MCX_FO, 
298                            if you want to trade 5 lots, you should pass just 5.
299            price (float): Price should be an integer multiple of Tick Size. For example, IDEA's tick size is 0.05. 
300                        So the price entered can be 9.5 or 9.65. It cannot be 9.67. 
301                        In case of market orders, you should send the Last Trade Price received from the Quote API or Websocket API
302            trigger_price (float): To be used for Stop loss orders. For BUY side SL orders, trigger_price should be 
303                                lesser than price. for SELL side SL orders, trigger_price should be greater than price.
304            disclosed_quantity (int): Can be any number lesser than or equal to quantity, including 0
305            validity (Constants.ValidityTypes): Can be DAY for orders which are valid throughout the day, or IOC. 
306                            IOC order will be cancelled if it is not traded immediately
307        Returns:
308            dict: JSON response containing the details of the placed order
309
310        Raises:
311            HTTPError: If any HTTP error occurs during the API call
312        """
313
314        endpoint = "/trading/orders/regular"
315        if validity == Constants.ValidityTypes.FULL_DAY: 
316            validity_days = 1 
317            is_amo = False
318        elif validity == Constants.ValidityTypes.IMMEDIATE_OR_CANCEL: 
319            validity_days = 0 
320            is_amo = False
321        else: 
322            validity_days = 1 
323            is_amo = True 
324
325        data = {
326            "exchange": exchange,
327            "token": token,
328            "transaction_type": transaction_type,
329            "product": product,
330            "variety": variety,
331            "quantity": quantity,
332            "price": price,
333            "trigger_price": trigger_price,
334            "disclosed_quantity": disclosed_quantity,
335            "validity": validity,
336            "validity_days": validity_days,
337            "is_amo": is_amo
338        }
339        
340        return self._make_api_request("POST", endpoint, data=data)

Place an order for a specific security

Documentation:

https://vortex.rupeezy.in/docs/latest/order/#placing-an-order

Arguments:
  • exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
  • token (int): Security token of the scrip. It can be found in the scripmaster file
  • transaction_type (Constants.TransactionSides): Possible values: [BUY, SELL]
  • product (Constants.ProductTypes): Possible values: [INTRADAY, DELIVERY, MTF]. MTF product can only be used in NSE_EQ exchange.
  • variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. MKT means that the trade will happen at market price
  • quantity (int): For exchange NSE_FO, if you want to trade in 2 lots and lot size is 50, you should pass 100. In all other exchanges, you should pass just the number of lots. For example, in MCX_FO, if you want to trade 5 lots, you should pass just 5.
  • price (float): Price should be an integer multiple of Tick Size. For example, IDEA's tick size is 0.05. So the price entered can be 9.5 or 9.65. It cannot be 9.67. In case of market orders, you should send the Last Trade Price received from the Quote API or Websocket API
  • trigger_price (float): To be used for Stop loss orders. For BUY side SL orders, trigger_price should be lesser than price. for SELL side SL orders, trigger_price should be greater than price.
  • disclosed_quantity (int): Can be any number lesser than or equal to quantity, including 0
  • validity (Constants.ValidityTypes): Can be DAY for orders which are valid throughout the day, or IOC. IOC order will be cancelled if it is not traded immediately
Returns:

dict: JSON response containing the details of the placed order

Raises:
  • HTTPError: If any HTTP error occurs during the API call
def modify_order( self, order_id: str, variety: vortex_api.Constants.VarietyTypes, quantity: int, traded_quantity: int, price: float, trigger_price: float, disclosed_quantity: int, validity: vortex_api.Constants.ValidityTypes) -> dict:
342    def modify_order(self, order_id: str, variety: Constants.VarietyTypes, quantity: int, traded_quantity: int, price: float, trigger_price: float, disclosed_quantity: int, validity: Constants.ValidityTypes) -> dict:
343        """
344        Method to modify an order using the Vortex API.
345
346        Documentation:
347            https://vortex.rupeezy.in/docs/latest/order/#modifying-an-order
348
349        Args:
350            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
351            order_id (str): The unique ID of the order to modify.
352            variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. 
353                    MKT means that the trade will happen at market price
354            quantity (int): The new quantity for the order.
355            traded_quantity (int): The quantity of the order that has already been traded.
356            price (float): The new price for the order.
357            trigger_price (float): The new trigger price for the order. Required for SL and SL-M orders.
358            disclosed_quantity (int): The new quantity to be disclosed publicly.
359            validity (Constants.ValidityTypes): The new validity for the order (e.g. DAY, IOC, GTD).
360
361        Returns:
362            dict: Dictionary containing the response data from the API.
363        """
364
365        endpoint = f"/trading/orders/regular/{order_id}"
366        if validity == Constants.ValidityTypes.FULL_DAY: 
367            validity_days = 1 
368        elif validity == Constants.ValidityTypes.IMMEDIATE_OR_CANCEL: 
369            validity_days = 0 
370        else: 
371            validity_days = 1 
372
373        data = {
374            "variety": variety,
375            "quantity": quantity,
376            "traded_quantity": traded_quantity,
377            "price": price,
378            "trigger_price": trigger_price,
379            "disclosed_quantity": disclosed_quantity,
380            "validity": validity,
381            "validity_days": validity_days
382        }
383        return self._make_api_request("PUT", endpoint, data=data)

Method to modify an order using the Vortex API.

Documentation:

https://vortex.rupeezy.in/docs/latest/order/#modifying-an-order

Arguments:
  • exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
  • order_id (str): The unique ID of the order to modify.
  • variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. MKT means that the trade will happen at market price
  • quantity (int): The new quantity for the order.
  • traded_quantity (int): The quantity of the order that has already been traded.
  • price (float): The new price for the order.
  • trigger_price (float): The new trigger price for the order. Required for SL and SL-M orders.
  • disclosed_quantity (int): The new quantity to be disclosed publicly.
  • validity (Constants.ValidityTypes): The new validity for the order (e.g. DAY, IOC, GTD).
Returns:

dict: Dictionary containing the response data from the API.

def cancel_order(self, order_id: str) -> dict:
385    def cancel_order(self, order_id: str) -> dict:
386        """
387        Method to cancel an order using the Vortex API.
388
389        Documentation:
390            https://vortex.rupeezy.in/docs/latest/order/#cancel-an-order
391
392        Args:
393            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
394            order_id (str): The unique ID of the order to cancel.
395
396        Returns:
397            dict: Dictionary containing the response data from the API.
398        """
399        
400        endpoint = f"/trading/orders/regular/{order_id}"
401        return self._make_api_request("DELETE", endpoint)

Method to cancel an order using the Vortex API.

Documentation:

https://vortex.rupeezy.in/docs/latest/order/#cancel-an-order

Arguments:
  • exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
  • order_id (str): The unique ID of the order to cancel.
Returns:

dict: Dictionary containing the response data from the API.

def orders(self, limit: int, offset: int) -> dict:
403    def orders(self,limit: int, offset: int) -> dict:
404        """
405        Method to get all orders.
406
407        Documentation:
408            https://vortex.rupeezy.in/docs/latest/order/#fetching-order-book
409
410        Args:
411            limit (int): Limit is the number of orders to be fetched. 
412            offset (int): Offset should atleast be 1 
413
414        Returns:
415            dict: Dictionary containing the response data from the API.
416        """
417        endpoint = f"/trading/orders?limit={limit}&offset={offset}"
418        return self._make_api_request("GET", endpoint)

Method to get all orders.

Documentation:

https://vortex.rupeezy.in/docs/latest/order/#fetching-order-book

Arguments:
  • limit (int): Limit is the number of orders to be fetched.
  • offset (int): Offset should atleast be 1
Returns:

dict: Dictionary containing the response data from the API.

def order_history(self, order_id: str) -> dict:
420    def order_history(self,order_id: str) -> dict:
421        """
422        Method to get the order history of a particular order
423
424        Documentation:
425            https://vortex.rupeezy.in/docs/latest/order/
426
427        Args:
428            order_id (str): Order id for which history has to be fetched
429
430        Returns:
431            dict: Dictionary containing the response data from the API.
432        """
433        endpoint = f"/trading/orders/{order_id}"
434        return self._make_api_request("GET", endpoint)

Method to get the order history of a particular order

Documentation:

https://vortex.rupeezy.in/docs/latest/order/

Arguments:
  • order_id (str): Order id for which history has to be fetched
Returns:

dict: Dictionary containing the response data from the API.

def positions(self) -> dict:
436    def positions(self) -> dict:
437        """
438        Method to get the position book using the Vortex API.
439
440        Documentation:
441            https://vortex.rupeezy.in/docs/latest/positions/#fetch-all-positions
442
443        Returns:
444            dict: Dictionary containing the response data from the API.
445        """
446        endpoint = f"/trading/portfolio/positions"
447        return self._make_api_request("GET", endpoint)

Method to get the position book using the Vortex API.

Documentation:

https://vortex.rupeezy.in/docs/latest/positions/#fetch-all-positions

Returns:

dict: Dictionary containing the response data from the API.

def holdings(self) -> dict:
449    def holdings(self) -> dict:
450        """
451        Method to get the holdings of the user using the Vortex API.
452
453        Documentation:    
454            https://vortex.rupeezy.in/docs/latest/holdings/
455
456        Returns:
457            dict: Dictionary containing the response data from the API.
458        """
459        endpoint = "/trading/portfolio/holdings"
460        return self._make_api_request("GET", endpoint)

Method to get the holdings of the user using the Vortex API.

Documentation:
https://vortex.rupeezy.in/docs/latest/holdings/

Returns:

dict: Dictionary containing the response data from the API.

def trades(self) -> dict:
462    def trades(self) -> dict:
463        """
464        Method to get today's trades of the user using the Vortex API.
465
466        Documentation:    
467            https://vortex.rupeezy.in/docs/latest/positions/#get-trades
468
469        Returns:
470            dict: Dictionary containing the response data from the API.
471        """
472        endpoint = "/trading/trades"
473        return self._make_api_request("GET", endpoint)

Method to get today's trades of the user using the Vortex API.

Documentation:
https://vortex.rupeezy.in/docs/latest/positions/#get-trades

Returns:

dict: Dictionary containing the response data from the API.

def funds(self) -> dict:
475    def funds(self) -> dict:
476        """
477        Method to get the funds of the user using the Vortex API.
478
479        Documentation:    
480            https://vortex.rupeezy.in/docs/latest/user/#available-funds
481
482        Returns:
483            dict: Dictionary containing the response data from the API.
484        """
485        endpoint = "/user/funds"
486        return self._make_api_request("GET", endpoint)

Method to get the funds of the user using the Vortex API.

Documentation:
https://vortex.rupeezy.in/docs/latest/user/#available-funds

Returns:

dict: Dictionary containing the response data from the API.

def get_order_margin( self, exchange: vortex_api.Constants.ExchangeTypes, token: int, transaction_type: vortex_api.Constants.TransactionSides, product: vortex_api.Constants.ProductTypes, variety: vortex_api.Constants.VarietyTypes, quantity: int, price: float, mode: vortex_api.Constants.OrderMarginModes, old_quantity: int = 0, old_price: float = 0) -> dict:
488    def get_order_margin(self, exchange: Constants.ExchangeTypes, token: int, transaction_type: Constants.TransactionSides, product: Constants.ProductTypes, variety: Constants.VarietyTypes, 
489                     quantity: int, price: float,mode: Constants.OrderMarginModes, old_quantity: int = 0 , old_price: float = 0 ) -> dict:
490        """
491        Get the margin required for placing an order for a specific security.
492
493        Documentation:    
494            https://vortex.rupeezy.in/docs/latest/margin/#order-margin
495
496        Args:
497            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
498            token (int): Security token of the scrip. It can be found in the scripmaster file
499            transaction_type (Constants.TransactionSides): Possible values: [BUY, SELL]
500            product (Constants.ProductTypes): Possible values: [INTRADAY, DELIVERY, MTF]. MTF product can only be used in NSE_EQ exchange.
501            variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. 
502                        MKT means that the trade will happen at market price
503            quantity (int): For exchange NSE_FO, if you want to trade in 2 lots and lot size is 50, you should pass 100. 
504                            In all other exchanges, you should pass just the number of lots. For example, in MCX_FO, 
505                            if you want to trade 5 lots, you should pass just 5.
506            price (float): Price should be an integer multiple of Tick Size. For example, IDEA's tick size is 0.05. 
507                        So the price entered can be 9.5 or 9.65. It cannot be 9.67. 
508                        In case of market orders, you should send the Last Trade Price received from the Quote API or Websocket API
509            mode (Constants.OrderMarginModes): Possible values: [NEW, MODIFY] , Whether you are trying to modify an existing order or placing a new order.            
510            old_quantity (int): For NSE_FO segments, old_quantity is lots * lot_size. For all others, enter just the number of lots. Required if mode is MODIFY
511            old_price (float): Old Price in INR. Required if mode is MODIFY
512
513        Returns:
514            dict: JSON response containing the details of the margin required to place the order
515
516        Raises:
517            HTTPError: If any HTTP error occurs during the API call
518        """
519        
520        endpoint = "/margins/order"
521        
522        data = {
523            "exchange": exchange,
524            "token": token,
525            "transaction_type": transaction_type,
526            "product": product,
527            "variety": variety,
528            "quantity": quantity,
529            "price": price,
530            "old_quantity": old_quantity,
531            "old_price": old_price,
532            "mode": mode,
533        }
534        return self._make_api_request("POST", endpoint, data=data)

Get the margin required for placing an order for a specific security.

Documentation:
https://vortex.rupeezy.in/docs/latest/margin/#order-margin

Arguments:
  • exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
  • token (int): Security token of the scrip. It can be found in the scripmaster file
  • transaction_type (Constants.TransactionSides): Possible values: [BUY, SELL]
  • product (Constants.ProductTypes): Possible values: [INTRADAY, DELIVERY, MTF]. MTF product can only be used in NSE_EQ exchange.
  • variety (Constants.VarietyTypes): Possible values: [RL, RL-MKT, SL, SL-MKT]. RL means regular orders, SL means Stop Loss order. MKT means that the trade will happen at market price
  • quantity (int): For exchange NSE_FO, if you want to trade in 2 lots and lot size is 50, you should pass 100. In all other exchanges, you should pass just the number of lots. For example, in MCX_FO, if you want to trade 5 lots, you should pass just 5.
  • price (float): Price should be an integer multiple of Tick Size. For example, IDEA's tick size is 0.05. So the price entered can be 9.5 or 9.65. It cannot be 9.67. In case of market orders, you should send the Last Trade Price received from the Quote API or Websocket API
  • mode (Constants.OrderMarginModes): Possible values: [NEW, MODIFY] , Whether you are trying to modify an existing order or placing a new order.
  • old_quantity (int): For NSE_FO segments, old_quantity is lots * lot_size. For all others, enter just the number of lots. Required if mode is MODIFY
  • old_price (float): Old Price in INR. Required if mode is MODIFY
Returns:

dict: JSON response containing the details of the margin required to place the order

Raises:
  • HTTPError: If any HTTP error occurs during the API call
def brokerage_plan(self) -> dict:
536    def brokerage_plan(self)-> dict: 
537        """
538        Get brokerage plan details of the user.
539
540        Documentation:    
541            https://vortex.rupeezy.in/docs/latest/user/
542
543        Returns:
544            dict: JSON response containing the details of the brokerage plan of the user
545        """
546        endpoint = "/user/profile/brokerage"
547        return self._make_api_request("GET", endpoint, data=None,params=None)

Get brokerage plan details of the user.

Documentation:
https://vortex.rupeezy.in/docs/latest/user/

Returns:

dict: JSON response containing the details of the brokerage plan of the user

def quotes( self, instruments: list, mode: vortex_api.Constants.QuoteModes) -> dict:
549    def quotes(self, instruments: list, mode: Constants.QuoteModes)-> dict: 
550        """
551        Gets quotes of up to 1000 instruments at a time. 
552
553        Documentation:    
554            https://vortex.rupeezy.in/docs/latest/historical/#fetch-price-quotes
555
556        Args:
557            instrument(list): List of instruments. The items should be like ( "NSE_EQ-22", "NSE_FO-1234")
558            mode(Constants.QuoteModes): Quote mode. Can be ["ltp","ohlcv", "full"]. LTP quotes just give the last trade price, ohlc give open, high, low, close and volume, full mode also gives depth.
559
560        Returns:
561            dict: JSON response containing quotes. It is possible that not all the symbol identifiers you passed had a quote available. Those inputs will be missing from the response.
562            Also, the order of output might be different than the order of input
563        """
564        endpoint = "/data/quotes"
565        params = {"q": instruments,"mode": mode}
566        return self._make_api_request("GET", endpoint, data=None,params=params)

Gets quotes of up to 1000 instruments at a time.

Documentation:
https://vortex.rupeezy.in/docs/latest/historical/#fetch-price-quotes

Arguments:
  • instrument(list): List of instruments. The items should be like ( "NSE_EQ-22", "NSE_FO-1234")
  • mode(Constants.QuoteModes): Quote mode. Can be ["ltp","ohlcv", "full"]. LTP quotes just give the last trade price, ohlc give open, high, low, close and volume, full mode also gives depth.
Returns:

dict: JSON response containing quotes. It is possible that not all the symbol identifiers you passed had a quote available. Those inputs will be missing from the response. Also, the order of output might be different than the order of input

def historical_candles( self, exchange: vortex_api.Constants.ExchangeTypes, token: int, to: datetime.datetime, start: datetime.datetime, resolution: vortex_api.Constants.Resolutions):
568    def historical_candles(self, exchange: Constants.ExchangeTypes, token: int, to: datetime.datetime , start: datetime.datetime, resolution: Constants.Resolutions): 
569        """
570        Gets historical candle data of a particular instrument. 
571
572        Documentation:    
573            https://vortex.rupeezy.in/docs/latest/historical/#fetch-historical-candle-data
574
575        Args:
576            exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
577            token (int): Security token of the scrip. It can be found in the instruments master file: 
578            to (datetime): datetime up till when you want to receive candles 
579            start (datetime): datetime from when you want to receive candles 
580            resolution (Constants.Resolutions): resoulution of the candle. can be "1", "2", "3", "4", "5", "10", "15", "30", "45", "60", "120", "180", "240", "1D", "1W", "1M"
581        
582        Returns:
583            dict: JSON response containing the historical candles
584        """
585
586        if not isinstance(token, int):
587            raise TypeError("token must be an integer")
588        if not isinstance(to,datetime.datetime): 
589            raise TypeError("to must be a datetime")
590        if not isinstance(start,datetime.datetime): 
591            raise TypeError("start must be a datetime")
592
593
594        endpoint = "/data/history"
595        params = {"exchange": exchange,"token": token , "to": int(to.timestamp()), "from": int(start.timestamp()), "resolution": resolution}
596        return self._make_api_request("GET", endpoint, data=None,params=params)

Gets historical candle data of a particular instrument.

Documentation:
https://vortex.rupeezy.in/docs/latest/historical/#fetch-historical-candle-data

Arguments:
  • exchange (Constants.ExchangeTypes): Possible values: [NSE_EQ, NSE_FO, BSE_EQ, BSE_FO, NSE_CD or MCX_FO]
  • token (int): Security token of the scrip. It can be found in the instruments master file:
  • to (datetime): datetime up till when you want to receive candles
  • start (datetime): datetime from when you want to receive candles
  • resolution (Constants.Resolutions): resoulution of the candle. can be "1", "2", "3", "4", "5", "10", "15", "30", "45", "60", "120", "180", "240", "1D", "1W", "1M"
Returns:

dict: JSON response containing the historical candles

def login_url(self, callback_param: str) -> str:
598    def login_url(self, callback_param: str) -> str:
599        """
600        Returns the login URL for the Vortex API.
601
602        Documentation:
603            https://vortex.rupeezy.in/docs/latest/authentication/
604
605        Returns:
606            str: The login URL for the Vortex API.
607        """
608
609        return f"https://flow.rupeezy.in?applicationId={self.application_id}&cb_param={callback_param}"

Returns the login URL for the Vortex API.

Documentation:

https://vortex.rupeezy.in/docs/latest/authentication/

Returns:

str: The login URL for the Vortex API.

def exchange_token(self, auth_code: str) -> dict:
611    def exchange_token(self,auth_code: str) -> dict:
612        """
613        Exchange the auth code received from the login URL for an access token.
614
615        Documentation:
616            https://vortex.rupeezy.in/docs/latest/authentication/
617
618        Args:
619            auth_code (str): The authorization code received from the login URL.
620
621        Returns:
622            dict: JSON response containing the details of the user
623        """
624
625        endpoint = "/user/session"
626        data = {
627            "token": auth_code,
628            "applicationId": self.application_id, 
629            "checksum": self._sha256_hash(f"{self.application_id}{auth_code}{self.api_key}")
630        }
631        res = self._make_unauth_request("POST", endpoint= endpoint, data=data)
632        self._setup_client_code(login_object=res)
633        return res

Exchange the auth code received from the login URL for an access token.

Documentation:

https://vortex.rupeezy.in/docs/latest/authentication/

Arguments:
  • auth_code (str): The authorization code received from the login URL.
Returns:

dict: JSON response containing the details of the user

def save_backtest_result( self, stats, name: str, symbol: str = '', description: str = '', tags: list = None) -> dict:
657    def save_backtest_result(self, stats, name: str, symbol: str = "", description: str = "", tags: list = None) -> dict:
658        """
659        Save backtest results to Rupeezy for viewing on the developer portal.
660
661        Supports multiple backtesting libraries (auto-detected from the result type):
662        - **backtesting.py**: pass the stats object from Backtest.run()
663        - **vectorbt**: pass a vbt.Portfolio object
664        - **backtrader**: pass the strategy from cerebro.run() (i.e. results[0])
665
666        Args:
667            stats: The result object from any supported backtesting library.
668            name (str): A label for this backtest run (e.g. "SMA Crossover v2").
669            symbol (str, optional): Primary instrument symbol.
670            description (str, optional): Notes about this run.
671            tags (list[str], optional): Tags for filtering (e.g. ["intraday", "nifty"]).
672
673        Returns:
674            dict: { "status": "success", "backtest_id": "bt_abc123", "url": "https://..." }
675        """
676        from .backtest import serialize_stats
677        payload = serialize_stats(stats, name, symbol, description, tags or [])
678        endpoint = "/strategies/backtests"
679
680        return self._make_api_request("POST", endpoint, data=payload)

Save backtest results to Rupeezy for viewing on the developer portal.

Supports multiple backtesting libraries (auto-detected from the result type):

  • backtesting.py: pass the stats object from Backtest.run()
  • vectorbt: pass a vbt.Portfolio object
  • backtrader: pass the strategy from cerebro.run() (i.e. results[0])
Arguments:
  • stats: The result object from any supported backtesting library.
  • name (str): A label for this backtest run (e.g. "SMA Crossover v2").
  • symbol (str, optional): Primary instrument symbol.
  • description (str, optional): Notes about this run.
  • tags (list[str], optional): Tags for filtering (e.g. ["intraday", "nifty"]).
Returns:

dict: { "status": "success", "backtest_id": "bt_abc123", "url": "https://..." }

def save_optimization_result( self, stats, heatmap, name: str, symbol: str = '', description: str = '', maximize='Sharpe Ratio', param_ranges: dict = None) -> dict:
682    def save_optimization_result(
683        self,
684        stats,
685        heatmap,
686        name: str,
687        symbol: str = "",
688        description: str = "",
689        maximize="Sharpe Ratio",
690        param_ranges: dict = None,
691    ) -> dict:
692        """
693        Save optimization results to Rupeezy for viewing on the developer portal.
694
695        Supports multiple backtesting libraries (auto-detected from the result type):
696        - **backtesting.py**: pass stats + heatmap from bt.optimize(return_heatmap=True)
697        - **vectorbt**: pass Portfolio + metric Series from multi-param run
698        - **backtrader**: pass results list from cerebro.run() after optstrategy()
699
700        Args:
701            stats: The result object from any supported backtesting library.
702            heatmap: The heatmap/metric Series (backtesting.py/vectorbt) or
703                     metric extraction callable (backtrader).
704            name (str): A label for this optimization run (e.g. "SMA Grid Search").
705            symbol (str, optional): Primary instrument symbol (e.g. "NIFTY").
706            description (str, optional): Notes about this optimization run.
707            maximize: The metric that was optimized. Should match the `maximize`
708                      argument passed to Backtest.optimize(). Can be a string
709                      metric name (e.g. "Sharpe Ratio") or a callable.
710                      Defaults to "Sharpe Ratio".
711            param_ranges (dict, optional): Explicit parameter range definitions.
712                Keys are parameter names, values are range() objects or lists.
713                Example: {"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)}
714                If not provided, ranges are inferred from the heatmap index.
715
716        Returns:
717            dict: {"status": "success", "optimization_id": "opt_xxx", "backtest_id": "bt_xxx"}
718
719        Example::
720
721            stats, heatmap = bt.optimize(
722                sma_fast=range(5, 51, 5),
723                sma_slow=range(20, 201, 10),
724                maximize='Sharpe Ratio',
725                return_heatmap=True,
726            )
727            client.save_optimization_result(
728                stats=stats,
729                heatmap=heatmap,
730                name="SMA Crossover Grid Search",
731                symbol="NIFTY",
732                maximize='Sharpe Ratio',
733                param_ranges={"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)},
734            )
735        """
736        is_maximize = True
737        objective_metric = maximize
738
739        if isinstance(maximize, bool):
740            is_maximize = maximize
741            objective_metric = "Sharpe Ratio"
742
743        from .backtest import serialize_optimization
744        payload = serialize_optimization(
745            result=stats,
746            heatmap=heatmap,
747            name=name,
748            symbol=symbol,
749            description=description,
750            objective_metric=objective_metric,
751            maximize=is_maximize,
752            param_ranges=param_ranges,
753        )
754        endpoint = "/strategies/optimizations"
755        return self._make_api_request("POST", endpoint, data=payload)

Save optimization results to Rupeezy for viewing on the developer portal.

Supports multiple backtesting libraries (auto-detected from the result type):

  • backtesting.py: pass stats + heatmap from bt.optimize(return_heatmap=True)
  • vectorbt: pass Portfolio + metric Series from multi-param run
  • backtrader: pass results list from cerebro.run() after optstrategy()
Arguments:
  • stats: The result object from any supported backtesting library.
  • heatmap: The heatmap/metric Series (backtesting.py/vectorbt) or metric extraction callable (backtrader).
  • name (str): A label for this optimization run (e.g. "SMA Grid Search").
  • symbol (str, optional): Primary instrument symbol (e.g. "NIFTY").
  • description (str, optional): Notes about this optimization run.
  • maximize: The metric that was optimized. Should match the maximize argument passed to Backtest.optimize(). Can be a string metric name (e.g. "Sharpe Ratio") or a callable. Defaults to "Sharpe Ratio".
  • param_ranges (dict, optional): Explicit parameter range definitions. Keys are parameter names, values are range() objects or lists. Example: {"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)} If not provided, ranges are inferred from the heatmap index.
Returns:

dict: {"status": "success", "optimization_id": "opt_xxx", "backtest_id": "bt_xxx"}

Example::

stats, heatmap = bt.optimize(
    sma_fast=range(5, 51, 5),
    sma_slow=range(20, 201, 10),
    maximize='Sharpe Ratio',
    return_heatmap=True,
)
client.save_optimization_result(
    stats=stats,
    heatmap=heatmap,
    name="SMA Crossover Grid Search",
    symbol="NIFTY",
    maximize='Sharpe Ratio',
    param_ranges={"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)},
)
class Constants:
 11class Constants: 
 12    """
 13    Constants used in the API
 14    """
 15    class ExchangeTypes(str,Enum): 
 16        """
 17        Constants for exchanges
 18        """
 19        NSE_FO = "NSE_FO"
 20        BSE_FO = "BSE_FO"
 21        NSE_EQUITY = "NSE_EQ"
 22        BSE_EQUITY = "BSE_EQ"
 23        NSE_CURRENCY = "NSE_CD"
 24        MCX = "MCX_FO"
 25
 26        def __str__(self):
 27            return str(self.value)
 28    class VarietyTypes(str,Enum): 
 29        """
 30        Constants for varieties
 31        """
 32        REGULAR_LIMIT_ORDER = "RL"
 33        REGULAR_MARKET_ORDER = "RL-MKT"
 34        STOP_LIMIT_ORDER = "SL"
 35        STOP_MARKET_ORDER = "SL-MKT"
 36
 37        def __str__(self):
 38            return str(self.value)
 39    class ProductTypes(str,Enum): 
 40        """
 41        Constants for product types
 42        """
 43        INTRADAY = "INTRADAY"
 44        DELIVERY = "DELIVERY"
 45        MTF = "MTF"
 46
 47        def __str__(self):
 48            return str(self.value)
 49    class ValidityTypes(str,Enum): 
 50        """
 51        Constants for validity types
 52        """
 53        FULL_DAY = "DAY"
 54        IMMEDIATE_OR_CANCEL = "IOC"
 55        AFTER_MARKET = "AMO"
 56
 57        def __str__(self):
 58            return str(self.value)
 59    class TransactionSides(str,Enum): 
 60        """
 61        Constants for transaction sides
 62        """
 63        BUY = "BUY"
 64        SELL = "SELL"
 65        def __str__(self):
 66            return str(self.value)
 67    class QuoteModes(str,Enum): 
 68        """
 69        Constants for quote modes
 70        """
 71        LTP = "ltp"
 72        FULL = "full"
 73        OHLCV = "ohlcv"
 74        def __str__(self):
 75            return str(self.value)
 76    class OrderMarginModes(str,Enum): 
 77        """
 78        Constants for order margin modes
 79        """
 80        NEW_ORDER = "NEW"
 81        MODIFY_ORDER = "MODIFY"
 82
 83        def __str__(self):
 84            return str(self.value)
 85    class Resolutions(str,Enum): 
 86        """
 87        Constants for resolutions
 88        """
 89        MIN_1 = "1"
 90        MIN_2 = "2"
 91        MIN_3 = "3"
 92        MIN_4 = "4"
 93        MIN_5 = "5"
 94        MIN_10 = "10"
 95        MIN_15 = "15"
 96        MIN_30 = "30"
 97        MIN_45 = "45"
 98        MIN_60 = "60"
 99        MIN_120 = "120"
100        MIN_180 = "180"
101        MIN_240 = "240"
102        DAY = "1D"
103        WEEK = "1W"
104        MONTH = "1M"
105
106        def __str__(self):
107            return str(self.value)

Constants used in the API

class Constants.ExchangeTypes(builtins.str, enum.Enum):
15    class ExchangeTypes(str,Enum): 
16        """
17        Constants for exchanges
18        """
19        NSE_FO = "NSE_FO"
20        BSE_FO = "BSE_FO"
21        NSE_EQUITY = "NSE_EQ"
22        BSE_EQUITY = "BSE_EQ"
23        NSE_CURRENCY = "NSE_CD"
24        MCX = "MCX_FO"
25
26        def __str__(self):
27            return str(self.value)

Constants for exchanges

NSE_FO = <ExchangeTypes.NSE_FO: 'NSE_FO'>
BSE_FO = <ExchangeTypes.BSE_FO: 'BSE_FO'>
NSE_EQUITY = <ExchangeTypes.NSE_EQUITY: 'NSE_EQ'>
BSE_EQUITY = <ExchangeTypes.BSE_EQUITY: 'BSE_EQ'>
NSE_CURRENCY = <ExchangeTypes.NSE_CURRENCY: 'NSE_CD'>
MCX = <ExchangeTypes.MCX: 'MCX_FO'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.VarietyTypes(builtins.str, enum.Enum):
28    class VarietyTypes(str,Enum): 
29        """
30        Constants for varieties
31        """
32        REGULAR_LIMIT_ORDER = "RL"
33        REGULAR_MARKET_ORDER = "RL-MKT"
34        STOP_LIMIT_ORDER = "SL"
35        STOP_MARKET_ORDER = "SL-MKT"
36
37        def __str__(self):
38            return str(self.value)

Constants for varieties

REGULAR_LIMIT_ORDER = <VarietyTypes.REGULAR_LIMIT_ORDER: 'RL'>
REGULAR_MARKET_ORDER = <VarietyTypes.REGULAR_MARKET_ORDER: 'RL-MKT'>
STOP_LIMIT_ORDER = <VarietyTypes.STOP_LIMIT_ORDER: 'SL'>
STOP_MARKET_ORDER = <VarietyTypes.STOP_MARKET_ORDER: 'SL-MKT'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.ProductTypes(builtins.str, enum.Enum):
39    class ProductTypes(str,Enum): 
40        """
41        Constants for product types
42        """
43        INTRADAY = "INTRADAY"
44        DELIVERY = "DELIVERY"
45        MTF = "MTF"
46
47        def __str__(self):
48            return str(self.value)

Constants for product types

INTRADAY = <ProductTypes.INTRADAY: 'INTRADAY'>
DELIVERY = <ProductTypes.DELIVERY: 'DELIVERY'>
MTF = <ProductTypes.MTF: 'MTF'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.ValidityTypes(builtins.str, enum.Enum):
49    class ValidityTypes(str,Enum): 
50        """
51        Constants for validity types
52        """
53        FULL_DAY = "DAY"
54        IMMEDIATE_OR_CANCEL = "IOC"
55        AFTER_MARKET = "AMO"
56
57        def __str__(self):
58            return str(self.value)

Constants for validity types

FULL_DAY = <ValidityTypes.FULL_DAY: 'DAY'>
IMMEDIATE_OR_CANCEL = <ValidityTypes.IMMEDIATE_OR_CANCEL: 'IOC'>
AFTER_MARKET = <ValidityTypes.AFTER_MARKET: 'AMO'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.TransactionSides(builtins.str, enum.Enum):
59    class TransactionSides(str,Enum): 
60        """
61        Constants for transaction sides
62        """
63        BUY = "BUY"
64        SELL = "SELL"
65        def __str__(self):
66            return str(self.value)

Constants for transaction sides

BUY = <TransactionSides.BUY: 'BUY'>
SELL = <TransactionSides.SELL: 'SELL'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.QuoteModes(builtins.str, enum.Enum):
67    class QuoteModes(str,Enum): 
68        """
69        Constants for quote modes
70        """
71        LTP = "ltp"
72        FULL = "full"
73        OHLCV = "ohlcv"
74        def __str__(self):
75            return str(self.value)

Constants for quote modes

LTP = <QuoteModes.LTP: 'ltp'>
FULL = <QuoteModes.FULL: 'full'>
OHLCV = <QuoteModes.OHLCV: 'ohlcv'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.OrderMarginModes(builtins.str, enum.Enum):
76    class OrderMarginModes(str,Enum): 
77        """
78        Constants for order margin modes
79        """
80        NEW_ORDER = "NEW"
81        MODIFY_ORDER = "MODIFY"
82
83        def __str__(self):
84            return str(self.value)

Constants for order margin modes

NEW_ORDER = <OrderMarginModes.NEW_ORDER: 'NEW'>
MODIFY_ORDER = <OrderMarginModes.MODIFY_ORDER: 'MODIFY'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Constants.Resolutions(builtins.str, enum.Enum):
 85    class Resolutions(str,Enum): 
 86        """
 87        Constants for resolutions
 88        """
 89        MIN_1 = "1"
 90        MIN_2 = "2"
 91        MIN_3 = "3"
 92        MIN_4 = "4"
 93        MIN_5 = "5"
 94        MIN_10 = "10"
 95        MIN_15 = "15"
 96        MIN_30 = "30"
 97        MIN_45 = "45"
 98        MIN_60 = "60"
 99        MIN_120 = "120"
100        MIN_180 = "180"
101        MIN_240 = "240"
102        DAY = "1D"
103        WEEK = "1W"
104        MONTH = "1M"
105
106        def __str__(self):
107            return str(self.value)

Constants for resolutions

MIN_1 = <Resolutions.MIN_1: '1'>
MIN_2 = <Resolutions.MIN_2: '2'>
MIN_3 = <Resolutions.MIN_3: '3'>
MIN_4 = <Resolutions.MIN_4: '4'>
MIN_5 = <Resolutions.MIN_5: '5'>
MIN_10 = <Resolutions.MIN_10: '10'>
MIN_15 = <Resolutions.MIN_15: '15'>
MIN_30 = <Resolutions.MIN_30: '30'>
MIN_45 = <Resolutions.MIN_45: '45'>
MIN_60 = <Resolutions.MIN_60: '60'>
MIN_120 = <Resolutions.MIN_120: '120'>
MIN_180 = <Resolutions.MIN_180: '180'>
MIN_240 = <Resolutions.MIN_240: '240'>
DAY = <Resolutions.DAY: '1D'>
WEEK = <Resolutions.WEEK: '1W'>
MONTH = <Resolutions.MONTH: '1M'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class VortexFeed:

The WebSocket client for connecting to vortex's live price and order streaming service

VortexFeed( access_token: str = None, websocket_endpoint='wss://wire.rupeezy.in/ws', reconnect=True, reconnect_max_tries=50, reconnect_max_delay=60, connect_timeout=30, debug=False)
239    def __init__(self, access_token: str=None, websocket_endpoint="wss://wire.rupeezy.in/ws",reconnect=True, reconnect_max_tries=RECONNECT_MAX_TRIES, reconnect_max_delay=RECONNECT_MAX_DELAY,
240                 connect_timeout=CONNECT_TIMEOUT, debug = False) -> None:
241        self._maximum_reconnect_max_tries = self.RECONNECT_MAX_TRIES
242        self._minimum_reconnect_max_delay = 0 
243        if reconnect == False: 
244            self.reconnect_max_tries = 0 
245        elif reconnect_max_tries > self._maximum_reconnect_max_tries:
246            log.warning("`reconnect_max_tries` can not be more than {val}. Setting to highest possible value - {val}.".format(
247                val=self._maximum_reconnect_max_tries))
248            self.reconnect_max_tries = self._maximum_reconnect_max_tries
249        else:
250            self.reconnect_max_tries = reconnect_max_tries
251        
252        if reconnect_max_delay < self._minimum_reconnect_max_delay:
253            log.warning("`reconnect_max_delay` can not be less than {val}. Setting to lowest possible value - {val}.".format(
254                val=self._minimum_reconnect_max_delay))
255            self.reconnect_max_delay = self._minimum_reconnect_max_delay
256        else:
257            self.reconnect_max_delay = reconnect_max_delay
258        
259        self.connect_timeout = connect_timeout
260        if access_token == None:
261            if os.getenv("VORTEX_ACCESS_TOKEN") != None:
262                access_token = os.getenv("VORTEX_ACCESS_TOKEN")
263            else:
264                raise ValueError("Access token must be provided either as an argument or through the VORTEX_ACCESS_TOKEN environment variable.")
265            
266        self.socket_url = websocket_endpoint+"?auth_token="+access_token
267        self.access_token = access_token
268        self.socket_token = self.__getSocketToken__(self.access_token)
269
270        self.debug = debug
271        # self.on_price_update = None
272        self.on_price_update = None
273        self.on_open = None
274        self.on_close = None
275        self.on_error = None
276        self.on_connect = None
277        self.on_message = None
278        self.on_reconnect = None
279        self.on_noreconnect = None
280        self.on_order_update = None
281        self.subscribed_tokens = {}
282        pass
def connect(self, threaded=False, disable_ssl_verification=False):
306    def connect(self, threaded=False, disable_ssl_verification=False):
307        """
308        Establish a websocket connection.
309        - `disable_ssl_verification` disables building ssl context
310        """
311        # Init WebSocket client factory
312        self._create_connection(self.socket_url,
313                                useragent=self._user_agent())
314
315        # Set SSL context
316        context_factory = None
317        if self.factory.isSecure and not disable_ssl_verification:
318            from urllib.parse import urlparse
319            hostname = urlparse(self.socket_url).hostname
320            context_factory = ssl.optionsForClientTLS(hostname)
321
322        # Establish WebSocket connection to a server
323        connectWS(self.factory, contextFactory=context_factory, timeout=self.connect_timeout)
324
325        if self.debug:
326            twisted_log.startLogging(sys.stdout)
327
328        # Run in seperate thread of blocking
329        opts = {}
330        # Run when reactor is not running
331        if not reactor.running:
332            if threaded:
333                # Signals are not allowed in non main thread by twisted so suppress it.
334                opts["installSignalHandlers"] = False
335                self.websocket_thread = threading.Thread(target=reactor.run, kwargs=opts)
336                self.websocket_thread.daemon = True
337                self.websocket_thread.start()
338            else:
339                reactor.run(**opts)
340        else: 
341            print(reactor.running)

Establish a websocket connection.

  • disable_ssl_verification disables building ssl context
def is_connected(self):
343    def is_connected(self):
344        """Check if WebSocket connection is established."""
345        if self.ws and self.ws.state == self.ws.STATE_OPEN:
346            return True
347        else:
348            return False

Check if WebSocket connection is established.

def close(self, code=None, reason=None):
355    def close(self, code=None, reason=None):
356        """Close the WebSocket connection."""
357        self.stop_retry()
358        self._close(code, reason)

Close the WebSocket connection.

def stop(self):
360    def stop(self):
361        """Stop the event loop. Should be used if main thread has to be closed in `on_close` method.
362        Reconnection mechanism cannot happen past this method
363        """
364        reactor.stop()

Stop the event loop. Should be used if main thread has to be closed in on_close method. Reconnection mechanism cannot happen past this method

def stop_retry(self):
366    def stop_retry(self):
367        """Stop auto retry when it is in progress."""
368        if self.factory:
369            self.factory.stopTrying()

Stop auto retry when it is in progress.

def subscribe(self, exchange: str, token: int, mode: str) -> bool:
371    def subscribe(self, exchange: str,token: int,mode: str)->bool:
372        """
373        Subscribe to a list of instrument_tokens.
374        - `instrument_tokens` is list of instrument instrument_tokens to subscribe
375        """
376        try:
377            self.ws.sendMessage(six.b(json.dumps({"message_type": self._message_subscribe, "exchange": exchange,"token": token,"mode": mode})))     
378
379            try: 
380                self.subscribed_tokens[exchange][token] = mode
381            except KeyError: 
382                self.subscribed_tokens[exchange] = {}
383                self.subscribed_tokens[exchange][token] = mode
384
385            return True
386        except Exception as e:
387            self._close(reason="Error while subscribe: {}".format(str(e)))
388            raise

Subscribe to a list of instrument_tokens.

  • instrument_tokens is list of instrument instrument_tokens to subscribe
def unsubscribe(self, exchange: str, token: int) -> bool:
390    def unsubscribe(self, exchange: str,token: int)->bool:
391        """
392        Unsubscribe the given list of instrument_tokens.
393        - `instrument_tokens` is list of instrument_tokens to unsubscribe.
394        """
395        try:
396            self.ws.sendMessage(six.b(json.dumps({"message_type": self._message_unsubscribe, "exchange": exchange,"token": token})))            
397
398            try: 
399                del(self.subscribed_tokens[exchange][token])
400            except KeyError: 
401                pass 
402
403            return True
404        except Exception as e:
405            self._close(reason="Error while unsubscribe: {}".format(str(e)))
406            raise

Unsubscribe the given list of instrument_tokens.

  • instrument_tokens is list of instrument_tokens to unsubscribe.
def resubscribe(self):
408    def resubscribe(self):
409        """Resubscribe to all current subscribed tokens."""
410        modes = {}
411
412        for exchange in self.subscribed_tokens: 
413            for token in self.subscribed_tokens[exchange]: 
414                self.subscribe(exchange=exchange, token=token,mode=self.subscribed_tokens[exchange][token])

Resubscribe to all current subscribed tokens.