|
import streamlit as st |
|
import yfinance as yf |
|
import pandas as pd |
|
import plotly.graph_objs as go |
|
from st_pages import show_pages_from_config, add_page_title |
|
|
|
|
|
show_pages_from_config(".streamlit/pages_sections.toml") |
|
|
|
add_page_title("BBMA Oma Ally", "🌐") |
|
|
|
|
|
stock_exchanges = { |
|
"Kuala Lumpur Stock Exchange (KLSE)": { |
|
"CIMB Group Holdings Bhd - CIMB": "1023.KL", |
|
"RHB Bank Bhd - RHBBANK": "1066.KL", |
|
"Hong Leong Financial Group Bhd - HLFG": "1082.KL", |
|
"Malayan Banking Bhd - MAYBANK": "1155.KL", |
|
"Public Bank Bhd - PBBANK": "1295.KL", |
|
"IOI Corporation Bhd - IOICORP": "1961.KL", |
|
"Kuala Lumpur Kepong Bhd - KLK": "2445.KL", |
|
"Genting Bhd - GENTING": "3182.KL", |
|
"MISC Bhd - MISC": "3816.KL", |
|
"PPB Group Bhd - PPB": "4065.KL", |
|
"Sime Darby Bhd - SIME": "4197.KL", |
|
"Telekom Malaysia Bhd - TM": "4863.KL", |
|
"Tenaga Nasional Bhd - TENAGA": "5278.KL", |
|
"Top Glove Corporation Bhd - TOPGLOV": "7113.KL", |
|
"AirAsia X Bhd - AAX": "5238.KL", |
|
"Ramssol Group Bhd - RAMSSOL": "0236.KL", |
|
"Uzma Bhd - UZMA": "7250.KL", |
|
"WZ Satu Bhd - WZSATU": "7245.KL", |
|
"Systech Bhd - SYSTECH": "0050.KL", |
|
"Yong Tai Bhd - YONGTAI": "7066.KL", |
|
}, |
|
"Euronext": { |
|
"LVMH Moet Hennessy Louis Vuitton SE - LVMH": "MC.PA", |
|
"TotalEnergies SE - TotalEnergies": "TTE.PA", |
|
"Sanofi SA - Sanofi": "SAN.PA", |
|
"Air Liquide SA - Air Liquide": "AI.PA", |
|
"Schneider Electric SE - Schneider Electric": "SU.PA", |
|
"Kering SA - Kering": "KER.PA", |
|
"BNP Paribas SA - BNP Paribas": "BNP.PA", |
|
"Hermès International SCA - Hermès": "RMS.PA", |
|
"L'Oréal SA - L'Oréal": "OR.PA", |
|
"AXA SA - AXA": "CS.PA", |
|
"Vinci SA - Vinci": "DG.PA", |
|
"Dassault Systèmes SE - Dassault Systèmes": "DSY.PA", |
|
"Engie SA - Engie": "ENGI.PA", |
|
"Société Générale SA - Société Générale": "GLE.PA", |
|
"Pernod Ricard SA - Pernod Ricard": "RI.PA", |
|
"Safran SA - Safran": "SAF.PA", |
|
"ArcelorMittal SA - ArcelorMittal": "MT.AS", |
|
"Saint-Gobain SA - Saint-Gobain": "SGO.PA", |
|
"Capgemini SE - Capgemini": "CAP.PA", |
|
"Danone SA - Danone": "BN.PA", |
|
}, |
|
"London Stock Exchange (LSE)": { |
|
"HSBC Holdings plc - HSBC": "HSBA.L", |
|
"Royal Dutch Shell plc - Shell": "RDSA.L", |
|
"BP plc - BP": "BP.L", |
|
"GlaxoSmithKline plc - GlaxoSmithKline": "GSK.L", |
|
"AstraZeneca plc - AstraZeneca": "AZN.L", |
|
"Unilever plc - Unilever": "ULVR.L", |
|
"British American Tobacco plc - British American Tobacco": "BATS.L", |
|
"Diageo plc - Diageo": "DGE.L", |
|
"Barclays plc - Barclays": "BARC.L", |
|
"Lloyds Banking Group plc - Lloyds": "LLOY.L", |
|
"Vodafone Group plc - Vodafone": "VOD.L", |
|
"Rio Tinto plc - Rio Tinto": "RIO.L", |
|
"Reckitt Benckiser Group plc - Reckitt Benckiser": "RKT.L", |
|
"Tesco plc - Tesco": "TSCO.L", |
|
"Glencore plc - Glencore": "GLEN.L", |
|
"National Grid plc - National Grid": "NG.L", |
|
"BT Group plc - BT Group": "BT-A.L", |
|
"Aviva plc - Aviva": "AV.L", |
|
"Imperial Brands plc - Imperial Brands": "IMB.L", |
|
"Rolls-Royce Holdings plc - Rolls-Royce": "RR.L", |
|
}, |
|
"NYSE": { |
|
"Berkshire Hathaway Inc. (Class B) - BRK-B": "BRK-B", |
|
"Johnson & Johnson - JNJ": "JNJ", |
|
"JPMorgan Chase & Co. - JPM": "JPM", |
|
"Procter & Gamble Co. - PG": "PG", |
|
"Visa Inc. (Class A) - V": "V", |
|
"Walmart Inc. - WMT": "WMT", |
|
"Mastercard Incorporated (Class A) - MA": "MA", |
|
"The Home Depot, Inc. - HD": "HD", |
|
"Bank of America Corporation - BAC": "BAC", |
|
"Walt Disney Company (The) - DIS": "DIS", |
|
"Pfizer Inc. - PFE": "PFE", |
|
"Chevron Corporation - CVX": "CVX", |
|
"Coca-Cola Company (The) - KO": "KO", |
|
"Exxon Mobil Corporation - XOM": "XOM", |
|
"AbbVie Inc. - ABBV": "ABBV", |
|
"Merck & Co., Inc. - MRK": "MRK", |
|
"AT&T Inc. - T": "T", |
|
"Verizon Communications Inc. - VZ": "VZ", |
|
"Morgan Stanley - MS": "MS", |
|
"Goldman Sachs Group, Inc. (The) - GS": "GS", |
|
}, |
|
"NASDAQ": { |
|
"Apple Inc. - AAPL": "AAPL", |
|
"Microsoft Corporation - MSFT": "MSFT", |
|
"Amazon.com, Inc. - AMZN": "AMZN", |
|
"Tesla, Inc. - TSLA": "TSLA", |
|
"Alphabet Inc. (Class A) - GOOGL": "GOOGL", |
|
"Alphabet Inc. (Class C) - GOOG": "GOOG", |
|
"NVIDIA Corporation - NVDA": "NVDA", |
|
"Meta Platforms, Inc. - META": "META", |
|
"Netflix, Inc. - NFLX": "NFLX", |
|
"Intel Corporation - INTC": "INTC", |
|
"Adobe Inc. - ADBE": "ADBE", |
|
"Cisco Systems, Inc. - CSCO": "CSCO", |
|
"PepsiCo, Inc. - PEP": "PEP", |
|
"Comcast Corporation - CMCSA": "CMCSA", |
|
"Advanced Micro Devices, Inc. - AMD": "AMD", |
|
"Broadcom Inc. - AVGO": "AVGO", |
|
"Charter Communications, Inc. - CHTR": "CHTR", |
|
"PayPal Holdings, Inc. - PYPL": "PYPL", |
|
"Starbucks Corporation - SBUX": "SBUX", |
|
"Booking Holdings Inc. - BKNG": "BKNG", |
|
}, |
|
} |
|
|
|
def fetch_data(ticker, start_date, end_date): |
|
data = yf.download(ticker, start=start_date, end=end_date) |
|
return data |
|
|
|
def fetch_weekly_data(ticker, start_date, end_date): |
|
data = yf.download(ticker, start=start_date, end=end_date, interval='1wk') |
|
return data |
|
|
|
def calculate_indicators(data): |
|
|
|
data['Middle Band'] = data['Close'].rolling(window=20).mean() |
|
data['Upper Band'] = data['Middle Band'] + 1.96 * data['Close'].rolling(window=20).std() |
|
data['Lower Band'] = data['Middle Band'] - 1.96 * data['Close'].rolling(window=20).std() |
|
|
|
|
|
data['MA5'] = data['Close'].rolling(window=5).mean() |
|
data['MA10'] = data['Close'].rolling(window=10).mean() |
|
|
|
return data |
|
|
|
def identify_signals(data): |
|
|
|
data['Buy Signal'] = ((data['Close'] < data['Lower Band']) & (data['Close'].shift(1) > data['Lower Band'])) | \ |
|
((data['Close'] > data['MA5']) & (data['Close'].shift(1) < data['MA5'])) |
|
data['Sell Signal'] = ((data['Close'] > data['Upper Band']) & (data['Close'].shift(1) < data['Upper Band'])) | \ |
|
((data['Close'] < data['MA5']) & (data['Close'].shift(1) > data['MA5'])) |
|
|
|
|
|
avg_volume = data['Volume'].rolling(window=20).mean() |
|
data['Buy Signal'] = data['Buy Signal'] & (data['Volume'] > avg_volume) |
|
data['Sell Signal'] = data['Sell Signal'] & (data['Volume'] > avg_volume) |
|
|
|
return data |
|
|
|
def identify_weekly_signals(weekly_data): |
|
weekly_data['Buy Signal'] = ((weekly_data['Close'] < weekly_data['Lower Band']) & (weekly_data['Close'].shift(1) > weekly_data['Lower Band'])) | \ |
|
((weekly_data['Close'] > weekly_data['MA5']) & (weekly_data['Close'].shift(1) < weekly_data['MA5'])) |
|
weekly_data['Sell Signal'] = ((weekly_data['Close'] > weekly_data['Upper Band']) & (weekly_data['Close'].shift(1) < weekly_data['Upper Band'])) | \ |
|
((weekly_data['Close'] < weekly_data['MA5']) & (weekly_data['Close'].shift(1) > weekly_data['MA5'])) |
|
return weekly_data |
|
|
|
def confirm_signals_with_weekly(data, weekly_data): |
|
weekly_data = calculate_indicators(weekly_data) |
|
weekly_data = identify_weekly_signals(weekly_data) |
|
data['Weekly Buy Signal'] = weekly_data['Buy Signal'].reindex(data.index, method='ffill') |
|
data['Weekly Sell Signal'] = weekly_data['Sell Signal'].reindex(data.index, method='ffill') |
|
data['Buy Signal'] = data['Buy Signal'] & data['Weekly Buy Signal'] |
|
data['Sell Signal'] = data['Sell Signal'] & data['Weekly Sell Signal'] |
|
|
|
return data |
|
|
|
def plot_data(data): |
|
fig = go.Figure() |
|
|
|
|
|
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], name='Close Price', line=dict(color='blue', width=2))) |
|
|
|
|
|
fig.add_trace(go.Scatter(x=data.index, y=data['Upper Band'], name='Upper Bollinger Band', line=dict(color='red', dash='dash'))) |
|
fig.add_trace(go.Scatter(x=data.index, y=data['Middle Band'], name='Middle Bollinger Band', line=dict(color='white', dash='dash'))) |
|
fig.add_trace(go.Scatter(x=data.index, y=data['Lower Band'], name='Lower Bollinger Band', line=dict(color='red', dash='dash'))) |
|
|
|
|
|
fig.add_trace(go.Scatter(x=data.index, y=data['MA5'], name='5-Day MA', line=dict(color='green', dash='dot'))) |
|
fig.add_trace(go.Scatter(x=data.index, y=data['MA10'], name='10-Day MA', line=dict(color='orange', dash='dot'))) |
|
|
|
|
|
buys = data[data['Buy Signal']] |
|
sells = data[data['Sell Signal']] |
|
fig.add_trace(go.Scatter(x=buys.index, y=buys['Close'], mode='markers', name='Buy Signal', marker=dict(symbol='triangle-up', size=10, color='green'))) |
|
fig.add_trace(go.Scatter(x=sells.index, y=sells['Close'], mode='markers', name='Sell Signal', marker=dict(symbol='triangle-down', size=10, color='red'))) |
|
|
|
|
|
fig.update_layout(title='Stock Price and Trading Signals', xaxis_title='Date', yaxis_title='Price', template='plotly_dark') |
|
fig.update_xaxes(rangeslider_visible=True) |
|
|
|
return fig |
|
|
|
def main(): |
|
st.title("OMA Ally BBMA Trading Strategy Visualization") |
|
|
|
|
|
st.sidebar.title("Select Ticker Symbol") |
|
exchange = st.sidebar.selectbox("Select Stock Exchange", list(stock_exchanges.keys())) |
|
ticker_symbols = stock_exchanges[exchange] |
|
ticker = st.sidebar.selectbox("Select Ticker Symbol", list(ticker_symbols.keys())) |
|
ticker_symbol = ticker_symbols[ticker] |
|
|
|
start_date = st.date_input("Select the start date") |
|
end_date = st.date_input("Select the end date") |
|
|
|
if st.button("Analyze"): |
|
data = fetch_data(ticker_symbol, start_date, end_date) |
|
weekly_data = fetch_weekly_data(ticker_symbol, start_date, end_date) |
|
data = calculate_indicators(data) |
|
data = identify_signals(data) |
|
data = confirm_signals_with_weekly(data, weekly_data) |
|
fig = plot_data(data) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
if __name__ == "__main__": |
|
main() |
|
|