Cleanup (no new features)

This commit is contained in:
George 2024-10-03 21:05:39 -07:00
parent b070bcdd9f
commit f25156896d

View File

@ -21,20 +21,13 @@ Could also come up with a value that ties to the trading volume.
import pandas as pd
import numpy as np
import datetime as dt
# from util import get_data, get_price_volume, plot_data, get_watchlist
from util import get_price_volume, plot_data, get_watchlist
# from marketsim import compute_portvals, compute_portfolio_stats, normalize_data
# import matplotlib.pyplot as plt
# import matplotlib
from util import get_watchlist
from numpy.fft import fft, ifft
import scipy.signal as sig
import plotly.express as px
from plotly.subplots import make_subplots
# import plotly.graph_objects as go
from dash import Dash, html, dcc, callback, Output, Input, State, no_update
from waitress import serve
import json
import io
from flask_caching import Cache
from dash.exceptions import PreventUpdate
import dash_auth
@ -106,7 +99,6 @@ def smooth(price, hsize=10, sigma=3):
return data
class security:
"""
This can be a list of stocks, bonds, or otherinvestment vehicles.
@ -350,100 +342,6 @@ class security:
norm_hist = macd_hist.div(macd_long.values)
return macd_line, macd_sig, macd_hist, norm_hist
# def bollinger_sell(stock, wd=200):
# """
# Parameters
# ----------
# stock : TYPE class 'serurity'
# wd : TYPE, int, optional
# DESCRIPTION. Moving average windows. The default is 200.
# Returns
# -------
# TYPE DataFrame
# DESCRIPTION - +1 when stock price is above bollinger upper band
# . -1 when vice versa. transition days are of value +3 and -3
# respectively. A value of -3 is a sell signal
# """
# _, bol_up = stock.bollinger(wd)
# # bol_up = bol_up[bol_up.columns[-1]].to_frame()
# # bol_up = bol_up.iloc[:, [-1]]
# sell = np.sign(stock.price.sub(bol_up.values))
# sell_diff = sell.diff()
# return sell.add(sell_diff.values)
# def bollinger_buy(stock, wd=200):
# """
# Parameters
# ----------
# stock : TYPE class 'serurity'
# wd : TYPE, int, optional
# DESCRIPTION. Moving average windows. The default is 200.
# Returns
# -------
# TYPE DataFrame
# DESCRIPTION - +1 when stock price is above bollinger lower band
# . -1 when vice versa. transition days are of value +3 and -3
# respectively. A value of +3 is a buy signal
# """
# bol_low, _ = stock.bollinger(wd)
# buy = np.sign(stock.price.sub(bol_low.values))
# buy_diff = buy.diff()
# return buy.add(buy_diff.values)
# def simple_bollinger_strategy(stk):
# # buy orders
# buy = bollinger_buy(stk, 190)
# buy_orders = buy[np.any(buy>2, axis=1)]
# sell = bollinger_sell(stk, 190)
# sell_orders = sell[np.any(sell<-2, axis=1)]
# orders = pd.concat([buy_orders, sell_orders])
# orders = orders.sort_index()
# order_list = pd.DataFrame(columns = ['Date', 'Symbol', 'Order', 'Shares'])
# 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']):
# df_temp = df.copy()
# df_sym = get_data(sym, pd.to_datetime(df_temp.index.values), addSPY=False)
# df_temp[sym[0]] = df_sym.values
# df_temp = normalize_data(df_temp)
# plot_data(df_temp)
# return df_sym
# def test_bollinger_sell():
# sd = dt.datetime(2010,1,1)
# # ed = dt.datetime.today()
# ed = dt.datetime(2012,12,31)
# symbol = ['XOM']
# dates = dates = pd.date_range(sd, ed)
# prices = get_data(symbol, dates, addSPY=False)
# # prices = prices.dropna()
# stk = security(prices)
# sell = bollinger_sell(stk)
# plot_data(sell)
# buy = bollinger_buy(stk, 190)
# plot_data(buy)
def get_crossing(stocks):
"""
Parameters
@ -488,49 +386,9 @@ def get_sma_slope(stocks, wd = 50):
return slope
# def modified_bollinger_strategy(stk):
# rsi = stk.rsi()
# btemp = pd.DataFrame()
# # buy orders
# buy = bollinger_buy(stk, 190)
# buy_orders = buy[np.any(buy>2, axis=1)]
# for col in buy_orders.columns:
# buy_stk = buy_orders[col]
# buy_stk = buy_stk[buy_stk > 2]
# buy_stk = buy_stk[rsi[col].loc[buy_stk.index] < 70]
# btemp = btemp.join(buy_stk, how='outer')
# stemp = pd.DataFrame()
# sell = bollinger_sell(stk, 190)
# sell_orders = sell[np.any(sell<-2, axis=1)]
# for col in sell_orders.columns:
# sell_stk = sell_orders[col]
# sell_stk = sell_stk[sell_stk < -2]
# sell_stk = sell_stk[rsi[col].loc[sell_stk.index] > 30]
# stemp = stemp.join(sell_stk, how='outer')
# orders = pd.concat([btemp, stemp])
# # TODO - refine orders based on slope
# # TODO - revine further based on other conditions (RSI, MACD)
# # TODO - transaction shares determination
# orders = orders.sort_index()
# order_list = pd.DataFrame(columns = ['Date', 'Symbol', 'Order', 'Shares'])
# 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
LB_YEAR = 3 # years of stock data to retrieve
# PLT_YEAR = 2 # number of years data to plot
LB_TRIGGER = 5 # days to lookback for triggering events
def intelligent_loop_plots(sym, stk_data):
# Only plot ones that are standing out meaning:
@ -563,25 +421,25 @@ def intelligent_loop_plots(sym, stk_data):
# print('{:5}: '.format(sym), end = '')
# RSI outside window (over bought / over sold)
rsi_tail = rsi.tail(lb_trigger)
rsi_tail = rsi.tail(LB_TRIGGER)
if (rsi_tail >= 70).any() or (rsi_tail <= 30).any():
# print('--RSI', end = '')
plot_indicator += 'RSI, '
# VoRSI outside window (over bought / over sold)
vorsi_tail = vorsi.tail(lb_trigger)
vorsi_tail = vorsi.tail(LB_TRIGGER)
if (vorsi_tail >= 70).any() or (vorsi_tail <= 30).any():
# print('--VoRSI', end = '')
plot_indicator += 'VoRSI, '
# Normalized MACD histogram out of 3% range
norm_hist_tail = abs(norm_hist.tail(lb_trigger))
norm_hist_tail = abs(norm_hist.tail(LB_TRIGGER))
if (abs(norm_hist_tail) >= 0.02).any():
# print('--MACD/R', end = '') # outside normal range
plot_indicator += 'MACD/R, '
# MACD histogram zero crossing
macd_hist_tail = macd_hist.tail(lb_trigger)
macd_hist_tail = macd_hist.tail(LB_TRIGGER)
macd_hist_sign = np.sign(macd_hist_tail)
macd_hist_diff = macd_hist_sign.diff()
if (abs(macd_hist_diff) > 1).any():
@ -589,7 +447,7 @@ def intelligent_loop_plots(sym, stk_data):
plot_indicator += 'MACD, '
# Stock price crosses SMA50
sma50_cross_tail = sma50.tail(lb_trigger) - price.tail(lb_trigger)
sma50_cross_tail = sma50.tail(LB_TRIGGER) - price.tail(LB_TRIGGER)
sma50_cross_sign = np.sign(sma50_cross_tail)
sma50_cross_diff = sma50_cross_sign.diff()
if (abs(sma50_cross_diff) > 1).any():
@ -597,7 +455,7 @@ def intelligent_loop_plots(sym, stk_data):
plot_indicator += 'SMA50, '
# Death cross or golden cross - SMA50 vs SMA200
sma_cross_tail = sma50.tail(lb_trigger) - sma200.tail(lb_trigger).values
sma_cross_tail = sma50.tail(LB_TRIGGER) - sma200.tail(LB_TRIGGER).values
sma_cross_sign = np.sign(sma_cross_tail)
sma_cross_diff = sma_cross_sign.diff()
if (abs(sma_cross_diff) > 1).any():
@ -605,9 +463,9 @@ def intelligent_loop_plots(sym, stk_data):
plot_indicator += 'Golden/Death, '
# Price outside bollinger band or crossing
price_tail = price.tail(lb_trigger)
bol_low_tail = bol_low.tail(lb_trigger)
bol_up_tail = bol_up.tail(lb_trigger)
price_tail = price.tail(LB_TRIGGER)
bol_low_tail = bol_low.tail(LB_TRIGGER)
bol_up_tail = bol_up.tail(LB_TRIGGER)
price_high = price_tail - bol_up_tail.values
price_low = price_tail - bol_low_tail.values
if (price_high >= 0).any() or (price_low <= 0).any():
@ -615,7 +473,7 @@ def intelligent_loop_plots(sym, stk_data):
plot_indicator += 'Bollinger, '
# Price cross 200 day moving average
sma200_tail = sma200.tail(lb_trigger)
sma200_tail = sma200.tail(LB_TRIGGER)
sma200_cross = price_tail - sma200_tail.values
sma200_cross_sign = np.sign(sma200_cross)
sma200_cross_diff = sma200_cross_sign.diff()
@ -624,7 +482,7 @@ def intelligent_loop_plots(sym, stk_data):
plot_indicator += 'SMA200, '
# Large trading volume trigger
volume_tail = vol.tail(lb_trigger)
volume_tail = vol.tail(LB_TRIGGER)
vol_mean = vol.tail(50).mean()
vol_std = vol.tail(50).std()
if ((volume_tail[1] - vol_mean - 2*vol_std) > 0).any():
@ -667,35 +525,29 @@ def user_login(username, password):
return True
return False
if __name__ == "__main__":
lb_year = 3 # years of stock data to retrieve
plt_year = 2 # number of years data to plot
lb_trigger = 5 # days to lookback for triggering events
# Initialize the app
app = Dash()
dash_auth.BasicAuth(
# Initialize the app
app = Dash()
dash_auth.BasicAuth(
app,
# VALID_USERNAME_PASSWORD_PAIRS,
auth_func=user_login,
secret_key="MK8dyS6PyDDuEuzrmqa7dJTJZ7eH2Jkh",
)
)
watchlist = get_watchlist()
# symbols = watchlist.index.values.tolist()
symbols = (watchlist.index.values + " - " + watchlist["Sub Segment"]).tolist()
watchlist = get_watchlist()
# symbols = watchlist.index.values.tolist()
symbols = (watchlist.index.values + " - " + watchlist["Sub Segment"]).tolist()
CACHE_CONFIG = {'CACHE_TYPE': 'SimpleCache'}
cache = Cache()
cache.init_app(app.server, config=CACHE_CONFIG)
CACHE_CONFIG = {'CACHE_TYPE': 'SimpleCache'}
cache = Cache()
cache.init_app(app.server, config=CACHE_CONFIG)
@cache.memoize(timeout=14400) # cache timeout set to 4 hours
def fetch_stk_data(sym, sd):
@cache.memoize(timeout=14400) # cache timeout set to 4 hours
def fetch_stk_data(sym, sd):
return si.get_data(sym, start_date=sd)[["adjclose", "volume"]]
# App layout
app.layout = [
# App layout
app.layout = [
html.Div([
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'}),
@ -721,9 +573,9 @@ if __name__ == "__main__":
# max_intervals=3, # for testing
disabled=True,
),
]
]
app.clientside_callback(
app.clientside_callback(
"""
function(id) {
document.addEventListener("keyup", function(event) {
@ -737,16 +589,16 @@ if __name__ == "__main__":
""",
Output("start-button", "id"),
Input("start-button", "id")
)
)
# start / stop button callback
@callback(Output('interval-component', 'disabled'),
# start / stop button callback
@callback(Output('interval-component', 'disabled'),
Output("start-button", "children"),
Output('symbols_dropdown_list', 'disabled'),
Input("start-button", "n_clicks"),
State("start-button", "children"),)
def start_cycle(n, value):
def start_cycle(n, value):
if n:
if value == "Pause":
return True, "Auto Play", False
@ -754,49 +606,47 @@ if __name__ == "__main__":
return False, "Pause", True
return no_update
# reload button callback
@callback(Output('symbols_dropdown_list', 'options'),
# reload button callback
@callback(Output('symbols_dropdown_list', 'options'),
Output('interval-component', 'n_intervals'),
Input("reload-button", "n_clicks"),)
def reload_syms(n):
def reload_syms(n):
if n:
watchlist = get_watchlist()
symbols = (watchlist.index.values + " - " + watchlist["Sub Segment"]).tolist()
return symbols, 0
return no_update
# interval callback
@callback(Output('symbols_dropdown_list', 'value'),
# interval callback
@callback(Output('symbols_dropdown_list', 'value'),
Input("interval-component", "n_intervals"),
State('symbols_dropdown_list', 'options'),
# State('dropdown-index', 'data'),
)
def cycle_syms(n, syms):
def cycle_syms(n, syms):
if syms:
return syms[n % len(syms)]
else:
return no_update
# dropdown callback
@callback(
# dropdown callback
@callback(
Output(component_id='controls-and-graph', component_property='figure'),
# Output("dropdown-index", "data"),
Input(component_id='symbols_dropdown_list', component_property='value'),
# State('symbols_dropdown_list', 'options')
# Input(component_id='signal', component_property='data'),
)
)
def update_graph(col_chosen):
def update_graph(col_chosen):
if not col_chosen:
raise PreventUpdate
# return no_update
# get data
# tmp = si.get_data(sym, start_date=sd)[["adjclose", "volume"]]
sym = col_chosen.split()[0]
end_date = dt.datetime.today()
start_date = end_date - dt.timedelta(days = 365 * lb_year)
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
@ -815,7 +665,6 @@ if __name__ == "__main__":
data,
x=data.index,
y=data.columns.to_list(),
# y=[sym, sym+'_BOL200L', sym+'_BOL200U', sym+'_SMA200', sym+'_SMA50', sym+'_WVMA50'],
)
volume_line = px.bar(
@ -876,5 +725,6 @@ if __name__ == "__main__":
return fig
serve(app.server, host="0.0.0.0", port=8050, threads=7)
if __name__ == "__main__":
serve(app.server, host="0.0.0.0", port=8050, threads=7) # using production quality WSGI server Waitress
# app.run(debug=True)