This notebook explores the relative volatity of different countries, above and below a given dollar volume threshold.
from quantrocket import get_prices
from quantrocket.account import download_exchange_rates
from quantrocket.master import get_securities_reindexed_like
import pandas as pd
import io
Get exchange rates (for converting non-USD stocks to USD dollar volume):
f = io.StringIO()
download_exchange_rates(f, latest=True, base_currencies="USD")
rates = pd.read_csv(f)
rates = rates.set_index("QuoteCurrency").Rate.to_dict()
def get_std(db, start_date, min_dollar_volume=0, max_dollar_volume=None, universes=None):
"""
Returns the average standard dev of monthly, daily, overnight, and intraday returns from
the start date until now.
"""
prices = get_prices(
db,
start_date=start_date,
universes=universes,
fields=["Close","Open","Volume"])
closes = prices.loc["Close"]
opens = prices.loc["Open"]
securities = get_securities_reindexed_like(closes, fields=["Currency","Multiplier","PriceMagnifier"])
currencies = securities.loc["Currency"]
multipliers = securities.loc["Multiplier"]
magnifiers = securities.loc["PriceMagnifier"]
volumes = prices.loc["Volume"]
currencies = prices.loc["Currency"]
multipliers = prices.loc["Multiplier"].reindex(closes.index, method="ffill")
magnifiers = prices.loc["PriceMagnifier"].reindex(closes.index, method="ffill")
exchange_rates = currencies.applymap(lambda x: rates[x])
exchange_rates = exchange_rates.reindex(closes.index, method="ffill")
closes = closes * multipliers.fillna(1) / magnifiers.fillna(1)
opens = opens * multipliers.fillna(1) / magnifiers.fillna(1)
dollar_volumes = closes.div(exchange_rates) * volumes
avg_dollar_volumes = dollar_volumes.mean()
sufficient_dollar_volumes = avg_dollar_volumes >= min_dollar_volume
if max_dollar_volume:
sufficient_dollar_volumes = sufficient_dollar_volumes & (avg_dollar_volumes <= max_dollar_volume)
monthly_returns = (closes - closes.shift(22)) / closes.shift(22)
monthly_returns = monthly_returns
monthly_returns = monthly_returns.where((monthly_returns > -0.9) & (monthly_returns < 9), 0)
monthly_std = monthly_returns.std()[sufficient_dollar_volumes].mean()
cc_returns = closes.pct_change()
# Ignore outliers
cc_returns = cc_returns.where((cc_returns > -0.5) & (cc_returns < 1))
cc_std = cc_returns.std()[sufficient_dollar_volumes].mean()
co_returns = (opens - closes.shift()) / closes.shift()
co_returns = co_returns.where((co_returns > -0.5) & (co_returns < 1))
co_std = co_returns.std()[sufficient_dollar_volumes].mean()
oc_returns = (closes - opens) / opens
oc_returns = oc_returns.where((oc_returns > -0.5) & (oc_returns < 1))
oc_std = oc_returns.std()[sufficient_dollar_volumes].mean()
return {
"monthly": monthly_std,
"daily": cc_std,
"intraday (open-to-close)": oc_std,
"overnight (close-to-open)": co_std,
}
def get_daily_std_by_year(db, years=[2015,2016,2017,2018], min_dollar_volume=0, max_dollar_volume=None, universes=None):
"""
Returns the average daily standard deviation for each of the years.
"""
all_years = {}
for year in years:
print(year)
prices = get_prices(
db,
start_date="{0}-01-01".format(year),
end_date="{0}-12-31".format(year),
universes=universes,
fields=["Close","Open","Volume"])
closes = prices.loc["Close"]
opens = prices.loc["Open"]
volumes = prices.loc["Volume"]
securities = get_securities_reindexed_like(closes, fields=["Currency","Multiplier","PriceMagnifier"])
currencies = securities.loc["Currency"]
multipliers = securities.loc["Multiplier"]
magnifiers = securities.loc["PriceMagnifier"]
exchange_rates = currencies.applymap(lambda x: rates[x])
exchange_rates = exchange_rates.reindex(closes.index, method="ffill")
closes = closes * multipliers.fillna(1) / magnifiers.fillna(1)
opens = opens * multipliers.fillna(1) / magnifiers.fillna(1)
dollar_volumes = closes.div(exchange_rates) * volumes
avg_dollar_volumes = dollar_volumes.mean()
sufficient_dollar_volumes = avg_dollar_volumes >= min_dollar_volume
if max_dollar_volume:
sufficient_dollar_volumes = sufficient_dollar_volumes & (avg_dollar_volumes <= max_dollar_volume)
cc_returns = closes.pct_change()
# Ignore outliers
cc_returns = cc_returns.where((cc_returns > -0.5) & (cc_returns < 1))
cc_std = cc_returns.std()[sufficient_dollar_volumes].mean()
all_years[year] = cc_std
return all_years
Countries for which numbers should be run:
countries = (
# North America
("usa-stk-1d", "United States"),
("canada-stk-1d", "Canada"),
("mexico-stk-1d", "Mexico"),
# Asia
("australia-stk-1d", "Australia"),
("japan-stk-1d", "Japan"),
("hongkong-stk-1d", "Hong Kong"),
("singapore-stk-1d", "Singapore"),
# Europe
("spain-stk-1d", "Spain"),
("netherlands-stk-1d", "Netherlands"),
("uk-stk-1d", "United Kingdom"),
("germany-stk-1d", "Germany"),
("france-stk-1d", "France"),
("belgium-stk-1d", "Belgium"),
("swiss-stk-1d", "Switzerland"),
("austria-stk-1d", "Austria"),
("sweden-stk-1d", "Sweden"),
("italy-stk-1d", "Italy"),
)
Run numbers for each country:
volatilities = {}
start_date = "2018-01-01"
for db, country in countries:
print(country)
volatilities[country] = get_std(db, start_date)
volatilities = pd.DataFrame(volatilities).T * 100
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(palette=sns.color_palette('deep', n_colors=3))
ax = volatilities.drop("monthly", axis=1).sort_values("daily", ascending=False).plot(kind="bar", title="Volatility of returns (all stocks)", figsize=(16,6))
ax.set_ylabel("Standard deviation (%)")
Repeat with a minimum dollar volume threshold of $1M USD:
volatilities = {}
start_date = "2018-01-01"
for db, country in countries:
print(country)
volatilities[country] = get_std(db, start_date, min_dollar_volume=1000000)
volatilities = pd.DataFrame(volatilities).T * 100
ax = volatilities.drop("monthly", axis=1).sort_values("daily", ascending=False).plot(kind="bar", title="Volatility of returns for stocks with daily average dollar volume above $1M USD", figsize=(16,6))
ax.set_ylabel("Standard deviation (%)")
Repeat with a maximum dollar volume threshold of $1M USD:
volatilities = {}
start_date = "2018-01-01"
for db, country in countries:
print(country)
volatilities[country] = get_std(db, start_date, max_dollar_volume=1000000)
volatilities = pd.DataFrame(volatilities).T * 100
ax = volatilities.drop("monthly", axis=1).sort_values("daily", ascending=False).plot(kind="bar", title="Volatility of returns for stocks with daily average dollar volume below $1M USD", figsize=(16,6))
ax.set_ylabel("Standard deviation (%)")
volatilities = {}
for db, country in countries:
print(country)
volatilities[country] = get_daily_std_by_year(db, min_dollar_volume=1000000)
volatilities = pd.DataFrame(volatilities).T * 100
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(palette=sns.color_palette('deep', n_colors=4))
ax = volatilities.sort_values(2018, ascending=False).plot(kind="bar", title="Volatility of daily returns for stocks with daily average dollar volume above $1M USD", figsize=(16,6))
ax.set_ylabel("Standard deviation (%)")
# Get top 3 most volatile by year
volatilities.apply(lambda x: pd.Series(x.sort_values(ascending=False).head(3).index, index=["1st","2nd","3rd"]))