How to create your own stock market and finance app (Part 1)

Share:
Indexes
  1. Build your own mobile friendly website with Flask.
  2. Develop your own financial dashboard with Flask and Plotly
  3. Flask Tutorial: Simple Login for your webpage (python)
  4. Flask tutorial: How to deploy and publish an app on pythonanywhere
  5. Develop your own financial dashboard with Flask and Plotly
  6. Disclaimer:

TL;DR: In the previous stories, I was describing how to build a mobile web app, add a user management, build a stock database and a dashboard. Now, I want to combine everything and make an app out of it. This first part will be a work in progress, a minimal viable product (MVP), which we will further work on.

This is the first MVP

  • you can choose a ticker from a list from the Russel 2000 ETF Index
  • dynamic endpoint with flask
  • you get a candle stick diagram and basic facts from Ishares

In the next stories, I want to implement following features:

picture from wikipedia.com
  • Calculate your optimal portfolio with markowitz

If you have suggestions please let me know in the comments below.

How to start

As a baseline, you should know a little about python and flask. I created several tutorials about it. Read them before, if you start from zero. You can also deploy your app easily on pythonanywhere* and access it from everywere in the world via your web browser, I show that in Part 4 of my tutorials. Below a list:

  • Part 1: Build your own mobile friendly website with Flask (Link)
  • Part 2: Develop your own financial dashboard with Flask and Plotly (Link)
  • Part 3: Flask Tutorial: Simple Login for your webpage (python) (Link)
  • Part 4: Flask tutorial: How to deploy and publish an app on pythonanywhere (Link)

Setup

Use the here the repository from Part 3: Flask Tutorial: Simple Login for your webpage (python).

My setup:

  • python 3.8
  • virtual environment with conda
  • pip list
absl-py 0.12.0
aiodns 3.0.0
aiohttp 3.7.4.post0
astunparse 1.6.3
async-timeout 3.0.1
attrs 22.1.0
Babel 2.9.0
bcrypt 3.2.0
beautifulsoup4 4.9.3
blinker 1.4
cachetools 5.2.0
ccxt 1.61.42
certifi 2022.6.15
cffi 1.14.5
chardet 4.0.0
charset-normalizer 2.1.0
click 8.1.2
colorama 0.4.5
cPython 0.0.6
cryptography 37.0.4
dnspython 2.1.0
dominate 2.6.0
email-validator 1.1.2
Flask 2.1.3pi
Flask-Bootstrap 3.3.7.1
Flask-Limiter 2.2.0
Flask-Login 0.6.1
Flask-Mail 0.9.1
Flask-Principal 0.4.0
Flask-SQLAlchemy 2.4.4
Flask-WTF 0.14.3
flatbuffers 1.12
gast 0.4.0
greenlet 1.1.1
grpcio 1.34.1
h5py 3.1.0
idna 2.10
importlib-metadata 4.11.3
itsdangerous 2.1.2
Jinja2 3.0.3
limits 2.4.0
lxml 4.6.3
Markdown 3.3.4
MarkupSafe 2.1.1
multidict 6.0.2
multitasking 0.0.9
numpy 1.23.1
oauthlib 3.2.0
opt-einsum 3.3.0
pandas 1.2.2
paramiko 2.7.2
passlib 1.7.4
pbr 5.6.0
pip 22.2.2
plotly 5.9.0
protobuf 3.17.1
pyasn1 0.4.8
pyasn1-modules 0.2.8
pycares 4.1.2
pycparser 2.20
PyNaCl 1.4.0
python-dateutil 2.8.2
pytz 2021.1
requests 2.28.1
requests-oauthlib 1.3.1
rsa 4.9
setuptools 61.2.0
six 1.16.0
soupsieve 2.2.1
speaklater 1.3
SQLAlchemy 1.4.39
sshtunnel 0.4.0
tenacity 8.0.1
termcolor 1.1.0
testresources 2.0.1
typing-extensions 3.7.4.3
update-checker 0.18.0
urllib3 1.26.11
visitor 0.1.3
websocket-client 0.57.0
Werkzeug 2.0.0
wheel 0.37.1
wincertstore 0.2
wrapt 1.12.1
WTForms 2.3.3
yarl 1.6.3
yfinance 0.1.74
zipp 3.8.1

Create your stock database

First, create a folder named “helpers” and a python script named stocks.py.

## Import the modules
import yfinance as yf
import pandas as pd
import requests as re
import os

Now, we need a database. I chose the the Russel 2000 from Ishares, but you can also choose any different Index or ETF. To have a daily updated database, it is neccessary to pull it daily and update it. We go to the website from Ishares and pull the csv file from them.

url = "https://www.ishares.com/us/products/239710/ishares-russell-2000-etf/1467271812596.ajax?fileType=csv&fileName=IWM_holdings&dataType=fund"
data = re.get(url).content
## Data Cleaning and Prep
df = pd.read_csv('temp.csv', delimiter=',^(\(.*,.*\))', skiprows= 9)
df2 = df[df.columns[0]].str.split('","', expand=True)

df2.columns = df.columns[0].split(",")

df2 = df2.dropna(axis=0, subset = ['Market Value'], how = "any")
df2["Ticker"] = df2["Ticker"].str.replace('"','')
tickers = df2["Ticker"].tolist()

In the end, we have almost 2000 tickers.

We pull the data from yfinance, basically the comments below are copied from yfinance documentation.

data = yf.download( # or pdr.get_data_yahoo(...
# tickers list or string as well
tickers = tickers,

# use "period" instead of start/end
# valid periods: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max
# (optional, default is '1mo')
period = "5y",

# fetch data by interval (including intraday if period < 60 days)
# valid intervals: 1m,2m,5m,15m,30m,
#60m,90m,1h,1d,5d,1wk,1mo,3mo
# (optional, default is '1d')
interval = "1d",

# group by ticker (to access via data['SPY'])
# (optional, default is 'column')
group_by = 'column',

# adjust all OHLC automatically
# (optional, default is False)
auto_adjust = True,

# download pre/post regular market hours data
# (optional, default is False)
prepost = True,

# use threads for mass downloading? (True/False/Integer)
# (optional, default is True)
threads = True,

# proxy URL scheme use use when downloading?
# (optional, default is None)
proxy = None
)

Then we have to prepare the data for our sqlite database:

## Some Data prep. The data is in a wide format, we do not want that. # We have to put it in the long format.
data2 = data.unstack(level=-1)
data2 = data2.reset_index()
data2 = data2.pivot(index = ["level_1","Date"], columns = ["level_0"], values = 0)
data2 = data2.reset_index()
data2 = data2.rename(columns = {"level_1": "Ticker"})
## Save it to a sqlite file and name the tables
import sqlite3 as sq
db = "database"
table_name = "stock_database" # table and file name
conn = sq.connect('{}.sqlite'.format(db)) # creates file
# writes to file
data2.to_sql(table_name, conn, if_exists='replace', index=False)
df2.to_sql("stock_infos",conn, if_exists='replace', index=False)

os.remove("temp.csv")
conn.close()

Plotly Layouts

Next, we have to determine the plotly layouts. In the story “Part 2: Develop your own financial dashboard with Flask and Plotly (Link)”, I explained it already. So, now I do not explain it very detailed.

We just define a script plotly_layouts.py and save the code below in it:

import plotly.express as px
import plotly.graph_objects as go
## we have a function which gets the data
def create_plotly(data):

Ticker = data["Ticker"].unique()[0]

fig0 = go.Figure(data=[go.Candlestick(x=data["Date"],
open=data['Open'],
high=data['High'],
low=data['Low'],
close=data['Close'],
name=Ticker)])
bars1 = go.Bar(
x=data["Date"],
y=data['Volume'],
yaxis="y2",
marker={'color': "black",
'opacity': 0.8},
name="Volume",
)

fig0.add_trace(bars1)

Header = Ticker
candle_stick = True

fig0 = def_fig(fig0, Header, candle_stick)

return fig0

def def_fig(fig, Header, candle_stick = False, color_percentage = "#03d338",xticks_show = True ,show_legend = True,
buttons = True):

if candle_stick == False:

fig.update_layout(
yaxis_tickformat=',.0%',
yaxis=dict(
tickfont=dict(
color=color_percentage
)),
yaxis2=dict(
tickformat=',',
# range= [0, 1],
titlefont=dict(
color="grey"
),
tickfont=dict(
color="grey"
),
anchor="x",
overlaying="y",
side="right"
),
)

else:
fig.update_layout(
yaxis_title='Price',
yaxis_tickprefix='$',
yaxis2=dict(
tickformat=',',
# range= [0, 1],
titlefont=dict(
color="grey"
),
tickfont=dict(
color="grey"
),
anchor="x",
overlaying="y",
side="right"
),
)

fig.update_layout(
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)',
#autosize=True,
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01
),
title={
'text': '{}'.format(Header),
'y': 0.85,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'},
titlefont=dict(
size=12,
color="black"),

template="simple_white",
xaxis=dict(
showticklabels=xticks_show),
showlegend=show_legend,
font=dict(
# family="Courier New, monospace",
size=12,
color="black"
),

)

if buttons == True:
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label="1m", step="month", stepmode="backward"),
dict(count=3, label="3m", step="month", stepmode="backward"),
dict(count=6, label="6m", step="month", stepmode="backward"),
dict(count=1, label="YTD", step="year", stepmode="todate"),
dict(count=1, label="1y", step="year", stepmode="backward"),
dict(count=3, label="3y", step="year", stepmode="backward"),
dict(step="all")
])
)
)

fig.update_xaxes(rangeslider_visible=False)
#fig.update_layout(width=width, height=height)
fig.update_yaxes(automargin=True,
showgrid=True)
fig.update_xaxes(automargin=True,
showgrid=True
)

return fig

## we have a fuunction for the plotly layout
def fig_layout(fig, ytitle, ytickfromat, xtitle,ticker, legendtitle, type_of_plot, yaxis_tickprefix=None):

fig.update_layout(
yaxis={
"title": ytitle,
"tickformat": ytickfromat,

},
yaxis_tickprefix = yaxis_tickprefix,
paper_bgcolor="#FFFFFF", # rgba(0,0,0,0)',
plot_bgcolor="#FFFFFF", # 'rgba(0,0,0,0)',
# autosize=True,
legend=dict(
title=legendtitle,
yanchor="top",
y=0.99,
xanchor="left",
x=0.01
),
title={
'text': '{} - {} <br><sup>tenxassets.com</sup>'.format(type_of_plot,ticker),
'y': 0.85,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'},
titlefont=dict(
size=12,
color="black"),

template="simple_white",
xaxis=dict(
title=xtitle,
showticklabels=True),
showlegend=True,
font=dict(
# family="Courier New, monospace",
size=12,
color="black"
),
)
return fig

## This is how we can test our script
if __name__ == '__main__':
import sqlite3 as sq
import pandas as pd
table_name = "stock_database" # table and file name
conn = sq.connect('{}.sqlite'.format("database"))
df = pd.read_sql('select * from {}'.format(table_name), conn)

data = df[df["Ticker"]=="AAN"]

fig = create_plotly(data)
fig.show()
conn.close()

A window should pop up and show the candle stick diagram.

Modify your run.py

Add following imports:

import json
import sqlite3 as sq
import pandas as pd
import plotly

import helpers.plotly_layouts as plt

We also want to load our databases:

## Database for stocks

conn = sq.connect('helpers/{}.sqlite'.format("database"),check_same_thread=False)
df = pd.read_sql('select * from {}'.format("stock_database"), conn)
stock_infos = pd.read_sql("select * from {}".format("stock_infos"), conn)

Add a new route. This is the dynamic endpoint for our stock tickers. The <ticker> will be replaced by the ticker name.

@app.route("/stocks/<ticker>", methods=['POST','GET'])

We have to also filter our data by that ticker.

@app.route("/stocks/<ticker>", methods=['POST','GET'])
@auth_required()
def stocks(ticker):
## Dropdown
df_tickers = df["Ticker"].unique()

if ticker is None or ticker not in df_tickers:
ticker = "AAN"
## filter our data by that ticker
data = df[df["Ticker"]==ticker]
## We create the plotly diagram plot = plt.create_plotly(data)

## Stock info
ticker_info = stock_infos[stock_infos["Ticker"]==ticker]
ticker_info = ticker_info.transpose()
ticker_info.columns = ["Ticker Information"]
df1 = ticker_info.iloc[:round(len(ticker_info)/2)-1, :]
df2 = ticker_info.iloc[(round(len(ticker_info)/2)):, :]

df1 = df1.to_html()
df2 = df2.to_html()
plotly_plot = json.dumps(plot, cls=plotly.utils.PlotlyJSONEncoder)

return render_template("stocks.html", plotly_plot= plotly_plot, ticker = ticker,df_tickers = df_tickers, ticker_info = [df1,df2])

We also add two other endpoints:

## This endpoint is the landing page to choose a stock
@app.route("/stocks")
@auth_required()
def stocks_redirect():
df_tickers = df["Ticker"].unique()
return render_template("stocks.html", df_tickers= df_tickers)

## An error handler, if a stock ticker is not valid
@app.errorhandler(404)
def not_found_error(error):
return render_template('404.html'), 404

Create the jinja layouts for the html output

For our stock tickers, create stocks.html. In our header we have to include a dynamic endpoint for our stylsheets, otherwise the css will get lost. We can do it with url_for:

<link rel="stylesheet" href=url_for('/static/css/normalize.css', ticker = {{ticker}})/>

We also define a formwhere we can choose a ticker from:

Jinja is very practical in this way, an we can define a function to use pass out tickers as a list in there

{% for i in df_tickers %}
<option value=”{{i}}”>{{i}}</option>
{% endfor %}

<form method="get"></form>
<label for="stocks">Choose a stock ticker:</label>
<select id="stocks" name="stocks">
{%for i in df_tickers%}
<option value="{{i}}">{{i}}</option>
{% endfor %}
<input type="button" value="Submit" onclick="stock_choose()">
</form>

When we confirm our selection, we submit the ticker to a javascript function and the route in the run.py.

<input type="button" value="Submit" onclick="stock_choose()">

The javascript will fetch this and replace the url:

<script>
function stock_choose() {
var e = document.getElementById("stocks");
var list_stocks = e.options[e.selectedIndex].value;
var theUrl = "/".concat("stocks/"+list_stocks);
window.location.replace(theUrl);
}
</script>

This is the whole jinja layout:

{% extends "layouts/main.html" %}

{% block head %}

<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="//code.jquery.com/jquery-1.12.4.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<link rel="stylesheet" href=url_for('/static/css/normalize.css', ticker = {{ticker}})/>
<link rel="stylesheet" href=url_for('/static/css/skeleton.css', ticker = {{ticker}})/>
<link rel="stylesheet" href=url_for('/static/css/style.css', ticker = {{ticker}})/>
{% endblock %}

{% block content%}


</body>

<form method="get"></form>
<label for="stocks">Choose a stock ticker:</label>
<select id="stocks" name="stocks">
{%for i in df_tickers%}
<option value="{{i}}">{{i}}</option>
{% endfor %}
<input type="button" value="Submit" onclick="stock_choose()">
</form>



<div>
<div id="plotly-stock-candle"></div>
<script>
//Parse your Json variable here
var graphs = {{ plotly_plot | safe }};
Plotly.plot('plotly-stock-candle', graphs, {});
</script>
</div>

<div class="row">
<div class="column">
{{ ticker_info[0]|safe }}
</div>
<div class="column">
{{ ticker_info[1]|safe }}
</div>
</div>

</body>

<script>
function stock_choose() {
var e = document.getElementById("stocks");
var list_stocks = e.options[e.selectedIndex].value;
var theUrl = "/".concat("stocks/"+list_stocks);
window.location.replace(theUrl);
}
</script>

{% endblock %}

E voilà, you are done! Just modify this example to your needs and upload it on pythonanywhere.com*. Here is the repo: https://github.com/AntonioBlago/Webpage_Tutorial_4.

Thanks for reading my story. I hope you liked it. Please feel free to clap, share, and comment on it. Follow me on more content about cryptos, stocks, data analysis and web development. I am hosting my apps on pythonanywhere.com*.

  • Read my other article about creating a financial dashboard in flask

Disclaimer:

*this are affliate links