(20241224) Live Feed From Kafka (to test).

This commit is contained in:
2024-12-24 14:06:05 +05:30
parent 7c7b10f27c
commit ae6a959a21
18 changed files with 1018 additions and 538 deletions
+222
View File
@@ -0,0 +1,222 @@
"""
AUTHOR:
Khushal P Soonderji
DATE:
Monday, 23rd Dec., 2024.
OBJECTIVE:
To provide a structure to represent trading symbols.
REFERENCES:
N/A
DOWNLOADS:
N/A
"""
# *****************************************************************************************************************
# ***** ****
# *** IMPORT ***
# ***** ****
# *****************************************************************************************************************
# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")
# For making data behaviour_models:
from pydantic import BaseModel, Field, field_validator, PastDatetime, model_validator, AwareDatetime
from typing import Optional, Literal, Union, List
# My utils:
from utils_v2.string import regex
from utils_v2.date_time import date_time
# To work with date and time:
import datetime
# *****************************************************************************************************************
# ***** ****
# *** MACROS / ONE-TIME INIT ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** VARIABLES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** FUNCTIONS ***
# ***** ****
# *****************************************************************************************************************
class TradingSymbol(BaseModel):
exchange: Literal["NSE", "NFO", "BSE", "BFO", "MCX", "CDS", "BCD"] = Field(
description = "the exchange on which this symbol is traded",
frozen = True
)
exchangeToken: str | int = Field(
description = "the code by which the exchange identifies this instrument",
frozen = True
)
broker: Literal["zerodhaKite", "iciciBreeze"] = Field(
description = "the broker that gave you the details of this instrument",
frozen = True
)
brokerToken: str | int = Field(
description = "the code by which the broker identifies this instrument",
frozen = True
)
name: str = Field(
description = "the name of the co./asset",
frozen = True
)
symbol: str = Field(
description = "tha trading symbol pf the co./asset",
frozen = True
)
tickSize: float = Field(
description = "the minimum step size in the change of price of the instrument",
frozen = True
)
ltp: float = Field(
description = "the last price of this instrument at the time of requesting the symbol list",
frozen = True
)
segment: str = Field(
description = "the segment which this asset represents",
frozen = True
)
type: str = Field(
description = "the type of the instrument in the segment",
frozen = True
)
lotSize: int = Field(
description = "the minimum tradeable qty of this instrument",
frozen = True
)
strike: int | float | None = Field(
description = "the strike price of the instrument if it is a derivative",
default = None,
frozen = True
)
expiryTs: AwareDatetime | None = Field(
description = "the expiry (utc) of this instrument if it is a derivative",
default = None,
frozen = True
)
expiryTz: str = Field(
description = "the timezone in which the expiry timestamp my be interpreted; should be compatible with pytz",
frozen = True
)
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# ┏┓ ┏┓
# ┃ ┓┏┏╋┏┓┏┳┓ ┣ ┓┏┏┓┏┏
# ┗┛┗┻┛┗┗┛┛┗┗ ┻ ┗┻┛┗┗┛
@staticmethod
def from_zerodha_kite(instrument: dict):
return TradingSymbol(
exchange = instrument["exchange"],
exchangeToken = instrument["exchange_token"],
broker = "zerodhaKite",
brokerToken = instrument["instrument_token"],
name = instrument["name"],
symbol = instrument["tradingsymbol"],
tickSize = instrument["tick_size"],
ltp = instrument["last_price"],
segment = instrument["segment"],
type = instrument["instrument_type"],
lotSize = instrument["lot_size"],
strike = instrument["strike"],
expiryTs = date_time.to_timezone(
datetime_object = datetime.datetime.combine(
instrument["expiry"],
datetime.time(hour = 0, minute = 0, second = 0)
),
timezone = date_time.TIMEZONE_UTC
) if instrument["expiry"] else None,
expiryTz = "Asia/Kolkata"
)
# ┓┏ ┓• ┓ •
# ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓
# ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗
@field_validator("expiryTs", mode = "before")
def parse_date_time(cls, value):
if not value: value = None
if isinstance(value, str):
value = value.strip()
value = date_time.parse_date_time(
input_value = value,
timezone = date_time.TIMEZONE_UTC
)
if isinstance(value, datetime.datetime):
value = date_time.to_timezone(
value,
timezone = date_time.TIMEZONE_UTC
)
return value
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
# ***** ****
# *****************************************************************************************************************
if __name__ == "__main__":
pass
+476
View File
@@ -0,0 +1,476 @@
"""
AUTHOR:
Khushal P Soonderji
DATE:
Tuesday, 24th Dec., 2024.
OBJECTIVE:
To provide a structure to represent trading tick updates.
REFERENCES:
N/A
DOWNLOADS:
N/A
"""
# *****************************************************************************************************************
# ***** ****
# *** IMPORT ***
# ***** ****
# *****************************************************************************************************************
# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")
# For making data behaviour_models:
from pydantic import BaseModel, Field, field_validator, PastDatetime, model_validator, AwareDatetime, computed_field
from typing import Optional, Literal, Union, List
# My utils:
from utils_v2.string import regex
from utils_v2.date_time import date_time
# To work with date and time:
import datetime
# *****************************************************************************************************************
# ***** ****
# *** MACROS / ONE-TIME INIT ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** VARIABLES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** FUNCTIONS ***
# ***** ****
# *****************************************************************************************************************
class OneMarketDepth(BaseModel):
price: float = Field(
description = "a price at which trader(s) are willing to trade this instrument",
frozen = True
)
qty: int = Field(
description = "the no. of shares available at the above price",
frozen = True
)
orders: int = Field(
description = "how many orders have contributed to the above quantity"
)
@computed_field
def liquidity(self) -> float:
return self.price * self.qty
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# ---------------------------------------------------------------------------------------------------------------------
class MarketDepth(BaseModel):
buy: List[OneMarketDepth] = Field(
description = "the buying side market depth",
frozen = True
)
sell: List[OneMarketDepth] = Field(
description = "the selling side market depth",
frozen = True
)
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# ---------------------------------------------------------------------------------------------------------------------
class TradingTick(BaseModel):
symbol: str = Field(
description = "the symbol of the instrument"
)
exchange: Literal["NSE", "NFO", "BSE", "BFO", "MCX", "CDS", "BCD"] = Field(
description = "the exchange on which this instrument is traded",
frozen = True
)
exchangeToken: str | int = Field(
description = "the code by which the exchange identifies this instrument",
frozen = True
)
broker: Literal["zerodhaKite", "iciciBreeze"] = Field(
description = "the broker that gave you the details of this instrument",
frozen = True
)
brokerToken: str | int = Field(
description = "the code by which the broker identifies this instrument",
frozen = True
)
tradeable: bool = Field(
description = "whether, or not, this instrument is tradeable",
frozen = True
)
segment: str = Field(
description = "the segment which this asset represents",
frozen = True
)
type: str = Field(
description = "the type of the instrument in the segment",
frozen = True
)
strike: int | float | None = Field(
description = "the strike price of the instrument if it is a derivative",
default = None,
frozen = True
)
expiryTs: AwareDatetime | None = Field(
description = "the expiry (utc) of this instrument if it is a derivative",
default = None,
frozen = True
)
expiryTz: str | None = Field(
description = "the timezone in which the expiry timestamp my be interpreted; should be compatible with pytz",
frozen = True,
examples = ["UTC", "Asia/Kolkata"]
)
ltp: float = Field(
description = "the last price of this instrument at the time of requesting the symbol list",
frozen = True
)
chg: float = Field(
description = "the absolute change since the previous close",
frozen=True
)
pChg: float = Field(
description = "the percentage change since last close",
frozen = True
)
o: float = Field(
description = "this session's open price",
frozen = True
)
h: float = Field(
description = "this session's high price",
frozen = True
)
l: float = Field(
description = "this session's low price",
frozen = True
)
c: float = Field(
description = "this session's close price",
frozen = True
)
totVol: int = Field(
description = "the total volume of this instrument that has been traded in this session",
frozen = True
)
vwap: float | None = Field(
description = "the volume weighted average price in this session",
frozen = True
)
totBuyQty: int = Field(
description = "the total open buy qty. on the exchange for this symbol",
frozen = True
)
totSellQty: int = Field(
description = "the total open sell qty. on the exchange for this symbol",
frozen = True
)
oi: int | None = Field(
description = "the total open interest of this instrument (if derivative)",
frozen = True
)
oiDayHigh: int | None = Field(
description = "this session's highest open interest of this instrument (if derivative)",
frozen = True
)
oiDayLow: int | None = Field(
description="this session's lowest open interest of this instrument (if derivative)",
frozen=True
)
tradeTs: AwareDatetime | None = Field(
description = "the last trade time (utc) of this instrument",
default = None,
frozen = True
)
tradeTz: str = Field(
description = "the timezone in which the last trade time should be interpreted; should be compatible with pytz",
frozen = True,
examples = ["UTC", "Asia/Kolkata"]
)
exchgTs: AwareDatetime | None = Field(
description = "the time (utc) at which this update was received from the exchange",
default = None,
frozen = True
)
exchgTz: str = Field(
description = "the timezone in which the exchange's time should be interpreted; should be compatible with pytz",
frozen = True,
examples = ["UTC", "Asia/Kolkata"]
)
depth: MarketDepth = Field(
description = "the market depth data for this instrument at the time of this update"
)
# ┏┓ ┏•
# ┃ ┏┓┏┓╋┓┏┓
# ┗┛┗┛┛┗┛┗┗┫
# ┛
class Config:
extra = "forbid"
# ┏┓ ┏┓
# ┃ ┓┏┏╋┏┓┏┳┓ ┣ ┓┏┏┓┏┏
# ┗┛┗┻┛┗┗┛┛┗┗ ┻ ┗┻┛┗┗┛
@staticmethod
def from_zerodha_kite(
ticks: dict | List[dict],
lookup: dict
) -> list:
# Ensure that we are working with a list:
if not isinstance(ticks, list): ticks = [ticks]
# Iterate through the ticks and fit them into the model:
modelled_ticks = []
for tick in ticks:
broker_token = tick["instrument_token"]
tick_lookup = lookup[broker_token]
change = tick["change"]
modelled_ticks.append(
TradingTick(
symbol = tick_lookup["symbol"],
exchange = tick_lookup["exchange"],
exchangeToken = tick_lookup["exchangeToken"],
broker = "zerodhaKite",
brokerToken = broker_token,
tradeable = tick["tradeable"],
segment = tick_lookup["segment"],
type = tick_lookup["type"],
expiryTs = tick_lookup["expiryTs"],
expiryTz = tick_lookup["expiryTz"],
ltp = ,
chg = change,
pChg =
)
)
# Done here:
return modelled_ticks
# ┓┏ ┓• ┓ •
# ┃┃┏┓┃┓┏┫┏┓╋┓┏┓┏┓
# ┗┛┗┻┗┗┗┻┗┻┗┗┗┛┛┗
@field_validator(
"expiryTs",
"tradeTs", "exchgTs",
mode = "before"
)
def parse_date_time(cls, value):
# If the input is null,
# we can't do anything:
if not value: value = None
# When the input is a string, we try to parse it:
elif isinstance(value, str):
value = value.strip()
value = date_time.parse_date_time(
input_value = value,
timezone = date_time.TIMEZONE_UTC
)
# When the input is a datetime obj.,
# we only work on the timezone:
elif isinstance(value, datetime.datetime):
value = date_time.to_timezone(
value,
timezone = date_time.TIMEZONE_UTC
)
# Done here:
return value
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
# ***** ****
# *****************************************************************************************************************
if __name__ == "__main__":
zerodha_tick = {
"tradable": True,
"mode": "full",
"instrument_token": 408065,
"last_price": 1928.25,
"last_traded_quantity": 157,
"average_traded_price": 1942.5,
"volume_traded": 5596931,
"total_buy_quantity": 217126,
"total_sell_quantity": 380092,
"ohlc": {
"open": 1975.15,
"high": 1979.95,
"low": 1911.25,
"close": 1946.2
},
"change": -0.9223101428424646,
"last_trade_time": "2024-12-20 14:46:14",
"oi": 0,
"oi_day_high": 0,
"oi_day_low": 0,
"exchange_timestamp": "2024-12-20 14:46:15",
"depth": {
"buy": [
{
"quantity": 3,
"price": 1928.15,
"orders": 2
},
{
"quantity": 6,
"price": 1927.95,
"orders": 2
},
{
"quantity": 66,
"price": 1927.7,
"orders": 2
},
{
"quantity": 25,
"price": 1927.65,
"orders": 1
},
{
"quantity": 194,
"price": 1927.6,
"orders": 6
}
],
"sell": [
{
"quantity": 134,
"price": 1928.25,
"orders": 5
},
{
"quantity": 3,
"price": 1928.3,
"orders": 1
},
{
"quantity": 560,
"price": 1928.35,
"orders": 2
},
{
"quantity": 64,
"price": 1928.4,
"orders": 2
},
{
"quantity": 400,
"price": 1928.45,
"orders": 1
}
]
}
}
zerodha_lookup = {
408065: {
"symbol": "MYSTOCK",
"exchange": "NSE",
"exchangeToken": 12345678,
"segment": "NFO-OPT",
"type": "CE"
}
}
my_ticks = TradingTick.from_zerodha_kite(
ticks = zerodha_tick,
instrument_lookup = zerodha_lookup
)
print(my_ticks[0])