<jupyter_start><jupyter_text>US Equity market performance from 1995 to 2020In this visualization we'll look S&P 500 Index performance from 1995 to 2020. The visulization consists of two charts:* Time Series Chart of Index prices* Histogram of the log returnsWe'll use the [Fast Interval Selector](../Interactions/Selectors.ipynb) to quickly select different time slices and perform time series analysis on the selected slice (in this case, displaying the total return and the distribution of log returns). **Click on the time series chart to activate the interval selector!**Fun things to try:1. `FastIntervalSelector` responds to mouse moves! Move the mouse pointer up to expand the interval, down to contract the interval. Single click toggles between freezing/unfreezing the window length. Double click totally freezes the interval selector. Move the interval selector to perform quick visual time series analysis!2. Notice that the color of the return label changes with the sign of the total return (red for negative returns and green for positive returns)3. What was the index performance from 1995-2005? 2000-2010? 2010-2020?4. Does the return distribution have fat tails? (especially during 2008 financial crisis, 2020 covid crisis)<jupyter_code>import numpy as np
import pandas as pd

import ipywidgets as widgets
from bqplot import DateScale, LinearScale
import bqplot.pyplot as plt
from bqplot.interacts import FastIntervalSelector
format_date = lambda d: d.strftime("%m/%d/%Y")

spx_prices = pd.read_csv("spx_prices.csv", index_col=0, parse_dates=True, squeeze=True)
log_returns = np.log(spx_prices / spx_prices.shift(1))
dates = spx_prices.index
fig_title_tmpl = "S&P Index Prices (from {start} to {end})"
fig_title = fig_title_tmpl.format(
    start=format_date(dates[0]), end=format_date(dates[-1])
)
time_series_fig = plt.figure(
    title=fig_title, layout=widgets.Layout(width="1300px", height="500px")
)

plt.scales(scales={"x": DateScale()})
axes_options = {"y": {"tick_format": ","}}
time_series = plt.plot(
    dates,
    spx_prices,
    colors=["dodgerblue"],
    stroke_width=1.5,
    axes_options=axes_options,
)
return_label = plt.label(
    [],
    x=[],
    y=[spx_prices.max() * 0.9],
    align="middle",
    default_size=36,
    font_weight="bolder",
    colors=["green"],
)
trend_line = plt.plot([], [])
intsel = FastIntervalSelector(scale=time_series.scales["x"], marks=[time_series])
time_series_fig.interaction = intsel

hist_fig = plt.figure(
    title="Histogram Of Returns",
    layout=widgets.Layout(width="900px", height="450px", margin="0px 150px 0px 150px"),
)
plt.scales(scales={"x": LinearScale(min=-0.1, max=0.1)})
hist = plt.hist(log_returns, colors=["salmon"], bins=75)
for axis in hist_fig.axes:
    axis.grid_lines = "none"
    if axis.orientation != "vertical":
        axis.tick_format = ".0%"


def update_hist(*args):
    selected_ix = time_series.selected
    if selected_ix is not None:
        s, e = selected_ix[0], selected_ix[-1]
        spx_prices_slice = spx_prices[s:e]
        pct_return = spx_prices[e] / spx_prices[s] - 1

        # update the position of the total return label
        with return_label.hold_sync():
            return_label.text = ["{:.0%}".format(pct_return)]
            return_label.x = [time_series.x[(s + e) // 2]]
            return_label.colors = ["red"] if pct_return < 0.0 else ["green"]

        time_series_fig.title = fig_title_tmpl.format(
            start=format_date(dates[s]), end=format_date(dates[e])
        )
        # update hist with returns
        hist.sample = np.log(spx_prices_slice / spx_prices_slice.shift(1))


time_series.observe(update_hist, "selected")
widgets.VBox([time_series_fig, hist_fig])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1128040630847367, 'fraction_numerical': 0.026998128842555466, 'mean_word_length': 4.446870451237263, 'pattern_counts': {'":': 5, '<': 6, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '626917', 'license': 'Apache-2.0', 'n_tokens_mistral': 1273, 'n_tokens_neox': 1174, 'n_words': 347, 'path': 'examples/Applications/Equity Index Performance/Time Series.ipynb', 'repo_name': 'trungleduc/bqplot', 'size': '3741'}