# OptionWaveEngine/src/OW_User_Interface/vizro_base_3.py
# import vizro
from vizro import Vizro
import vizro.plotly.express as px
import vizro.models as vm
from vizro.models.types import capture
from vizro.managers import data_manager
# import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# import yfinance and pandas
import pandas as pd
import yfinance as yf
# import datetime
from datetime import datetime, timedelta
# import functions
import func_tfunc as tfunc
import app_utlities as utl
# Calculate default dates: 180 days ago and today
today = datetime.today().date()
x_days_ago = today - timedelta(days=180)
# Define the date range
start_date = x_days_ago
end_date = today
'''
def get_data_by_ticker(ticker="AAPL", timeframe="daily", st=start_date, ed=end_date):
return utl.get_stock_data(ticker, timeframe, st, ed)
def calculate_candle_signals(data_frame):
return utl.calculate_candlestick_patterns(data_frame)
# Load stock data - standard without candle signals
def load_stock_data(ticker="AAPL", timeframe="daily", st=start_date, ed=end_date):
data = get_data_by_ticker(ticker=ticker, timeframe=timeframe, st=st, ed=ed)
return data
data_manager["stock_data"] = load_stock_data
'''
def calculate_candlestick_patterns(df):
import pandas as pd
import talib
"""
This function calculates all candlestick patterns available in the `TA-Lib` library
and adds them as new columns to the DataFrame. The patterns are binary (1 if detected, 0 otherwise).
All column names are standardized to begin with uppercase letters.
Parameters:
df (pd.DataFrame): DataFrame containing stock data with 'Open', 'High', 'Low', 'Close' columns.
Returns:
pd.DataFrame: DataFrame with added candlestick pattern signal columns.
"""
# List of all candlestick patterns in TA-Lib
patterns = {
'2Crows': talib.CDL2CROWS,
'3BlackCrows': talib.CDL3BLACKCROWS,
'3Inside': talib.CDL3INSIDE,
'3LineStrike': talib.CDL3LINESTRIKE,
'3Outside': talib.CDL3OUTSIDE,
'3StarsInSouth': talib.CDL3STARSINSOUTH,
'3WhiteSoldiers': talib.CDL3WHITESOLDIERS,
'AbandonedBaby': talib.CDLABANDONEDBABY,
'AdvanceBlock': talib.CDLADVANCEBLOCK,
'BeltHold': talib.CDLBELTHOLD,
'Breakaway': talib.CDLBREAKAWAY,
'ClosingMarubozu': talib.CDLCLOSINGMARUBOZU,
'ConcealBabySwall': talib.CDLCONCEALBABYSWALL,
'CounterAttack': talib.CDLCOUNTERATTACK,
'DarkCloudCover': talib.CDLDARKCLOUDCOVER,
'Doji': talib.CDLDOJI,
'DojiStar': talib.CDLDOJISTAR,
'DragonflyDoji': talib.CDLDRAGONFLYDOJI,
'Engulfing': talib.CDLENGULFING,
'EveningDojiStar': talib.CDLEVENINGDOJISTAR,
'EveningStar': talib.CDLEVENINGSTAR,
'GapSideSideWhite': talib.CDLGAPSIDESIDEWHITE,
'GravestoneDoji': talib.CDLGRAVESTONEDOJI,
'Hammer': talib.CDLHAMMER,
'HangingMan': talib.CDLHANGINGMAN,
'Harami': talib.CDLHARAMI,
'HaramiCross': talib.CDLHARAMICROSS,
'HighWave': talib.CDLHIGHWAVE,
'Hikkake': talib.CDLHIKKAKE,
'HikkakeMod': talib.CDLHIKKAKEMOD,
'HomingPigeon': talib.CDLHOMINGPIGEON,
'Identical3Crows': talib.CDLIDENTICAL3CROWS,
'InNeck': talib.CDLINNECK,
'InvertedHammer': talib.CDLINVERTEDHAMMER,
'Kicking': talib.CDLKICKING,
'KickingByLength': talib.CDLKICKINGBYLENGTH,
'LadderBottom': talib.CDLLADDERBOTTOM,
'LongLeggedDoji': talib.CDLLONGLEGGEDDOJI,
'LongLine': talib.CDLLONGLINE,
'Marubozu': talib.CDLMARUBOZU,
'MatchingLow': talib.CDLMATCHINGLOW,
'MatHold': talib.CDLMATHOLD,
'MorningDojiStar': talib.CDLMORNINGDOJISTAR,
'MorningStar': talib.CDLMORNINGSTAR,
'OnNeck': talib.CDLONNECK,
'Piercing': talib.CDLPIERCING,
'RickshawMan': talib.CDLRICKSHAWMAN,
'RiseFall3Methods': talib.CDLRISEFALL3METHODS,
'SeparatingLines': talib.CDLSEPARATINGLINES,
'ShootingStar': talib.CDLSHOOTINGSTAR,
'ShortLine': talib.CDLSHORTLINE,
'SpinningTop': talib.CDLSPINNINGTOP,
'StalledPattern': talib.CDLSTALLEDPATTERN,
'StickSandwich': talib.CDLSTICKSANDWICH,
'Takuri': talib.CDLTAKURI,
'TasukiGap': talib.CDLTASUKIGAP,
'Thrusting': talib.CDLTHRUSTING,
'Tristar': talib.CDLTRISTAR,
'Unique3River': talib.CDLUNIQUE3RIVER,
'UpsideGap2Crows': talib.CDLUPSIDEGAP2CROWS,
'XsideGap3Methods': talib.CDLXSIDEGAP3METHODS
}
'''
# Apply each pattern and create corresponding columns
for pattern_name, pattern_function in patterns.items():
df[pattern_name] = pattern_function(df['Open'], df['High'], df['Low'], df['Close'])
# Convert signal values from -100, 100 to binary (1 = pattern detected, 0 = no pattern)
df = df.applymap(lambda x: 1 if x > 0 else (0 if x == 0 else -1))
# Ensure columns begin with uppercase letters
df.columns = [col.capitalize() for col in df.columns]
'''
# Apply each pattern and create corresponding columns
for pattern_name, pattern_function in patterns.items():
df[pattern_name] = pattern_function(df['Open'], df['High'], df['Low'], df['Close'])
# Convert signal values from -100, 100 to binary (1 = pattern detected, 0 = no pattern)
# Exclude the 'Open', 'High', 'Low', 'Close', and 'Volume' columns from the conversion
exclude_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
df.loc[:, ~df.columns.isin(exclude_columns)] = df.loc[:, ~df.columns.isin(exclude_columns)].applymap(lambda x: 1 if x > 0 else (0 if x == 0 else -1))
# Ensure columns begin with uppercase letters
df.columns = [col.capitalize() for col in df.columns]
return df
def get_stock_data(ticker, timeframe, start_date, end_date):
def get_data(ticker, timeframe, start_date, end_date):
import func_tfunc as tfunc
data_dict = tfunc.historicaldata(sym=ticker,inter=timeframe,start=start_date,end=end_date)
# Convert the list of dictionaries into a DataFrame
df = pd.DataFrame(data_dict)['history']['day']
# Convert to DataFrame
df2 = pd.DataFrame(df)
# Optionally, convert the 'date' column to datetime format and sort by date
df2['date'] = pd.to_datetime(df2['date'], format='%Y-%m-%d')
df2 = df2.sort_values(by='date')
# Capitalize the column names
df2.columns = [col.capitalize() for col in df2.columns]
# Ensure the 'Date' column is in datetime format
df2['Date'] = pd.to_datetime(df2['Date'])
# Calculate the variation for each row compared to the previous row
df2['Variation'] = df2['Close'].pct_change()
df2.dropna(inplace=True)
final = df2.copy()
final['Date'] = pd.to_datetime(final['Date'], format='%Y-%m-%d')
return final
# Get the data
df = get_data(ticker, timeframe, start_date, end_date)
#Clean again
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
return df
# Get Data Functions;
def app_get_data(ticker="AAPL", timeframe="daily", st="start_date", ed="end_date", selected_data="stock_data"):
def get_data_by_ticker(ticker="AAPL", timeframe="daily", st=st, ed=ed):
return get_stock_data(ticker, timeframe, st, ed)
def calculate_candle_signals(data_frame):
return calculate_candlestick_patterns(data_frame)
if selected_data=="stock_data":
final_data_frame = get_data_by_ticker(ticker=ticker, timeframe=timeframe, st=st, ed=ed)
elif selected_data=="with_signals":
final_data_frame = calculate_candle_signals(get_data_by_ticker(ticker=ticker, timeframe=timeframe, st=st, ed=ed))
return final_data_frame
def load_selected_data(ticker="AAPL", timeframe="daily", st=start_date, ed=end_date, selected_data="with_signals"):
data = app_get_data(ticker=ticker, timeframe=timeframe, st=st, ed=ed, selected_data=selected_data)
return data
data_manager["stock_data_v2"] = load_selected_data
# Define dropdown options
ticker_options = [{'label': 'AAPL', 'value': 'AAPL'}, {'label': 'MSFT', 'value': 'MSFT'}, {'label': 'GOOG', 'value': 'GOOG'}]
data_choice_options = [{'label': 'Basic Data', 'value': 'stock_data'}, {'label': 'With Candle Signals', 'value': 'with_signals'}]
candlestick_signal_options = patterns = [
{'label': 'None', 'value': 'None'},
{'label': '2 Crows', 'value': 'CDL2CROWS'},
{'label': '3 Black Crows', 'value': 'CDL3BLACKCROWS'},
{'label': '3 Inside Up/Down', 'value': 'CDL3INSIDE'},
{'label': '3 Line Strike', 'value': 'CDL3LINESTRIKE'},
{'label': '3 Outside Up/Down', 'value': 'CDL3OUTSIDE'},
{'label': '3 Stars In South', 'value': 'CDL3STARSINSOUTH'},
{'label': '3 White Soldiers', 'value': 'CDL3WHITESOLDIERS'},
{'label': 'Abandoned Baby', 'value': 'CDLABANDONEDBABY'},
{'label': 'Advance Block', 'value': 'CDLADVANCEBLOCK'},
{'label': 'Belt Hold', 'value': 'CDLBELTHOLD'},
{'label': 'Breakaway', 'value': 'CDLBREAKAWAY'},
{'label': 'Closing Marubozu', 'value': 'CDLCLOSINGMARUBOZU'},
{'label': 'Concealing Baby Swallow', 'value': 'CDLCONCEALBABYSWALL'},
{'label': 'Counterattack', 'value': 'CDLCOUNTERATTACK'},
{'label': 'Dark Cloud Cover', 'value': 'CDLDARKCLOUDCOVER'},
{'label': 'Doji', 'value': 'CDLDOJI'},
{'label': 'Doji Star', 'value': 'CDLDOJISTAR'},
{'label': 'Dragonfly Doji', 'value': 'CDLDRAGONFLYDOJI'},
{'label': 'Engulfing', 'value': 'CDLENGULFING'},
{'label': 'Evening Doji Star', 'value': 'CDLEVENINGDOJISTAR'},
{'label': 'Evening Star', 'value': 'CDLEVENINGSTAR'},
{'label': 'Gap Side-by-Side White', 'value': 'CDLGAPSIDESIDEWHITE'},
{'label': 'Gravestone Doji', 'value': 'CDLGRAVESTONEDOJI'},
{'label': 'Hammer', 'value': 'CDLHAMMER'},
{'label': 'Hanging Man', 'value': 'CDLHANGINGMAN'},
{'label': 'Harami', 'value': 'CDLHARAMI'},
{'label': 'Harami Cross', 'value': 'CDLHARAMICROSS'},
{'label': 'High-Wave Candle', 'value': 'CDLHIGHWAVE'},
{'label': 'Hikkake', 'value': 'CDLHIKKAKE'},
{'label': 'Modified Hikkake', 'value': 'CDLHIKKAKEMOD'},
{'label': 'Homing Pigeon', 'value': 'CDLHOMINGPIGEON'},
{'label': 'Identical Three Crows', 'value': 'CDLIDENTICAL3CROWS'},
{'label': 'In Neck', 'value': 'CDLINNECK'},
{'label': 'Inverted Hammer', 'value': 'CDLINVERTEDHAMMER'},
{'label': 'Kicking', 'value': 'CDLKICKING'},
{'label': 'Kicking By Length', 'value': 'CDLKICKINGBYLENGTH'},
{'label': 'Ladder Bottom', 'value': 'CDLLADDERBOTTOM'},
{'label': 'Long-Legged Doji', 'value': 'CDLLONGLEGGEDDOJI'},
{'label': 'Long Line Candle', 'value': 'CDLLONGLINE'},
{'label': 'Marubozu', 'value': 'CDLMARUBOZU'},
{'label': 'Matching Low', 'value': 'CDLMATCHINGLOW'},
{'label': 'Mat Hold', 'value': 'CDLMATHOLD'},
{'label': 'Morning Doji Star', 'value': 'CDLMORNINGDOJISTAR'},
{'label': 'Morning Star', 'value': 'CDLMORNINGSTAR'},
{'label': 'On Neck', 'value': 'CDLONNECK'},
{'label': 'Piercing', 'value': 'CDLPIERCING'},
{'label': 'Rickshaw Man', 'value': 'CDLRICKSHAWMAN'},
{'label': 'Rising/Falling Three Methods', 'value': 'CDLRISEFALL3METHODS'},
{'label': 'Separating Lines', 'value': 'CDLSEPARATINGLINES'},
{'label': 'Shooting Star', 'value': 'CDLSHOOTINGSTAR'},
{'label': 'Short Line Candle', 'value': 'CDLSHORTLINE'},
{'label': 'Spinning Top', 'value': 'CDLSPINNINGTOP'},
{'label': 'Stalled Pattern', 'value': 'CDLSTALLEDPATTERN'},
{'label': 'Stick Sandwich', 'value': 'CDLSTICKSANDWICH'},
{'label': 'Takuri', 'value': 'CDLTAKURI'},
{'label': 'Tasuki Gap', 'value': 'CDLTASUKIGAP'},
{'label': 'Thrusting', 'value': 'CDLTHRUSTING'},
{'label': 'Tristar', 'value': 'CDLTRISTAR'},
{'label': 'Unique 3 River', 'value': 'CDLUNIQUE3RIVER'},
{'label': 'Upside Gap Two Crows', 'value': 'CDLUPSIDEGAP2CROWS'},
{'label': 'Upside/Downside Gap Three Methods', 'value': 'CDLXSIDEGAP3METHODS'}
]
@capture("graph")
def chart_v7(data_frame, ticker, start_date, end_date):
fig = go.Figure()
# Candlestick chart trace
fig.add_trace(go.Candlestick(x=data_frame.index,
open=data_frame['Open'],
close=data_frame['Close'],
high=data_frame['High'],
low=data_frame['Low']))
# Left-side buttons (updatemenus) with new options
updatemenus = [dict(
type='buttons',
showactive=False,
buttons=[
dict(label='1 Wk', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=7), data_frame.index[-1]]}]),
dict(label='2 Wk', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=14), data_frame.index[-1]]}]),
dict(label='3 Wk', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=21), data_frame.index[-1]]}]),
dict(label='1 Mth', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=30), data_frame.index[-1]]}]),
dict(label='1 Qtr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=92), data_frame.index[-1]]}]),
dict(label='2 Qtr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=183), data_frame.index[-1]]}]),
dict(label='3 Qtr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=276), data_frame.index[-1]]}]),
dict(label='1 Yr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=365), data_frame.index[-1]]}]),
dict(label='Max', method='relayout', args=[{'xaxis.range': [data_frame.index[0], data_frame.index[-1]]}])
],
direction='down',
pad={"r": 10, "t": 10}, # Add margin/padding
x=1.1, # Move buttons closer to the graph
y=0.5, # Center buttons vertically with the y-axis
xanchor='left',
yanchor='middle', # Center with the y-axis
bgcolor='green',
bordercolor='white',
font=dict(color='black', size=10) # Smaller font size
)]
# Get the min and max of the y-axis based on visible data range
def calculate_yaxis_range(df):
visible_data = df.loc[start_date:end_date] # Only consider the data in the current x-axis range
price_min = visible_data['Low'].min()
price_max = visible_data['High'].max()
# Safe space: Add 5% padding above and below the data range
padding = 0.05 * (price_max - price_min)
return [price_min - padding, price_max + padding]
# Dynamically adjust the y-axis range based on visible data
yaxis_range = calculate_yaxis_range(data_frame)
# Layout updates for dynamic y-axis and chart appearance
fig.update_layout(
template='plotly_dark',
xaxis=dict(
rangeslider_visible=False, # Disable range slider
domain=[0, 1], # Full width for x-axis
anchor='y'
),
yaxis=dict(
domain=[0, 1], # Full height for y-axis to consume the extra space
autorange=False, # Disable autorange since we are setting a custom range
range=yaxis_range, # Dynamic range with safe space
tickprefix='$'
),
updatemenus=updatemenus,
height=900, # Increase chart height
margin=dict(l=80, r=20, t=30, b=30), # Reduce margins for better space utilization
showlegend=False, # Hide the legend
)
# Set x-axis range based on user-specified start and end dates
fig.update_xaxes(range=[start_date, end_date])
return fig
@capture("graph")
def chart_v8(data_frame, ticker="AAPL", start_date="2022-01-01", end_date="2022-12-31", patterns=None, indicator=None):
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Create subplots with 2 rows, shared x-axis
fig = make_subplots(rows=2, cols=1, shared_xaxes=True,
row_heights=[0.7, 0.3], # Adjust height ratios
vertical_spacing=0.05) # Spacing between the plots
# Candlestick chart trace (Main Chart)
fig.add_trace(go.Candlestick(x=data_frame.index,
open=data_frame['Open'],
close=data_frame['Close'],
high=data_frame['High'],
low=data_frame['Low']), row=1, col=1)
# Add candlestick pattern signals from DataFrame
if patterns is not None:
for pattern in patterns:
# Check if the pattern column exists in the DataFrame
if pattern in data_frame.columns:
# Plot bullish patterns (green triangles below candles)
bullish_signals = data_frame[data_frame[pattern] == 1]
fig.add_trace(go.Scatter(
x=bullish_signals.index,
y=bullish_signals['Low'], # Plot below the low price for bullish patterns
mode='markers',
marker=dict(symbol='triangle-up', color='green', size=10),
name=f'{pattern} Bullish',
showlegend=False
), row=1, col=1)
# Plot bearish patterns (red triangles above candles)
bearish_signals = data_frame[data_frame[pattern] == -1]
fig.add_trace(go.Scatter(
x=bearish_signals.index,
y=bearish_signals['High'], # Plot above the high price for bearish patterns
mode='markers',
marker=dict(symbol='triangle-down', color='red', size=10),
name=f'{pattern} Bearish',
showlegend=False
), row=1, col=1)
# Add indicator subplot (e.g., RSI or Moving Average)
if indicator is not None:
fig.add_trace(go.Scatter(
x=data_frame.index,
y=data_frame[indicator['value']], # Assuming this contains your indicator values
mode='lines',
line=dict(color='blue'),
name=indicator['name'] # Indicator name, e.g., 'RSI'
), row=2, col=1)
# Update x-axis range selector (same as before)
fig.update_xaxes(
rangeslider_visible=False,
domain=[0, 1], # Full width for x-axis
anchor='y'
)
# Left-side buttons (same as before)
updatemenus = [dict(
type='buttons',
showactive=False,
buttons=[
dict(label='1 Wk', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=7), data_frame.index[-1]]}]),
dict(label='2 Wk', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=14), data_frame.index[-1]]}]),
dict(label='3 Wk', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=21), data_frame.index[-1]]}]),
dict(label='1 Mth', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=30), data_frame.index[-1]]}]),
dict(label='1 Qtr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=92), data_frame.index[-1]]}]),
dict(label='2 Qtr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=183), data_frame.index[-1]]}]),
dict(label='3 Qtr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=276), data_frame.index[-1]]}]),
dict(label='1 Yr', method='relayout', args=[{'xaxis.range': [data_frame.index[-1] - pd.Timedelta(days=365), data_frame.index[-1]]}]),
dict(label='Max', method='relayout', args=[{'xaxis.range': [data_frame.index[0], data_frame.index[-1]]}])
],
direction='down',
pad={"r": 10, "t": 10}, # Add margin/padding
x=1.1, # Move buttons closer to the graph
y=0.5, # Center buttons vertically with the y-axis
xanchor='left',
yanchor='middle', # Center with the y-axis
bgcolor='green',
bordercolor='white',
font=dict(color='black', size=10) # Smaller font size
)]
# Layout updates for dynamic y-axis and chart appearance
fig.update_layout(
template='plotly_dark',
xaxis=dict(domain=[0, 1]), # Full width for x-axis
yaxis=dict(
autorange=False, # Disable autorange since we are setting a custom range
tickprefix='$'
),
updatemenus=updatemenus,
height=900, # Increase chart height
margin=dict(l=80, r=20, t=30, b=30), # Reduce margins for better space utilization
showlegend=False, # Hide the legend
)
# Set x-axis range based on user-specified start and end dates
fig.update_xaxes(range=[start_date, end_date], row=1, col=1)
return fig
# app page layout
page1 = vm.Page(
title="Stock Chart Basic",
components=[
vm.Graph(id="graph2",
figure=chart_v7(
"stock_data_v2",
'stock_data_v2.ticker',
start_date,
end_date))
],
controls=[
vm.Parameter(
targets=["graph2.data_frame.ticker"],
selector=vm.Dropdown(
id='ticker_dropdown',
options=ticker_options,
value='AAPL',
multi=False),
)
],
)
page2 = vm.Page(
title="Stock Chart Advanced",
components=[
vm.Graph(id="graph3",
figure=chart_v8(
"stock_data_v2",
"stock_data_v2.ticker",
start_date,
end_date,
patterns="None",
indicator="None"))
],
controls=[
vm.Parameter(
targets=["graph3.data_frame.ticker"],
selector=vm.Dropdown(id='ticker_dropdown2',
options=ticker_options,
value='AAPL',
multi=False),
),
#vm.Parameter(
# targets=["graph3.data_frame.selected_data"],
# selector=vm.Dropdown(id='selected_data_dropdown',
# options=data_choice_options,
# value='stock_data',
# multi=False)
#),
vm.Parameter(
targets=["graph3.patterns"],
selector=vm.Dropdown(id='patterns_dropdown',
options=candlestick_signal_options,
value='None',
multi=False)
)
],
)
dashboard = vm.Dashboard(pages=[page1, page2])
Vizro().build(dashboard).run()