From e0079b956e5c0e45500e3ef6471077ba39db6925 Mon Sep 17 00:00:00 2001 From: Cuttlas Date: Wed, 31 Jan 2024 17:22:51 +0330 Subject: [PATCH 1/3] linting --- main.py | 244 +++++++++--------- .../{01 Dollar_Base.py => 01_Dollar_Base.py} | 0 ...nthly_Comapre.py => 02_Monthly_Comapre.py} | 0 pages/{03 workbench.py => 03_Workbench.py} | 0 pages/{04 portfolio.py => 04_Portfolio.py} | 43 ++- pages/{10 changelog.py => 10_Changelog.py} | 0 slider.py | 3 +- 7 files changed, 158 insertions(+), 132 deletions(-) rename pages/{01 Dollar_Base.py => 01_Dollar_Base.py} (100%) rename pages/{02 Monthly_Comapre.py => 02_Monthly_Comapre.py} (100%) rename pages/{03 workbench.py => 03_Workbench.py} (100%) rename pages/{04 portfolio.py => 04_Portfolio.py} (63%) rename pages/{10 changelog.py => 10_Changelog.py} (100%) diff --git a/main.py b/main.py index ab60f32..c4d9f54 100644 --- a/main.py +++ b/main.py @@ -33,7 +33,7 @@ # # """, unsafe_allow_html=True # ) -with open( "style.css" ) as css: +with open( "style.css", encoding="utf-8") as css: st.markdown( f'' , unsafe_allow_html= True) @@ -42,17 +42,19 @@ def get_email_callback(): - hasError, message = get_nonce(st.session_state.email) - if hasError: + has_error, message = get_nonce(st.session_state.email) + if has_error: st.error(message, icon="🚨") else: submit_nonce = st.form("submit_nonce") - nonce = submit_nonce.text_input('کد تایید خود را وارد کنید', placeholder='XXXX', key="nonce") - submitted = submit_nonce.form_submit_button("ارسال", on_click = get_nonce_callback ) + submit_nonce.text_input('کد تایید خود را وارد کنید', + placeholder='XXXX', + key="nonce") + submit_nonce.form_submit_button("ارسال", on_click = get_nonce_callback ) def get_nonce_callback(): - hasError, message = get_key(st.session_state.email, st.session_state.nonce) - if hasError: + has_error, message = get_key(st.session_state.email, st.session_state.nonce) + if has_error: st.error(message, icon="🚨") del st.session_state["nonce"] else: @@ -61,18 +63,20 @@ def get_nonce_callback(): if "token" not in st .session_state: get_email = st.form("get_email") - email = get_email.text_input('ایمیل خود را وارد کنید', placeholder='example@mail.com', key="email") + email = get_email.text_input('ایمیل خود را وارد کنید', + placeholder='example@mail.com', + key="email") # Every form must have a submit button. submitted = get_email.form_submit_button("دریافت کد", on_click = get_email_callback ) else: - df = pd.read_csv("data.csv").dropna() - list_of_name = df['name'].to_list() + df = pd.read_csv("data.csv").dropna() + list_of_name = df['name'].to_list() - name = st.sidebar.selectbox("لیست سهام", options = list_of_name) + name = st.sidebar.selectbox("لیست سهام", options = list_of_name) - st.header('گزارش ماهانه فروش', divider='rainbow') + st.header('گزارش ماهانه فروش', divider='rainbow') - queryString = queryString = """select + queryString = queryString = f"""select \"rowTitle\", sum(value) as value, \"endToPeriod\" @@ -85,32 +89,32 @@ def get_nonce_callback(): or public.\"MonthlyData\".\"columnTitle\" = 'درآمد شناسایی شده' or public.\"MonthlyData\".\"columnTitle\" = 'درآمد محقق شده طی دوره یک ماهه - لیزینگ' ) - and stocks.name = '{}' + and stocks.name = '{name}' group by public.\"MonthlyData\".\"rowTitle\", public.\"MonthlyData\".\"endToPeriod\" - """.format(name) - error, stock_data = vasahm_query(queryString) - if error: - st.error(stock_data, icon="🚨") - else: - stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", - "value", - "endToPeriod"]) - stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) - # specify the type of selection, here single selection is used - selector = alt.selection_single(encodings=['x', 'color']) - - chart = alt.Chart(stock_data_history).mark_bar().encode( - color='rowTitle:N', - y='sum(value):Q', - x='endToPeriod:N' - ) - st.altair_chart(chart, use_container_width=True) - - - st.header('گزارش تعداد تولید', divider='rainbow') - queryString = queryString = """select + """ + error, stock_data = vasahm_query(queryString) + if error: + st.error(stock_data, icon="🚨") + else: + stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", + "value", + "endToPeriod"]) + stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) + # specify the type of selection, here single selection is used + selector = alt.selection_single(encodings=['x', 'color']) + + chart = alt.Chart(stock_data_history).mark_bar().encode( + color='rowTitle:N', + y='sum(value):Q', + x='endToPeriod:N' + ) + st.altair_chart(chart, use_container_width=True) + + + st.header('گزارش تعداد تولید', divider='rainbow') + queryString = queryString = f"""select \"rowTitle\", sum(value) as value, \"endToPeriod\" @@ -121,31 +125,31 @@ def get_nonce_callback(): ( \"MonthlyData\".\"columnTitle\" = 'تعداد تولید' ) - and stocks.name = '{}' + and stocks.name = '{name}' group by \"MonthlyData\".\"rowTitle\", \"MonthlyData\".\"endToPeriod\" - """.format(name) - error, stock_data = vasahm_query(queryString) - if error: - st.error(stock_data, icon="🚨") - else: - stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", - "value", - "endToPeriod"]) - stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) - # specify the type of selection, here single selection is used - selector = alt.selection_single(encodings=['x', 'color']) - - chart_product = alt.Chart(stock_data_history).mark_bar().encode( - color='rowTitle:N', - y='sum(value):Q', - x='endToPeriod:N' - ) - st.altair_chart(chart_product, use_container_width=True) - - st.header('گزارش تعداد فروش', divider='rainbow') - queryString = queryString = """select + """ + error, stock_data = vasahm_query(queryString) + if error: + st.error(stock_data, icon="🚨") + else: + stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", + "value", + "endToPeriod"]) + stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) + # specify the type of selection, here single selection is used + selector = alt.selection_single(encodings=['x', 'color']) + + chart_product = alt.Chart(stock_data_history).mark_bar().encode( + color='rowTitle:N', + y='sum(value):Q', + x='endToPeriod:N' + ) + st.altair_chart(chart_product, use_container_width=True) + + st.header('گزارش تعداد فروش', divider='rainbow') + queryString = f"""select \"rowTitle\", sum(value) as value, \"endToPeriod\" @@ -156,32 +160,32 @@ def get_nonce_callback(): ( \"MonthlyData\".\"columnTitle\" = 'تعداد فروش' ) - and stocks.name = '{}' + and stocks.name = '{name}' group by \"MonthlyData\".\"rowTitle\", \"MonthlyData\".\"endToPeriod\" - """.format(name) - error, stock_data = vasahm_query(queryString) - if error: - st.error(stock_data, icon="🚨") - else: - stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", - "value", - "endToPeriod"]) - stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) - # specify the type of selection, here single selection is used - selector = alt.selection_single(encodings=['x', 'color']) - - chart_product = alt.Chart(stock_data_history).mark_bar().encode( - color='rowTitle:N', - y='sum(value):Q', - x='endToPeriod:N' - ) - st.altair_chart(chart_product, use_container_width=True) - - - st.header('درآمدهای عملیاتی و سود', divider='rainbow') - queryString = """select + """ + error, stock_data = vasahm_query(queryString) + if error: + st.error(stock_data, icon="🚨") + else: + stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", + "value", + "endToPeriod"]) + stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) + # specify the type of selection, here single selection is used + selector = alt.selection_single(encodings=['x', 'color']) + + chart_product = alt.Chart(stock_data_history).mark_bar().encode( + color='rowTitle:N', + y='sum(value):Q', + x='endToPeriod:N' + ) + st.altair_chart(chart_product, use_container_width=True) + + + st.header('درآمدهای عملیاتی و سود', divider='rainbow') + queryString = f"""select \"rowTitle\", \"value\", \"endToPeriod\" @@ -194,28 +198,28 @@ def get_nonce_callback(): or \"QuarterlyData\".\"rowTitle\" = 'سود(زیان) ناخالص' or \"QuarterlyData\".\"rowTitle\" = 'سود(زیان) خالص' ) - and stocks.name = '{}' - """.format(name) - error, stock_data = vasahm_query(queryString) - if error: - st.error(stock_data, icon="🚨") - else: - stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", - "value", - "endToPeriod"]) - stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) - # specify the type of selection, here single selection is used - chart2 = alt.Chart(stock_data_history).mark_area(opacity=0.3).encode( - color='rowTitle:N', - y=alt.Y('value:Q').stack(None), - x='endToPeriod:N' - ) - - st.altair_chart(chart2, use_container_width=True) - - - st.header('حاشیه سود خالص', divider='rainbow') - queryString = """select + and stocks.name = '{name}' + """ + error, stock_data = vasahm_query(queryString) + if error: + st.error(stock_data, icon="🚨") + else: + stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", + "value", + "endToPeriod"]) + stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) + # specify the type of selection, here single selection is used + chart2 = alt.Chart(stock_data_history).mark_area(opacity=0.3).encode( + color='rowTitle:N', + y=alt.Y('value:Q').stack(None), + x='endToPeriod:N' + ) + + st.altair_chart(chart2, use_container_width=True) + + + st.header('حاشیه سود خالص', divider='rainbow') + queryString = f"""select \"rowTitle\", \"value\", \"endToPeriod\" @@ -228,17 +232,23 @@ def get_nonce_callback(): or \"QuarterlyData\".\"rowTitle\" = 'سود(زیان) ناخالص' or \"QuarterlyData\".\"rowTitle\" = 'سود(زیان) خالص' ) - and stocks.name = '{}' - """.format(name) - error, stock_data = vasahm_query(queryString) - if error: - st.error(stock_data, icon="🚨") - else: - stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", - "value", - "endToPeriod"]) - stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) - pivot_df = stock_data_history.pivot_table(index='endToPeriod', columns='rowTitle', values='value', aggfunc='sum').reset_index() - pivot_df["profit_ratio"] = pivot_df["سود(زیان) خالص"].astype(float)/pivot_df["درآمدهای عملیاتی"].astype(float) - pe_df=pivot_df[["profit_ratio", "endToPeriod"]] - st.line_chart(data=pe_df, x="endToPeriod", y="profit_ratio", color=None, use_container_width=True) \ No newline at end of file + and stocks.name = '{name}' + """ + error, stock_data = vasahm_query(queryString) + if error: + st.error(stock_data, icon="🚨") + else: + stock_data_history = pd.DataFrame(stock_data, columns=["rowTitle", + "value", + "endToPeriod"]) + stock_data_history["endToPeriod"] = stock_data_history["endToPeriod"].astype(str) + pivot_df = stock_data_history.pivot_table(index='endToPeriod', + columns='rowTitle', + values='value', + aggfunc='sum').reset_index() + pivot_df["profit_ratio"] = pivot_df["سود(زیان) خالص"].astype(float)/pivot_df["درآمدهای عملیاتی"].astype(float) + pe_df=pivot_df[["profit_ratio", "endToPeriod"]] + st.line_chart(data=pe_df, x="endToPeriod", + y="profit_ratio", + color=None, + use_container_width=True) diff --git a/pages/01 Dollar_Base.py b/pages/01_Dollar_Base.py similarity index 100% rename from pages/01 Dollar_Base.py rename to pages/01_Dollar_Base.py diff --git a/pages/02 Monthly_Comapre.py b/pages/02_Monthly_Comapre.py similarity index 100% rename from pages/02 Monthly_Comapre.py rename to pages/02_Monthly_Comapre.py diff --git a/pages/03 workbench.py b/pages/03_Workbench.py similarity index 100% rename from pages/03 workbench.py rename to pages/03_Workbench.py diff --git a/pages/04 portfolio.py b/pages/04_Portfolio.py similarity index 63% rename from pages/04 portfolio.py rename to pages/04_Portfolio.py index a70c022..51fb90e 100644 --- a/pages/04 portfolio.py +++ b/pages/04_Portfolio.py @@ -22,11 +22,11 @@ list_of_name = df['name'].to_list() # st.sidebar.image(image="./assets/logo.png") def del_porto_submition_variable(): - del st.session_state.porto_submition + del st.session_state.porto_submition st.sidebar.header(f'Vasahm DashBoard `{st.session_state.ver}`') def add_submit_state(): - st.session_state["porto_submition"] = True + st.session_state["porto_submition"] = True def create_form(): if "portfolio_analyzer" in locals(): @@ -34,14 +34,18 @@ def create_form(): portfolio_analyzer = st.form("portfolio_analyzer") cols2 = portfolio_analyzer.columns(2, gap="small") - cols2[0].selectbox("سال", options = ('1400','1401','1402'), key="portfolio-year") - cols2[1].selectbox("ماه", options = ('فروردین','اردیبهشت','خرداد','تیر','مرداد','شهریور','مهر','آبان','آذر','دی','بهمن','اسفند'), key="portfolio-month") + cols2[0].text_input('تاریخ شروع', placeholder='14010130', key=f"portfolio_month_start") + cols2[1].text_input('تاریخ شروع', placeholder='14010130', key=f"portfolio_month_finish") cols = portfolio_analyzer.columns(3, gap="small") for i in range(st.session_state.portfo_number): - cols[0].selectbox("لیست سهام", options = list_of_name, key=f"stock_name-{i}") - cols[1].text_input('قیمت خرید', placeholder='12345', key=f"stock-number-{i}") - cols[2].text_input('سهم از کل پورتفو (درصد)', placeholder='20', key=f"stock-percent-{i}") + cols[0].selectbox("لیست سهام", options = list_of_name, key=f"stock_name_{i}") + cols[1].text_input('قیمت خرید', placeholder='12345', key=f"stock_number_{i}") + cols[2].text_input('سهم از کل پورتفو (درصد)', placeholder='20', key=f"stock_percent_{i}") # Every form must have a submit button. + options = portfolio_analyzer.multiselect( + 'شاخصهای مورد نظر خود برای مقایسه را انتخاب کنید', + ['شاخص کل', 'شاخص همزون', 'طلا', 'زعفران (نهال)'], + ['شاخص کل'], key="indexes") portfolio_analyzer.form_submit_button("بررسی عملکرد سبد", on_click=add_submit_state) def get_email_callback(): @@ -69,12 +73,23 @@ def get_nonce_callback(): submitted = get_email.form_submit_button("دریافت کد", on_click = get_email_callback ) else: - if "porto_submition" not in st.session_state: - st.number_input('تعداد سهام موجود در سبد خود را وارد کنید', min_value=1, max_value=10, on_change=create_form, value=1, key="portfo_number") - else: - st.button("ثبت مجدد سبد سرمایه گذاری", key="resubmmition_portfo", help=None, on_click=del_porto_submition_variable, args=None, kwargs=None, type="secondary", disabled=False, use_container_width=True) - st.write("you are here") - - # name = st.sidebar.selectbox("لیست سهام", options = list_of_name) + if "porto_submition" not in st.session_state: + st.number_input('تعداد سهام موجود در سبد خود را وارد کنید', + min_value=1, + max_value=10, + on_change=create_form, + value=1, + key="portfo_number") + else: + st.button("ثبت مجدد سبد سرمایه گذاری", + key="resubmmition_portfo", + help=None, + on_click=del_porto_submition_variable, + args=None, + kwargs=None, + type="secondary", + disabled=False, + use_container_width=True) + st.write("you are here") diff --git a/pages/10 changelog.py b/pages/10_Changelog.py similarity index 100% rename from pages/10 changelog.py rename to pages/10_Changelog.py diff --git a/slider.py b/slider.py index 0976b2f..5013eb8 100644 --- a/slider.py +++ b/slider.py @@ -1,6 +1,7 @@ -import streamlit as st import datetime +import streamlit as st + def create_slider(df, key, title): min_value = df['date_column'].min() max_value = df['date_column'].max() From 24c56544daf806b626fb04f3035d7360ab181536 Mon Sep 17 00:00:00 2001 From: Cuttlas Date: Wed, 31 Jan 2024 19:22:07 +0330 Subject: [PATCH 2/3] get stock & fund history --- pages/04_Portfolio.py | 75 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/pages/04_Portfolio.py b/pages/04_Portfolio.py index 51fb90e..a7118be 100644 --- a/pages/04_Portfolio.py +++ b/pages/04_Portfolio.py @@ -1,3 +1,5 @@ +import requests + import streamlit as st import pandas as pd import plotly.express as px @@ -18,19 +20,45 @@ with open( "style.css" ) as css: st.markdown( f'' , unsafe_allow_html= True) +def index_price_history(insCode): + url = f"https://cdn.tsetmc.com/api/ClosingPrice/GetChartData/{insCode}/D" + header = {"User-Agent": "PostmanRuntime/7.29.0"} + response = requests.get(url, headers=header).json() + shiraz = pd.json_normalize(response['closingPriceChartData']) + shiraz['datetime'] = pd.to_datetime(shiraz["dEven"]+19603987200, unit='s').dt.strftime("%Y%m%d").astype(int) + return shiraz + +def index_price_history2(insCode): + url = f"https://cdn.tsetmc.com/api/Index/GetIndexB2History/{insCode}" + header = {"User-Agent": "PostmanRuntime/7.29.0"} + response = requests.get(url, headers=header).json() + return pd.json_normalize(response['indexB2']) + + df = pd.read_csv("data.csv").dropna() list_of_name = df['name'].to_list() # st.sidebar.image(image="./assets/logo.png") def del_porto_submition_variable(): - del st.session_state.porto_submition + del st.session_state.porto_submition + if "portfolio_analyzer" in locals(): + del portfolio_analyzer + st.sidebar.header(f'Vasahm DashBoard `{st.session_state.ver}`') def add_submit_state(): st.session_state["porto_submition"] = True +def create_query_String(): + string = "" + for i in range(st.session_state.portfo_number - 1): + stro = f"stock_name_{i}" + string = string + f"stocks.name = '{st.session_state[stro]}' OR " + stro = f"stock_name_{st.session_state.portfo_number - 1}" + string = string + f"stocks.name = '{st.session_state[stro]}'" + return string + def create_form(): - if "portfolio_analyzer" in locals(): - del portfolio_analyzer + portfolio_analyzer = st.form("portfolio_analyzer") cols2 = portfolio_analyzer.columns(2, gap="small") @@ -90,6 +118,45 @@ def get_nonce_callback(): type="secondary", disabled=False, use_container_width=True) - st.write("you are here") + string = create_query_String() + st.write(st.session_state.stock_name_1) + i = 1 + stro = f"stock_name_{i}" + st.write(stro) + st.write(st.session_state[stro]) + queryString = f""" + SELECT stocks.name as name, "tradeDate", "lastAdjPrice" + FROM public."stockPrice" + INNER JOIN stocks ON "stockPrice".stock_id = stocks.id + where + ({string}) + and "tradeDate" > '{st.session_state.portfolio_month_start}' + and "tradeDate" < '{st.session_state.portfolio_month_finish}' + order by "tradeDate";""" + st.write(queryString) + error, stock_data = vasahm_query(queryString) + if error: + st.error(stock_data, icon="🚨") + else: + stock_data_history = pd.DataFrame(stock_data, columns=["name", + "tradeDate", + "lastAdjPrice"]) + # st.dataframe(stock_data_history) + pivot_df = stock_data_history.pivot_table(index='tradeDate', + columns='name', + values='lastAdjPrice', + aggfunc='sum').reset_index() + pivot_df.fillna(method='ffill') + ind = {} + for i in st.session_state.indexes: + if i == "طلا": + ind["tala"] = index_price_history(46700660505281786) + elif i == 'زعفران (نهال)': + ind["nahal"] = index_price_history(12913156843322499) + elif i == 'شاخص همزون': + ind["kol"] = index_price_history2(67130298613737946) + elif i == 'شاخص کل': + ind["ham"] = index_price_history2(32097828799138957) + st.dataframe(pivot_df) From 76b7e4adc5d4a6b1e3d586164765a257bbae4771 Mon Sep 17 00:00:00 2001 From: mohsen Date: Thu, 1 Feb 2024 18:55:07 +0330 Subject: [PATCH 3/3] finish portfolio page & some linting --- main.py | 21 ++--- pages/01_Dollar_Base.py | 2 - pages/04_Portfolio.py | 170 +++++++++++++++++++++++----------------- pages/10_Changelog.py | 10 ++- request.py | 47 ++++++++--- slider.py | 6 +- style.css | 5 +- 7 files changed, 166 insertions(+), 95 deletions(-) diff --git a/main.py b/main.py index c4d9f54..c41f95f 100644 --- a/main.py +++ b/main.py @@ -1,14 +1,14 @@ +"""Plot Some main monthly and quarterly charts""" + + import streamlit as st import pandas as pd -import plotly.express as px -import plost -from request import vasahm_query -from slider import create_slider -from request import get_nonce -from request import get_key import altair as alt -st.session_state.ver = '0.1.3' +from request import vasahm_query, get_nonce, get_key + + +st.session_state.ver = '0.1.5' st.set_page_config(layout='wide', page_title="Vasahm Dashboard", @@ -42,6 +42,7 @@ def get_email_callback(): + """Send nonce to entered email.""" has_error, message = get_nonce(st.session_state.email) if has_error: st.error(message, icon="🚨") @@ -53,6 +54,7 @@ def get_email_callback(): submit_nonce.form_submit_button("ارسال", on_click = get_nonce_callback ) def get_nonce_callback(): + """Confirm nonce for login.""" has_error, message = get_key(st.session_state.email, st.session_state.nonce) if has_error: st.error(message, icon="🚨") @@ -63,7 +65,7 @@ def get_nonce_callback(): if "token" not in st .session_state: get_email = st.form("get_email") - email = get_email.text_input('ایمیل خود را وارد کنید', + email = get_email.text_input('ایمیل خود را وارد کنید', placeholder='example@mail.com', key="email") # Every form must have a submit button. @@ -246,7 +248,8 @@ def get_nonce_callback(): columns='rowTitle', values='value', aggfunc='sum').reset_index() - pivot_df["profit_ratio"] = pivot_df["سود(زیان) خالص"].astype(float)/pivot_df["درآمدهای عملیاتی"].astype(float) + pivot_df["profit_ratio"] = (pivot_df["سود(زیان) خالص"].astype(float) + /pivot_df["درآمدهای عملیاتی"].astype(float)) pe_df=pivot_df[["profit_ratio", "endToPeriod"]] st.line_chart(data=pe_df, x="endToPeriod", y="profit_ratio", diff --git a/pages/01_Dollar_Base.py b/pages/01_Dollar_Base.py index 7020969..6ff0d7d 100644 --- a/pages/01_Dollar_Base.py +++ b/pages/01_Dollar_Base.py @@ -8,8 +8,6 @@ from request import get_key import altair as alt -st.session_state.ver = '0.1.2' - st.set_page_config(layout='wide', page_title="Vasahm Dashboard", page_icon="./assets/favicon.ico", diff --git a/pages/04_Portfolio.py b/pages/04_Portfolio.py index a7118be..e9aabe0 100644 --- a/pages/04_Portfolio.py +++ b/pages/04_Portfolio.py @@ -1,93 +1,83 @@ -import requests +"""Module providing a logic that let user understand his portfolio performance +in compare to other indexes or funds.""" import streamlit as st import pandas as pd -import plotly.express as px -import plost -from request import vasahm_query -from slider import create_slider -from request import get_nonce -from request import get_key import altair as alt -st.session_state.ver = '0.1.2' +from request import vasahm_query, get_nonce, get_key, index_price_history, index_price_history2 st.set_page_config(layout='wide', page_title="Vasahm Dashboard", page_icon="./assets/favicon.ico", initial_sidebar_state='expanded') -with open( "style.css" ) as css: +with open("style.css", encoding="utf-8") as css: st.markdown( f'' , unsafe_allow_html= True) -def index_price_history(insCode): - url = f"https://cdn.tsetmc.com/api/ClosingPrice/GetChartData/{insCode}/D" - header = {"User-Agent": "PostmanRuntime/7.29.0"} - response = requests.get(url, headers=header).json() - shiraz = pd.json_normalize(response['closingPriceChartData']) - shiraz['datetime'] = pd.to_datetime(shiraz["dEven"]+19603987200, unit='s').dt.strftime("%Y%m%d").astype(int) - return shiraz - -def index_price_history2(insCode): - url = f"https://cdn.tsetmc.com/api/Index/GetIndexB2History/{insCode}" - header = {"User-Agent": "PostmanRuntime/7.29.0"} - response = requests.get(url, headers=header).json() - return pd.json_normalize(response['indexB2']) - df = pd.read_csv("data.csv").dropna() list_of_name = df['name'].to_list() # st.sidebar.image(image="./assets/logo.png") def del_porto_submition_variable(): + """Deletes portfolio_analyzer form that let user to + fill a new form again.""" del st.session_state.porto_submition if "portfolio_analyzer" in locals(): del portfolio_analyzer st.sidebar.header(f'Vasahm DashBoard `{st.session_state.ver}`') - + def add_submit_state(): + """Create st.session_state['porto_submition'] that help logic + whether user submit his portfolio or not.""" st.session_state["porto_submition"] = True -def create_query_String(): - string = "" - for i in range(st.session_state.portfo_number - 1): - stro = f"stock_name_{i}" - string = string + f"stocks.name = '{st.session_state[stro]}' OR " - stro = f"stock_name_{st.session_state.portfo_number - 1}" - string = string + f"stocks.name = '{st.session_state[stro]}'" - return string +def create_query_string(): + """Create a query string for retrieving all portfolio prices.""" + temp_str = "" + for _ in range(st.session_state.portfo_number): + stock_name_i = f"stock_name_{_}" + temp_str = temp_str + f"stocks.name = '{st.session_state[stock_name_i]}' OR " + return temp_str[:-4] def create_form(): - + """Create a form that let user enter his portfolio.""" portfolio_analyzer = st.form("portfolio_analyzer") cols2 = portfolio_analyzer.columns(2, gap="small") - cols2[0].text_input('تاریخ شروع', placeholder='14010130', key=f"portfolio_month_start") - cols2[1].text_input('تاریخ شروع', placeholder='14010130', key=f"portfolio_month_finish") - cols = portfolio_analyzer.columns(3, gap="small") - for i in range(st.session_state.portfo_number): - cols[0].selectbox("لیست سهام", options = list_of_name, key=f"stock_name_{i}") - cols[1].text_input('قیمت خرید', placeholder='12345', key=f"stock_number_{i}") - cols[2].text_input('سهم از کل پورتفو (درصد)', placeholder='20', key=f"stock_percent_{i}") - # Every form must have a submit button. - options = portfolio_analyzer.multiselect( + cols2[0].text_input('تاریخ شروع', placeholder='14010130', key="portfolio_month_start") + cols2[1].text_input('تاریخ پایان', placeholder='14010130', key="portfolio_month_finish") + cols = portfolio_analyzer.columns(2, gap="small") + for _ in range(st.session_state.portfo_number): + cols[0].selectbox("لیست سهام", options = list_of_name, key=f"stock_name_{_}") + cols[1].number_input('سهم از کل پورتفو (درصد)', + min_value=1, + max_value=100, + step=1, + placeholder='20', + key=f"stock_percent_{_}" + ) + portfolio_analyzer.multiselect( 'شاخصهای مورد نظر خود برای مقایسه را انتخاب کنید', ['شاخص کل', 'شاخص همزون', 'طلا', 'زعفران (نهال)'], ['شاخص کل'], key="indexes") portfolio_analyzer.form_submit_button("بررسی عملکرد سبد", on_click=add_submit_state) def get_email_callback(): - hasError, message = get_nonce(st.session_state.email) - if hasError: + """Send nonce to entered email.""" + has_error, message = get_nonce(st.session_state.email) + if has_error: st.error(message, icon="🚨") else: submit_nonce = st.form("submit_nonce") - nonce = submit_nonce.text_input('کد تایید خود را وارد کنید', placeholder='XXXX', key="nonce") - submitted = submit_nonce.form_submit_button("ارسال", on_click = get_nonce_callback ) + submit_nonce.text_input('کد تایید خود را وارد کنید', placeholder='XXXX', key="nonce") + submit_nonce.form_submit_button("ارسال", on_click = get_nonce_callback ) def get_nonce_callback(): - hasError, message = get_key(st.session_state.email, st.session_state.nonce) - if hasError: + """Confirm nonce for login.""" + has_error, message = get_key(st.session_state.email, st.session_state.nonce) + if has_error: st.error(message, icon="🚨") del st.session_state["nonce"] else: @@ -96,11 +86,11 @@ def get_nonce_callback(): if "token" not in st .session_state: get_email = st.form("get_email") - email = get_email.text_input('ایمیل خود را وارد کنید', placeholder='example@mail.com', key="email") - # Every form must have a submit button. + email = get_email.text_input('ایمیل خود را وارد کنید', + placeholder='example@mail.com', + key="email") submitted = get_email.form_submit_button("دریافت کد", on_click = get_email_callback ) else: - if "porto_submition" not in st.session_state: st.number_input('تعداد سهام موجود در سبد خود را وارد کنید', min_value=1, @@ -118,14 +108,9 @@ def get_nonce_callback(): type="secondary", disabled=False, use_container_width=True) - string = create_query_String() - st.write(st.session_state.stock_name_1) - i = 1 - stro = f"stock_name_{i}" - st.write(stro) - st.write(st.session_state[stro]) + string = create_query_string() queryString = f""" - SELECT stocks.name as name, "tradeDate", "lastAdjPrice" + SELECT stocks.name as name, "tradeDate", "lastAdjPrice","tradeDateGre" FROM public."stockPrice" INNER JOIN stocks ON "stockPrice".stock_id = stocks.id where @@ -133,30 +118,75 @@ def get_nonce_callback(): and "tradeDate" > '{st.session_state.portfolio_month_start}' and "tradeDate" < '{st.session_state.portfolio_month_finish}' order by "tradeDate";""" - st.write(queryString) + error, stock_data = vasahm_query(queryString) if error: st.error(stock_data, icon="🚨") else: stock_data_history = pd.DataFrame(stock_data, columns=["name", "tradeDate", - "lastAdjPrice"]) - # st.dataframe(stock_data_history) - pivot_df = stock_data_history.pivot_table(index='tradeDate', + "lastAdjPrice", + "tradeDateGre"]) + pivot_df = stock_data_history.pivot_table(index='tradeDateGre', columns='name', values='lastAdjPrice', aggfunc='sum').reset_index() - pivot_df.fillna(method='ffill') + pivot_df['date'] = pd.to_datetime(pivot_df['tradeDateGre'], format="%Y-%m-%dT%H:%M:%S") + pivot_df['datetime'] = pivot_df["date"].dt.strftime("%Y%m%d").astype(str) + pivot_df = pivot_df.drop(columns=['date', 'tradeDateGre']) + pivot_df.fillna(method='ffill', inplace=True) + pivot_df['پورتفو'] = 0 + for i in range(st.session_state.portfo_number): + stro = f"stock_name_{i}" + stro1 = f"stock_percent_{i}" + pivot_df['پورتفو'] = pivot_df['پورتفو'] + ( + pivot_df[st.session_state[stro]].astype(int)*st.session_state[stro1]/100 + ) + pivot_df = pivot_df.drop(columns=[st.session_state[stro]]) ind = {} for i in st.session_state.indexes: if i == "طلا": - ind["tala"] = index_price_history(46700660505281786) + ind["طلا"] = index_price_history(46700660505281786, "طلا") + pivot_df = pivot_df.merge(ind["طلا"], how='left',on='datetime') elif i == 'زعفران (نهال)': - ind["nahal"] = index_price_history(12913156843322499) + ind["زعفران (نهال)"] = index_price_history(12913156843322499, "زعفران (نهال)") + pivot_df = pivot_df.merge(ind["زعفران (نهال)"], how='left',on='datetime') elif i == 'شاخص همزون': - ind["kol"] = index_price_history2(67130298613737946) + ind["شاخص هموزن"] = index_price_history2(67130298613737946, "شاخص هموزن") + pivot_df = pivot_df.merge(ind["شاخص هموزن"], how='left',on='datetime') elif i == 'شاخص کل': - ind["ham"] = index_price_history2(32097828799138957) - st.dataframe(pivot_df) - - + ind["شاخص کل"] = index_price_history2(32097828799138957, "شاخص کل") + pivot_df = pivot_df.merge(ind["شاخص کل"], how='left',on='datetime') + + pivot_df.fillna(method='ffill', inplace=True) + pivot_df['datetime'] = pd.to_datetime( + pivot_df['datetime'], + format='%Y%m%d', + errors='coerce') + pivot_df.sort_values('datetime', inplace=True) + change_df = pivot_df[['datetime']] + my_list = pivot_df.columns.values.tolist() + my_list.remove('datetime') + for i in my_list: + change_df[i] = pivot_df[i].pct_change().fillna(0).cumsum() + # change_df[i] = change_df[i].map('{:.2%}'.format) + + p2 = change_df.melt(id_vars=['datetime'], var_name='column_name', value_name='value') + p2.fillna(0, inplace=True) + # st.line_chart(p2,x="datetime", y="value", color="column_name", height=500) + + chart_product = alt.Chart(p2, height=600).mark_line().encode( + alt.X('datetime:T', title='تاریخ'), + alt.Y('value:Q', title="میزان عمکرد").axis(format='%'), + alt.Color('column_name:N', title='دسته ها'), + + ) + chart_product.configure_title( + fontSize=20, + font='Vazirmatn', + ) + + chart_product.configure( + font='Vazirmatn' + ) + st.altair_chart(chart_product, use_container_width=True) diff --git a/pages/10_Changelog.py b/pages/10_Changelog.py index aa54df2..016c9ce 100644 --- a/pages/10_Changelog.py +++ b/pages/10_Changelog.py @@ -1,8 +1,14 @@ +"""Page log for changes and new improvement in project.""" + import streamlit as st -with open( "style.css" ) as css: +with open("style.css", encoding="utf-8") as css: st.markdown( f'' , unsafe_allow_html= True) +st.subheader('changelog: `version 0.1.5`', divider='rainbow') +st.markdown(''' + * اضافه شدن صفحه بررسی عملکرد پورتفو + * رفع باگهای کوچک''', unsafe_allow_html=False, help=None) st.subheader('changelog: `version 0.1.4`', divider='rainbow') st.markdown(''' * اضافه کردن نمودار پی به ای @@ -24,4 +30,4 @@ * گزارش ماهانه فروش * گزارش تعداد تولید * گزارش تعداد فروش - * درآمدهای عملیاتی و سود''', unsafe_allow_html=False, help=None) \ No newline at end of file + * درآمدهای عملیاتی و سود''', unsafe_allow_html=False, help=None) diff --git a/request.py b/request.py index 23bd64f..2cce705 100644 --- a/request.py +++ b/request.py @@ -1,49 +1,76 @@ +"""A helper for requesting different data""" + +import configparser import requests + import streamlit as st -import configparser +import pandas as pd config = configparser.ConfigParser() config.read('config.ini') @st.cache_data(ttl=int(config["server"]["cache_time"])) -def vasahm_query(queryString): +def vasahm_query(query_string): + """Query query_string on database and retrieve the results.""" url = 'https://back.vasahm.com/user/runQuery' - myobj = {'queryString': queryString} + myobj = {'queryString': query_string} headers = { "accept": "*/*", "accept-language": "en-GB,en;q=0.9,en-US;q=0.8,fa;q=0.7", "authtoken": st.session_state.token, "content-type": "application/json", - "sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Microsoft Edge\";v=\"120\"", + "sec-ch-ua": "\"Not_A Brand\";v=\"8\",\"Chromium\";v=\"120\", \"Microsoft Edge\";v=\"120\"", "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": "\"Windows\"", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-site" } - x = requests.post(url, json = myobj, headers=headers).json() + x = requests.post(url, json = myobj, headers=headers, timeout=60).json() if x["hasError"]: - x["hasError"], x["error"] + return x["hasError"], x["error"] else: return x["hasError"], x["data"]["result"] def get_nonce(email): + """get nonce to entered email.""" url = "https://back.vasahm.com/user/getNonce" myobj = {'Email': email} - x = requests.post(url, json = myobj).json() + x = requests.post(url, json = myobj, timeout=60).json() if x["hasError"]: - x["hasError"], x["error"]["message"] + return x["hasError"], x["error"]["message"] else: return x["hasError"], x["message"] def get_key(email, nonce): + """Confirm nonce for login.""" url = "https://back.vasahm.com/user/login" myobj = {'Email': email, "Nonce": nonce} - x = requests.post(url, json = myobj).json() + x = requests.post(url, json = myobj, timeout=60).json() if x["hasError"]: - x["hasError"], x["error"]["message"] + return x["hasError"], x["error"]["message"] else: return x["hasError"], x["data"]["Token"] +def index_price_history(ins_code, name): + """Get history price of a tehran exchange Fund.""" + url = f"https://cdn.tsetmc.com/api/ClosingPrice/GetChartData/{ins_code}/D" + header = {"User-Agent": "PostmanRuntime/7.29.0"} + response = requests.get(url, headers=header, timeout=60).json() + shiraz = pd.json_normalize(response['closingPriceChartData']) + shiraz['datetime'] = pd.to_datetime( + shiraz["dEven"]+19603987200, unit='s' + ).dt.strftime("%Y%m%d").astype(str) + shiraz = shiraz.rename(columns={'pDrCotVal': name}) + return shiraz[["datetime", name]] +def index_price_history2(ins_code, name): + """Get history price of a tehran exchange index.""" + url = f"https://cdn.tsetmc.com/api/Index/GetIndexB2History/{ins_code}" + header = {"User-Agent": "PostmanRuntime/7.29.0"} + response = requests.get(url, headers=header, timeout=60).json() + shiraz = pd.json_normalize(response['indexB2']).rename( + columns={'dEven': 'datetime', 'xNivInuClMresIbs': name}) + shiraz['datetime'] = shiraz['datetime'].astype(str) + return shiraz[["datetime", name]] diff --git a/slider.py b/slider.py index 5013eb8..232ff3f 100644 --- a/slider.py +++ b/slider.py @@ -1,8 +1,11 @@ +"""A helper for creating sliders""" + import datetime import streamlit as st def create_slider(df, key, title): + """Creates a date slider""" min_value = df['date_column'].min() max_value = df['date_column'].max() date_slider = st.sidebar.slider( @@ -15,6 +18,7 @@ def create_slider(df, key, title): return date_slider def create_range_slider(df, key, title, column): + """Creates a range slider""" min_value = df[column].astype(float).min() max_value = df[column].astype(float).max() range_slider = st.sidebar.slider( @@ -25,4 +29,4 @@ def create_range_slider(df, key, title, column): format="%.2f", step=0.01, key=key) - return range_slider \ No newline at end of file + return range_slider diff --git a/style.css b/style.css index 54a8fdf..7cc75a6 100644 --- a/style.css +++ b/style.css @@ -11,7 +11,10 @@ /* Card */ /* Adapted from https://startbootstrap.com/theme/sb-admin-2 */ -@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100&display=swap'); +@font-face { + font-family: 'Vazirmatn'; + src: url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100&display=swap'); +} html, body, [class*="css"] { font-family: 'Vazirmatn', sans-serif;