2020-04-10 利用Python进行股票投资组合分析(二)

扩展Jupyter Notebook股票投资组合分析与互动图表在Dash by Plotly

By Carlos Muza on Unsplash

在本系列的第1部分中,我讨论了自从我更习惯于使用pandas之后,我如何显著地增加了使用Python进行金融分析。在第1部分的帖子中,我们回顾了如何利用pandasYahoo Finance API在很大程度上自动化跟踪和基准股票投资组合的表现。在这篇帖子结束时,你已经生成了一个丰富的数据集,可以计算投资组合position与相同持有期内同等规模的标准普尔500指数position的相对百分比和美元价值回报。你还可以确定每个position对你整体投资组合回报的贡献,或许最重要的是,如果你投资标准普尔500指数ETF或指数基金会更好。最后,您将Plotly用于可视化,这使您更容易理解哪些position推动了最大的价值,它们的YTD势头相对于标准普尔500指数是什么样子的,如果有任何position下跌,您可能想要考虑divesting,也就是打了一个“落后止损”。

作为在Jupyter notebook中构建这个初始过程的一部分,我学到了很多,我也发现写一篇帖子来遍历笔记本,解释代码,并讲述我在每个可视化背后的想法是非常有帮助的。这个后续的帖子将比前一个更短,目的也更直接。虽然我一直在寻找与创建一个更有益于来跟踪我的股票投资组合的记录方式,但我一直打算学习并整合一个用于构建分析dashboards / web应用程序的Python框架。对我来说,最重要的用处之一是能够选择特定的位置和时间范围,然后动态评估每个位置的相对表现。未来,我很可能会将这个评估案例扩大到我不拥有但正在考虑收购的股票。在今年剩下的时间里,我希望通过学习Flask,使用Heroku部署应用程序,理想情况下开发某种类型的数据管道来自动为最终的Web应用程序提取和加载新数据,来进一步加深我对构建Web应用程序的理解。虽然我还处于这个过程的早期,但在这篇文章中,我将讨论我上次使用Dash by Plotly(又名Dash)进行初始开发时讨论的笔记本的扩展。

Dash by Plotly

如果您已经阅读或参考了第1部分,您将会看到,一旦您创建了主数据框,您就可以使用Plotly生成了评估相对于标准普尔500指数的投资组合绩效的可视化效果。Plotly是一个非常丰富的库,相对于其他Python可视化库(如Seborn和Matplotlib),我更喜欢使用Plotly创建可视化。在此基础上,我的最终目标是拥有一个交互式dashboard / web应用程序来进行投资组合分析。我正在继续寻找这个问题的最佳解决方案,同时我已经开始探索Dash的使用。PlotlyDash定义为构建Web应用程序的Python框架,并增加了不需要JavaScript的好处。正如我链接到的登录页面所示,它构建在Plotly.jsReactFlask之上。

到目前为止,我看到的最初好处是,一旦您熟悉并熟悉了PlotlyDash就是dashboard开发的自然扩展。与其简单地把你的可视化图像放在Jupyter notebook里进行分析,我更肯定的看到了创建一个独立的交互式网络应用程序的价值。Dash提供了更高的交互性和使用“现代UI元素,如下拉式菜单、滑块和图形”操作数据的能力。此功能直接支持我的股票投资组合分析的最终目标,包括进行“假设分析”的能力,以及交互式研究潜在机会和快速了解关键驱动因素和情景的能力。考虑到所有这些因素,使用Dash的学习曲线(至少对我而言)并不是微不足道的。

上面的均为原博文作者(Kevin Boller),我不太理解Dash,但是通过他的描述像是R里的shiny,同样都是我不熟悉的领域。继续学习吧:https://plotly.com/dash/

Jose Portilla’s “Interactive Python Dashboards with Plotly and Dash”

为了缩短我阅读Dash文档并对其进行广泛故障排除所需的时间,我参加了Jose Portilla在Udemy上的PlotlyDash课程。可以在此处https://medium.com/@josemarcialportilla找到该课程的详细页面。我已经选修了何塞的几门课程,目前正在选修他的Flask课程。我认为他是一位非常sound and helpful的讲师-虽然他通常不认为广泛的编程经验是他课程的先决条件,但在这门Dash课程中,他确实建议至少非常熟悉Python。尤其是,强烈建议您扎实理解Plotly用于可视化的语法,包括使用pandas。在参加完课程之后,您仍然会接触到使用Dash所能构建的内容的皮毛。然而,我发现这门课程是一个非常有帮助的开始,特别是因为Jose使用了datareader和财务数据和示例,包括动态绘制股票价格图表。

Porting Data from Jupyter Notebook to Interact with it in Dash.

与第1部分类似,我在GitHub上创建了另一个repo,其中包含创建最终Dash dashboard所需的所有文件和代码。
以下是包含的内容和入门方法的摘要:

1.投资组合Python Notebook_dash_blog_example.ipynb

  • 这与第1部分中的Jupyter笔记本非常相似;增加的内容包括最后两个部分:“股票回报比较”部分和“数据输出”部分,“股票回报比较”部分是我在使用Dash之前构建的概念验证部分,我在“数据输出”部分创建分析生成的数据的CSV文件;它们用作Dash dashboard中使用的数据源。

2.Sample stocks acquisition dates_costs.xlsx

  • 这是toy投资组合文件,您将在投资组合评估中使用或修改该文件。

3.requirements.txt

  • 这里应该包含您需要的所有库。我建议在Anaconda中创建一个虚拟环境,下面将进一步讨论。

4.Mock_Portfolio_Dash.py

  • 下面我们将介绍Dash dashboard的代码。

根据我的repo的README文件,我建议使用Anaconda创建一个虚拟环境。以下是关于Anaconda虚拟环境的快速解释和更多详细信息的链接:
我推荐Python3.6或更高版本,这样您就可以使用提供的CSV文件在本地运行Dash dashboard。下面是关于如何在Anaconda中设置虚拟环境的非常详细的说明。https://www.freecodecamp.org/news/why-you-need-python-environments-and-how-to-manage-them-with-conda-85f155f4353c/
最后,正如第1部分中提到的,一旦设置了环境,除了需求文件中的库之外,如果希望在notebook中运行Yahoo Finance datareader部分,还需要在虚拟环境中通过pip install fix-yahoo-finance安装fix-yahoo-Finance。

Working with Dash

如果到目前为止,您已经使用Python3.6设置了虚拟环境,并且安装了必要的库,那么您应该能够使用Dash dashboard代码运行Python文件。

对于那些不太熟悉的人:进入虚拟环境后,您需要将cd目录更改为保存repo文件的位置。举个简单的例子,如果您打开了Anaconda Prompt,并且您位于Documents文件夹中,并且文件保存在桌面上,则可以执行以下操作:

cd .. # This will take you up one folder in the directory.
cd  Desktop # this will take you to your Desktop.
dir # This is the windows command to display all files in the directory.  You should see the Mock_Portfolio_Dash.py file listed.
python Mock_Portfolio_Dash.py # this will run the Dash file
# You will then go to your browser and input the URL where Python says your dashboard is running on localhost.

如果您想了解有关Jupyter notebook和生成投资组合数据集的完整说明,请参阅第1部分。在Jupyter notebook的末尾,您将在“数据输出”部分看到以下代码。这些次要添加的内容会将CSV文件发送到您的本地目录。第一个是完整的投资组合数据集,您可以从中生成所有的可视化,第二个提供您将在第一个新股票图表的下拉选择中使用的股票代码列表。

# Generate the base file that will be used for Dash dashboard.
merged_portfolio_sp_latest_YTD_sp_closing_high.to_csv('analyzed_portfolio.csv')

我将重点介绍Mock Portfolio Python文件的一些关键方面,并分享如何在本地运行dashboard。
下面是运行此Dash dashboard时应看到的前三个图表的屏幕截图,以供我们细分.py文件时参考。


First three charts in Dash Stock Portfolio Analyses dashboard

在.py文件的开头,您导入包括在requirements.txt文件中的库,然后编写

app = dash.Dash()

以便实例化Dash应用程序。然后创建两个DataFrame对象,Ticker和Data。报价器将用于图表下拉列表中的股票报价器,而data dataframe是用于所有可视化评估的最终数据集。

您将整个dashboard包装在一个Div中,然后开始在这个Main Div中添加图表组件。py文件中的第35-72行生成“相对回报比较”图表,包括股票代码下拉列表、开始/结束日期范围、提交按钮和图表输出。为简洁起见,我将分解.py文件这一部分中三个部分中的第一个部分。

html.H1('Relative Returns Comparison'),
    html.Div([html.H3('Enter a stock symbol:', style={'paddingRight': '30px'}),
        dcc.Dropdown(
            id='my_ticker_symbol',
            options = options,
            value = ['SPY'], 
            multi = True
            # style={'fontSize': 24, 'width': 75}
            )   ]

如前所述,使用Dash意味着您不需要向应用程序添加JavaScript。在上面的代码块中,我们用H1标记标记输出,创建另一个Div,然后使用dash_core_components库中的下拉列表。您将id设置为my_ticker_symbol,我们将很快查看它的播放位置,从选项列表(从tickers dataframe生成)中设置默认值SPY,然后将多选(multi-select)设置为True。这里有一点学习曲线,至少对我来说是这样,这就是像Jose Portilla这样的课程可以通过提供总结Dash文档的有形示例来缩短您的学习时间-Jose实际上在他的课程中使用了与股票列表下拉列表和日期范围选择器类似的示例。

在此下方的第75-93行,您将看到dashboard上左下角图表的代码。此图表与第1部分中的Jupyter Notebook中提供的图表相同,但我发现在dashboard布局中使用Dash处理所有这些输出会比在Jupyter Notebook中使用Dash更好的用户体验,并且更容易使用(与我迄今使用的其他任何东西相比,我仍然更喜欢使用笔记本进行分析)。

# YTD Returns versus S&P 500 section
    html.H1('YTD and Total Position Returns versus S&P 500'),
        dcc.Graph(id='ytd1',
                figure = {'data':[
                            go.Bar(
                    x = data['Ticker'][0:20],
                    y = data['Share YTD'][0:20],
                        name = 'Ticker YTD'),
            go.Scatter(
                x = data['Ticker'][0:20],
                y = data['SP 500 YTD'][0:20],
                name = 'SP500 YTD')
                            ],
               'layout':go.Layout(title='YTD Return vs S&P 500 YTD',
                            barmode='group', 
                            xaxis = {'title':'Ticker'},
                            yaxis = {'title':'Returns', 'tickformat':".2%"}
                            )}, style={'width': '50%', 'display':'inline-block'}
                            )

对于那些习惯使用Plotly的人来说,在创建绘制Plotly图形所需的数据和布局对象方面,语法应该很熟悉。上面包含的语法与笔记本中的图表使用的语法不同,因为我更喜欢创建跟踪,基于这些跟踪生成数据对象,并在Layout对象中使用dict语法。在学习Jose的课程和查看Dash文档的过程中,我发现在Dash中更容易遵循这种语法-在排除结束标记、圆括号、大括号等的故障时,它有时会变得很笨拙,所以我把重点放在习惯这种结构上。

@app.callback(Output('my_graph', 'figure'),
            [Input('submit-button', 'n_clicks')],
            [State('my_ticker_symbol', 'value'),
            State('my_date_picker', 'start_date'),
            State('my_date_picker', 'end_date')
                ])
def update_graph(n_clicks, stock_ticker, start_date, end_date):
    start = datetime.strptime(start_date[:10], '%Y-%m-%d')
    end = datetime.strptime(end_date[:10], '%Y-%m-%d')
    traces = []
    for tic in stock_ticker:
        df = web.DataReader(tic, 'iex', start, end)
        traces.append({'x':df.index, 'y':(df['close']/df['close'].iloc[0])-1, 'name': tic})
    
    fig = {
        'data': traces,
        'layout': {'title':stock_ticker}
    }
    return fig
if __name__ == '__main__':
    app.run_server()

第229-252行(上面提供)驱动第一个“相对收益比较”图表的交互性。下面是此代码正在执行的操作的概述:

  • 为了创建交互式图表,Dash使用了回调修饰符:“我们的应用程序接口的”输入“和”输出“是通过app.callback修饰符以声明方式描述的。”
  • 在应用程序回调中,我们使用idmy_graph输出前面指定的dcc.Graph
  • 您使用Submit按钮作为输入,我们有三个默认状态my_ticker_symbol,其中包含前面讨论的dcc.Dropdown中声明的默认SPY值,以及默认的开始日期1/1/2018和结束日期今天。
  • 回调下面是回调修饰器包装的函数。如Dash文件中所述,当输入属性更改时,将自动调用装饰器包装的函数。“Dash为函数提供输入属性的新值作为输入参数,并且Dash使用函数返回的任何内容更新输出组件的属性。”
  • 在for循环中,对于y值,我将任意给定日期的收盘价df['close']除以所提供的日期范围(df['close'].iloc[0])生成的系列中的第一个收盘价。
  • 我这样做是为了查看指数为0的两只或更多股票的相对表现,0是日期范围的开始。考虑到股价的巨大差异,这使得比较交易价格在1800美元以上的股票(例如AMZN)和交易价格低于100美元的股票(例如WMT)的相对表现要容易得多。
  • 我很快就会提到,有时会有一种误解,认为一只股票如果交易价格较低,就是“便宜”的,如果它的交易价格与AMZN目前的交易价格一样,就是“昂贵”的。鉴于这种误解,公司有时会拆分股票,以使小投资者更容易负担得起股价,即使公司的市值/市值保持不变。
  • 无论如何,这张图表的好处是,它允许您使用动态日期范围快速发现相对于标准普尔500指数的股票表现高于或低于标准普尔500指数的股票。这提供了关于你整个投资组合的价值贡献的有用信息,也提供了何时可能考虑剥离表现不佳的持股的时候。

Conclusion and Future considerations

这就是我对Dash for Stock Portfolio分析的初步回顾。与以前一样,您拥有一个可扩展的Jupyter notebook和投资组合数据集,现在您可以将其作为CSV文件读出,并在交互式Dash dashboard中进行查看。正如前面在第1部分中讨论的,这种方法仍然有一些需要改进的地方,包括需要将股息作为股东总回报的一部分,以及需要能够评估活跃和所有(包括剥离)positions。

我发现这种方法最显著的好处包括更多的交互性,以及我更喜欢所有图表的dashboard布局,而不是在Jupyter笔记本中的单独单元格中。未来,我计划增加互动性,包括更多的“假设分析”,以评估个股对整体业绩的贡献。

我目前正在考虑使用数据pipeline交付端到端Web应用程序的其他选项包括:

  • 使用Google BigQuery的Mode Analytics:我以前写过我有多喜欢在我以前的公司使用Mode Analytics。Mode的好处包括它已经支持丰富的可视化效果(不需要编码),包括内置的Python笔记本。然而,我不相信在Mode中有一种方法可以从包括Yahoo Finance和IEX在内的金融API中提取数据。来自这些API源的数据可以读入私有数据库,例如,使用Google BigQuery,您可以连接到Mode。然而,就目前而言,这似乎是一个限制,因为我相信我未来的图表和用例将更多地需要从API中提取数据(而不是存储在数据库中)。
  • Heroku with Postgres and Pipeline:作为Jose课程的一部分,他向您展示了如何将Dashapp部署到Heroku(他课程的另一个好处)。到目前为止,我相信利用Heroku的应用程序功能是一个潜在的长期解决方案。这是我选择Jose的FlaskCourse的另一个原因;我从未构建过Web应用程序,他展示了如何使用SQLAlChemy作为Flask应用程序的数据库。为了加深我对在Heroku上部署带有数据库的应用程序的理解,我的重点将是确定在交互式Web应用程序中引入要分析的财务API数据的最佳方式,我可以按照指定的时间表使用新数据刷新该应用程序。
    对我来说,长期的解决方案需要更多的学习,但这绝对是我想在今年剩余的时间里接受的挑战。我希望你觉得这篇文章很有用,我欢迎评论中的任何反馈,包括我没有提到的其他选项,你认为这些选项会更适合这个应用程序及其分析。
    如果你喜欢这个帖子,如果你能点击“在看”图标让我知道并帮助增加我的人气,那就太棒了。
    参考学习资料:
    https://towardsdatascience.com/python-for-finance-dash-by-plotly-ccf84045b8be
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容