├── .gitignore ├── README.md ├── app.py ├── images ├── logo-250-100-transparente.png ├── pie-chart-dollar-svgrepo-com.svg └── pie-chart-svgrepo-com.svg ├── requirements.txt └── tickers_ibra.csv /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | venv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stock Market Dashboard 2 | A Python Dashboard for brazilian stock market made with Streamlit. 3 | 4 | ![vlcsnap-2024-02-04-06h58m59s330](https://github.com/codigoquant/stock_dashboard/assets/64567849/57e8fe5a-6cdc-4a32-a795-a18886e43c23) 5 | 6 | Live Demo: https://stockdashboardcq.streamlit.app/ 7 | 8 | Youtube video: https://youtu.be/LAHlE_Lt9N4 9 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | import yfinance as yf 5 | import plotly.express as px 6 | from datetime import datetime 7 | from streamlit_extras.metric_cards import style_metric_cards 8 | from streamlit_extras.grid import grid 9 | 10 | 11 | def build_sidebar(): 12 | st.image("images/logo-250-100-transparente.png") 13 | ticker_list = pd.read_csv("tickers_ibra.csv", index_col=0) 14 | tickers = st.multiselect(label="Selecione as Empresas", options=ticker_list, placeholder='Códigos') 15 | tickers = [t+".SA" for t in tickers] 16 | start_date = st.date_input("De", format="DD/MM/YYYY", value=datetime(2023,1,2)) 17 | end_date = st.date_input("Até", format="DD/MM/YYYY", value="today") 18 | 19 | if tickers: 20 | prices = yf.download(tickers, start=start_date, end=end_date)["Adj Close"] 21 | if len(tickers) == 1: 22 | prices = prices.to_frame() 23 | prices.columns = [tickers[0].rstrip(".SA")] 24 | 25 | prices.columns = prices.columns.str.rstrip(".SA") 26 | prices['IBOV'] = yf.download("^BVSP", start=start_date, end=end_date)["Adj Close"] 27 | return tickers, prices 28 | return None, None 29 | 30 | def build_main(tickers, prices): 31 | weights = np.ones(len(tickers))/len(tickers) 32 | prices['portfolio'] = prices.drop("IBOV", axis=1) @ weights 33 | norm_prices = 100 * prices / prices.iloc[0] 34 | returns = prices.pct_change()[1:] 35 | vols = returns.std()*np.sqrt(252) 36 | rets = (norm_prices.iloc[-1] - 100) / 100 37 | 38 | mygrid = grid(5 ,5 ,5 ,5 ,5 , 5, vertical_align="top") 39 | for t in prices.columns: 40 | c = mygrid.container(border=True) 41 | c.subheader(t, divider="red") 42 | colA, colB, colC = c.columns(3) 43 | if t == "portfolio": 44 | colA.image("images/pie-chart-dollar-svgrepo-com.svg") 45 | elif t == "IBOV": 46 | colA.image("images/pie-chart-svgrepo-com.svg") 47 | else: 48 | colA.image(f'https://raw.githubusercontent.com/thefintz/icones-b3/main/icones/{t}.png', width=85) 49 | colB.metric(label="retorno", value=f"{rets[t]:.0%}") 50 | colC.metric(label="volatilidade", value=f"{vols[t]:.0%}") 51 | style_metric_cards(background_color='rgba(255,255,255,0)') 52 | 53 | col1, col2 = st.columns(2, gap='large') 54 | with col1: 55 | st.subheader("Desempenho Relativo") 56 | st.line_chart(norm_prices, height=600) 57 | 58 | with col2: 59 | st.subheader("Risco-Retorno") 60 | fig = px.scatter( 61 | x=vols, 62 | y=rets, 63 | text=vols.index, 64 | color=rets/vols, 65 | color_continuous_scale=px.colors.sequential.Bluered_r 66 | ) 67 | fig.update_traces( 68 | textfont_color='white', 69 | marker=dict(size=45), 70 | textfont_size=10, 71 | ) 72 | fig.layout.yaxis.title = 'Retorno Total' 73 | fig.layout.xaxis.title = 'Volatilidade (anualizada)' 74 | fig.layout.height = 600 75 | fig.layout.xaxis.tickformat = ".0%" 76 | fig.layout.yaxis.tickformat = ".0%" 77 | fig.layout.coloraxis.colorbar.title = 'Sharpe' 78 | st.plotly_chart(fig, use_container_width=True) 79 | 80 | 81 | st.set_page_config(layout="wide") 82 | 83 | with st.sidebar: 84 | tickers, prices = build_sidebar() 85 | 86 | st.title('Python para Investidores') 87 | if tickers: 88 | build_main(tickers, prices) -------------------------------------------------------------------------------- /images/logo-250-100-transparente.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codigoquant/stock_dashboard/e3474ef684f4c92b026b331048afe52566f71e4d/images/logo-250-100-transparente.png -------------------------------------------------------------------------------- /images/pie-chart-dollar-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 11 | 14 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /images/pie-chart-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 11 | 13 | 15 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altair==5.2.0 2 | anyio==4.2.0 3 | appdirs==1.4.4 4 | attrs==23.2.0 5 | beautifulsoup4==4.12.3 6 | blinker==1.7.0 7 | cachetools==5.3.2 8 | certifi==2023.11.17 9 | charset-normalizer==3.3.2 10 | click==8.1.7 11 | contourpy==1.2.0 12 | cycler==0.12.1 13 | entrypoints==0.4 14 | exceptiongroup==1.2.0 15 | Faker==22.6.0 16 | favicon==0.7.0 17 | fonttools==4.47.2 18 | frozendict==2.4.0 19 | gitdb==4.0.11 20 | GitPython==3.1.41 21 | h11==0.14.0 22 | htbuilder==0.6.2 23 | html5lib==1.1 24 | httpcore==0.17.3 25 | httpx==0.24.1 26 | idna==3.6 27 | importlib-metadata==7.0.1 28 | Jinja2==3.1.3 29 | jsonschema==4.20.0 30 | jsonschema-specifications==2023.12.1 31 | kiwisolver==1.4.5 32 | lxml==4.9.4 33 | Markdown==3.5.2 34 | markdown-it-py==3.0.0 35 | markdownlit==0.0.7 36 | MarkupSafe==2.1.3 37 | matplotlib==3.8.2 38 | mdurl==0.1.2 39 | more-itertools==10.2.0 40 | multitasking==0.0.11 41 | numpy==1.26.3 42 | packaging==23.2 43 | pandas==2.1.4 44 | peewee==3.17.0 45 | pillow==10.2.0 46 | plotly==5.18.0 47 | protobuf==4.25.2 48 | pyarrow==14.0.2 49 | pydeck==0.8.1b0 50 | Pygments==2.17.2 51 | pymdown-extensions==10.7 52 | pyparsing==3.1.1 53 | python-dateutil==2.8.2 54 | pytz==2023.3.post1 55 | PyYAML==6.0.1 56 | referencing==0.32.1 57 | requests==2.31.0 58 | rich==13.7.0 59 | rpds-py==0.17.1 60 | six==1.16.0 61 | smmap==5.0.1 62 | sniffio==1.3.0 63 | soupsieve==2.5 64 | st-annotated-text==4.0.1 65 | streamlit==1.30.0 66 | streamlit-camera-input-live==0.2.0 67 | streamlit-card==1.0.0 68 | streamlit-embedcode==0.1.2 69 | streamlit-extras==0.3.6 70 | streamlit-faker==0.0.3 71 | streamlit-image-coordinates==0.1.6 72 | streamlit-keyup==0.2.2 73 | streamlit-toggle-switch==1.0.2 74 | streamlit-vertical-slider==2.5.5 75 | tenacity==8.2.3 76 | toml==0.10.2 77 | toolz==0.12.0 78 | tornado==6.4 79 | typing_extensions==4.9.0 80 | tzdata==2023.4 81 | tzlocal==5.2 82 | urllib3==2.1.0 83 | validators==0.22.0 84 | watchdog==3.0.0 85 | webencodings==0.5.1 86 | yfinance 87 | zipp==3.17.0 88 | -------------------------------------------------------------------------------- /tickers_ibra.csv: -------------------------------------------------------------------------------- 1 | ,0 2 | 0,AALR3 3 | 1,ABCB4 4 | 2,ABEV3 5 | 3,AESB3 6 | 4,AGRO3 7 | 6,ALPA4 8 | 7,ALUP11 9 | 8,AMAR3 10 | 9,AMBP3 11 | 10,ANIM3 12 | 11,ARML3 13 | 12,ARZZ3 14 | 13,ASAI3 15 | 14,AURE3 16 | 15,AZUL4 17 | 16,B3SA3 18 | 17,BBAS3 19 | 18,BBDC3 20 | 19,BBDC4 21 | 20,BBSE3 22 | 21,BEEF3 23 | 22,BHIA3 24 | 23,BLAU3 25 | 24,BMOB3 26 | 25,BPAC11 27 | 26,BPAN4 28 | 27,BRAP4 29 | 28,BRFS3 30 | 29,BRKM5 31 | 30,BRSR6 32 | 31,CAML3 33 | 32,CASH3 34 | 33,CBAV3 35 | 34,CCRO3 36 | 35,CEAB3 37 | 36,CIEL3 38 | 37,CMIG3 39 | 38,CMIG4 40 | 39,CMIN3 41 | 40,COGN3 42 | 41,CPFE3 43 | 42,CPLE6 44 | 43,CRFB3 45 | 44,CSAN3 46 | 45,CSMG3 47 | 46,CSNA3 48 | 47,CURY3 49 | 48,CVCB3 50 | 49,CXSE3 51 | 50,CYRE3 52 | 51,DASA3 53 | 52,DIRR3 54 | 53,DXCO3 55 | 54,ECOR3 56 | 55,EGIE3 57 | 56,ELET3 58 | 57,ELET6 59 | 58,EMBR3 60 | 59,ENAT3 61 | 60,ENEV3 62 | 61,ENGI11 63 | 62,EQTL3 64 | 63,ESPA3 65 | 64,EVEN3 66 | 65,EZTC3 67 | 66,FESA4 68 | 67,FLRY3 69 | 68,FRAS3 70 | 69,GFSA3 71 | 70,GGBR4 72 | 71,GGPS3 73 | 72,GMAT3 74 | 73,GOAU4 75 | 74,GRND3 76 | 75,GUAR3 77 | 76,HAPV3 78 | 77,HBSA3 79 | 78,HYPE3 80 | 79,IFCM3 81 | 80,IGTI11 82 | 81,INTB3 83 | 82,IRBR3 84 | 83,ITSA4 85 | 84,ITUB3 86 | 85,ITUB4 87 | 86,JALL3 88 | 87,JBSS3 89 | 88,JHSF3 90 | 89,KEPL3 91 | 90,KLBN11 92 | 91,LAVV3 93 | 92,LEVE3 94 | 93,LJQQ3 95 | 94,LOGG3 96 | 95,LREN3 97 | 96,LUPA3 98 | 97,LWSA3 99 | 98,MATD3 100 | 99,MBLY3 101 | 100,MDIA3 102 | 101,MGLU3 103 | 102,MILS3 104 | 103,MLAS3 105 | 104,MOVI3 106 | 105,MRFG3 107 | 106,MRVE3 108 | 107,MTRE3 109 | 108,MULT3 110 | 109,MYPK3 111 | 110,NEOE3 112 | 111,NTCO3 113 | 112,ODPV3 114 | 113,ONCO3 115 | 114,ORVR3 116 | 115,PCAR3 117 | 116,PETR3 118 | 117,PETR4 119 | 118,PETZ3 120 | 119,PGMN3 121 | 120,PLPL3 122 | 121,PNVL3 123 | 122,POMO4 124 | 123,POSI3 125 | 124,PRIO3 126 | 125,PSSA3 127 | 126,PTBL3 128 | 127,QUAL3 129 | 128,RADL3 130 | 129,RAIL3 131 | 130,RAIZ4 132 | 131,RANI3 133 | 132,RAPT4 134 | 133,RCSL3 135 | 134,RDOR3 136 | 135,RECV3 137 | 136,RENT3 138 | 137,ROMI3 139 | 138,RRRP3 140 | 139,SANB11 141 | 140,SAPR11 142 | 141,SBFG3 143 | 142,SBSP3 144 | 143,SEER3 145 | 144,SIMH3 146 | 145,SLCE3 147 | 146,SMFT3 148 | 147,SMTO3 149 | 148,SOMA3 150 | 149,SRNA3 151 | 150,STBP3 152 | 151,SUZB3 153 | 152,TAEE11 154 | 153,TASA4 155 | 154,TEND3 156 | 155,TGMA3 157 | 156,TIMS3 158 | 157,TOTS3 159 | 158,TRIS3 160 | 159,TRPL4 161 | 160,TTEN3 162 | 161,TUPY3 163 | 162,UGPA3 164 | 163,UNIP6 165 | 164,USIM5 166 | 165,VALE3 167 | 166,VAMO3 168 | 167,VBBR3 169 | 168,VIVA3 170 | 169,VIVT3 171 | 170,VLID3 172 | 171,VULC3 173 | 172,VVEO3 174 | 173,WEGE3 175 | 174,WIZC3 176 | 175,YDUQ3 177 | 176,ZAMP3 178 | --------------------------------------------------------------------------------