add dash_auth (use hashed password for security - never store plain text password)

This commit is contained in:
George 2024-10-03 10:50:07 -07:00
parent 9fbc3f6162
commit b070bcdd9f
2 changed files with 135 additions and 112 deletions

View File

@ -12,7 +12,7 @@ Subplot3
MACD MACD
TODO: validate the plots with online resource TODO: validate the plots with online resource
Created on Mon Feb 17 14:50:17 2020 Updated on Mon Sep. 30, 2024
Use one as buy/sell trigger and verify a bag of indicators to make final decision. Use one as buy/sell trigger and verify a bag of indicators to make final decision.
Could also come up with a value that ties to the trading volume. Could also come up with a value that ties to the trading volume.
@author: thomwang @author: thomwang
@ -37,10 +37,24 @@ import json
import io import io
from flask_caching import Cache from flask_caching import Cache
from dash.exceptions import PreventUpdate from dash.exceptions import PreventUpdate
import dash_auth
import yahoo_fin.stock_info as si import yahoo_fin.stock_info as si
import hashlib
pd.options.mode.chained_assignment = None # default='warn' pd.options.mode.chained_assignment = None # default='warn'
def hash_password(password):
# Encode the password as bytes
password_bytes = password.encode('utf-8')
# Use SHA-256 hash function to create a hash object
hash_object = hashlib.sha256(password_bytes)
# Get the hexadecimal representation of the hash
password_hash = hash_object.hexdigest()
return password_hash
# def fill_missing_data(df): # def fill_missing_data(df):
# df.ffill(inplace=True) # df.ffill(inplace=True)
# df.bfilln(inplace=True) # df.bfilln(inplace=True)
@ -337,76 +351,75 @@ class security:
return macd_line, macd_sig, macd_hist, norm_hist return macd_line, macd_sig, macd_hist, norm_hist
def bollinger_sell(stock, wd=200): # def bollinger_sell(stock, wd=200):
""" # """
Parameters # Parameters
---------- # ----------
stock : TYPE class 'serurity' # stock : TYPE class 'serurity'
wd : TYPE, int, optional # wd : TYPE, int, optional
DESCRIPTION. Moving average windows. The default is 200. # DESCRIPTION. Moving average windows. The default is 200.
Returns # Returns
------- # -------
TYPE DataFrame # TYPE DataFrame
DESCRIPTION - +1 when stock price is above bollinger upper band # DESCRIPTION - +1 when stock price is above bollinger upper band
. -1 when vice versa. transition days are of value +3 and -3 # . -1 when vice versa. transition days are of value +3 and -3
respectively. A value of -3 is a sell signal # respectively. A value of -3 is a sell signal
""" # """
_, bol_up = stock.bollinger(wd) # _, bol_up = stock.bollinger(wd)
# bol_up = bol_up[bol_up.columns[-1]].to_frame() # # bol_up = bol_up[bol_up.columns[-1]].to_frame()
# bol_up = bol_up.iloc[:, [-1]] # # bol_up = bol_up.iloc[:, [-1]]
sell = np.sign(stock.price.sub(bol_up.values)) # sell = np.sign(stock.price.sub(bol_up.values))
sell_diff = sell.diff() # sell_diff = sell.diff()
return sell.add(sell_diff.values) # return sell.add(sell_diff.values)
def bollinger_buy(stock, wd=200): # def bollinger_buy(stock, wd=200):
""" # """
Parameters # Parameters
---------- # ----------
stock : TYPE class 'serurity' # stock : TYPE class 'serurity'
wd : TYPE, int, optional # wd : TYPE, int, optional
DESCRIPTION. Moving average windows. The default is 200. # DESCRIPTION. Moving average windows. The default is 200.
Returns # Returns
------- # -------
TYPE DataFrame # TYPE DataFrame
DESCRIPTION - +1 when stock price is above bollinger lower band # DESCRIPTION - +1 when stock price is above bollinger lower band
. -1 when vice versa. transition days are of value +3 and -3 # . -1 when vice versa. transition days are of value +3 and -3
respectively. A value of +3 is a buy signal # respectively. A value of +3 is a buy signal
""" # """
bol_low, _ = stock.bollinger(wd) # bol_low, _ = stock.bollinger(wd)
buy = np.sign(stock.price.sub(bol_low.values)) # buy = np.sign(stock.price.sub(bol_low.values))
buy_diff = buy.diff() # buy_diff = buy.diff()
return buy.add(buy_diff.values) # return buy.add(buy_diff.values)
# def simple_bollinger_strategy(stk):
def simple_bollinger_strategy(stk): # # buy orders
# buy = bollinger_buy(stk, 190)
# buy_orders = buy[np.any(buy>2, axis=1)]
# buy orders # sell = bollinger_sell(stk, 190)
buy = bollinger_buy(stk, 190) # sell_orders = sell[np.any(sell<-2, axis=1)]
buy_orders = buy[np.any(buy>2, axis=1)]
sell = bollinger_sell(stk, 190) # orders = pd.concat([buy_orders, sell_orders])
sell_orders = sell[np.any(sell<-2, axis=1)] # orders = orders.sort_index()
orders = pd.concat([buy_orders, sell_orders]) # order_list = pd.DataFrame(columns = ['Date', 'Symbol', 'Order', 'Shares'])
orders = orders.sort_index() # for index, row in orders.iterrows():
# for sym in orders.columns.values:
# if row[sym] > 2: # buy order
# order_list = order_list.append({'Date' : index, 'Symbol' : sym,
# 'Order' : 'BUY', 'Shares' : 100}, ignore_index = True )
# elif row[sym] < -2: # sell order
# order_list = order_list.append({'Date' : index, 'Symbol' : sym,
# 'Order' : 'SELL', 'Shares' : 100}, ignore_index = True )
# order_list = order_list.set_index('Date')
order_list = pd.DataFrame(columns = ['Date', 'Symbol', 'Order', 'Shares']) # return order_list
for index, row in orders.iterrows():
for sym in orders.columns.values:
if row[sym] > 2: # buy order
order_list = order_list.append({'Date' : index, 'Symbol' : sym,
'Order' : 'BUY', 'Shares' : 100}, ignore_index = True )
elif row[sym] < -2: # sell order
order_list = order_list.append({'Date' : index, 'Symbol' : sym,
'Order' : 'SELL', 'Shares' : 100}, ignore_index = True )
order_list = order_list.set_index('Date')
return order_list
# def plot_against_sym(df, sym=['SPY']): # def plot_against_sym(df, sym=['SPY']):
# df_temp = df.copy() # df_temp = df.copy()
@ -476,50 +489,50 @@ def get_sma_slope(stocks, wd = 50):
return slope return slope
def modified_bollinger_strategy(stk): # def modified_bollinger_strategy(stk):
rsi = stk.rsi() # rsi = stk.rsi()
btemp = pd.DataFrame() # btemp = pd.DataFrame()
# buy orders # # buy orders
buy = bollinger_buy(stk, 190) # buy = bollinger_buy(stk, 190)
buy_orders = buy[np.any(buy>2, axis=1)] # buy_orders = buy[np.any(buy>2, axis=1)]
for col in buy_orders.columns: # for col in buy_orders.columns:
buy_stk = buy_orders[col] # buy_stk = buy_orders[col]
buy_stk = buy_stk[buy_stk > 2] # buy_stk = buy_stk[buy_stk > 2]
buy_stk = buy_stk[rsi[col].loc[buy_stk.index] < 70] # buy_stk = buy_stk[rsi[col].loc[buy_stk.index] < 70]
btemp = btemp.join(buy_stk, how='outer') # btemp = btemp.join(buy_stk, how='outer')
stemp = pd.DataFrame() # stemp = pd.DataFrame()
sell = bollinger_sell(stk, 190) # sell = bollinger_sell(stk, 190)
sell_orders = sell[np.any(sell<-2, axis=1)] # sell_orders = sell[np.any(sell<-2, axis=1)]
for col in sell_orders.columns: # for col in sell_orders.columns:
sell_stk = sell_orders[col] # sell_stk = sell_orders[col]
sell_stk = sell_stk[sell_stk < -2] # sell_stk = sell_stk[sell_stk < -2]
sell_stk = sell_stk[rsi[col].loc[sell_stk.index] > 30] # sell_stk = sell_stk[rsi[col].loc[sell_stk.index] > 30]
stemp = stemp.join(sell_stk, how='outer') # stemp = stemp.join(sell_stk, how='outer')
orders = pd.concat([btemp, stemp]) # orders = pd.concat([btemp, stemp])
# TODO - refine orders based on slope # # TODO - refine orders based on slope
# TODO - revine further based on other conditions (RSI, MACD) # # TODO - revine further based on other conditions (RSI, MACD)
# TODO - transaction shares determination # # TODO - transaction shares determination
orders = orders.sort_index() # orders = orders.sort_index()
order_list = pd.DataFrame(columns = ['Date', 'Symbol', 'Order', 'Shares']) # order_list = pd.DataFrame(columns = ['Date', 'Symbol', 'Order', 'Shares'])
for index, row in orders.iterrows(): # for index, row in orders.iterrows():
for sym in orders.columns.values: # for sym in orders.columns.values:
if row[sym] > 2: # buy order # if row[sym] > 2: # buy order
order_list = order_list.append({'Date' : index, 'Symbol' : sym, # order_list = order_list.append({'Date' : index, 'Symbol' : sym,
'Order' : 'BUY', 'Shares' : 100}, ignore_index = True ) # 'Order' : 'BUY', 'Shares' : 100}, ignore_index = True )
elif row[sym] < -2: # sell order # elif row[sym] < -2: # sell order
order_list = order_list.append({'Date' : index, 'Symbol' : sym, # order_list = order_list.append({'Date' : index, 'Symbol' : sym,
'Order' : 'SELL', 'Shares' : 100}, ignore_index = True ) # 'Order' : 'SELL', 'Shares' : 100}, ignore_index = True )
order_list = order_list.set_index('Date') # order_list = order_list.set_index('Date')
return order_list # return order_list
def intelligent_loop_plots(sym, tmp): def intelligent_loop_plots(sym, stk_data):
# Only plot ones that are standing out meaning: # Only plot ones that are standing out meaning:
# 1. outside of bollinger bands or recently crossed over (within 9 days) # 1. outside of bollinger bands or recently crossed over (within 9 days)
# 2. RSI above 70 or below 30 # 2. RSI above 70 or below 30
@ -533,10 +546,8 @@ def intelligent_loop_plots(sym, tmp):
# symbol = ['AMZN', 'SPY', 'GOOG', 'BAC', 'BA', 'XLE', 'CTL', 'ATVI', 'JD',\ # symbol = ['AMZN', 'SPY', 'GOOG', 'BAC', 'BA', 'XLE', 'CTL', 'ATVI', 'JD',\
# 'COST', 'HD', 'UBER', 'XOM', 'UAL', 'LUV', 'T', 'WMT'] # 'COST', 'HD', 'UBER', 'XOM', 'UAL', 'LUV', 'T', 'WMT']
# matplotlib.rcParams.update({'figure.max_open_warning': 0}) price = stk_data["adjclose"]
vol = stk_data["volume"]
price = tmp["adjclose"]
vol = tmp["volume"]
stk = security(sym, price, vol) stk = security(sym, price, vol)
rsi = stk.rsi() rsi = stk.rsi()
@ -646,24 +657,30 @@ def intelligent_loop_plots(sym, tmp):
return data, vol.to_frame('_VOL'), macd, rsi, plot_indicator return data, vol.to_frame('_VOL'), macd, rsi, plot_indicator
VALID_USERNAME_PASSWORD_PAIRS = {
'ce16559af2caf7bb54bebd57a1602e29ada331b3356004265abeab0e568278cc':
'b4db36eb3f887ca0bc15de7feb0b41a9589b65dbd5325aedf1c76b3ee63b8871'
}
def user_login(username, password):
if VALID_USERNAME_PASSWORD_PAIRS.get(hash_password(username)) == hash_password(password):
return True
return False
if __name__ == "__main__": if __name__ == "__main__":
lb_year = 3 # years of stock data to retrieve lb_year = 3 # years of stock data to retrieve
plt_year = 2 # number of years data to plot plt_year = 2 # number of years data to plot
lb_trigger = 5 # days to lookback for triggering events lb_trigger = 5 # days to lookback for triggering events
end_date = dt.datetime.today()
start_date = end_date - dt.timedelta(days = 365 * lb_year)
plot_sd = end_date - dt.timedelta(days = 365 * plt_year)
plot_ed = end_date
# test_sec_class()
# test_smooth()
# test_bollinger_sell()
# test_get_orders()
# Initialize the app # Initialize the app
app = Dash() app = Dash()
dash_auth.BasicAuth(
app,
# VALID_USERNAME_PASSWORD_PAIRS,
auth_func=user_login,
secret_key="MK8dyS6PyDDuEuzrmqa7dJTJZ7eH2Jkh",
)
watchlist = get_watchlist() watchlist = get_watchlist()
# symbols = watchlist.index.values.tolist() # symbols = watchlist.index.values.tolist()
@ -680,7 +697,7 @@ if __name__ == "__main__":
# App layout # App layout
app.layout = [ app.layout = [
html.Div([ html.Div([
html.Button('Reload Syms', id="reload-button", n_clicks=0, html.Button('Reload', 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': '120px', 'display': 'inline-block', 'margin-bottom': '10px', 'margin-right': '5px', 'height':'36px', 'verticalAlign': 'top'}),
html.Div([ html.Div([
dcc.Dropdown(symbols, symbols[0], id='symbols_dropdown_list',), dcc.Dropdown(symbols, symbols[0], id='symbols_dropdown_list',),
@ -700,7 +717,8 @@ if __name__ == "__main__":
id='interval-component', id='interval-component',
interval=3*1000, # in milliseconds interval=3*1000, # in milliseconds
n_intervals=0, n_intervals=0,
# max_intervals=len(symbols), max_intervals=2*len(symbols),
# max_intervals=3, # for testing
disabled=True, disabled=True,
), ),
] ]
@ -738,13 +756,14 @@ if __name__ == "__main__":
# reload button callback # reload button callback
@callback(Output('symbols_dropdown_list', 'options'), @callback(Output('symbols_dropdown_list', 'options'),
Output('interval-component', 'n_intervals'),
Input("reload-button", "n_clicks"),) Input("reload-button", "n_clicks"),)
def reload_syms(n): def reload_syms(n):
if n: if n:
watchlist = get_watchlist() watchlist = get_watchlist()
symbols = (watchlist.index.values + " - " + watchlist["Sub Segment"]).tolist() symbols = (watchlist.index.values + " - " + watchlist["Sub Segment"]).tolist()
return symbols return symbols, 0
return no_update return no_update
# interval callback # interval callback
@ -776,7 +795,11 @@ if __name__ == "__main__":
# get data # get data
# tmp = si.get_data(sym, start_date=sd)[["adjclose", "volume"]] # tmp = si.get_data(sym, start_date=sd)[["adjclose", "volume"]]
sym = col_chosen.split()[0] sym = col_chosen.split()[0]
tmp = fetch_stk_data(sym, start_date) end_date = dt.datetime.today()
start_date = end_date - dt.timedelta(days = 365 * lb_year)
# plot_sd = end_date - dt.timedelta(days = 365 * plt_year)
# plot_ed = end_date
tmp = fetch_stk_data(sym, start_date) # cached function all
data, vol, macd, rsi, plot_ind = intelligent_loop_plots(sym, tmp) data, vol, macd, rsi, plot_ind = intelligent_loop_plots(sym, tmp)
fig = make_subplots( fig = make_subplots(

Binary file not shown.