python股市_如何使用python和破折号创建仪表板来主导股市

python股市

始终关注大局 (Keep Your Eyes on the Big Picture)

I’ve been fascinated with the stock market since I was a little kid. There is certainly no shortage of data to analyze, and if you find an edge you can make some easy money. To stay on top of the market, I designed a dashboard that incorporates interesting option market order flow, price charts, chatter and fundamentals. Having it all in one place makes it easy to monitor market sentiment and find potential plays! In this article, I’ll describe the components of the dashboard and explain how I created it using Python and Plotly’s Dash.

从小我就着迷于股票市场。 当然,不乏要分析的数据,如果您找到优势,可以轻松赚钱。 为了保持在市场的领先地位,我设计了一个仪表板,其中包含有趣的期权市场订单流程,价格图表,震荡和基本面。 将所有功能集中在一处,可以轻松监控市场情绪并找到潜在的机会! 在本文中,我将描述仪表板的组件,并说明如何使用Python和Plotly's Dash创建仪表板。

内容: (Contents:)

Reviewing the DashboardSourcing the DataReviewing Dash Framework Designing the File StructureCreating the Function FilesAdding CallbacksFinal Thoughts and Complete Code

查看仪表板采购数据查看仪表板框架设计文件结构创建功能文件添加回调最终想法和完整代码

The full code and GitHub link are toward the bottom of the page if you’re already familiar with Dash.

如果您已经熟悉Dash,则完整的代码和GitHub链接位于页面底部。

If you’re completely new to Dash, I recommend starting here:

如果您完全不熟悉Dash,建议从这里开始

查看仪表板 (Reviewing the Dashboard)

The dashboard is designed using Dash Bootstrap CSS and is fairly responsive by default! On startup, the dashboard will load data from Twitter and Reddit. These data feeds are used to monitor interesting option flow and market chatter/sentiment.

仪表板是使用Dash Bootstrap CSS设计的,默认情况下响应速度非常快 ! 启动时,仪表板将从Twitter和Reddit加载数据。 这些数据馈送用于监视有趣的期权流和市场波动/情绪。

Image for post
The dashboard (zoomed out for a smaller image)
仪表板(缩小以显示较小的图像)

Beyond the data sources, the dashboard takes 3 initial inputs from a user:

除了数据源之外,仪表板还接受了来自用户的3个初始输入:

Stock ticker, Start Date, End date

股票行情,开始日期,结束日期

Image for post
Input fields
输入栏位

The Start Date and End Date are pre-populated with the maximum date range. When a ticker is entered, the dashboard pulls data from Yahoo! Finance and Market Watch to produce information about the company’s financials and price history. The price history data from Yahoo! Finance is used to produce three charts:

开始日期”和“ 结束日期”已预先填充了最大日期范围 。 输入代码后 ,仪表板将从Yahoo!提取数据。 Finance and Market Watch产生有关公司财务和价格历史的信息。 来自Yahoo!的价格历史数据 财务用于生成三个图表:

3 Year daily chart, 5 day 15 min chart, 1 day 1 minute chart

3年日图,5天15分钟图,1天1分钟图

Image for post
1 Minute chart
1分钟图

The 3 Year daily Chart can be adjusted by the Start Date and End Date fields giving a little bit more flexibility with the price data.

可以通过“ 开始日期”和“ 结束日期”字段来调整3年日图表,从而使价格数据更具灵活性。

采购数据 (Sourcing the Data)

The dashboard pulls data from multiple sources. Most of the data is scraped from the web on the fly, but the Twitter data is also stored and read from an SQLite database to make refreshing and filtering much more performant.

仪表板从多个来源提取数据。 大部分数据都是从网络上即时获取的,但Twitter数据也可以存储和从SQLite数据库读取,以提高刷新和过滤性能。

Image for post
Data sources feeding the Dash App
数据源提供给Dash App

a ! 金融 (Yahoo! Finance)

Although Yahoo! Finance decommissioned their API a while back, the python library yfinance offers a reliable, threaded, and Pythonic way to download historical market data for free!

虽然雅虎! 不久前 ,Finance停用了他们的API,python库yfinance提供了一种可靠的,线程化的和Python的方式来免费下载历史市场数据!

pip install yfinance

I use Yahoo! Finance to pull price history and company information like the beta and sector.

我使用Yahoo! 财务可以获取价格历史记录和Beta和行业等公司信息。

市场观察 (Market Watch)

Market Watch is a market and business news information website.

Market Watch是一个市场和商业新闻信息网站。

Image for post
Marketwatch financials tabMarketwatch的“财务”标签

Scraping Market Watch, it is easy to pull financial information about the company going back 5 years. The data is scraped from the website using the Beautiful Soup library.

刮擦市场观察,很容易提取有关公司5年的财务信息。 使用Beautiful Soup库从网站上抓取数据。

pip install beautifulsoup4

It helps if you are somewhat familiar with the html before getting into web scraping. The basics of web scraping are beyond the scope of this article.

如果您在进入网络抓取之前对html有点熟悉,那么它会有所帮助。 Web抓取的基本知识超出了本文的范围。

推特 (Twitter)

For this tutorial, you will need to register an app with Twitter to get API Keys. I use the Python library Tweepy to interact with Twitter. Connecting Tweepy to Twitter uses OAuth1. Check out this tutorial for getting started with Tweepy and the Twitter API if needed!

对于本教程, 您将需要在Twitter上注册一个应用程序以获取API密钥 。 我使用Python库Tweepy与Twitter进行交互。 将Tweepy连接到Twitter使用OAuth1 。 如果需要,请查看本教程以开始使用Tweepy和Twitter API !

pip install tweepy

Twitter is used to download free options order flow data. Two users post free order flow data: SwaggyStocks and Unusual_whales. The premise behind watching option order flow is that big orders in the options market can indicate momentum in the underlying asset. Some people believe following big orders is following smart money. Just remember that even smart money can be wrong!

Twitter用于下载免费期权订单流数据 。 两个用户发布了免费订单流数据: SwaggyStocks和Unusual_whales 。 观察期权订单流的前提是,期权市场中的大订单可以表明基础资产的动量 。 有些人认为跟随大订单正在追随精明的钱。 只要记住,即使是聪明的钱也可能是错误的!

Reddit (Reddit)

I am using PRAW to connect to the Reddit API. A user account to Reddit is required to use the API. It is completely free and only requires an email address! Read this tutorial if you’re completely new to using Reddit and the Reddit API.

我正在使用PRAW连接到Reddit API。 使用该API需要Reddit用户帐户。 它是完全免费的,只需要一个电子邮件地址! 如果您不熟悉Reddit和Reddit API的使用,请阅读本教程 。

pip install praw

Reddit is used to scrape new posts from the subreddit WallStreet Bets. It is a large community in which traders place high risk/high reward trades. It is useful for gauging market chatter and sentiment.

Reddit用来从subreddit WallStreet Bets中抓取新帖子。 这是一个大型社区,交易员在其中进行高风险/高奖励交易。 它对于衡量市场的chat不休和情绪很有用。

Dash框架刷新器 (Dash Framework Refresher)

Dash is a framework for Python written on top of Flask, Plotly.js, and React.js. Dash and Plotly are vast and powerful tools, and this is only a taste of what they can do! Check out the Dash community or Learn Python Dashboards for more examples and tutorials!

Dash是在Flask,Plotly.js和React.js之上编写的Python框架 。 Dash和Plotly是强大的工具,这只是他们可以做的事! 查看Dash社区或“ 学习Python仪表板”以获取更多示例和教程!

pip install dash

Dash apps are composed of a Layout and Callbacks:

Dash应用程序由LayoutCallbacks组成

布局 (Layout)

The layout is made up of a tree of components that describe what the application looks like and how users experience the content.

布局由描述应用程序外观以及用户如何体验内容的组件树组成。

回呼 (Callbacks)

Callbacks make the Dash apps interactive. Callbacks are Python functions that are automatically called whenever an input property changes.

回调使Dash应用程序具有交互性。 回调是Python函数,只要输入属性发生更改,就会自动调用它们。

短跑引导组件 (Dash Bootstrap Components)

To make it easier to design the app layout, I’m using Dash Bootstrap Components. Similar to how the dash-html-components library allows you to apply HTML using Python, the dash-bootstrap-components library allows you to apply Bootstraps front-end components that are affected by the Bootstrap CSS framework.

为了使设计应用程序布局更容易,我使用了Dash Bootstrap组件。 与dash-html-components库允许您使用Python应用HTML相似, dash-bootstrap-components库允许您应用受Bootstrap CSS框架影响的Bootstraps前端组件。

pip install dash-bootstrap-components

The responsive grid system in bootstrap CSS and the convenient container wrappers allow for a lot of customization. Bootstrap’s grid system uses a series of containers, rows, and 12 columns in which one can lay out and align content. Those have been included as components in dash-bootstrap-components library as Container, Row, and Col.

引导CSS中的响应式网格系统和方便的容器包装器允许进行大量自定义 。 Bootstrap的网格系统使用一系列容器,行和12列,在其中可以布置和对齐内容。 这些已作为dash-bootstrap-components中的组件包括在内 图书馆为 ContainerRow ,和Col

文件结构 (The File Structure)

Although this seems like a lot of files, it isn’t really that much code! It is completely fine to have all the code in 1 file for this dashboard. I personally like short files, so I put the functions into their own file and call index.py to run the app.

尽管这看起来像很多文件,但实际上并不是那么多代码! 将该仪表板的所有代码都放在1个文件中完全可以。 我个人比较喜欢短文件,因此我将这些函数放入自己的文件中,然后调用index.py来运行该应用程序。

Image for post
File structure
档案结构

文件config.py和管理API密钥 (File config.py & Managing API Keys)

This project uses API keys to get data from Reddit and Twitter. If you’re new to managing your keys, make sure to save them into a config.py file instead of hard-coding them in your app. API keys can be very valuable and must be protected. Add the config file to your gitignore file to prevent it from being pushed to your repo too!

该项目使用API​​密钥从Reddit和Twitter获取数据 。 如果您不熟悉密钥管理,请确保将其保存到config.py文件中,而不是在应用程序中对其进行硬编码。 API密钥可能非常有价值,必须加以保护 。 将配置文件添加到您的gitignore文件中,以防止其也被推送到您的仓库中

文件stocks.sqlite (File stocks.sqlite)

The file stocks.sqlite is the database file used to store the tweet data. That makes it easier to stream into the app as new tweets are generated. The refresh occurs automatically thanks to Dash’s Interval component!

文件stocks.sqlite是用于存储推文数据的数据库文件。 这样可以在生成新的推文时更轻松地将其流式传输到应用程序。 由于Dash的Interval组件,刷新自动发生!

创建文件 (Creating the Files)

Finally on to the code! I’ll start with the simplest functions that pull the data and then end with creating the actual app file, index.py. I do my best to include comments in the code, as well as describe the overall functions.

终于到了代码! 我将从提取数据的最简单函数开始,然后以创建实际的应用程序文件index.py结尾 。 我会尽力在代码中包含注释,并描述整体功能。

导入依赖 (Import dependencies)

Start by importing the required libraries

首先导入所需的库

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_table
from dash.exceptions import PreventUpdateimport flask
from flask import Flask
import pandas as pd
import dateutil.relativedelta
from datetime import date
import datetime
import yfinance as yf
import numpy as np
import praw
import sqlite3import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots

创建dash_utils.py (Create dash_utils.py)

The file dash_utils.py contains helper functions for the dash components used in the app’s layout. Anyone who has used dash before knows the layout can get pretty long pretty fast, especially if using a lot of data tables like this dashboard! To reduce the repetitiveness of adding components and bloating the layout, I created functions that I can reuse to add components to the dash app:

文件dash_utils.py包含用于应用程序布局中的破折号组件的辅助函数。 以前使用破折号的任何人都知道布局可以很快变长,特别是在使用大量数据表(例如仪表板)的情况下! 为了减少添加组件和使布局过大的重复性,我创建了一些函数,可以重复使用这些函数以将组件添加到dash应用程序:

  • ticker_inputs

    ticker_inputs
  • make_table

    make_table
  • make_card

    make_card
  • make_item

    make_item

ticker_inputs (ticker_inputs)

The function ticker_inputs is used to return the components that allow the user to enter the stock ticker and select a Start Date and End Date.

函数ticker_inputs用于返回允许用户输入股票报价器并选择开始日期和结束日期的组件。

def ticker_inputs(inputID, pickerID, MONTH_CUTTOFF):#calculate the current date
currentDate = date.today() #calculate past date for the max allowed date
pastDate = currentDate - dateutil.relativedelta.relativedelta(months=MONTH_CUTTOFF)#return the layout components
return html.Div([
dcc.Input(id = inputID, type="text", placeholder="MSFT")
, html.P(" ")
, dcc.DatePickerRange(
id = pickerID,
min_date_allowed=pastDate,
start_date = pastDate,
#end_date = currentDate
)])

Notice the function takes inputID and pickerID as arguments to use as the component ID. Component ID’s must be unique and are used by the callbacks.

注意,该函数将inputIDpickerID作为参数用作组件ID 。 组件ID必须是唯一的,并由回调使用 。

make_card (make_card)

To make things look nice, I like using Cards! The function make_card is used to combine Dash Bootstrap components Card and Alert. Bootstrap’s cards provide a flexible content container and allow a fair amount of customization. Alert is used to easily add color and more style if desired.

为了让事情看起来更好,我喜欢使用Cards! 函数make_card用于组合Dash Bootstrap组件Card和Alert 。 引导卡提供了灵活的内容容器,并允许进行大量的自定义。 如果需要,可以使用Alert轻松添加颜色和更多样式。

def make_card(alert_message, color, cardbody, style_dict = None):
return dbc.Card([ dbc.Alert(alert_message, color=color)
,dbc.CardBody(cardbody)
], style = style_dict)

Notice the function takes in an alert message for the header, a color, a card body, and a style dictionary.

注意,该函数接收标题, 颜色卡片正文和样式字典的警报消息

make_item (make_item)

The function make_item is used to build the price chart Accordion.

函数make_item用于构建手风琴价格图表。

Image for post
Price chart Accordion
价格表手风琴
def make_item(button, cardbody, i):# This function makes the accordion items 
return dbc.Card([
dbc.CardHeader(
html.H2(
dbc.Button(
button,
color="link",
id=f"group-{i}-toggle"))),
dbc.Collapse(
dbc.CardBody(cardbody),
id=f"collapse-{i}")])

Notice the function takes a button for the button name, cardbody for card body, and i for the Dash Bootstrap Collapse component ID. The component ID’s must be unique!

注意,该函数使用一个按钮来表示按钮名称,使用cardbody表示卡体 ,使用i表示Dash Bootstrap折叠组件ID。 组件ID必须是唯一的!

make_table (make_table)

The last of the dash_util functions is make_table. Since Dash DataTable’s have so many parameters, tweaking them all individually can be tedious. Therefore, I made a function!

dash_util函数的最后一个是make_table 。 由于Dash DataTable的参数太多,因此分别调整它们可能很乏味。 因此,我做了一个功能!

def make_table(id, dataframe, lineHeight = '17px', page_size = 5):
return dash_table.DataTable(
id=id,
css=[{'selector': '.row', 'rule': 'margin: 0'}],
columns=[
{"name": i, "id": i} for i in dataframe.columns
],
style_header={
'backgroundColor': 'rgb(230, 230, 230)',
'fontWeight': 'bold'},
style_cell={'textAlign': 'left'},
style_data={
'whiteSpace': 'normal',
'height': 'auto',
'lineHeight': lineHeight
},
style_data_conditional=[
{
'if': {'row_index': 'odd'},
'backgroundColor': 'rgb(248, 248, 248)'
}
],
style_cell_conditional=[
{'if': {'column_id': 'title'},
'width': '130px'},
{'if': {'column_id': 'post'},
'width': '500px'},
{'if': {'column_id': 'datetime'},
'width': '130px'},
{'if': {'column_id': 'text'},
'width': '500px'}],
page_current=0,
page_size=page_size,
page_action='custom',filter_action='custom',
filter_query='',sort_action='custom',
sort_mode='multi',
sort_by=[]
)#end table

The only arguments needed for the function are id for a unique Component ID, and df for a Pandas DataFrame. Review this tutorial for details on all the Dash Data_table parameters.

该函数所需的唯一参数是唯一组件ID的id和熊猫数据框的df 。 查看本教程以获取有关所有Dash Data_table参数的详细信息 。

创建fin_report_data.py (Create fin_report_data.py)

This file contains the get_financial_report function used to scrape Market Watch and return financial data like EPS, EPS Growth, Net Income, and EBITDA. This function is a little complicated so I’ll explain it in chunks.

该文件包含get_financial_report函数,该函数用于抓取Market Watch并返回财务数据,例如EPS,EPS增长,净收入和EBITDA。 这个函数有点复杂,所以我将分块解释它。

The function get_financial_report takes in the stock ticker and it builds two URLs to scrape:

函数get_financial_report接受股票行情自动收录器,并构建两个要抓取的URL:

  • Market Watch’s /financials

    市场观察的/金融
  • Market Watch’s /financials/balance-sheet

    市场观察的/金融/资产负债表
def get_financial_report(ticker):#build URLs
urlfinancials = 'https://www.marketwatch.com/investing/stock/'+ticker+'/financials'
urlbalancesheet = 'https://www.marketwatch.com/investing/stock/'+ticker+'/financials/balance-sheet'#request the data using beautiful soup
text_soup_financials = BeautifulSoup(requests.get(urlfinancials).text,"html")
text_soup_balancesheet = BeautifulSoup(requests.get(urlbalancesheet).text,"html")

Now that the web data is scraped, I want to find all the row titles. If the row title matches the value we want to use in the dashboard, I’ll save it to a list.

现在已经抓取了Web数据,我想查找所有行标题。 如果行标题与我们要在仪表板中使用的值匹配,我将其保存到列表中。

Image for post
Example of rowTitle on Market Watch
市场观察中rowTitle的示例
# build lists for Income statement    titlesfinancials = text_soup_financials.findAll('td', {'class': 'rowTitle'})
epslist=[]
netincomelist = []
longtermdebtlist = []
interestexpenselist = []
ebitdalist= []#load data into lists if the row title is found
for title in titlesfinancials:
if 'EPS (Basic)' in title.text:
epslist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'Net Income' in title.text:
netincomelist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'Interest Expense' in title.text:
interestexpenselist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'EBITDA' in title.text:
ebitdalist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])# find the table headers for the Balance sheet titlesbalancesheet = text_soup_balancesheet.findAll('td', {'class': 'rowTitle'})
equitylist=[]
for title in titlesbalancesheet:
if 'Total Shareholders\' Equity' in title.text:
equitylist.append( [td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'Long-Term Debt' in title.text:
longtermdebtlist.append( [td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])

Notice findNextSiblings (beautifulsoup) is used to return the siblings of the Tag that match the given criteria and appear after this Tag in the document. To clarify, since the left most item (rowTitle) is what I use to match, findNextSiblings gets me to the value I want to save in the list.

注意findNextSiblings (beautifulsoup)用于返回与给定条件匹配并在文档中此Tag之后出现的Tag的同级。 需要说明的是,由于最左边的项(rowTitle)是我用来匹配的项,因此findNextSiblings将我带到要保存在列表中的值。

Once the values are scraped, the helper function get_element is used to load the data.

刮取值后,将使用辅助函数get_element加载数据。

#get the data from the income statement lists 
#use helper function get_element

eps = get_element(epslist,0)
epsGrowth = get_element(epslist,1)
netIncome = get_element(netincomelist,0)
shareholderEquity = get_element(equitylist,0)
roa = get_element(equitylist,1)longtermDebt = get_element(longtermdebtlist,0)
interestExpense = get_element(interestexpenselist,0)
ebitda = get_element(ebitdalist,0)

Once the values have been saved to the lists, transform them into a pandas DataFrame. Reset the dataframe index and return the dataframe!

将值保存到列表后,将其转换为pandas DataFrame。 重置数据帧索引并返回数据帧!

# load all the data into dataframe 
fin_df= pd.DataFrame({'eps': eps,'eps Growth': epsGrowth,'net Income': netIncome,'shareholder Equity': shareholderEquity,'roa':
roa,'longterm Debt': longtermDebt,'interest Expense': interestExpense,'ebitda': ebitda},index=range(date.today().year-5,date.today().year))
fin_df.reset_index(inplace=True)return fin_df#helper functiondef get_element(list,element):
try:
return list[element]
except:
return '-'

Notice the helper function get_element is used to return an “ — “ if it cannot find an item in the list of scraped data.

请注意,如果辅助函数get_element不能在已抓取的数据列表中找到某项,则该函数将返回“ —”。

创建reddit_data.py (Create reddit_data.py)

The file reddit_data.py contains the functions to interact with the Reddit API through Praw. Import the dependencies and the API key from the config.py file, then use a function to transform the data into a data frame.

文件reddit_data.py包含用于通过Praw与Reddit API进行交互的函数。 从config.py文件中导入依赖项和API密钥,然后使用函数将数据转换为数据框。

import pandas as pd
import praw
from config import r_cid, r_csec, r_uagdef get_reddit(cid= r_cid, csec= r_csec, uag= r_uag, subreddit='wallstreetbets'): #connect to reddit reddit = praw.Reddit(client_id= cid, client_secret= csec, user_agent= uag)#get the new reddit posts posts = reddit.subreddit(subreddit).new(limit=None)#load the posts into a pandas dataframe p = []
for post in posts:
p.append([post.title, post.score, post.selftext])posts_df = pd.DataFrame(p,columns=['title', 'score', 'post'])return posts_df

Notice I define the function get_reddit(). It takes in the API credentials and the subreddit name (wallstreetbets by default).Notice within the function the data gets saved from Reddit to the variable posts. The data is then unpacked, and appended in list variable p, and then saved as pandas DataFrame object named posts_df.

注意,我定义了函数get_reddit() 。 它接受API凭据和subreddit名称(默认情况下为wallstreetbets)。请注意,函数中的数据已从Reddit保存到变量posts 。 然后将数据解压缩,并附加到列表变量p中,然后另存为名为posts_df的 pandas DataFrame对象。

创建twitter_data.py (Create twitter_data.py)

The file tweet_data.py contains the functions to interact with the twitter API through Tweepy. Pulling historical tweets from users is a little complex since we want to save the results to SQLite. To make it less complex, I split it into two functions. The first is responsible for getting the twitter data. The second is responsible for cleaning and saving it:

文件tweet_data.py包含通过Tweepy与Twitter API交互的函数。 从用户提取历史推文有点复杂,因为我们希望将结果保存到SQLite。 为了使其不那么复杂,我将其分为两个功能。 第一个负责获取Twitter数据。 第二个负责清理和保存它:

  • get_all_tweets

    get_all_tweets
  • get_options_flow

    get_options_flow

The function get_all_tweets pulls as many historical tweets as possible from the user, up to around 3200 max.

函数get_all_tweets从用户拉取尽可能多的历史推文,最大到3200个左右。

def get_all_tweets(screen_name
,consumer_key = t_conkey
, consumer_secret= t_consec
, access_key= t_akey
, access_secret= t_asec
): #Twitter only allows access to a users most recent 3240 tweets with this method #authorize twitter, initialize tweepy
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_key, access_secret)
api = tweepy.API(auth) #initialize a list to hold all the tweepy Tweets
alltweets = [] #make initial request for most recent tweets (200 is the maximum allowed count)
new_tweets = api.user_timeline(screen_name = screen_name,count=200)#save most recent tweets
alltweets.extend(new_tweets)#save the id of the oldest tweet less one
oldest = alltweets[-1].id - 1#keep grabbing tweets until there are no tweets left to grab
while len(new_tweets) > 0:#all subsequent requests use the max_id param to prevent duplicates
new_tweets = api.user_timeline(screen_name = screen_name,count=200,max_id=oldest)#save most recent tweets
alltweets.extend(new_tweets)#update the id of the oldest tweet less one
oldest = alltweets[-1].id - 1
outtweets = [[tweet.id_str, tweet.created_at, tweet.text] for tweet in alltweets]
tweets_df = pd.DataFrame(outtweets, columns = ['time', 'datetime', 'text'])return tweets_df

Notice the function takes the twitter API credentials and the username of the twitter account I want to track. The function cycles through the historical tweets, adding them to a list. The list is then transformed into a pandas DataFrame objected named tweets_df.

请注意,该函数采用了Twitter API凭据和我要跟踪的Twitter帐户的用户名。 该功能循环浏览历史推文,并将它们添加到列表中。 然后将该列表转换为名为tweets_df的熊猫DataFrame 对象

The function get_options_flow takes no arguments. It calls the get_all_tweets function, cleans the tweet data and saves it to the SQLite database so it can be called into the app frequently and automatically without impacting performance.

函数get_options_flow不带参数。 它调用get_all_tweets函数,清除tweet数据并将其保存到SQLite数据库,以便可以在不影响性能的情况下自动频繁地将其调用到应用程序中。

def get_options_flow():#connect to the sqlite database
conn = sqlite3.connect('stocks.sqlite')#use get_all_tweets to pull the data from the twitter users
ss = get_all_tweets(screen_name ="SwaggyStocks")
uw = get_all_tweets(screen_name ="unusual_whales")#clean the text data
ss['source'] = 'swaggyStocks'
ss['text'] = hero.remove_urls(ss['text'])
ss['text'] = [n.replace('$','') for n in ss['text']] #clean the text data
uw['source'] = 'unusual_whales'
uw['text'] = hero.remove_urls(uw['text'])
uw['text'] = [n.replace('$','') for n in uw['text']]
uw['text'] = [n.replace(':','') for n in uw['text']]
uw['text'] = [n.replace('\n',' ') for n in uw['text']]
uw['text'] = [n.replace(' ',' ') for n in uw['text']] #concat the tweets into one dataframe tweets = pd.concat([ss, uw])#save the tweets to sqlite database
tweets.to_sql('tweets', conn, if_exists = 'replace')return print('done')

Notice the function uses regex and Texthero to clean the tweet text of special characters and URLs. Notice the two dataframes ss and uw of tweets are concatenated together and saved as one table in stocks.sqlite.

注意,该函数使用正则表达式和Texthero清除特殊字符和URL的tweet文本。 请注意,推文的两个数据帧ssuw串联在一起,并保存为stocks.sqlite中的一个表。

创建index.py (Create index.py)

The index.py file consists of the code to instantiate the Dash app. It contains the layout and callbacks used to make the app interactive. This is the file I execute in the terminal using a command like $ python index.py

index.py文件包含用于实例化Dash应用程序的代码。 它包含用于使应用程序具有交互性的布局和回调。 这是我在终端中使用$ python index.py之类的命令执行的文件

It is finally time to put it all together and construct the Dash App! Assuming the dependencies have been imported, start by instantiating the Dash App and calling the data functions to load the Twitter and Reddit data.

终于是时候将所有内容放在一起并构建Dash App了! 假设已导入依赖项,请先实例化Dash App并调用数据函数以加载Twitter和Reddit数据。

#Connect to sqlite database
conn = sqlite3.connect('stocks.sqlite')#instantiate dash app server using flask for easier hosting
server = Flask(__name__)
app = dash.Dash(__name__,server = server ,meta_tags=[{ "content": "width=device-width"}], external_stylesheets=[dbc.themes.BOOTSTRAP])#used for dynamic callbacks
app.config.suppress_callback_exceptions = True#get options flow from twitter
get_options_flow()
flow = pd.read_sql("select datetime, text from tweets order by datetime desc", conn)#get reddit data
global dfr
dfr = get_reddit()

After instantiating the server and loading the data, create the layout. The layout I went with is fairly simple. The layout components are wrapped around each other to achieve the desired layout look. I use an html.Div component to wrap the bootstrap grid components dbc.Row and dbc.Col. I construct a layout organizing Rows and Columns within one another like so:

实例化服务器并加载数据后,创建布局。 我使用的布局非常简单。 布局组件彼此缠绕以实现所需的布局外观。 我使用html.Div组件包装引导网格组件dbc.Rowdbc.Col 。 我构建了一个布局,将行和列相互组织起来,如下所示:

Image for post
Showing how the layout components are wrapped within each other to produce the desired layout.
显示布局组件如何相互包裹以产生所需的布局。
layout1 = html.Div([
dbc.Row([dbc.Col(make_card("Enter Ticker", "success", ticker_inputs('ticker-input', 'date-picker', 36)))]) #row 1
,dbc.Row([dbc.Col([make_card("Twitter Order Flow", 'primary', make_table('table-sorting-filtering2', flow, '17px', 10))])
,dbc.Col([make_card("Fin table ", "secondary", html.Div(id="fin-table"))])
])
, dbc.Row([make_card("select ticker", "warning", "select ticker")],id = 'cards') #row 2
, dbc.Row([
dbc.Col([
dbc.Row([make_card("Wallstreet Bets New Posts", 'primary'
,[html.P(html.Button('Refresh', id='refresh'))
, make_table('table-sorting-filtering', dfr, '17px', 4)])], justify = 'center')
])
,dbc.Col([dbc.Row([dbc.Alert("_Charts_", color="primary")], justify = 'center')
,dbc.Row(html.Div(id='x-vol-1'), justify = 'center')
, dcc.Interval(
id='interval-component',
interval=1*150000, # in milliseconds
n_intervals=0)
, dcc.Interval(
id='interval-component2',
interval=1*60000, # in milliseconds
n_intervals=0)
,dbc.Row([html.Div(id='tweets')])
])#end col
])#end row
]) #end divapp.layout= layout1

Notice two things: Bold functions and Interval components.

注意两件事: 粗体函数Interval组件

I set all of the dash_util functions to bold so it is easier to see how they are strung together to produce cards with tables inside. For example, look at the Twitter Order Flow card:

我将所有dash_util函数设置为粗体,因此更容易看到它们如何串在一起以产生内部带有表的卡。 例如,查看Twitter订单流程卡:

make_card("Twitter Order Flow", 'primary', make_table('table-sorting-filtering2', flow, '17px', 10))
Image for post
Table inside the Card
卡内表

Notice I pass function make_table as cardbody in function make_card(title, color, cardbody, style_dict). That is how the tables appear inside the card in the layout!

注意,我在函数make_card( title,color,cardbody,style_dict ) 中将函数make_table作为卡片体传递。 这就是表格在布局中卡内的显示方式!

Dash core component dcc.Interval is used to automatically refresh the Twitter feed every minute or so. That is why there is no Refresh button like the Reddit data.

Dash核心组件dcc.Interval用于每分钟左右自动刷新Twitter feed。 这就是为什么没有像Reddit数据这样的Refresh按钮的原因。

添加回调 (Adding Callbacks)

Now that the layout is complete, I’ll make the app functional by adding the callbacks. There are seven callbacks. I’ll break them down as follows:

现在布局已经完成,我将通过添加回调使应用程序正常运行。 有七个回调。 我将它们分解如下:

Refreshing Twitter DataLoading the Company Info Cards Sorting and Filtering Reddit and Twitter tablesPopulating the Financial ReportPopulating the Charts

刷新Twitter数据加载公司信息卡排序和过滤Reddit和Twitter表格填充财务报告填充图表

刷新Twitter数据 (Refreshing Twitter Data)

This callback uses the interval component to automatically execute the get_options_flow function every minute.

此回调使用间隔组件每分钟自动执行get_options_flow函数。

@app.callback(
Output('tweets', 'children'),
[Input('interval-component2', 'n_intervals'),
])
def new_tweets(n):
get_options_flow()
return html.P(f"Reloaded Tweets {n}")

加载公司信息卡 (Loading the Company Info Cards)

This callback passes the input Ticker to yfinance and returns bootstrap component Cards populated with company and historic price data. The make_card function is bold to make it easier to see.

此回调将输入的股票行情传递给yfinance,并返回包含公司和历史价格数据的引导程序组件卡。 make_card函数为粗体,以便于查看。

@app.callback(Output('cards', 'children'),
[Input('ticker-input', 'value')])
def refresh_cards(ticker):
ticker = ticker.upper()
if ticker is None:
TICKER = 'MSFT'
else:
TICKER = yf.Ticker(ticker)
cards = [ dbc.Col(make_card("Previous Close ", "secondary", TICKER.info['previousClose']))
, dbc.Col(make_card("Open", "secondary", TICKER.info['open']))
, dbc.Col(make_card("Sector", 'secondary', TICKER.info['sector']))
, dbc.Col(make_card("Beta", 'secondary', TICKER.info['beta']))
, dbc.Col(make_card("50d Avg Price", 'secondary', TICKER.info['fiftyDayAverage']))
, dbc.Col(make_card("Avg 10d Vol", 'secondary', TICKER.info['averageVolume10days']))
] #end cards list
return cards

Notice I’m setting the ticker to upper case using .upper() for best results using yfinance and Market Watch.

请注意,我使用将股票行情设置为大写。 上()使用yfinance和市场观察最好的结果。

排序和过滤Reddit和Twitter表 (Sorting and Filtering Reddit and Twitter tables)

These callbacks are fairly similar so I’m only going to review one here. Check the full code at the end for both. The callbacks use mostly boiler plate code for Dash tables with sorting and filtering enabled.

这些回调非常相似,因此我只在这里回顾一下。 最后检查全部代码。 回调函数在启用了排序和过滤功能的Dash表中主要使用样板代码 。

The Reddit data callback takes in the n_clicks input from the dcc.Button component so the Refresh button can be used to reload the results.

Reddit数据回调从dcc.Button组件接受n_clicks输入,因此可以使用Refresh按钮来重新加载结果。

@app.callback(
Output('table-sorting-filtering', 'data'),
[Input('table-sorting-filtering', "page_current"),
Input('table-sorting-filtering', "page_size"),
Input('table-sorting-filtering', 'sort_by'),
Input('table-sorting-filtering', 'filter_query'),
Input('refresh', 'n_clicks')])
def update_table(page_current, page_size, sort_by, filter, n_clicks):
filtering_expressions = filter.split(' && ') if n_clicks is None:
raise PreventUpdate
else:
dff = get_reddit()
for filter_part in filtering_expressions:
col_name, operator, filter_value = split_filter_part(filter_part) if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):# these operators match pandas series operator method names
dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
elif operator == 'contains':
dff = dff.loc[dff[col_name].str.contains(filter_value)]
elif operator == 'datestartswith':# this is a simplification of the front-end filtering logic,
# only works with complete fields in standard format

dff = dff.loc[dff[col_name].str.startswith(filter_value)] if len(sort_by):
dff = dff.sort_values(
[col['column_id'] for col in sort_by],
ascending=[
col['direction'] == 'asc'
for col in sort_by
],
inplace=False) page = page_current
size = page_size
return dff.iloc[page * size: (page + 1) * size].to_dict('records')

填充财务报告 (Populating the Financial Report)

This callback takes the ticker and passes it to the get_fin_report function. It returns a Dash Bootstrap table instead of using the make_table function.

此回调采用代码,并将其传递给get_fin_report函数。 它返回一个Dash Bootstrap表,而不使用make_table函数。

@app.callback(Output('fin-table', 'children'),
[Input('ticker-input', 'value')])
def fin_report(sym):
sym = sym.upper()
df = get_financial_report(sym)
table = dbc.Table.from_dataframe(df, striped=True
, bordered=True, hover=True)
return table

填充图表 (Populating the Charts)

All three charts are called at once in the same callback. The callback takes the Ticker, Start Date and End Date as inputs. It also takes an interval input from dbc.Interval to refresh the chart data automatically. The graphs are generated using Plotly’s candlestick charts for the authentic trader experience! The callback returns the chart Accordion.

在同一个回调中一次调用所有三个图表。 回调将代码行开始日期结束日期作为输入。 它还需要从dbc.Interval输入间隔,以自动刷新图表数据。 这些图是使用Plotly的烛台图生成的 ,以提供真实的交易经验! 回调返回图表Accordion。

Image for post
Example Plotly Candlestick chart
情节烛台图表示例
@app.callback(Output('x-vol-1', 'children'),
[Input('ticker-input', 'value')
, Input('date-picker', 'start_date')
, Input('date-picker', 'end_date')
, Input('interval-component', 'n_intervals')
])
def create_graph(ticker,startdate, enddate, n):
ticker = ticker.upper()
df1 = yf.download(ticker,startdate, enddate)
df1.reset_index(inplace=True)
fig1 = go.Figure(data=[go.Candlestick(x=df1['Date'],
open=df1['Open'], high=df1['High'],
low=df1['Low'], close=df1['Close'])
])
df2 = yf.download(ticker, period = "5d", interval = "1m")
df2.reset_index(inplace=True)
fig2 = go.Figure(data=[go.Candlestick(x=df2['Datetime'],
open=df2['Open'], high=df2['High'],
low=df2['Low'], close=df2['Close'])
]) df3 = yf.download(ticker, period = "1d", interval = "1m")
df3.reset_index(inplace=True)
fig3 = go.Figure(data=[go.Candlestick(x=df3['Datetime'],
open=df3['Open'], high=df3['High'],
low=df3['Low'], close=df3['Close'])
])
accordion = html.Div([make_item("Daily Chart",
dcc.Graph(figure = fig1), 1 )
, make_item("5d 5m Chart"
, dcc.Graph( figure = fig2), 2)
, make_item("1d 1m Chart"
, dcc.Graph(figure = fig3), 3)
], className="accordion")
return accordion

Congratulations, the dashboard is completed! You are on your way to finding an edge and dominating the stock market!

恭喜,仪表板已完成! 您正在寻找优势并主导股票市场的方式!

Image for post
Photo by Viacheslav Bublyk on Unsplash
Viacheslav Bublyk在Unsplash上拍摄的照片

最终想法和完整代码 (Final Thoughts and Complete Code)

Although this seems like a lot of code to go through, using Dash is fairly easy once you understand the layout and callback patterns. Using functions makes putting together the layout a breeze and saves on repeating code. This app is a great place to start, but can easily be enhanced to include technical analysis, more company info, and predictive analytics. It might even be possible to set up a broker API and trade through it!

尽管这似乎需要很多代码,但是一旦您了解了布局和回调模式,使用Dash就会相当容易。 使用函数使布局变得轻而易举,并节省了重复代码。 这个应用程序是一个很好的起点,但可以轻松进行增强以包括技术分析,更多公司信息和预测分析。 甚至可以设置经纪人API并通过它进行交易!

Thanks for reading. Check out my other articles if you’re interested in the stock market, programming and data science:

谢谢阅读。 如果您对股市,编程和数据科学感兴趣,请查看我的其他文章:

代码 (The Code)

Find it on Github too!

也可以在Github上找到它!

Dash_utils.py (Dash_utils.py)

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_tableimport flask
from flask import Flask
import pandas as pd
import dateutil.relativedelta
from datetime import date
import datetime
import yfinance as yf
import numpy as np
import praw
import sqlite3import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplotsdef make_table(id, dataframe, lineHeight = '17px', page_size = 5):
return dash_table.DataTable(
id=id,
css=[{'selector': '.row', 'rule': 'margin: 0'}],
columns=[
{"name": i, "id": i} for i in dataframe.columns
],
style_header={
'backgroundColor': 'rgb(230, 230, 230)',
'fontWeight': 'bold'},
style_cell={'textAlign': 'left'},
style_data={
'whiteSpace': 'normal',
'height': 'auto',
'lineHeight': lineHeight
},
# style_table = {'width':300},
style_data_conditional=[
{
'if': {'row_index': 'odd'},
'backgroundColor': 'rgb(248, 248, 248)'
}
],
style_cell_conditional=[
{'if': {'column_id': 'title'},
'width': '130px'},
{'if': {'column_id': 'post'},
'width': '500px'},
{'if': {'column_id': 'datetime'},
'width': '130px'},
{'if': {'column_id': 'text'},
'width': '500px'}],
page_current=0,
page_size=page_size,
page_action='custom',filter_action='custom',
filter_query='',sort_action='custom',
sort_mode='multi',
sort_by=[],
#dataframe.to_dict('records')
)def make_card(alert_message, color, cardbody, style_dict = None):
return dbc.Card([ dbc.Alert(alert_message, color=color)
,dbc.CardBody(cardbody)
], style = style_dict)#end carddef ticker_inputs(inputID, pickerID, MONTH_CUTTOFF):
currentDate = date.today()
pastDate = currentDate - dateutil.relativedelta.relativedelta(months=MONTH_CUTTOFF)
return html.Div([
dcc.Input(id = inputID, type="text", placeholder="MSFT")
, html.P(" ")
, dcc.DatePickerRange(
id = pickerID,
min_date_allowed=pastDate,
#max_date_allowed=currentDate,
#initial_visible_month=dt(2017, 8, 5),
start_date = pastDate,
#end_date = currentDate
)])def make_item(button, cardbody, i):
# we use this function to make the example items to avoid code duplication
return dbc.Card([
dbc.CardHeader(
html.H2(
dbc.Button(
button,
color="link",
id=f"group-{i}-toggle",
))
),
dbc.Collapse(
dbc.CardBody(cardbody),
id=f"collapse-{i}",
)])

Reddit_data.py (Reddit_data.py)

import pandas as pd
import dateutil.relativedelta
from datetime import date
import datetime
import yfinance as yf
import numpy as np
import praw
import sqlite3from config import r_cid, r_csec, r_uag#return a dataframe for the newest reddit posts
def get_reddit(cid= r_cid, csec= r_csec, uag= r_uag, subreddit='wallstreetbets'):
#connect to reddit
reddit = praw.Reddit(client_id= cid, client_secret= csec, user_agent= uag)#get the new reddit posts
posts = reddit.subreddit(subreddit).new(limit=None)#load the posts into a pandas dataframe
p = []
for post in posts:
p.append([post.title, post.score, post.selftext])
posts_df = pd.DataFrame(p,columns=['title', 'score', 'post'])
return posts_df

tweet_data.py (tweet_data.py)

import tweepy
import pandas as pd
import sqlite3
import json
import datetime
from datetime import date
import texthero as hero
import regex as re
import stringfrom config import t_conkey, t_consec, t_akey, t_asecpd.set_option('display.max_colwidth',None)def get_all_tweets(screen_name
,consumer_key = t_conkey
, consumer_secret= t_consec
, access_key= t_akey
, access_secret= t_asec
):
#Twitter only allows access to a users most recent 3240 tweets with this method
#authorize twitter, initialize tweepy
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_key, access_secret)
api = tweepy.API(auth)
#initialize a list to hold all the tweepy Tweets
alltweets = []
#make initial request for most recent tweets (200 is the maximum allowed count)
new_tweets = api.user_timeline(screen_name = screen_name,count=200)
#save most recent tweets
alltweets.extend(new_tweets)
#save the id of the oldest tweet less one
oldest = alltweets[-1].id - 1
#keep grabbing tweets until there are no tweets left to grab
while len(new_tweets) > 0:
#print(f"getting tweets before {oldest}")
#all subsiquent requests use the max_id param to prevent duplicates
new_tweets = api.user_timeline(screen_name = screen_name,count=200,max_id=oldest)
#save most recent tweets
alltweets.extend(new_tweets)
#update the id of the oldest tweet less one
oldest = alltweets[-1].id - 1
outtweets = [[tweet.id_str, tweet.created_at, tweet.text] for tweet in alltweets]
tweets_df = pd.DataFrame(outtweets, columns = ['time', 'datetime', 'text'])return tweets_dfdef get_options_flow():
conn = sqlite3.connect('stocks.sqlite')ss = get_all_tweets(screen_name ="SwaggyStocks")
uw = get_all_tweets(screen_name ="unusual_whales")
ss['source'] = 'swaggyStocks'
ss['text'] = hero.remove_urls(ss['text'])
ss['text'] = [n.replace('$','') for n in ss['text']]
uw['source'] = 'unusual_whales'
uw['text'] = hero.remove_urls(uw['text'])
uw['text'] = [n.replace('$','') for n in uw['text']]
uw['text'] = [n.replace(':','') for n in uw['text']]
uw['text'] = [n.replace('\n',' ') for n in uw['text']]
uw['text'] = [n.replace(' ',' ') for n in uw['text']]
tweets = pd.concat([ss, uw])
tweets.to_sql('tweets', conn, if_exists = 'replace')return print('done')

get_fin_report.py (get_fin_report.py)

import pandas as pd
from bs4 import BeautifulSoup
import requests
from datetime import datedef get_financial_report(ticker):# try:
urlfinancials = 'https://www.marketwatch.com/investing/stock/'+ticker+'/financials'
urlbalancesheet = 'https://www.marketwatch.com/investing/stock/'+ticker+'/financials/balance-sheet'text_soup_financials = BeautifulSoup(requests.get(urlfinancials).text,"html") #read in
text_soup_balancesheet = BeautifulSoup(requests.get(urlbalancesheet).text,"html") #read in# build lists for Income statement
titlesfinancials = text_soup_financials.findAll('td', {'class': 'rowTitle'})
epslist=[]
netincomelist = []
longtermdebtlist = []
interestexpenselist = []
ebitdalist= []for title in titlesfinancials:
if 'EPS (Basic)' in title.text:
epslist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'Net Income' in title.text:
netincomelist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'Interest Expense' in title.text:
interestexpenselist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'EBITDA' in title.text:
ebitdalist.append ([td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])# find the table headers for the Balance sheet
titlesbalancesheet = text_soup_balancesheet.findAll('td', {'class': 'rowTitle'})
equitylist=[]
for title in titlesbalancesheet:
if 'Total Shareholders\' Equity' in title.text:
equitylist.append( [td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])
if 'Long-Term Debt' in title.text:
longtermdebtlist.append( [td.text for td in title.findNextSiblings(attrs={'class': 'valueCell'}) if td.text])#get the data from the income statement lists
#use helper function get_element
eps = get_element(epslist,0)
epsGrowth = get_element(epslist,1)
netIncome = get_element(netincomelist,0)
shareholderEquity = get_element(equitylist,0)
roa = get_element(equitylist,1)longtermDebt = get_element(longtermdebtlist,0)
interestExpense = get_element(interestexpenselist,0)
ebitda = get_element(ebitdalist,0)# load all the data into dataframe
fin_df= pd.DataFrame({'eps': eps,'eps Growth': epsGrowth,'net Income': netIncome,'shareholder Equity': shareholderEquity,'roa':
roa,'longterm Debt': longtermDebt,'interest Expense': interestExpense,'ebitda': ebitda},index=range(date.today().year-5,date.today().year))
fin_df.reset_index(inplace=True)
return fin_dfdef get_element(list,element):
try:
return list[element]
except:
return '-'

index.py (index.py)

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_table
from dash.exceptions import PreventUpdateimport flask
from flask import Flask
import pandas as pd
import dateutil.relativedelta
from datetime import date
import datetime
import yfinance as yf
import numpy as np
import praw
import sqlite3import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplotsfrom dash_utils import make_table, make_card, ticker_inputs, make_item
from reddit_data import get_reddit
from tweet_data import get_options_flow
from fin_report_data import get_financial_report #, get_financial_reportformattedconn = sqlite3.connect('stocks.sqlite')server = Flask(__name__)
app = dash.Dash(__name__,server = server ,meta_tags=[{ "content": "width=device-width"}], external_stylesheets=[dbc.themes.BOOTSTRAP])app.config.suppress_callback_exceptions = Trueget_options_flow()
flow = pd.read_sql("select datetime, text from tweets order by datetime desc", conn)global dfr
dfr = get_reddit()
layout1 = html.Div([
dbc.Row([dbc.Col(make_card("Enter Ticker", "success", ticker_inputs('ticker-input', 'date-picker', 36)))]) #row 1
,dbc.Row([dbc.Col([make_card("Twitter Order Flow", 'primary', make_table('table-sorting-filtering2', flow, '17px', 10))])
,dbc.Col([make_card("Fin table ", "secondary", html.Div(id="fin-table"))])
])
, dbc.Row([make_card("select ticker", "warning", "select ticker")],id = 'cards') #row 2
, dbc.Row([
dbc.Col([
dbc.Row([make_card("Wallstreet Bets New Posts"
, 'primary'
,[html.P(html.Button('Refresh', id='refresh'))
, make_table('table-sorting-filtering', dfr, '17px', 4)]
)], justify = 'center')
])
,dbc.Col([dbc.Row([dbc.Alert("__Charts__", color="primary")], justify = 'center')
,dbc.Row(html.Div(id='x-vol-1'), justify = 'center')
, dcc.Interval(
id='interval-component',
interval=1*150000, # in milliseconds
n_intervals=0)
, dcc.Interval(
id='interval-component2',
interval=1*60000, # in milliseconds
n_intervals=0)
,dbc.Row([html.Div(id='tweets')])
])#end col
])#end row
]) #end divapp.layout= layout1operators = [['ge ', '>='],
['le ', '<='],
['lt ', '<'],
['gt ', '>'],
['ne ', '!='],
['eq ', '='],
['contains '],
['datestartswith ']]def split_filter_part(filter_part):
for operator_type in operators:
for operator in operator_type:
if operator in filter_part:
name_part, value_part = filter_part.split(operator, 1)
name = name_part[name_part.find('{') + 1: name_part.rfind('}')]value_part = value_part.strip()
v0 = value_part[0]
if (v0 == value_part[-1] and v0 in ("'", '"', '`')):
value = value_part[1: -1].replace('\\' + v0, v0)
else:
try:
value = float(value_part)
except ValueError:
value = value_part# word operators need spaces after them in the filter string,
# but we don't want these later
return name, operator_type[0].strip(), valuereturn [None] * 3@app.callback(Output('cards', 'children'),
[Input('ticker-input', 'value')])
def refresh_cards(ticker):
ticker = ticker.upper()
if ticker is None:
TICKER = 'MSFT'
else:
TICKER = yf.Ticker(ticker)
cards = [ dbc.Col(make_card("Previous Close ", "secondary", TICKER.info['previousClose']))
, dbc.Col(make_card("Open", "secondary", TICKER.info['open']))
, dbc.Col(make_card("Sector", 'secondary', TICKER.info['sector']))
, dbc.Col(make_card("Beta", 'secondary', TICKER.info['beta']))
, dbc.Col(make_card("50d Avg Price", 'secondary', TICKER.info['fiftyDayAverage']))
, dbc.Col(make_card("Avg 10d Vol", 'secondary', TICKER.info['averageVolume10days']))
] #end cards list
return cards@app.callback(
[Output(f"collapse-{i}", "is_open") for i in range(1, 4)],
[Input(f"group-{i}-toggle", "n_clicks") for i in range(1, 4)],
[State(f"collapse-{i}", "is_open") for i in range(1, 4)],
)
def toggle_accordion(n1, n2, n3, is_open1, is_open2, is_open3):
ctx = dash.callback_context
if not ctx.triggered:
return ""
else:
button_id = ctx.triggered[0]["prop_id"].split(".")[0]
if button_id == "group-1-toggle" and n1:
return not is_open1, False, False
elif button_id == "group-2-toggle" and n2:
return False, not is_open2, False
elif button_id == "group-3-toggle" and n3:
return False, False, not is_open3
return False, False, False@app.callback(Output('x-vol-1', 'children'),
[Input('ticker-input', 'value')
, Input('date-picker', 'start_date')
, Input('date-picker', 'end_date')
, Input('interval-component', 'n_intervals')
])
def create_graph(ticker,startdate, enddate, n):
ticker = ticker.upper()
df1 = yf.download(ticker,startdate, enddate)
df1.reset_index(inplace=True)
fig1 = go.Figure(data=[go.Candlestick(x=df1['Date'],
open=df1['Open'], high=df1['High'],
low=df1['Low'], close=df1['Close'])
])
df2 = yf.download(ticker, period = "5d", interval = "1m")
df2.reset_index(inplace=True)
fig2 = go.Figure(data=[go.Candlestick(x=df2['Datetime'],
open=df2['Open'], high=df2['High'],
low=df2['Low'], close=df2['Close'])
])df3 = yf.download(ticker, period = "1d", interval = "1m")
df3.reset_index(inplace=True)
fig3 = go.Figure(data=[go.Candlestick(x=df3['Datetime'],
open=df3['Open'], high=df3['High'],
low=df3['Low'], close=df3['Close'])
])
accordion = html.Div([make_item("Daily Chart", dcc.Graph(figure = fig1), 1 )
, make_item("5d 5m Chart",dcc.Graph( figure = fig2), 2)
, make_item("1d 1m Chart", dcc.Graph(figure = fig3), 3)
], className="accordion")
return accordion@app.callback(
Output('tweets', 'children'),
[Input('interval-component2', 'n_intervals'),
])
def new_tweets(n):
get_options_flow()
return html.P(f"Reloaded Tweets {n}")@app.callback(
Output('table-sorting-filtering', 'data'),
[Input('table-sorting-filtering', "page_current"),
Input('table-sorting-filtering', "page_size"),
Input('table-sorting-filtering', 'sort_by'),
Input('table-sorting-filtering', 'filter_query'),
Input('refresh', 'n_clicks')])
def update_table(page_current, page_size, sort_by, filter, n_clicks):
filtering_expressions = filter.split(' && ')if n_clicks is None:
raise PreventUpdate
else:
dff = get_reddit()
for filter_part in filtering_expressions:
col_name, operator, filter_value = split_filter_part(filter_part)if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
# these operators match pandas series operator method names
dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
elif operator == 'contains':
dff = dff.loc[dff[col_name].str.contains(filter_value)]
elif operator == 'datestartswith':
# this is a simplification of the front-end filtering logic,
# only works with complete fields in standard format
dff = dff.loc[dff[col_name].str.startswith(filter_value)]if len(sort_by):
dff = dff.sort_values(
[col['column_id'] for col in sort_by],
ascending=[
col['direction'] == 'asc'
for col in sort_by
],
inplace=False)page = page_current
size = page_size
return dff.iloc[page * size: (page + 1) * size].to_dict('records')@app.callback(
Output('table-sorting-filtering2', 'data'),
[Input('table-sorting-filtering2', "page_current"),
Input('table-sorting-filtering2', "page_size"),
Input('table-sorting-filtering2', 'sort_by'),
Input('table-sorting-filtering2', 'filter_query'),
Input('interval-component', 'n_intervals')
])
def update_table2(page_current, page_size, sort_by, filter, n):
filtering_expressions = filter.split(' && ')
conn = sqlite3.connect('stocks.sqlite')
flow = pd.read_sql("select datetime, text, source from tweets order by datetime desc", conn)
dff = flowfor filter_part in filtering_expressions:
col_name, operator, filter_value = split_filter_part(filter_part)if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
# these operators match pandas series operator method names
dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
elif operator == 'contains':
dff = dff.loc[dff[col_name].str.contains(filter_value)]
elif operator == 'datestartswith':
# this is a simplification of the front-end filtering logic,
# only works with complete fields in standard format
dff = dff.loc[dff[col_name].str.startswith(filter_value)]if len(sort_by):
dff = dff.sort_values(
[col['column_id'] for col in sort_by],
ascending=[
col['direction'] == 'asc'
for col in sort_by
],
inplace=False
)page = page_current
size = page_size
return dff.iloc[page * size: (page + 1) * size].to_dict('records')@app.callback(Output('fin-table', 'children'),
[Input('ticker-input', 'value')])
def fin_report(sym):
sym = sym.upper()
df = get_financial_report(sym)
#table = make_table('table-sorting-filtering3', df, '20px',8)
table = dbc.Table.from_dataframe(df, striped=True, bordered=True, hover=True)return tableif __name__ == '__main__':
app.run_server()

Thanks!

谢谢!

翻译自: https://medium.com/swlh/how-to-create-a-dashboard-to-dominate-the-stock-market-using-python-and-dash-c35a12108c93

python股市

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/389957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里巴巴开源 Sentinel,进一步完善 Dubbo 生态

为什么80%的码农都做不了架构师&#xff1f;>>> 阿里巴巴开源 Sentinel&#xff0c;进一步完善 Dubbo 生态 Sentinel 开源地址&#xff1a;https://github.com/alibaba/Sentinel 转载于:https://my.oschina.net/dyyweb/blog/1925839

离群值如何处理_有理处理离群值的局限性

离群值如何处理ARIMA models can be quite adept when it comes to modelling the overall trend of a series along with seasonal patterns.ARIMA模型可以很好地建模一系列总体趋势以及季节性模式。 In a previous article titled SARIMA: Forecasting Seasonal Data with P…

10生活便捷:购物、美食、看病时这样搜,至少能省一半心

本次课程介绍实实在在能够救命、省钱的网站&#xff0c;解决了眼前这些需求后&#xff0c;还有“诗和远方”——不花钱也能点亮自己的生活&#xff0c;获得美的享受&#xff01; 1、健康医疗这么搜&#xff0c;安全又便捷 现在的医疗市场确实有些混乱&#xff0c;由于医疗的专业…

ppt图表图表类型起始_梅科图表

ppt图表图表类型起始There are different types of variable width bar charts but two are the most popular: 1) Bar Mekko chart; 2) Marimekko chart.可变宽度条形图有不同类型&#xff0c;但最受欢迎的有两种&#xff1a;1)Mekko条形图&#xff1b; 2)Marimekko图表。 Th…

Tomcat日志乱码了怎么处理?

【前言】 tomacat日志有三个地方&#xff0c;分别是Output(控制台)、Tomcat Localhost Log(tomcat本地日志)、Tomcat Catalina Log。 启动日志和大部分报错日志、普通日志都在output打印;有些错误日志&#xff0c;在Tomcat Localhost Log。 三个日志显示区&#xff0c;都可能…

5888. 网络空闲的时刻

5888. 网络空闲的时刻 给你一个有 n 个服务器的计算机网络&#xff0c;服务器编号为 0 到 n - 1 。同时给你一个二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi] 表示服务器 ui 和 vi 之间有一条信息线路&#xff0c;在 一秒 内它们之间可以传输 任意 数目的信息。再…

django框架预备知识

内容&#xff1a; 1.web预备知识 2.django介绍 3.web框架的本质及分类 4.django安装与基本设置 1.web预备知识 HTTP协议&#xff1a;https://www.cnblogs.com/wyb666/p/9383077.html 关于web的本质&#xff1a;http://www.cnblogs.com/wyb666/p/9034042.html 如何自定义web框架…

现实世界 机器学习_公司沟通分析简介现实世界的机器学习方法

现实世界 机器学习In my previous posts I covered analytical subjects from a scientific point of view, rather than an applied real world problem. For this reason, this article aims at approaching an analytical idea from a managerial point of view, rather tha…

拷贝构造函数和赋值函数

1、拷贝构造函数&#xff1a;用一个已经有的对象构造一个新的对象。 CA&#xff08;const CA & c &#xff09;函数的名称必须和类名称相一致&#xff0c;它的唯一的一个参数是本类型的一个引用变量&#xff0c;该参数是const 类型&#xff0c;不可变。 拷贝构造函数什么时…

Chrome keyboard shortcuts

2019独角兽企业重金招聘Python工程师标准>>> Chrome keyboard shortcuts https://support.google.com/chrome/answer/157179?hlen 转载于:https://my.oschina.net/qwfys200/blog/1927456

数据中心细节_当细节很重要时数据不平衡

数据中心细节定义不平衡数据 (Definition Imbalanced Data) When we speak of imbalanced data, what we mean is that at least one class is underrepresented. For example, when considering the problem of building a classifier, let’s call it the Idealisstic-Voter.…

辛普森悖论_所谓的辛普森悖论

辛普森悖论We all know the Simpsons family from Disneyland, but have you heard about the Simpson’s Paradox from statistic theory? This article will illustrate the definition of Simpson’s Paradox with an example, and show you how can it harm your statisti…

查看NVIDIA使用率工具目录

2019独角兽企业重金招聘Python工程师标准>>> C:\Program Files\NVIDIA Corporation\Display.NvContainer\NVDisplay.Container.exe 转载于:https://my.oschina.net/u/2430809/blog/1927560

余弦相似度和欧氏距离_欧氏距离和余弦相似度

余弦相似度和欧氏距离Photo by Markus Winkler on UnsplashMarkus Winkler在Unsplash上拍摄的照片 This is a quick and straight to the point introduction to Euclidean distance and cosine similarity with a focus on NLP.这是对欧氏距离和余弦相似度的快速而直接的介绍&…

七、 面向对象(二)

匿名类对象 创建的类的对象是匿名的。当我们只需要一次调用类的对象时&#xff0c;我们就可以考虑使用匿名的方式创建类的对象。特点是创建的匿名类的对象只能够调用一次&#xff01; package day007;//圆的面积 class circle {double radius;public double getArea() {// TODO…

机器学习 客户流失_通过机器学习预测流失

机器学习 客户流失介绍 (Introduction) This article is part of a project for Udacity “Become a Data Scientist Nano Degree”. The Jupyter Notebook with the code for this project can be downloaded from GitHub.本文是Udacity“成为数据科学家纳米学位”项目的一部分…

Qt中的坐标系统

转载&#xff1a;原野追逐 Qt使用统一的坐标系统来定位窗口部件的位置和大小。 以屏幕的左上角为原点即(0, 0)点&#xff0c;从左向右为x轴正向&#xff0c;从上向下为y轴正向&#xff0c;这整个屏幕的坐标系统就用来定位顶层窗口&#xff1b; 此外&#xff0c;窗口内部也有自己…

预测股票价格 模型_建立有马模型来预测股票价格

预测股票价格 模型前言 (Preface) If you are reading this, it’s most likely because you love to solve puzzles. I’m a very competitive person by nature. The Mt. Everest of puzzles, in my opinion, is trying to find excess returns through active trading in th…

Python 模块 timedatetime

time & datetime 模块 在平常的代码中&#xff0c;我们常常需要与时间打交道。在Python中&#xff0c;与时间处理有关的模块就包括&#xff1a;time&#xff0c;datetime,calendar(很少用&#xff0c;不讲)&#xff0c;下面分别来介绍。 在开始之前&#xff0c;首先要说明几…

柠檬工会_工会经营者

柠檬工会Hey guys! This week we’ll be going over some ways to work with result sets in MySQL. These result sets are the outputs of your everyday queries, such as:大家好&#xff01; 本周&#xff0c;我们将介绍一些在MySQL中处理结果集的方法。 这些结果集是您日常…