""" AUTHOR: Khushal P Soonderji DATE: Saturday, 21st Dec. 2024 OBJECTIVE: To simulate stock market updates to test on SocketIO. REFERENCES: N/A DOWNLOADS: N/A """ # ***************************************************************************************************************** # ***** **** # *** IMPORT *** # ***** **** # ***************************************************************************************************************** # To make sibling directories accessible for imports: import sys sys.path.append(".") sys.path.append("..") # System-level activities: import io import os # My utils: from utils_v2.string import json # For pseudo-random simulations: import random # To work with SocketIO import socketio from aiohttp import web # To make HTTP calls: import httpx # To work with date and time: import datetime import time # For asynchronous behaviour: import asyncio # ***************************************************************************************************************** # ***** **** # *** MACROS / ONE-TIME INIT *** # ***** **** # ***************************************************************************************************************** # For SocketIO: # Create a Socket.IO server instance sio = socketio.AsyncServer(cors_allowed_origins = "*") app = web.Application() sio.attach(app) # A list of stocks to simulate: SYMBOL_TO_PRICE_MAP = { "HDFCBANK": { "prevClose": 1_763.95, "ltp": 1_771.50, "totVol": 55_96_931, "buyVol": 16_79_079, "sellVol": 39_17_852, }, "RELIANCE": { "prevClose": 1_213.35, "ltp": 1_205.30, "totVol": 7_34_568, "buyVol": 1_04_873, "sellVol": 6_29_695, }, "INFY": { "prevClose": 1_925.70, "ltp": 1_922.15, "totVol": 5_54_108, "buyVol": 2_61_593, "sellVol": 2_92_515, }, "TCS": { "prevClose": 4_203.50, "ltp": 4_170.30, "totVol": 7_24_932, "buyVol": 1_34_666, "sellVol": 5_90_266, }, "HINDUNILVR": { "prevClose": 2_312.95, "ltp": 2_333.90, "totVol": 5_04_533, "buyVol": 9_252, "sellVol": 4_95_281, }, "ITC": { "prevClose": 463.20, "ltp": 464.65, "totVol": 7_07_905, "buyVol": 3_27_422, "sellVol": 3_80_483, }, "KOTAKBANK": { "prevClose": 1_751.65, "ltp": 1_743.55, "totVol": 4_49_104, "buyVol": 2_47_489, "sellVol": 2_01_615, } } # ***************************************************************************************************************** # ***** **** # *** VARIABLES *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** FUNCTIONS *** # ***** **** # ***************************************************************************************************************** @sio.event async def connect(sid, environ): print(f"Client {sid} connected") async with httpx.AsyncClient() as client: try: await client.post( url = r"https://api.thecaoffice.com/converse/tech/alert/chat/backend", json = { "type": "info", "chatClient": "telegram", "chatId": "-4206946032", # "chatId": "1275560043", "message": f"*SocketIO Connected!*\nšŸ‘ SID: {sid}" } ) except: pass # --------------------------------------------------------------------------------------------------------------------- @sio.event async def disconnect(sid): print(f"Client {sid} disconnected") async with httpx.AsyncClient() as client: try: await client.post( url = r"https://api.thecaoffice.com/converse/tech/alert/chat/backend", json = { "type": "info", "chatClient": "telegram", "chatId": "-4206946032", # "chatId": "1275560043", "message": f"*SocketIO Disconnected!*\nāŒ SID: {sid}" } ) except: pass def round_tick(price): return round(price * 20) / 20 # --------------------------------------------------------------------------------------------------------------------- def simulate_one_stock(symbol, price): global SYMBOL_TO_PRICE_MAP # Simulate a change in the price: pos_bias = [1] * 10 no_bias = [0] * 1 neg_bias = [-1] * 10 bias = random.choice(pos_bias + no_bias + neg_bias) change_factor = random.random() / 100.0 change = price * change_factor * bias ltp = round_tick(price + change) # Simulate the volume. # Assume a trade qty. worth 1L to 10L rupees: traded_amt = random.uniform(1_00_000, 10_00_000) ltq = int(traded_amt / price) SYMBOL_TO_PRICE_MAP[symbol]["totVol"] += ltq if bias >= 0: SYMBOL_TO_PRICE_MAP[symbol]["buyVol"] += ltq else: SYMBOL_TO_PRICE_MAP[symbol]["sellVol"] += ltq # Create the basic JSON payload: stock_json = { "symbol": symbol, "last_traded_quantity": ltq, "average_traded_price": round_tick(price + (bias * price * (random.random() / 100.0))), "volume_traded": SYMBOL_TO_PRICE_MAP[symbol]["totVol"], "total_buy_quantity": SYMBOL_TO_PRICE_MAP[symbol]["buyVol"], "total_sell_quantity": SYMBOL_TO_PRICE_MAP[symbol]["sellVol"], "ohlc": { "open": round_tick(price + (price * 0.005)), "high": round_tick(price + (price * 0.015)), "low": round_tick(price - (price * 0.015)), "close": ltp }, "change": ((ltp - SYMBOL_TO_PRICE_MAP[symbol]["prevClose"]) / SYMBOL_TO_PRICE_MAP[symbol]["prevClose"]) * 100, "last_trade_time": (datetime.datetime.now() - datetime.timedelta(seconds = random.uniform(0.0, 2.5))).strftime("%Y-%m-%d %H:%M:%S"), "oi": 0, "oi_day_high": 0, "oi_day_low": 0, "exchange_timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "depth": { "buy": [ { "quantity": random.randint(0, 100), "price": round(ltp - 0.05, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp - 0.10, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp - 0.15, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp - 0.20, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp - 0.25, 2), "orders": random.randint(0, 10) } ], "sell": [ { "quantity": random.randint(0, 100), "price": round(ltp + 0.05, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp + 0.10, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp + 0.15, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp + 0.20, 2), "orders": random.randint(0, 10) }, { "quantity": random.randint(0, 100), "price": round(ltp + 0.25, 2), "orders": random.randint(0, 10) } ] } } # Done here: return stock_json # --------------------------------------------------------------------------------------------------------------------- def simulate_ticks_once(): # Pick a no. of stocks to simulate: count = random.randint(1, len(SYMBOL_TO_PRICE_MAP)) symbols = random.sample(list(SYMBOL_TO_PRICE_MAP.keys()), count) # Create the tick JSON: tick_json = [ simulate_one_stock( symbol = symbol, price = SYMBOL_TO_PRICE_MAP[symbol]["ltp"] ) for symbol in symbols ] # Done here: return tick_json # --------------------------------------------------------------------------------------------------------------------- async def broadcast_random_data(): while True: await sio.emit("ticks", simulate_ticks_once()) await asyncio.sleep(random.uniform(0.15, 1.0)) # ***************************************************************************************************************** # ***** **** # *** MAIN PROGRAM *** # ***** **** # ***************************************************************************************************************** if __name__ == "__main__": async def server(): # Start broadcasting random data in the background asyncio.create_task(broadcast_random_data()) # Run the web server runner = web.AppRunner(app) await runner.setup() site = web.TCPSite(runner, "0.0.0.0", 5000) print("Server running on http://0.0.0.0:5000") await site.start() # Keep the server running while True: await asyncio.sleep(3600) asyncio.run(server())