From b2064ee57e7f92c3bf53320110bdc5f5decd7c8b Mon Sep 17 00:00:00 2001 From: Mike Kipnis Date: Sun, 13 Sep 2026 02:00:08 -0400 Subject: [PATCH] clean-up: move dash trader to another branch --- .../cpp_ws_reactjs/dash_trader/dash_trader.py | 189 ---------------- MiscClients/dash_ws/README.md | 9 - MiscClients/dash_ws/components/login_panel.py | 185 ---------------- .../components/positions_and_market_data.py | 57 ----- .../dash_ws/components/price_level_panel.py | 59 ----- .../dash_ws/components/ticket_panel.py | 24 -- MiscClients/dash_ws/dash_trader.py | 208 ------------------ MiscClients/dash_ws/requirements.txt | 5 - MiscClients/dash_ws/ws_server.py | 68 ------ 9 files changed, 804 deletions(-) delete mode 100644 MiscClients/cpp_ws_reactjs/dash_trader/dash_trader.py delete mode 100644 MiscClients/dash_ws/README.md delete mode 100644 MiscClients/dash_ws/components/login_panel.py delete mode 100644 MiscClients/dash_ws/components/positions_and_market_data.py delete mode 100644 MiscClients/dash_ws/components/price_level_panel.py delete mode 100644 MiscClients/dash_ws/components/ticket_panel.py delete mode 100644 MiscClients/dash_ws/dash_trader.py delete mode 100644 MiscClients/dash_ws/requirements.txt delete mode 100644 MiscClients/dash_ws/ws_server.py diff --git a/MiscClients/cpp_ws_reactjs/dash_trader/dash_trader.py b/MiscClients/cpp_ws_reactjs/dash_trader/dash_trader.py deleted file mode 100644 index 94b89427..00000000 --- a/MiscClients/cpp_ws_reactjs/dash_trader/dash_trader.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (c) Mike Kipnis - DistributedATS - -import os -import logging -from datetime import datetime, timedelta - -import dash -import dash_ag_grid as dag -import dash_bootstrap_components as dbc -from dash import html, dcc, Output, Input -from components import login_panel -from components import positions_and_market_data -from components import price_level_panel -from components import ticket_panel - -# ---------------------------- -# Logging configuration -# ---------------------------- -logging.basicConfig( - level=logging.DEBUG, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - handlers=[ - logging.StreamHandler(), - logging.FileHandler("dash_trader.log", "a"), - ], -) - -logger = logging.getLogger("dash_trader") - -# ============================= -# Dash App Initialization -# ============================= -app = dash.Dash( - __name__, - title="Dash Trader", - external_stylesheets=[dag.themes.BASE, dag.themes.BALHAM, dbc.themes.SUPERHERO], -) - -server = app.server # Gunicorn expects this - -class DashTrader: - def __init__(self, app: dash.Dash): - self.app = app - self.login_panel = login_panel.LogonPanel(app) - self.positions_and_market_data_panel = positions_and_market_data.PositionAndMarketDataPanel(app) - self.price_level_panel = price_level_panel.PriceLevelPanel(app) - self.ticket_panel = ticket_panel.TicketPanel(app) - - def layout(self): - return dbc.Container( - [ - dbc.Navbar( - dbc.Container( - [ - # Left side - dbc.NavbarBrand( - "Dash Trader", - href="#", - style={"fontSize": "20px"}, - ), - - # Right side - html.Div( - [ - dbc.Row( - [ - self.login_panel.layout(), - self.ticket_panel.layout() - ] - ), - - html.Div(id="status", className="text-white ms-2"), - ], - className="d-flex flex-column align-items-start ms-auto", - ), - ], - fluid=True, - ), - color="#0f2538", - dark=True, - ), - - html.Div( - [ - dbc.Accordion( - [ - - # --- Sector / Industry --- - dbc.AccordionItem( - dbc.Row( - [ - dbc.Col( - self.positions_and_market_data_panel.layout() - ), - dbc.Col( - dbc.Row( - [ - self.price_level_panel.layout() - ]), - ) - ], - className="p-3", - ), - title="Sector & Industry", - item_id="sector-industry", - ), - - # --- Institution / Stock --- - dbc.AccordionItem( - dbc.Row( - [ - ], - className="p-3", - ), - title="Institution & Stock", - item_id="institution-stock", - ), - - # --- Price Chart / Market Data --- - dbc.AccordionItem( - dbc.Row( - [ - ], - className="p-3 flex-nowrap", # flex-nowrap keeps them on the same row - style={"overflowX": "auto"}, # optional: allows horizontal scroll if content is wide - # no_gutters=True # older versions of dash-bootstrap-components - ), - title="Price Chart & Market Data", - ), - - # --- News --- - dbc.AccordionItem( - dbc.Row( - [ - ], - className="p-3", - ), - title="News", - ), - - ], - id="main-accordion", - start_collapsed=False, # all collapsed on load - flush=True, # cleaner look - always_open=True, # allow multiple open at once - active_item=["sector-industry", "institution-stock"], - )], id="accordion-wrapper",), - - html.Div( - [ - "For support, contact: ", - html.A( - "mike.kipnis@gmail.com", - href="mailto:mike.kipnis@gmail.com", - style={ - "textDecoration": "underline", - "color": "#AAAAAA", - }, - ), - ], - style={ - "fontSize": "12px", - "color": "#AAAAAA", - "marginTop": "8px", - "textAlign": "center", - "width": "100%", - }, - ), - ], - fluid=True, - className="p-0", - ) - -# Create sandbox with this prefix -dash_trader = DashTrader(app) - -# Set layout once -app.layout = dash_trader.layout - -# ---------------------------- -# Main -# ---------------------------- -if __name__ == "__main__": - host = os.getenv("DASH_HOST", "127.0.0.1") - port = int(os.getenv("DASH_PORT", "8050")) - debug = os.getenv("DASH_DEBUG", "true").lower() == "true" - - app.run(host=host, port=port, debug=debug, use_reloader=False) diff --git a/MiscClients/dash_ws/README.md b/MiscClients/dash_ws/README.md deleted file mode 100644 index 49084a38..00000000 --- a/MiscClients/dash_ws/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## Dash Trader - DistributedATS - -### To build: -``` -python3 -m venv .venv -source .venv/bin/activate -python -m pip install --upgrade pip setuptools wheel -pip install -r requirements.txt -``` diff --git a/MiscClients/dash_ws/components/login_panel.py b/MiscClients/dash_ws/components/login_panel.py deleted file mode 100644 index fe5a4d1b..00000000 --- a/MiscClients/dash_ws/components/login_panel.py +++ /dev/null @@ -1,185 +0,0 @@ -# Copyright (c) Mike Kipnis - DistributedATS - -import datetime - -import dash -from dash import Input, Output, html, dcc, State -from dash import callback_context -import dash_bootstrap_components as dbc -from dash_extensions import WebSocket - - -class LogonPanel(object): - - def __init__(self, app: dash.Dash): - self.app = app - self._register_callbacks() - - def layout(self): - return dbc.Container( - [ - - # Store - dcc.Store( - id="login-state", - data={ - "text": "Please log in", - "sessionStateCode": 0, - }, - ), - - # Info message (used by callback) - html.Div( - id="info-message", - className="mb-3", - style={"fontWeight": "500"} - ), - - # Form wrapper (used by callback) - html.Div( - id="form-wrapper", - children=[ - dbc.Row( - [ - dcc.Location(id="url", refresh=False), - html.Div(id="ws-url"), - - # Username - dbc.Col( - [ - dbc.Label("Username"), - dbc.Input( - id="username", - type="text", - placeholder="Enter username", - style={ - "backgroundColor": "#0f1118", - "color": "white", - "border": "1px solid #2a2e39", - }, - ), - ], - md=4, - ), - - # Password - dbc.Col( - [ - dbc.Label("Password"), - dbc.Input( - id="password", - type="password", - placeholder="Enter password", - style={ - "backgroundColor": "#0f1118", - "color": "white", - "border": "1px solid #2a2e39", - }, - ), - ], - md=4, - ), - - # Button - dbc.Col( - dbc.Button( - "Login", - id="login-btn", - color="primary", - className="mt-4 w-100", - n_clicks=0, - ), - md=2, - align="end", - ), - ], - className="g-3 align-items-end", - style={"marginTop": "20px"}, - ), - WebSocket(id="ws", url="ws://127.0.0.1:8765") - ], - ), - ], - fluid=True, - ) - - - def _register_callbacks(self): - - @self.app.callback(Output("output", "children"), - Input("ws", "message")) - def display_message(msg): - if msg is None: - return "No data yet" - return f"Received: {msg['data']}" - - @self.app.callback( - Output("ws-url", "children"), - Input("url", "href") - ) - def get_websocket_url(href): - if not href: - return "" - - from urllib.parse import urlparse - - parsed = urlparse(href) - - host = parsed.hostname - port = 9002 - protocol = "wss" if parsed.scheme == "https" else "ws" - - return f"{protocol}://{host}:{port}" - - # ---------------------------- - # Login Submit Logic - # ---------------------------- - @self.app.callback( - Output("login-state", "data"), - Input("login-btn", "n_clicks"), - State("username", "value"), - State("password", "value"), - prevent_initial_call=True, - ) - def handle_login(n, username, password): - if not username or not password: - return { - "text": "Username and password required", - "sessionStateCode": 0 - } - - # This replaces props.logonCallback(...) - logon_value = { - "username": username, - "password": password, - } - - print("Logon value:", logon_value) - - return { - "text": "Ready to trade", - "sessionStateCode": 1 - } - - @self.app.callback( - Output("info-message", "children"), - Output("info-message", "className"), - Output("form-wrapper", "style"), - Input("login-state", "data"), - ) - def update_ui(login_state): - text = login_state["text"] - session_code = login_state["sessionStateCode"] - - # Equivalent to: - # props.loginState.text === "Ready to trade" - class_name = "header_ready_to_trade" if text == "Ready to trade" else "" - - # Equivalent to: - # pointerEvents: "none", opacity: "0.4" - if session_code != 0: - style = {"pointerEvents": "none", "opacity": "0.4"} - else: - style = {} - - return text, class_name, style \ No newline at end of file diff --git a/MiscClients/dash_ws/components/positions_and_market_data.py b/MiscClients/dash_ws/components/positions_and_market_data.py deleted file mode 100644 index c709badf..00000000 --- a/MiscClients/dash_ws/components/positions_and_market_data.py +++ /dev/null @@ -1,57 +0,0 @@ -import dash -import dash_ag_grid as dag -import dash_bootstrap_components as dbc - -class PositionAndMarketDataPanel(object): - - def __init__(self, app: dash.Dash): - self.app = app - - self.positions_and_market_data = dag.AgGrid( - id="position-and-market-data", - rowData=[], - defaultColDef={ - "resizable": False, - "sortable": False, - "filter": False, - }, - columnDefs=[ - { - "field": "key", - "width": 120, - "cellStyle": {"fontWeight": "bold"}, - }, - { - "field": "value", - "flex": 1, - }, - ], - dashGridOptions={ - "rowSelection": "single", - "animateRows": True, - "suppressMaintainUnsortedOrder": True, - "headerHeight": 0, - "theme": "legacy", - }, - className="ag-theme-balham-dark", - ) - - self._register_callbacks() - - def _register_callbacks(self): - pass - - def layout(self): - return dbc.Container( - [ - dbc.Row( - [ - dbc.Col( - self.positions_and_market_data, - ), - ], - className="p-3", - ), - ], - fluid=True, - ) \ No newline at end of file diff --git a/MiscClients/dash_ws/components/price_level_panel.py b/MiscClients/dash_ws/components/price_level_panel.py deleted file mode 100644 index 35af5aaf..00000000 --- a/MiscClients/dash_ws/components/price_level_panel.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright (c) Mike Kipnis - DistributedATS - -import dash -import dash_ag_grid as dag -import dash_bootstrap_components as dbc - -class PriceLevelPanel(object): - - def __init__(self, app: dash.Dash): - self.app = app - - self.price_level = dag.AgGrid( - id="price-level", - rowData=[], - defaultColDef={ - "resizable": False, - "sortable": False, - "filter": False, - }, - columnDefs=[ - { - "field": "key", - "width": 120, - "cellStyle": {"fontWeight": "bold"}, - }, - { - "field": "value", - "flex": 1, - }, - ], - dashGridOptions={ - "rowSelection": "single", - "animateRows": True, - "suppressMaintainUnsortedOrder": True, - "headerHeight": 0, - "theme": "legacy", - }, - className="ag-theme-balham-dark", - ) - - self._register_callbacks() - - def _register_callbacks(self): - pass - - def layout(self): - return dbc.Container( - [ - dbc.Row( - [ - dbc.Col( - self.price_level, - ), - ], - className="p-3", - ), - ], - fluid=True, - ) \ No newline at end of file diff --git a/MiscClients/dash_ws/components/ticket_panel.py b/MiscClients/dash_ws/components/ticket_panel.py deleted file mode 100644 index 58407c4d..00000000 --- a/MiscClients/dash_ws/components/ticket_panel.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) Mike Kipnis - DistributedATS - -import datetime - -import dash -from dash import Input, Output, html, dcc, State -from dash import callback_context -import dash_bootstrap_components as dbc - -class TicketPanel(object): - - def __init__(self, app: dash.Dash): - self.app = app - self._register_callbacks() - - def layout(self): - return dbc.Container( - [ - ], - fluid=True, - ) - - def _register_callbacks(self): - pass \ No newline at end of file diff --git a/MiscClients/dash_ws/dash_trader.py b/MiscClients/dash_ws/dash_trader.py deleted file mode 100644 index 890611fd..00000000 --- a/MiscClients/dash_ws/dash_trader.py +++ /dev/null @@ -1,208 +0,0 @@ -# Copyright (c) Mike Kipnis - DistributedATS - -import os -import logging -import threading -from datetime import datetime, timedelta - -import dash -import dash_ag_grid as dag -import dash_bootstrap_components as dbc -from dash import html, dcc, Output, Input -from components import login_panel -from components import positions_and_market_data -from components import price_level_panel -from components import ticket_panel - -from dash_extensions import WebSocket - -import asyncio -import websockets -import json -import random - -from ws_server import ws_main - -# ---------------------------- -# Logging configuration -# ---------------------------- -logging.basicConfig( - level=logging.DEBUG, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - handlers=[ - logging.StreamHandler(), - logging.FileHandler("dash_trader.log", "a"), - ], -) - -logger = logging.getLogger("dash_trader") - -# ============================= -# Dash App Initialization -# ============================= -app = dash.Dash( - __name__, - title="Dash Trader", - external_stylesheets=[dbc.themes.SUPERHERO], -) - -server = app.server # Gunicorn expects this - -class DashTrader: - def __init__(self, app: dash.Dash): - self.app = app - self.login_panel = login_panel.LogonPanel(app) - self.positions_and_market_data_panel = positions_and_market_data.PositionAndMarketDataPanel(app) - self.price_level_panel = price_level_panel.PriceLevelPanel(app) - self.ticket_panel = ticket_panel.TicketPanel(app) - - def layout(self): - return dbc.Container( - [ - dbc.Navbar( - dbc.Container( - [ - # Left side - dbc.NavbarBrand( - "Dash Trader", - href="#", - style={"fontSize": "20px"}, - ), - - # Right side - html.Div( - [ - dbc.Row( - [ - self.login_panel.layout(), - self.ticket_panel.layout() - ] - ), - - html.Div(id="status", className="text-white ms-2"), - ], - className="d-flex flex-column align-items-start ms-auto", - ), - ], - fluid=True, - ), - color="#0f2538", - dark=True, - ), - - html.Div( - [ - dbc.Accordion( - [ - - # --- Sector / Industry --- - dbc.AccordionItem( - dbc.Row( - [ - dbc.Col( - self.positions_and_market_data_panel.layout() - ), - dbc.Col( - dbc.Row( - [ - self.price_level_panel.layout() - ]), - ) - ], - className="p-3", - ), - title="Sector & Industry", - item_id="sector-industry", - ), - - # --- Institution / Stock --- - dbc.AccordionItem( - dbc.Row( - [ - ], - className="p-3", - ), - title="Institution & Stock", - item_id="institution-stock", - ), - - # --- Price Chart / Market Data --- - dbc.AccordionItem( - dbc.Row( - [ - ], - className="p-3 flex-nowrap", # flex-nowrap keeps them on the same row - style={"overflowX": "auto"}, # optional: allows horizontal scroll if content is wide - # no_gutters=True # older versions of dash-bootstrap-components - ), - title="Price Chart & Market Data", - ), - - # --- News --- - dbc.AccordionItem( - dbc.Row( - [ - ], - className="p-3", - ), - title="News", - ), - - ], - id="main-accordion", - start_collapsed=False, # all collapsed on load - flush=True, # cleaner look - always_open=True, # allow multiple open at once - active_item=["sector-industry", "institution-stock"], - )], id="accordion-wrapper",), - - html.Div( - [ - "For support, contact: ", - html.A( - "mike.kipnis@gmail.com", - href="mailto:mike.kipnis@gmail.com", - style={ - "textDecoration": "underline", - "color": "#AAAAAA", - }, - ), - ], - style={ - "fontSize": "12px", - "color": "#AAAAAA", - "marginTop": "8px", - "textAlign": "center", - "width": "100%", - }, - ), - ], - fluid=True, - className="p-0", - ) - - -def start_ws_server(): - asyncio.run(ws_main()) - -# Create sandbox with this prefix -dash_trader = DashTrader(app) - -# Set layout once -app.layout = dash_trader.layout - -# ---------------------------- -# Main -# ---------------------------- -if __name__ == "__main__": - - # Start WebSocket server in background thread - ws_thread = threading.Thread(target=start_ws_server, daemon=True) - ws_thread.start() - - host = os.getenv("DASH_HOST", "127.0.0.1") - port = int(os.getenv("DASH_PORT", "8050")) - debug = os.getenv("DASH_DEBUG", "true").lower() == "true" - - app.run(host=host, port=port, debug=debug, use_reloader=False) \ No newline at end of file diff --git a/MiscClients/dash_ws/requirements.txt b/MiscClients/dash_ws/requirements.txt deleted file mode 100644 index 01bbed93..00000000 --- a/MiscClients/dash_ws/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -dash>=3.2.0 -dash-bootstrap-components>=2.0.3 -dash-ag-grid>=31.4.0 -dash-extensions -websockets>=12.0 diff --git a/MiscClients/dash_ws/ws_server.py b/MiscClients/dash_ws/ws_server.py deleted file mode 100644 index 1cb1ade6..00000000 --- a/MiscClients/dash_ws/ws_server.py +++ /dev/null @@ -1,68 +0,0 @@ -import asyncio -import json -import random -import websockets -from websockets.exceptions import ConnectionClosed - -connected = set() - -# ---------------------------- -# Receive loop (client → server) -# ---------------------------- -async def receiver(websocket): - try: - async for message in websocket: - data = json.loads(message) - print("Received:", data) - except ConnectionClosed: - print("Receiver: client disconnected") - - -# ---------------------------- -# Send loop (server → client) -# ---------------------------- -async def sender(websocket): - try: - while True: - data = {"price": random.random()} - await websocket.send(json.dumps(data)) - await asyncio.sleep(1) - except ConnectionClosed: - print("Sender: client disconnected") - - -# ---------------------------- -# Connection handler -# ---------------------------- -async def handler(websocket): - connected.add(websocket) - print("Client connected") - - recv_task = asyncio.create_task(receiver(websocket)) - send_task = asyncio.create_task(sender(websocket)) - - try: - done, pending = await asyncio.wait( - [recv_task, send_task], - return_when=asyncio.FIRST_COMPLETED, - ) - finally: - # Cancel remaining tasks properly - for task in pending: - task.cancel() - try: - await task - except asyncio.CancelledError: - pass - - connected.remove(websocket) - print("Client disconnected") - - -# ---------------------------- -# Server -# ---------------------------- -async def ws_main(): - async with websockets.serve(handler, "127.0.0.1", 8765): - print("WebSocket server running on ws://127.0.0.1:8765") - await asyncio.Future() \ No newline at end of file