add dash_auth (use hashed password for security - never store plain text password)
This commit is contained in:
parent
9fbc3f6162
commit
b070bcdd9f
247
indicators.py
247
indicators.py
@ -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(
|
||||||
|
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user