def show_trends(request): user = request.user validate_user(user) stocks = user_watchlist(user) timeframe = Timeframe(past_n_days=300) ld = LazyDictionary() ld["cip_df"] = lambda ld: selected_cached_stocks_cip(stocks, timeframe) ld["trends"] = lambda ld: calculate_trends(ld["cip_df"]) ld["rank"] = lambda ld: rank_cumulative_change( ld["cip_df"].filter(ld["trends"].keys(), axis="index"), timeframe ) trending_companies_plot = cache_plot( f"{user.username}-watchlist-trends", lambda ld: plot_company_rank(ld), datasets=ld, ) context = { "watchlist_trends": ld["trends"], "timeframe": timeframe, "trending_companies_uri": trending_companies_plot, "trending_companies_plot_title": "Trending watchlist stocks (ranked): {}".format( timeframe.description ), } return render(request, "watchlist-rank.html", context=context)
def show_sector_outliers(request, sector_id=None, n_days=30): validate_user(request.user) assert isinstance(sector_id, int) and sector_id > 0 stocks = all_sector_stocks( Sector.objects.get(sector_id=sector_id).sector_name) return show_outliers(request, stocks, n_days=n_days)
def show_total_earnings(request): validate_user(request.user) def data_factory(df: pd.DataFrame) -> pd.DataFrame: df = df.pivot( index=["asx_code", "fetch_date"], columns="field_name", values="field_value" ) required = (df.number_of_shares > 0) & (df.eps > 0.0) df = df[required] # ignore stocks which have unknowns # print(df) df["total_earnings"] = df["eps"] * df["number_of_shares"] df = df.dropna(how="any", axis=0) df = df.reset_index() df = df.pivot(index="asx_code", columns="fetch_date", values="total_earnings") df = df.merge(stocks_by_sector(), left_index=True, right_on="asx_code") df = df.set_index("asx_code", drop=True) df = df.groupby("sector_name").sum() df["sector_name"] = df.index df = df.melt(id_vars="sector_name", var_name="fetch_date") assert set(df.columns) == set(["sector_name", "fetch_date", "value"]) df["fetch_date"] = pd.to_datetime(df["fetch_date"], format="%Y-%m-%d") return df def plot(df: pd.DataFrame) -> p9.ggplot: plot = ( p9.ggplot( df, p9.aes( x="fetch_date", y="value", color="sector_name", # group="sector_name" ), ) + p9.geom_line(size=1.2) + p9.facet_wrap("~sector_name", ncol=2, scales="free_y") + p9.scale_y_continuous(labels=label_shorten) ) return user_theme( plot, y_axis_label="Total sector earnings ($AUD, positive contributions only)", figure_size=(12, 14), subplots_adjust={"wspace": 0.25}, ) ld = LazyDictionary() ld["timeframe"] = Timeframe(past_n_days=180) ld["pe_trends_df"] = lambda ld: pe_trends_df(ld["timeframe"]) ld["df"] = lambda ld: data_factory(ld["pe_trends_df"]) context = { "title": "Earnings per sector over time", "timeframe": ld["timeframe"], "plot_uri": cache_plot( f"total-earnings-by-sector:{ld['timeframe'].description}", lambda ld: plot(ld["df"]), datasets=ld, ), } return render(request, "total_earnings_by_sector.html", context=context)
def test_validate_user(uw_fixture, django_user_model): u1 = django_user_model.objects.get(username='******') # since u1 is not active... with pytest.raises(AssertionError): validate_user(u1) u2 = django_user_model.objects.get(username='******') validate_user(u2)
def test_validate_user( uw_fixture, django_user_model ): # pylint: disable=unused-argument,redefined-outer-name u1 = django_user_model.objects.get(username="******") # since u1 is not active... with pytest.raises(AssertionError): validate_user(u1) u2 = django_user_model.objects.get(username="******") validate_user(u2)
def download_data(request, dataset=None, output_format="csv"): validate_user(request.user) with tempfile.NamedTemporaryFile() as fh: df = get_dataset(dataset) content_type = save_dataframe_to_file(df, fh.name, output_format) fh.seek(0) response = HttpResponse(fh.read(), content_type=content_type) response["Content-Disposition"] = "inline; filename=temp.{}".format( output_format) return response
def show_watched(request): validate_user(request.user) matching_companies = user_watchlist(request.user) timeframe = Timeframe() return show_companies( matching_companies, request, timeframe, { "title": "Stocks you are watching", "sentiment_heatmap_title": "Watchlist stocks sentiment: {}".format(timeframe.description), })
def show_etfs(request): validate_user(request.user) matching_codes = all_etfs() extra_context = { "title": "Exchange Traded funds over past 300 days", "sentiment_heatmap_title": "Sentiment for ETFs", } return show_companies( matching_codes, request, Timeframe(), extra_context, )
def show_increasing_yield_stocks(request): validate_user(request.user) matching_companies = increasing_yield(None) extra_context = { "title": "Stocks with increasing yield over past 300 days", "sentiment_heatmap_title": "Sentiment for selected stocks", } return show_companies( matching_companies, request, Timeframe(), extra_context, )
def ecb_index_view(request): validate_user(request.user) context = { "dataflows": sorted( [ flow for flow in ECBFlow.objects.filter( is_test_data=False).filter(data_available=True) ], key=lambda f: f.description, ) } return render(request, "index.html", context)
def show_watched(request): validate_user(request.user) matching_companies = user_watchlist(request.user) timeframe = Timeframe() return show_companies( matching_companies, request, timeframe, { "title": "Stocks you are watching", "sentiment_heatmap_title": "Watchlist sentiment heatmap", }, )
def login(): user = '' error = None # if already logged in, redirect to the trips overview if 'user' in session: user = escape(session['user']) return redirect('/trips') else: # login form = LoginForm() if form.validate_on_submit(): error = None # user input email = form.email.data pwd = form.insecure_password.data # return user first name only if email, pwd match DB record user = models.validate_user(email, pwd) # Send logged in users to the trips page if user is not None: session['user'] = user session['email'] = email return redirect('/trips') else: error = 'Invalid credentials' return render_template('login.html', error=error, form=form)
def form_valid(self, form): req = self.request resp = super().form_valid( form) # only if no exception raised do we save... self.object = form.save(commit=False) self.object.user = validate_user(req.user) self.object.save() info(req, "Saved purchase of {}".format(self.kwargs.get("stock"))) return resp
def show_purchase_performance(request): validate_user(request.user) ( portfolio_performance_uri, stock_performance_uri, contributors_uri, ) = cached_portfolio_performance(request.user) context = { "title": "Portfolio performance", "portfolio_title": "Overall", "performance_uri": portfolio_performance_uri, "stock_title": "Stock", "stock_performance_uri": stock_performance_uri, "contributors_uri": contributors_uri, } return render(request, "portfolio_trends.html", context=context)
def show_stock_sector(request, stock): validate_stock(stock) validate_user(request.user) _, company_details = stock_info(stock, lambda msg: warning(request, msg)) sector = company_details.sector_name if company_details else None all_stocks_cip = cached_all_stocks_cip(Timeframe(past_n_days=180)) # invoke separate function to cache the calls when we can c_vs_s_plot, sector_momentum_plot, sector_companies = analyse_sector_performance( stock, sector, all_stocks_cip) point_score_plot = net_rule_contributors_plot = None if sector_companies is not None: point_score_plot, net_rule_contributors_plot = \ plot_point_scores(stock, sector_companies, all_stocks_cip, default_point_score_rules()) context = { "is_sector": True, "asx_code": stock, "sector_momentum_plot": sector_momentum_plot, "sector_momentum_title": "{} sector stocks".format(sector), "company_versus_sector_plot": c_vs_s_plot, "company_versus_sector_title": "{} vs. {} performance".format(stock, sector), "point_score_plot": point_score_plot, "point_score_plot_title": "Points score due to price movements", "net_contributors_plot": net_rule_contributors_plot, "net_contributors_plot_title": "Contributions to point score by rule", } return render(request, "stock_sector.html", context)
def download_data(request, dataset=None, output_format="csv"): validate_user(request.user) with tempfile.NamedTemporaryFile() as fh: df = get_dataset(dataset, request, None) if df is None or len(df) < 1: raise Http404("No such dataset: {}".format(dataset)) content_type = save_dataframe_to_file(df, fh.name, output_format) fh.seek(0) response = HttpResponse(fh.read(), content_type=content_type) extension_by_format = { "csv": "csv", "excel": "xlsx", "parquet": "pq", "tsv": "tsv", } extension = extension_by_format[output_format] response["Content-Disposition"] = "inline; filename=temp.{}".format( extension) return response
def market_sentiment(request, n_days=21, n_top_bottom=20, sector_n_days=180): validate_user(request.user) assert n_days > 0 assert n_top_bottom > 0 timeframe = Timeframe(past_n_days=n_days) sector_timeframe = Timeframe(past_n_days=sector_n_days) df = cached_all_stocks_cip(timeframe) sector_df = cached_all_stocks_cip(sector_timeframe) sentiment_plot, top10, bottom10 = plot_heatmap(df, timeframe, n_top_bottom=n_top_bottom) sector_performance_plot = plot_market_wide_sector_performance(sector_df) context = { "sentiment_data": sentiment_plot, "n_days": timeframe.n_days, "n_stocks_plotted": len(df), "n_top_bottom": n_top_bottom, "best_ten": top10, "worst_ten": bottom10, "watched": user_watchlist(request.user), "sector_performance": sector_performance_plot, "sector_performance_title": "Cumulative sector avg. performance: {}".format( sector_timeframe.description), "title": "Market sentiment: {}".format(timeframe.description), "market_cap_distribution_plot": plot_market_cap_distribution(tuple(df.index), latest_quotation_date('ANZ'), sector_df.columns[0]) } return render(request, "market_sentiment_view.html", context=context)
def ajax_country_bond_autocomplete(request): validate_user(request.user) assert request.method == "GET" country = request.GET.get("country", None) bonds = get_bonds_for_country(country) assert isinstance(bonds, pd.DataFrame) hits = [] # print(bonds) for i, s in bonds.iterrows(): assert s["country"] == country hits.append({"id": s["name"], "name": s["full_name"]}) return render( request, "country_bond_autocomplete_hits.html", context={ "hits": hits, "current_id": country }, )
def get_initial(self, **kwargs): stock = kwargs.get("stock", self.kwargs.get("stock")) amount = kwargs.get("amount", self.kwargs.get("amount", 5000.0)) user = self.request.user validate_stock(stock) validate_user(user) quote, latest_date = latest_quote(stock) cur_price = quote.last_price if cur_price >= 1e-6: return { "asx_code": stock, "user": user, "buy_date": latest_date, "price_at_buy_date": cur_price, "amount": amount, "n": int(amount / cur_price), } else: warning(self.request, "Cannot buy {} as its price is zero/unknown".format(stock)) return {}
def show_trends(request): validate_user(request.user) watchlist_stocks = user_watchlist(request.user) timeframe = Timeframe(past_n_days=300) cip = selected_cached_stocks_cip(watchlist_stocks, timeframe) trends = calculate_trends(cip, watchlist_stocks) #print(trends) # for now we only plot trending companies... too slow and unreadable to load the page otherwise! cip = rank_cumulative_change(cip.filter(trends.keys(), axis="index"), timeframe) #print(cip) trending_companies_plot = plot_company_rank(cip) context = { "watchlist_trends": trends, "trending_companies_plot": trending_companies_plot, "trending_companies_plot_title": "Trending watchlist companies by rank: {}".format( timeframe.description), } return render(request, "trends.html", context=context)
def login(): if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if form.validate_on_submit(): try: user = models.validate_user(form.email.data, form.password.data) login_user(user, remember=form.remember_me.data) except ValueError as e: flash(e) return render_template('login.html', title='Sign In', form=form)
def show_fundamentals(request, stock=None, n_days=2 * 365): validate_user(request.user) validate_stock(stock) timeframe = Timeframe(past_n_days=n_days) df = company_prices( [stock], timeframe, fields=("eps", "volume", "last_price", "annual_dividend_yield", \ "pe", "change_in_percent", "change_price", "market_cap", \ "number_of_shares"), missing_cb=None ) #print(df) df['change_in_percent_cumulative'] = df['change_in_percent'].cumsum( ) # nicer to display cumulative df = df.drop('change_in_percent', axis=1) fundamentals_plot = plot_fundamentals(df, stock) context = { "asx_code": stock, "is_fundamentals": True, "fundamentals_plot": fundamentals_plot } return render(request, "stock_fundamentals.html", context)
def show_stock(request, stock=None, n_days=2 * 365): """ Displays a view of a single stock via the stock_view.html template and associated state """ validate_stock(stock) validate_user(request.user) timeframe = Timeframe( past_n_days=n_days + 200 ) # add 200 days so MA 200 can initialise itself before the plotting starts... stock_df = rsi_data( stock, timeframe) # may raise 404 if too little data available securities, company_details = stock_info(stock, lambda msg: warning(request, msg)) momentum_plot = make_rsi_plot(stock, stock_df) # plot the price over timeframe in monthly blocks prices = stock_df[[ 'last_price' ]].transpose() # use list of columns to ensure pd.DataFrame not pd.Series #print(prices) monthly_maximum_plot = plot_trend(prices, sample_period='M') # populate template and render HTML page with context context = { "asx_code": stock, "securities": securities, "cd": company_details, "rsi_plot": momentum_plot, "is_momentum": True, "monthly_highest_price_plot_title": "Maximum price each month trend", "monthly_highest_price_plot": monthly_maximum_plot, "timeframe": f"{n_days} days", "watched": user_watchlist(request.user), } return render(request, "stock_view.html", context=context)
def show_watchlist_outliers(request, n_days=30): validate_user(request.user) stocks = user_watchlist(request.user) return show_outliers(request, stocks, n_days=n_days)
def show_financial_metrics(request, stock=None): validate_user(request.user) validate_stock(stock) def data_factory(ld: LazyDictionary): data_df = financial_metrics(stock) if data_df is None or len(data_df) < 1: raise Http404(f"No financial metrics available for {stock}") return data_df def find_linear_metrics(ld: LazyDictionary) -> Iterable[str]: linear_metrics = calculate_trends(ld["data_df"]) good_linear_metrics = [] for k, t in linear_metrics.items(): if t[1] < 0.1: good_linear_metrics.append(k) return good_linear_metrics def find_exp_metrics(ld: LazyDictionary) -> Iterable[str]: exp_metrics = calculate_trends( ld["data_df"], polynomial_degree=2, nrmse_cutoff=0.05 ) good_linear_metrics = set(ld["linear_metrics"]) good_exp_metrics = [] for k, t in exp_metrics.items(): if t[1] < 0.1 and k not in good_linear_metrics: good_exp_metrics.append(k) return good_exp_metrics ld = LazyDictionary() ld["data_df"] = lambda ld: data_factory(ld) ld["linear_metrics"] = lambda ld: find_linear_metrics(ld) ld["exp_metrics"] = lambda ld: find_exp_metrics(ld) # print( # f"n_metrics == {len(data_df)} n_trending={len(linear_metrics.keys())} n_good_fit={len(good_linear_metrics)} n_good_exp={len(good_exp_metrics)}" # ) def plot_metrics(df: pd.DataFrame, use_short_labels=False, **kwargs): plot = ( p9.ggplot(df, p9.aes(x="date", y="value", colour="metric")) + p9.geom_line(size=1.3) + p9.geom_point(size=3) ) if use_short_labels: plot += p9.scale_y_continuous(labels=label_shorten) n_metrics = df["metric"].nunique() return user_theme( plot, subplots_adjust={"left": 0.2}, figure_size=(12, int(n_metrics * 1.5)), **kwargs, ) def plot_linear_trending_metrics(ld: LazyDictionary): df = ld["data_df"].filter(ld["linear_metrics"], axis=0) if len(df) < 1: return None df["metric"] = df.index df = df.melt(id_vars="metric").dropna(how="any", axis=0) plot = plot_metrics(df, use_short_labels=True) plot += p9.facet_wrap("~metric", ncol=1, scales="free_y") return plot def plot_exponential_growth_metrics(ld: LazyDictionary): df = ld["data_df"].filter(ld["exp_metrics"], axis=0) if len(df) < 1: return None df["metric"] = df.index df = df.melt(id_vars="metric").dropna(how="any", axis=0) plot = plot_metrics(df) plot += p9.facet_wrap("~metric", ncol=1, scales="free_y") return plot def plot_earnings_and_revenue(ld: LazyDictionary): df = ld["data_df"].filter(["Ebit", "Total Revenue", "Earnings"], axis=0) if len(df) < 2: print(f"WARNING: revenue and earnings not availabe for {stock}") return None df["metric"] = df.index df = df.melt(id_vars="metric").dropna(how="any", axis=0) plot = plot_metrics( df, use_short_labels=True, legend_position="right", y_axis_label="$ AUD", ) # need to show metric name somewhere on plot return plot er_uri = cache_plot( f"{stock}-earnings-revenue-plot", lambda ld: plot_earnings_and_revenue(ld), datasets=ld, ) trending_metrics_uri = cache_plot( f"{stock}-trending-metrics-plot", lambda ld: plot_linear_trending_metrics(ld), datasets=ld, ) exp_growth_metrics_uri = cache_plot( f"{stock}-exponential-growth-metrics-plot", lambda ld: plot_exponential_growth_metrics(ld), datasets=ld, ) warning( request, "Due to experimental data ingest - data on this page may be wrong/misleading/inaccurate/missing. Use at own risk.", ) context = { "asx_code": stock, "data": ld["data_df"], "earnings_and_revenue_plot_uri": er_uri, "trending_metrics_plot_uri": trending_metrics_uri, "exp_growth_metrics_plot_uri": exp_growth_metrics_uri, } return render(request, "stock_financial_metrics.html", context=context)
def toggle_watched(request, stock=None): validate_stock(stock) validate_user(request.user) toggle_watchlist_entry(request.user, stock) return redirect_to_next(request)
def cluster_stocks_view(request, stocks: str): """ ref: https://pythonforfinance.net/2018/02/08/stock-clusters-using-k-means-algorithm-in-python/ """ validate_user(request.user) timeframe = Timeframe(past_n_days=300) if stocks == "watchlist": asx_codes = user_watchlist(request.user) elif stocks == "etfs": asx_codes = all_etfs() elif stocks.startswith("sector-"): sector_id = int(stocks[7:]) sector = Sector.objects.get(sector_id=sector_id) if sector is None or sector.sector_name is None: raise Http404("No stocks associated with sector") asx_codes = all_sector_stocks(sector.sector_name) else: raise Http404("Unknown stock list {}".format(stocks)) chosen_k = 7 # often a reasonable tradeoff def elbow_curve_plot(ld: LazyDictionary): distortion, _, _, _, _ = make_kmeans_cluster_dataframe( timeframe, chosen_k, asx_codes ) fig = plt.figure(figsize=(15, 5)) plt.plot(range(2, 20), distortion) plt.grid(True) plt.title("Elbow curve") return fig def cluster_plot(ld: LazyDictionary): _, _, centroids, idx, data_df = make_kmeans_cluster_dataframe( timeframe, chosen_k, asx_codes ) centroids_df = pd.DataFrame.from_records( centroids, columns=["return", "volatility"] ) plot = ( p9.ggplot( data_df, p9.aes("return", "volatility", colour="factor(cluster_id)") ) + p9.geom_point(size=3) + p9.facet_wrap("~cluster_id", ncol=3, scales="free") ) return user_theme( plot, x_axis_label="Returns (%)", y_axis_label="Volatility (%)", figure_size=(15, 15), subplots_adjust={"hspace": 0.15, "wspace": 0.15}, ) stocks_as_str = "-".join(sorted(asx_codes)) elbow_curve_uri = cache_plot( f"{request.user.username}-cluster-{stocks_as_str}-elbow-curve-plot", elbow_curve_plot, ) cluster_uri = cache_plot( f"{request.user.username}-cluster-{stocks_as_str}-kmeans-cluster-plot", cluster_plot, ) context = { "elbow_curve_plot_uri": elbow_curve_uri, "k": chosen_k, "dataset": stocks, "n_stocks": len(asx_codes), "cluster_plot_uri": cluster_uri, "timeframe": timeframe, } return render(request, "cluster_stocks.html", context=context)
def show_stock(request, stock=None, n_days=2 * 365): """ Displays a view of a single stock via the template and associated state """ validate_stock(stock) validate_user(request.user) plot_timeframe = Timeframe(past_n_days=n_days) # for template def dataframe(ld: LazyDictionary) -> pd.DataFrame: momentum_timeframe = Timeframe( past_n_days=n_days + 200 ) # to warmup MA200 function df = company_prices( (stock,), momentum_timeframe, fields=all_stock_fundamental_fields, missing_cb=None, ) return df # key dynamic images and text for HTML response. We only compute the required data if image(s) not cached # print(df) ld = LazyDictionary() ld["stock_df"] = lambda ld: ld["stock_df_200"].filter( items=plot_timeframe.all_dates(), axis="rows" ) ld["cip_df"] = lambda: cached_all_stocks_cip(plot_timeframe) ld["stock_df_200"] = lambda ld: dataframe(ld) ld["sector_companies"] = lambda: companies_with_same_sector(stock) ld["company_details"] = lambda: stock_info(stock, lambda msg: warning(request, msg)) ld["sector"] = lambda ld: ld["company_details"].get("sector_name", "") # point_score_results is a tuple (point_score_df, net_points_by_rule) ld["point_score_results"] = lambda ld: make_point_score_dataframe( stock, default_point_score_rules(), ld ) ld["stock_vs_sector_df"] = lambda ld: make_stock_vs_sector_dataframe( ld["cip_df"], stock, ld["sector_companies"] ) print(ld["stock_vs_sector_df"]) momentum_plot = cache_plot( f"{plot_timeframe.description}-{stock}-rsi-plot", lambda ld: plot_momentum(stock, plot_timeframe, ld), datasets=ld, ) monthly_maximum_plot = cache_plot( f"{plot_timeframe.description}-{stock}-monthly-maximum-plot", lambda ld: plot_trend("M", ld), datasets=ld, ) monthly_returns_plot = cache_plot( f"{plot_timeframe.description}-{stock}-monthly returns", lambda ld: plot_monthly_returns(plot_timeframe, stock, ld), datasets=ld, ) company_versus_sector_plot = cache_plot( f"{stock}-{ld['sector']}-company-versus-sector", lambda ld: plot_company_versus_sector( ld["stock_vs_sector_df"], stock, ld["sector"] ), datasets=ld, ) point_score_plot = cache_plot( f"{plot_timeframe.description}-{stock}-point-score-plot", lambda ld: plot_series(ld["point_score_results"][0], x="date", y="points"), datasets=ld, ) net_rule_contributors_plot = cache_plot( f"{plot_timeframe.description}-{stock}-rules-by-points", lambda ld: plot_points_by_rule(ld["point_score_results"][1]), datasets=ld, ) # populate template and render HTML page with context context = { "asx_code": stock, "watched": user_watchlist(request.user), "timeframe": plot_timeframe, "information": ld["company_details"], "momentum": { "rsi_plot": momentum_plot, "monthly_highest_price": { "title": "Highest price each month", "plot_uri": monthly_maximum_plot, }, }, "fundamentals": { "plot_uri": cache_plot( f"{stock}-{plot_timeframe.description}-fundamentals-plot", lambda ld: plot_fundamentals( fundamentals_dataframe(plot_timeframe, stock, ld), stock, ), datasets=ld, ), "title": "Stock fundamentals: EPS, PE, DY etc.", "timeframe": plot_timeframe, }, "stock_vs_sector": { "plot_uri": company_versus_sector_plot, "title": "Company versus sector - percentage change", "timeframe": plot_timeframe, }, "point_score": { "plot_uri": point_score_plot, "title": "Points score due to price movements", }, "net_contributors": { "plot_uri": net_rule_contributors_plot, "title": "Contributions to point score by rule", }, "month_by_month_return_uri": monthly_returns_plot, } return render(request, "stock_page.html", context=context)
def login(): user = '' error = None # if already logged in, redirect to the trips overview if 'user' in session: user = escape(session['user']) return redirect('/main') else: # login form = LoginForm() if form.validate_on_submit(): error = None # user input email = form.email.data pwd = form.insecure_password.data # return user first name only if email, pwd match DB record user = models.validate_user(email, pwd) if user is not None: flash('Logging in') session['user'] = user session['email'] = email return redirect('/main') else: error = 'Invalid credentials' return render_template('login.html', error=error, form=form) # @myapp.route('/') # @myapp.route('/index') # def index(): # print("YAY") # return redirect(url_for('/login')) # @app.route('/') # @myapp.route('/index') # def index(): # print("YAY") # if 'linkedin_token' in session: # print("in session") # me = linkedin.get('people/~') # print(jsonify(me.data)) # return jsonify(me.data) # return redirect(url_for('/login')) # @app.route('/login') # def login(): # return linkedin.authorize(callback=url_for('authorized', _external=True)) # @app.route('/logout') # def logout(): # session.pop('linkedin_token', None) # return redirect(url_for('/index')) # @app.route('/login/authorized') # def authorized(): # resp = linkedin.authorized_response() # if resp is None: # return 'Access denied: reason=%s error=%s' % ( # request.args['error_reason'], # request.args['error_description'] # ) # session['linkedin_token'] = (resp['access_token'], '') # me = linkedin.get('people/~') # return jsonify(me.data) # @linkedin.tokengetter # def get_linkedin_oauth_token(): # return session.get('linkedin_token') # def change_linkedin_query(uri, headers, body): # auth = headers.pop('Authorization') # headers['x-li-format'] = 'json' # if auth: # auth = auth.replace('Bearer', '').strip() # if '?' in uri: # uri += '&oauth2_access_token=' + auth # else: # uri += '?oauth2_access_token=' + auth # return uri, headers, body # linkedin.pre_request = change_linkedin_query # if __name__ == '__main__': # app.run()
def show_pe_trends(request): """ Display a plot of per-sector PE trends across stocks in each sector ref: https://www.commsec.com.au/education/learn/choosing-investments/what-is-price-to-earnings-pe-ratio.html """ validate_user(request.user) timeframe = Timeframe(past_n_days=180) pe_df = company_prices(None, timeframe, fields="pe", missing_cb=None, transpose=True) eps_df = company_prices(None, timeframe, fields="eps", missing_cb=None, transpose=True) ss = stocks_by_sector() ss_dict = {row.asx_code: row.sector_name for row in ss.itertuples()} #print(ss_dict) eps_stocks = set(eps_df.index) n_stocks = len(pe_df) positive_pe_stocks = set(pe_df[pe_df.sum(axis=1) > 0.0].index) all_stocks = set(pe_df.index) n_non_zero_sum = len(positive_pe_stocks) #print(exclude_zero_sum) records = [] trading_dates = set(pe_df.columns) sector_counts_all_stocks = ss['sector_name'].value_counts() all_sectors = set(ss['sector_name'].unique()) pe_pos_df = pe_df.filter(items=positive_pe_stocks, axis=0).merge(ss, left_index=True, right_on='asx_code') assert len(pe_pos_df) <= len(positive_pe_stocks) and len(pe_pos_df) > 0 market_avg_pe_df = pe_pos_df.mean(axis=0).to_frame( name='market_pe') # avg P/E by date series market_avg_pe_df['date'] = pd.to_datetime(market_avg_pe_df.index) #print(market_avg_pe_df) breakdown_by_sector_pe_pos_stocks_only = pe_pos_df[ 'sector_name'].value_counts() #print(breakdown_by_sector_pe_pos_stocks_only) sector_counts_pe_pos_stocks_only = { s[0]: s[1] for s in breakdown_by_sector_pe_pos_stocks_only.items() } #print(sector_counts_pe_pos_stocks_only) #print(sector_counts_all_stocks) #print(sector_counts_pe_pos_stocks_only) for ymd in filter(lambda d: d in trading_dates, timeframe.all_dates( )): # needed to avoid KeyError raised during DataFrame.at[] calls below sum_pe_per_sector = defaultdict(float) sum_eps_per_sector = defaultdict(float) for stock in filter(lambda code: code in ss_dict, all_stocks): sector = ss_dict[stock] assert isinstance(sector, str) if stock in eps_stocks: eps = eps_df.at[stock, ymd] if isnan(eps): continue sum_eps_per_sector[sector] += eps if stock in positive_pe_stocks: pe = pe_df.at[stock, ymd] if isnan(pe): continue assert pe >= 0.0 sum_pe_per_sector[sector] += pe #print(sum_pe_per_sector) assert len(sector_counts_pe_pos_stocks_only) == len(sum_pe_per_sector) assert len(sector_counts_all_stocks) == len(sum_eps_per_sector) for sector in all_sectors: pe_sum = sum_pe_per_sector.get(sector, None) n_pe = sector_counts_pe_pos_stocks_only.get(sector, None) pe_mean = pe_sum / n_pe if pe_sum is not None else None eps_sum = sum_eps_per_sector.get(sector, None) records.append({ 'date': ymd, 'sector': sector, 'mean_pe': pe_mean, 'sum_pe': pe_sum, 'sum_eps': eps_sum, 'n_stocks': n_stocks, 'n_sector_stocks_pe_only': n_pe }) df = pd.DataFrame.from_records(records) #print(df[df["sector"] == 'Utilities']) #print(df) context = { "title": "PE Trends: {}".format(timeframe.description), "n_stocks": n_stocks, "timeframe": timeframe, "n_stocks_with_pe": n_non_zero_sum, "sector_pe_plot": plot_sector_field(df, field="mean_pe"), "sector_eps_plot": plot_sector_field(df, field="sum_eps"), "market_pe_plot": plot_series(market_avg_pe_df, x='date', y='market_pe', y_axis_label="Market-wide mean P/E", color=None, use_smooth_line=True) } return render(request, "pe_trends.html", context)