(20241221) Zerodha Auth Ready. Users can now integrate Kite.

This commit is contained in:
2024-12-21 15:22:55 +05:30
parent 8904ea394d
commit 1249841b05
15 changed files with 568 additions and 464 deletions
@@ -10,7 +10,7 @@
OBJECTIVE:
To handle all trading related behaviour from one place.
To handle all trading related behaviour for Zerodha's Kite platform.
REFERENCES:
@@ -36,6 +36,8 @@ sys.path.append(".")
sys.path.append("..")
# My async utils:
from utils_v2.string import json
from utils_v2.database.async_mysql_v2 import AsyncMySQL
from utils_v2.database.async_mongo_v2 import AsyncMongo
from utils_v2.cache.async_redis_cache_v2 import AsyncRedisCache
@@ -44,6 +46,10 @@ from controllers_v2.finstitutions.trading.base import TradingController
# Models:
from models.core.auth_token import CoreAuthTokenModel
from utils_v2.trading.zerodha_kite.models.auth_tokens import ZerodhaKiteAuthTokens
# To work with MongoDB:
from bson.objectid import ObjectId
# To work with datatypes:
from typing import List, Any
@@ -51,6 +57,9 @@ from typing import List, Any
# To make HTTP requests:
import httpx
# To work with Zerodha's Kite platform:
from kiteconnect import KiteConnect
# *****************************************************************************************************************
# ***** ****
@@ -89,7 +98,7 @@ import httpx
# *****************************************************************************************************************
class AllTradingController(TradingController):
class ZerodhaKiteTradingController(TradingController):
# ┏┓
# ┃ ┏┓┏┓┏╋┏┓┓┏┏╋┏┓┏┓
@@ -101,12 +110,12 @@ class AllTradingController(TradingController):
http_client: httpx.AsyncClient = None,
alert_url: str = None,
debug: bool = True,
debug_prefix: str = "All SMS (C) | ",
debug_prefix: str = "Zerodha kite (C) | ",
debug_only_errors: bool = True
):
"""
This is the foundational controller for all trading services. Use this for any smaller common tasks where you
may not know the exact client beforehand.
This is the foundational controller for Zerodha's Kite platform.
:param cache: The object to use for caching results from database calls.
:param http_client: The HTTP client
:param debug: Whether, or not, you would like to print debugging messages:
@@ -115,22 +124,110 @@ class AllTradingController(TradingController):
:return: None.
"""
# Declare the client:
this_client = "zerodhaKite"
# Prepare base filter:
this_filter = {"client": this_client}
# Invoke the parent's constructor:
super().__init__(
cache = cache,
alert_url = alert_url,
http_client = http_client,
base_filter = None,
base_filter = this_filter,
debug = debug,
debug_prefix = debug_prefix,
debug_only_errors = debug_only_errors
)
# Init a variable in a parent:
self._client = this_client
# ┏┓ ┓
# ┣┫┓┏╋┣┓
# ┛┗┗┻┗┛┗
pass
@staticmethod
async def get_authorization_url(
**kwargs
) -> str:
"""
To generate an authorization URL for this broker.
:param kwargs: Any no. of things needed by your broker to generate the URL.
:return: The authorization URL.
"""
return f"https://kite.zerodha.com/connect/login?api_key={kwargs['api_key']}"
async def handle_authorization_callback(
self,
sql_conn: AsyncMySQL,
mongo_data_conn: AsyncMongo,
inbound_data: dict
) -> bool:
"""
When the end user interacts with Zerodha's APIs, Zerodha's servers issue a callback like this:
http://127.0.0.1:5999/auth/callback?action=login&type=login&status=success&request_token=the-request-token
We must use the request token to get the access token. The access token is the thing that we must hold onto for
executing actual actions like subscribing to live market feed, placing trades, etc.
NOTE: Please ensure that you set the 'Redirect URL' such that is passes back Kite's 'api_key' back through the
callback URL. This can be one by setting the value manually as a query param on the app's configuration
page. E.g.: http://127.0.0.1:5999/auth/callback?api_key=user_api_key
:param sql_conn: The database connection to use to perform this activity.
:param mongo_data_conn: The database connection to use to perform this activity.
:param inbound_data: The data that came in from the broker. This could be in the JSON body, query params, etc.
:return: The model that hold the access tokens, or None if something failed.
"""
# Start by assuming failure:
success = False
zerodha_auth_token = None
# Get the token from the database:
auth_token = await self.get_token_from_filter(
mongo_data_conn = mongo_data_conn,
filter_json = mongo_data_conn.dict_to_dot_notation({
"auth": {
"apiKey": inbound_data.get(
"api_key",
"Hint: Put the user's app's key in the query params of the 'Redirect URL'"
)
}
})
)
# If not such auth token exists:
if not auth_token: return success
# Get the final access tokens set from Zerodha Kite:
kite = KiteConnect(api_key = auth_token.auth["apiKey"])
session_data = kite.generate_session(
request_token = inbound_data["request_token"],
api_secret = auth_token.auth["apiSecret"]
)
zerodha_auth_token = ZerodhaKiteAuthTokens(**session_data)
# Prepare the inputs to save to the database:
auth_url = await self.get_authorization_url(api_key = auth_token.auth["apiKey"])
auth_token.token = zerodha_auth_token.model_dump()
# Save the additional auth info to the database:
success = await self.set_token(
sql_conn = sql_conn,
mongo_data_conn = mongo_data_conn,
token_key = auth_token.key,
auth_token = auth_token,
token_notes = {
"apiKey": auth_token.auth["apiKey"],
"authUrl": auth_url
}
)
# Done here:
return success
# *****************************************************************************************************************