Skip to content

Commit

Permalink
- Added retry feature if connection fails
Browse files Browse the repository at this point in the history
- Methods raise ConnectionError no longer returns None
- historical_data accepts iso-formatted date string
- better 3.8 compatibility
  • Loading branch information
BennyThadikaran committed Jan 29, 2024
1 parent 652e17b commit 963ad77
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 69 deletions.
139 changes: 97 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,6 @@ Credentials file is no longer supported. Storing plain text passwords in a file,
- Session expired
- Bad request or invalid parameters
- Internal server error
- Any other HTTP error code will print a warning and return `None`.

Be sure to take this into consideration when writing your scripts.

## Available Methods

Expand Down Expand Up @@ -276,75 +273,133 @@ th = Throttle(throttle_config, max_penalty_count)
### Class and Method signature

```python
Help on class Kite in module Kite:

class Kite(builtins.object)
| Kite(credentials_path=None, enctoken=None)
|
| Kite(enctoken: Optional[str] = None)
|
| Unofficial implementation of Zerodha Kite api
|
|
| Methods defined here:
|
|
|
| __init__(self, enctoken: Optional[str] = None)
| Initialize self. See help(type(self)) for accurate signature.
|
| auctions(self)
| Retrieve the list of auctions that are currently being held
|
|
| cancel_order(self, variety, order_id)
| Cancel an order.
|
|
| close(self)
| Close the Requests session
|
| historical_data(self, instrument_token: str, from_dt: datetime,
| to_dt: datetime, interval: str,
| continuous=False, oi=False)
|
| historical_data(self, instrument_token: str, from_dt: Union[datetime.datetime, str], to_dt: Union[datetime.datetime, str], interval: str, continuous=False, oi=False)
| return historical candle records for a given instrument.
|
|
| holdings(self)
| Return the list of long term equity holdings
|
| instruments(self, exchange=None)
|
| instruments(self, exchange: Optional[str] = None)
| return a CSV dump of all tradable instruments
|
| ltp(self, instruments: str | list | tuple | set)
|
| ltp(self, instruments: Union[str, collections.abc.Collection])
| Returns the last traded price
|
| margins(self, segment=None)
|
| margins(self, segment: Optional[str] = None)
| Returns funds, cash, and margin information for the user
| for equity and commodity segments
|
| modify_order(self, variety, order_id, quantity=None, price=None, order_type=None,
| trigger_price=None, validity=None, disclosed_quantity=None)
|
| modify_order(self, variety, order_id, quantity=None, price=None, order_type=None, trigger_price=None, validity=None, disclosed_quantity=None)
| Modify an open order.
|
| ohlc(self, instruments: str | list | tuple | set)
|
| ohlc(self, instruments: Union[str, collections.abc.Collection])
| Returns ohlc and last traded price
|
|
| order_history(self, order_id)
| Get history of individual orders
|
|
| order_trades(self, order_id)
| Get the the trades generated by an order
|
|
| orders(self)
| Get list of all orders for the day
|
| place_order(self, variety, exchange, tradingsymbol, transaction_type, quantity,
| product, order_type, price=None, validity=None, validity_ttl=None,
| disclosed_quantity=None, trigger_price=None, iceberg_legs=None,
| iceberg_quantity=None, auction_number=None, tag=None)
|
| place_order(self, variety, exchange, tradingsymbol, transaction_type, quantity, product, order_type, price=None, validity=None, validity_ttl=None, disclosed_quantity=None, trigger_price=None, iceberg_legs=None, iceberg_quantity=None, auction_number=None, tag=None)
| Place an order of a particular variety
|
|
| positions(self)
| Retrieve the list of short term positions
|
|
| profile(self)
| Retrieve the user profile
|
| quote(self, instruments: str | list | tuple | set)
|
| quote(self, instruments: Union[str, collections.abc.Collection])
| Return the full market quotes - ohlc, OI, bid/ask etc
|
|
| trades(self)
| Get the list of all executed trades for the day

|
| Data and other attributes defined here:
|
| EXCHANGE_BCD = 'BCD'
|
| EXCHANGE_BFO = 'BFO'
|
| EXCHANGE_BSE = 'BSE'
|
| EXCHANGE_CDS = 'CDS'
|
| EXCHANGE_MCX = 'MCX'
|
| EXCHANGE_NFO = 'NFO'
|
| EXCHANGE_NSE = 'NSE'
|
| GTT_TYPE_OCO = 'two-leg'
|
| GTT_TYPE_SINGLE = 'single'
|
| MARGIN_COMMODITY = 'commodity'
|
| MARGIN_EQUITY = 'equity'
|
| ORDER_TYPE_LIMIT = 'LIMIT'
|
| ORDER_TYPE_MARKET = 'MARKET'
|
| ORDER_TYPE_SL = 'SL'
|
| ORDER_TYPE_SLM = 'SL-M'
|
| POSITION_TYPE_DAY = 'day'
|
| POSITION_TYPE_OVERNIGHT = 'overnight'
|
| PRODUCT_CNC = 'CNC'
|
| PRODUCT_CO = 'CO'
|
| PRODUCT_MIS = 'MIS'
|
| PRODUCT_NRML = 'NRML'
|
| TRANSACTION_TYPE_BUY = 'BUY'
|
| TRANSACTION_TYPE_SELL = 'SELL'
|
| VALIDITY_DAY = 'DAY'
|
| VALIDITY_IOC = 'IOC'
|
| VALIDITY_TTL = 'TTL'
|
| VARIETY_AMO = 'amo'
|
| VARIETY_AUCTION = 'auction'
|
| VARIETY_CO = 'co'
|
| VARIETY_ICEBERG = 'iceberg'
|
| VARIETY_REGULAR = 'regular'
|
```
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "kitetrader"
version = "1.1.1"
version = "2.0.0"
authors = [
{ name="Benny Thadikaran" },
]
Expand Down
68 changes: 42 additions & 26 deletions src/kitetrader/Kite.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import annotations
from collections.abc import Collection
from typing import Union
from requests import Session
from requests.exceptions import ReadTimeout
from urllib3.util import Retry
from requests.adapters import HTTPAdapter
from pathlib import Path
from pickle import loads as pickle_loads, dumps as pickle_dumps
from mthrottle import Throttle
Expand Down Expand Up @@ -82,24 +85,35 @@ class Kite:
cookies = None
config = None

def __init__(self, enctoken=None):
def __init__(self, enctoken: Union[str, None] = None):

self.cookie_path = self.base_dir / "kite_cookies"
self.enctoken = enctoken
self.session = Session()

retries = Retry(
total=None,
connect=3,
read=3,
redirect=0,
status=3,
other=0,
backoff_factor=0.1,
status_forcelist=["502", "503", "504"],
raise_on_status=False,
)

self.session.mount("https://", HTTPAdapter(max_retries=retries))

ua = (
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"
)

headers = {
"User-Agent": ua,
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
}

self.session.headers.update(headers)
self.session.headers.update(
{
"User-Agent": ua,
}
)

if self.cookie_path.exists():
self.cookies = self._get_cookie()
Expand Down Expand Up @@ -182,13 +196,9 @@ def _req(self, url, method, payload=None, timeout=15, hint=None):
reason = "Missing or bad request parameters or values"
raise ConnectionError(f"{hint} | {code}: {reason}")

if code >= 500:
raise ConnectionError(f"{hint} | {code}: {r.reason}")

print(f"WARN | {hint} | {code}: {r.reason}")
return None
raise ConnectionError(f"{hint} | {code}: {r.reason}")

def _authorize(self, user_id, pwd):
def _authorize(self, user_id: str, pwd: str):
"""Authenthicate the user"""

base_url = "https://kite.zerodha.com"
Expand Down Expand Up @@ -240,7 +250,7 @@ def _check_auth(self):

self._authorize(user_id, pwd)

def instruments(self, exchange=None):
def instruments(self, exchange: Union[str, None] = None):
"""return a CSV dump of all tradable instruments"""

th.check()
Expand All @@ -254,10 +264,10 @@ def instruments(self, exchange=None):
if res:
return res.content

def quote(self, instruments: str | list | tuple | set):
def quote(self, instruments: Union[str, Collection]):
"""Return the full market quotes - ohlc, OI, bid/ask etc"""

if type(instruments) in (list, tuple, set) and len(instruments) > 500:
if not isinstance(instruments, str) and len(instruments) > 500:
raise ValueError("Instruments length cannot exceed 500")

th.check("quote")
Expand All @@ -271,10 +281,10 @@ def quote(self, instruments: str | list | tuple | set):

return res.json()["data"] if res else None

def ohlc(self, instruments: str | list | tuple | set):
def ohlc(self, instruments: Union[str, Collection]):
"""Returns ohlc and last traded price"""

if type(instruments) in (list, tuple, set) and len(instruments) > 1000:
if not isinstance(instruments, str) and len(instruments) > 1000:
raise ValueError("Instruments length cannot exceed 1000")

th.check("quote")
Expand All @@ -288,10 +298,10 @@ def ohlc(self, instruments: str | list | tuple | set):

return res.json()["data"] if res else None

def ltp(self, instruments: str | list | tuple | set):
def ltp(self, instruments: Union[str, Collection]):
"""Returns the last traded price"""

if type(instruments) in (list, tuple, set) and len(instruments) > 1000:
if not isinstance(instruments, str) and len(instruments) > 1000:
raise ValueError("Instruments length cannot exceed 1000")

th.check("quote")
Expand Down Expand Up @@ -344,7 +354,7 @@ def auctions(self):

return res.json()["data"] if res else None

def margins(self, segment=None):
def margins(self, segment: Union[str, None] = None):
"""Returns funds, cash, and margin information for the user
for equity and commodity segments"""

Expand All @@ -371,8 +381,8 @@ def profile(self):
def historical_data(
self,
instrument_token: str,
from_dt: datetime,
to_dt: datetime,
from_dt: Union[datetime, str],
to_dt: Union[datetime, str],
interval: str,
continuous=False,
oi=False,
Expand All @@ -381,6 +391,12 @@ def historical_data(

url = f"{self.base_url}/instruments/historical/{instrument_token}/{interval}"

if isinstance(from_dt, str):
from_dt = datetime.fromisoformat(from_dt)

if isinstance(to_dt, str):
to_dt = datetime.fromisoformat(to_dt)

payload = {
"from": from_dt,
"to": to_dt,
Expand Down

0 comments on commit 963ad77

Please sign in to comment.