From 1b8612e504f37bc13db7fccc1365d4936fe5cf0e Mon Sep 17 00:00:00 2001 From: Charlie Date: Sat, 5 Oct 2024 11:23:52 -0700 Subject: [PATCH] Implemented add ticker feature. TODO - implement remvoe ticker feature. --- indicators.py | 57 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/indicators.py b/indicators.py index d5c3eea..c3329bd 100644 --- a/indicators.py +++ b/indicators.py @@ -36,6 +36,7 @@ from dotenv import load_dotenv import psycopg2 import os import sys +from sec_cik_mapper import StockMapper pd.options.mode.chained_assignment = None # default='warn' load_dotenv() @@ -81,6 +82,28 @@ def get_watchlist(username : str): return df +def insert_ticker(username : str, tick : str, name : str): + if username: + table_name = f"{username + '_watch_list'}" + else: # username is None, use default table + table_name = "stock_watch_list" + + QUERY1 = f'''CREATE TABLE IF NOT EXISTS {table_name} + ( + tick character varying(5) NOT NULL, + description text, + PRIMARY KEY (tick) + );''' + QUERY2 = f"INSERT INTO {table_name} SELECT 'SPY', 'SPDR S&P 500 ETF Trust' WHERE NOT EXISTS (SELECT NULL FROM {table_name});" + + QUERY3 = f"INSERT INTO {table_name} VALUES ('{tick}', '{name}') ON CONFLICT DO NOTHING;" + + with connect_db() as conn: + with conn.cursor() as curs: + curs.execute(QUERY1) + curs.execute(QUERY2) + curs.execute(QUERY3) + def hash_password(password): # Encode the password as bytes password_bytes = password.encode('utf-8') @@ -622,13 +645,19 @@ def fetch_stk_data(sym, sd): app.layout = [ html.Div([ html.Button('Load', id="reload-button", n_clicks=0, - style={'font-size': '12px', 'width': '120px', 'display': 'inline-block', 'margin-bottom': '10px', 'margin-right': '5px', 'height':'36px', 'verticalAlign': 'top'}), + style={'font-size': '12px', 'width': '80px', 'display': 'inline-block', 'margin-bottom': '10px', 'margin-right': '5px', 'height':'36px', 'verticalAlign': 'top'}), + # html.Label('Add ticker:', style={'width': '80px', 'display': 'inline-block', 'height':'36px', 'textAlign': 'center', 'adjust': 'right', 'margin-bottom': '10px', 'margin-right': '5px', 'verticalAlign': 'top'}), + html.Div([ + html.Label('Add ticker:'), + dcc.Input(id='input-ticker', type='text', maxLength=5, debounce=True, style={'height':'31px', 'width':'50px'}), + ], style={'width': '150px', 'text-align': 'center'}), + html.Button('Remove', id="remove-button", n_clicks=0, + style={'font-size': '12px', 'width': '80px', 'display': 'inline-block', 'margin-bottom': '10px', 'margin-right': '5px', 'height':'36px', 'verticalAlign': 'top'}), html.Div([ - # dcc.Dropdown({'SPY - SPDR S&P 500 ETF Trust'}, 'SPY - SPDR S&P 500 ETF Trust', id='symbols_dropdown_list',), dcc.Dropdown(id='symbols_dropdown_list',), ], style={'width': '330px', 'text-align': 'center'}), html.Button('Auto Play', id="start-button", n_clicks=0, - style={'font-size': '12px', 'width': '120px', 'display': 'inline-block', 'margin-bottom': '10px', 'margin-right': '5px', 'height':'36px', 'verticalAlign': 'top'}), + style={'font-size': '12px', 'width': '80px', 'display': 'inline-block', 'margin-bottom': '10px', 'margin-right': '5px', 'height':'36px', 'verticalAlign': 'top'}), ], style={'display':'flex', 'justify-content':'center'}), dcc.Graph( @@ -644,6 +673,8 @@ app.layout = [ max_intervals=1, disabled=True, ), + + dcc.Store(id="signaling"), ] app.clientside_callback( @@ -662,6 +693,19 @@ app.clientside_callback( Input("start-button", "id") ) +# tiker input callback +@callback(Output('signaling', component_property='data'), + Input('input-ticker', component_property='value')) + +def update_tickers(ticker): + if ticker: + long_name = StockMapper().ticker_to_company_name.get(ticker) + if long_name: + insert_ticker(auth.username, ticker.upper(), long_name) + return True + return no_update + + # start / stop button callback @callback(Output('interval-component', 'disabled'), Output("start-button", "children"), @@ -681,10 +725,11 @@ def start_cycle(n, value): @callback(Output('symbols_dropdown_list', 'options'), Output('interval-component', 'n_intervals'), Output('interval-component', 'max_intervals'), - Input("reload-button", "n_clicks"),) + Input("reload-button", "n_clicks"), + Input("signaling", "data")) -def reload_syms(n): - if n: +def reload_syms(n, s): + if n or s: watchlist = get_watchlist(auth.username) symbols = (watchlist.iloc[:, 0] + " - " + watchlist.iloc[:, 1]).tolist() return symbols, 0, 2*len(symbols)