Skip to content

Commit

Permalink
merge plot
Browse files Browse the repository at this point in the history
  • Loading branch information
Heerozh committed Nov 28, 2019
1 parent 24f37d8 commit 99615e4
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 159 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ engine.set_filter( universe )

f1 = -(factors.MA(5)-factors.MA(10)-factors.MA(30))
f2 = -factors.BBANDS(win=5)
f2 = f2.filter(f2 < 0.5)

engine.add( f1.rank(mask=universe).zscore(), 'ma_cross' )
engine.add( f2.filter(f2 > -0.5).rank(mask=universe).zscore(), 'bb' )
engine.add( f2.rank(mask=universe).zscore(), 'bb' )

engine.to_cuda()
%time factor_data = engine.full_run("2013-01-02", "2018-01-19", periods=(1,5,10,))
Expand Down
5 changes: 2 additions & 3 deletions spectre/factors/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from typing import Union, Iterable, Tuple
import warnings
from .factor import BaseFactor, DataFactor, FilterFactor, AdjustedDataFactor
from .plotting import plot_quantile_returns, plot_cumulative_return
from .plotting import plot_quantile_and_cumulative_returns
from .dataloader import DataLoader
from ..parallel import ParallelGroupBy
import pandas as pd
Expand Down Expand Up @@ -367,7 +367,6 @@ def full_run(self, start, end, trade_at='close', periods=(1, 4, 9),

# plot
if preview:
plot_quantile_returns(mean_return)
plot_cumulative_return(factor_data)
plot_quantile_and_cumulative_returns(factor_data, mean_return)

return factor_data, mean_return
14 changes: 10 additions & 4 deletions spectre/factors/factor.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,17 @@ def compute(self, *inputs: Sequence[torch.Tensor]) -> torch.Tensor:
| 0 | 0 | NaN | ... | 123.45 |
| | ... | ... | ... | ... |
| | N | xxx.xx | ... | 234.56 |
If this table too big, it will split to multiple tables and call the callback
function separately.
* Groupby time(set `is_timegroup = True`):
| time id | stock1 | ... | stockN |
|----------|--------|-----|--------|
| 0 | 100.00 | ... | 200.00 |
If this table too big, it will split to multiple tables.
set N = asset count, Max = Max asset count in all time
| time id | price(t+0) | ... | price(t+N) | price(t+N+1) | ... | price(t+Max) |
|----------|------------|-----|------------|--------------|-----|--------------|
| 0 | 100.00 | ... | 200.00 | NaN | ... | Nan |
The prices is all asset price in same tick time, this is useful for calculations
such as rank, quantile.
But the order of assets in each row (time) is random, so the column cannot be
considered as a particular asset.
* Custom:
Use `series = self._revert_to_series(input)` you can get `pd.Series` data type, and
manipulate by your own. Remember to call `return self._regroup(series)` when returning.
Expand Down
95 changes: 35 additions & 60 deletions spectre/factors/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
]


def plot_quantile_returns(mean_ret):
def plot_quantile_and_cumulative_returns(factor_data, mean_ret):
"""
install plotly extension first:
https://plot.ly/python/getting-started/
Expand All @@ -31,84 +31,59 @@ def plot_quantile_returns(mean_ret):
x = quantiles
factors = mean_ret.columns.levels[0]
periods = mean_ret.columns.levels[1]
rows = math.ceil(len(factors) / 2)
rows = math.ceil(len(factors))
cols = 2

colors = dict(zip(periods, cycle(DEFAULT_COLORS)))
styles = {
quantile_styles = {
period: {'name': period, 'legendgroup': period,
'hovertemplate': '<b>Quantile</b>:%{x}<br>'
'<b>Return</b>: %{y:.3f}%±%{error_y.array:.3f}%',
'marker': {'color': colors[period]}}
for period in periods
}
cumulative_styles = {
period: {'name': period, 'mode': 'lines', 'legendgroup': period, 'showlegend': False,
'hovertemplate': '<b>Date</b>:%{x}<br>'
'<b>Return</b>: %{y:.3f}x',
'marker': {'color': colors[period]}}
for period in periods
}

fig = go.Figure()
fig = subplots.make_subplots(
rows=rows, cols=2,
subplot_titles=factors,
vertical_spacing=0.03,
horizontal_spacing=0.06,
subplot_titles=['Quantile Return', 'Portfolio cumulative returns'],
)

for i, factor in enumerate(factors):
row = int(i / cols) + 1
col = i % cols + 1
row = i + 1
weight_col = (factor, 'factor_weight')
weighted = factor_data['Returns'].multiply(factor_data[weight_col], axis=0)
factor_return = weighted.groupby(level='date').sum()
for j, period in enumerate(periods):
y = mean_ret.loc[:, (factor, period, 'mean')] * 100
err_y = mean_ret.loc[:, (factor, period, 'sem')] * 100
fig.add_trace(go.Bar(
x=x, y=y, error_y=dict(type='data', array=err_y, thickness=0.2),
**styles[period]
), row=row, col=col)
styles[period]['showlegend'] = False
fig.update_xaxes(title_text="factor quantile", type="category", row=row, col=col)

fig.update_layout(height=400 * rows, barmode='group', bargap=0.5,
title_text="Mean return by quantile")
fig.show()


def plot_cumulative_return(factor_data):
import plotly.graph_objects as go
import plotly.subplots as subplots

factors = list(factor_data.columns.levels[0])
factors.remove('Demeaned')
factors.remove('Returns')
periods = factor_data['Returns'].columns

rows = math.ceil(len(factors) / 2)
cols = 2

fig = go.Figure()
fig = subplots.make_subplots(
rows=rows, cols=2,
subplot_titles=factors,
)

colors = dict(zip(periods, cycle(DEFAULT_COLORS)))
styles = {
period: {'name': period, 'mode': 'lines', 'legendgroup': period,
'marker': {'color': colors[period]}}
for period in periods
}

for i, factor in enumerate(factors):
row = int(i / cols) + 1
col = i % cols + 1
weight_col = (factor, 'factor_weight')
weighted = factor_data['Returns'].multiply(factor_data[weight_col], axis=0)
factor_return = weighted.groupby(level='date').sum()
for period in periods:
cumret = factor_return[period].resample('b' + period).mean().dropna()
cumret = (cumret + 1).cumprod()
fig.add_trace(go.Scatter(x=cumret.index, y=cumret.values, **styles[period]),
row=row, col=col)
styles[period]['showlegend'] = False

fig.add_shape(go.layout.Shape(type="line", y0=1, y1=1,
x0=cumret.index[0], x1=cumret.index[-1],
line=dict(width=1)),
row=row, col=col)

fig.update_layout(height=400 * rows, title_text="Portfolio cumulative return")
**quantile_styles[period]
), row=row, col=1)
quantile_styles[period]['showlegend'] = False
fig.update_xaxes(type="category", row=row, col=1)
fig.update_yaxes(title_text=factor, row=row, col=1, ticksuffix='%')

cum_ret = factor_return[period].resample('b' + period).mean().dropna()
cum_ret = (cum_ret + 1).cumprod()
fig.add_trace(go.Scatter(
x=cum_ret.index, y=cum_ret.values, **cumulative_styles[period]
), row=row, col=2)

fig.add_shape(go.layout.Shape(
type="line", line=dict(width=1),
y0=1, y1=1, x0=cum_ret.index[0], x1=cum_ret.index[-1],
), row=row, col=2)

fig.update_layout(height=300 * rows, barmode='group', bargap=0.5, margin={'t': 50})
fig.show()
Loading

0 comments on commit 99615e4

Please sign in to comment.