Skip to content

Commit

Permalink
deploy: 811a58c
Browse files Browse the repository at this point in the history
  • Loading branch information
BennyThadikaran committed Apr 29, 2024
1 parent b68587a commit 22929d4
Show file tree
Hide file tree
Showing 39 changed files with 5,321 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .buildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: cbfc8185f42158df9665e8f44e6319bc
tags: 645f666f9bcd5a90fca523b33c5a78b7
Binary file added .doctrees/environment.pickle
Binary file not shown.
Binary file added .doctrees/examples.doctree
Binary file not shown.
Binary file added .doctrees/implementation.doctree
Binary file not shown.
Binary file added .doctrees/index.doctree
Binary file not shown.
Binary file added .doctrees/kite_usage.doctree
Binary file not shown.
Empty file added .nojekyll
Empty file.
177 changes: 177 additions & 0 deletions _sources/examples.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
=================
Complete Examples
=================

Handling Keyboard Interupt
--------------------------

Graceful exit on KeyboardInterupts

.. code:: python
from aio_trader.kite import KiteFeed
from aio_trader import utils
from typing import List
import asyncio, sys
# Define handlers
def on_tick(tick: List, binary=False):
print(tick)
def on_connect(kws: KiteFeed):
print("connected")
# Subscribe to Aarti Industries
asyncio.create_task(kws.subscribe_symbols([1793]))
def on_order_update(data: dict):
# Store order data in database or other operations
pass
async def cleanup(kws, kite):
# perform cleanup operations here
await kws.close()
await kite.close()
async def main():
await kite.authorize(
request_token=request_token,
api_key=api_key,
secret=secret,
)
# Client session and logger instance are shared
async with KiteFeed(
user_id=user_id,
enctoken=enctoken,
session=kite.session
logger=kite.log
) as kws:
kws.on_tick = on_tick
kws.on_connect = on_connect
kws.on_order_update = on_order_update
# Handle KeyboardInterupt
utils.add_signal_handlers(cleanup, kws, kite)
# No code executes after this line
await kws.connect()
# Credentials loaded from a database or JSON file
enctoken = "STORED_ENCTOKEN"
user_id = "KITE_WEB_USER_ID"
# Defined globally to allow access across the script
kite = Kite(access_token=access_token)
if "win" in sys.platform:
# Required for windows users
asyncio.set_event_loop_policy(
asyncio.WindowsSelectorEventLoopPolicy(),
)
asyncio.run(main())
Downloading historical data
---------------------------

When trying to download historical data synchronously, the typical approach would be:

1. Request historical data
2. Wait for a response from the server
3. Process the data
4. Repeat in a loop for the remaining symbols.

The process is blocked while waiting for the server's response. No other task is executed, during this time.

**With Async,**

1. I am iterating over the symbols, creating a :py:obj:`Task` for each request.
2. The :py:obj:`Task` does not execute the request immediately. Instead, it is scheduled for concurrent execution.
3. Once the :py:obj:`Task` executes, it does not wait for the server response. It yields control back to the event loop to run other tasks.
4. As the response returns, the :py:obj:`Task` is marked completed, and the result is available for processing. It eliminates a lot of time waiting on the network.

Long CPU-intensive or IO-bound tasks can block the event loop. Use :py:obj:`loop.run_in_executor` to execute functions in separate threads or processes, without blocking the event loop.

.. code:: python
from aio_trader.kite import Kite
from aio_trader import utils
from pathlib import Path
from typing import List
import pandas as pd, asyncio
async def main():
DIR = Path(__file__).parent
instruments_file = DIR / "nse_instruments.csv"
sym_list = (
"AARTIIND",
"ASIANPAINT",
"BATAINDIA",
"HDFCLIFE",
"BLUEDART",
"BRITANNIA",
"DEEPAKFERT",
"DRREDDY",
"EICHERMOT",
"EIDPARRY",
)
columns = ("Date", "Open", "High", "Low", "Close", "Volume")
async with Kite() as kite:
await kite.authorize()
if not instruments_file.exists():
instruments_file.write_bytes(
await kite.instruments(exchange=kite.EXCHANGE_NSE)
)
df = pd.read_csv(instruments_file, index_col="tradingsymbol")
tasks: List[asyncio.Task] = []
for sym in sym_list:
# An async function returns a coroutine unless awaited
coroutine = kite.historical_data(
df.loc[sym, "instrument_token"],
from_dt="2024-04-22",
to_dt="2024-04-26",
interval="day",
)
# Assign the coroutine to a task, to schedule it for execution
# in the event loop.
# Here the task name is assigned the symbol name.
# We can use task.get_name to return this value
task = asyncio.create_task(coroutine, name=sym)
tasks.append(task)
# asyncio.as_completed returns a coroutine with no reference to the task.
# utils.as_completed returns the original task object from the previous
# step.
async for task in utils.as_completed(tasks):
sym_name = task.get_name()
result = task.result()
candles = result["data"]["candles"]
df = pd.DataFrame(candles, columns=columns, copy=False)
df["Date"] = pd.to_datetime(df["Date"])
df.set_index("Date", drop=True, inplace=True)
# save the result or perform further processing
asyncio.run(main())
71 changes: 71 additions & 0 deletions _sources/implementation.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
======================
Package Implementation
======================

.. code:: bash
aio_trader/
├── kite
│   ├── Kite.py
│   ├── KiteFeed.py
│   └── __init__.py
├── AbstractBroker.py
├── AbstractFeeder.py
├── AsyncRequest.py
├── __init__.py
└── utils.py
Each Stockbroker is packaged into a directory and can be imported independently of other brokers.

:py:obj:`aio_trader.kite` is the only package available now. I will be adding more soon.

.. code:: python
from aio_trader.kite import Kite, KiteFeed
Kite.py
-------

:py:obj:`Kite.py` is used to make and manage orders, access profiles, holdings, etc.

The :py:obj:`Kite` class implements the :py:obj:`AbstractBroker` class.

:py:obj:`AsyncRequest` is responsible for making HTTP requests, handling errors, and returning the response.

:py:obj:`AsyncRequest` has a mechanism for retrying requests in case of errors. It uses [exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff#Rate_limiting) to gradually increase the wait time between retries.

It starts with a 3-second wait after the first try and incrementally increases to a max wait of 30 seconds. See the simple implementation below. It is similar to [pykiteconnect](https://github.com/zerodha/pykiteconnect) implementation.

.. code:: python
# incremental backoff
base_wait = 3
max_wait = 30
retries = 0
for i in range(1, 5):
wait = min(base_wait * (2**retries), max_wait)
retries += 1
print(f"{retries} retry: {wait} seconds wait")
In case of any network error or HTTP status codes other than 200, the request is retried. The only exception is a RuntimeError, which closes the connection and raises the error.

The following status codes can trigger a RuntimeError:

- **400**: Incorrect method or params
- **429**: API Rate limit reached.
- **403**: Forbidden


KiteFeed.py
-----------

:py:obj:`KiteFeed.py` provides a WebSocket connection for receiving real-time market quotes.

KiteFeed implements the :py:obj:`AbstractFeeder` class. It has a similar retry mechanism to :py:obj:`Kite`. If there is an error, the connection is re-attempted. A response code of 403 indicates the session has expired or is invalid. The connection is closed, with no attempts to retry.

Utils.py
--------

Utils.py contains helper functions. It will be covered later.
46 changes: 46 additions & 0 deletions _sources/index.rst.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.. Aio-Trader documentation master file, created by
sphinx-quickstart on Mon Apr 29 12:32:23 2024.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Aio-Trader's documentation!
======================================

**Aio-Trader** is an async library for accessing Indian stockbroker API and real-time market feeds.

Python version: >= 3.8

.. toctree::

kite_usage
examples
implementation

Installation
------------

**1. Clone the repo**

.. code:: console
git clone https://github.com/BennyThadikaran/Aio-Trader.git
cd aio_trader
**2. Create a virtual env using venv and activate it.**

.. code:: console
py -m venv .
source bin/Activate
# On Windows, run the below to activate venv
.\Scripts\Activate.ps1
**3. Install aio_trader**

.. code:: console
pip install .
Loading

0 comments on commit 22929d4

Please sign in to comment.