1
0
mirror of https://github.com/polakowo/vectorbt.git synced 2022-03-22 01:31:39 +03:00

Small fixes

[ci deploy-pages] [ci test-cov]
This commit is contained in:
Oleg Polakow
2021-08-28 01:23:35 +02:00
parent 619f0dfe82
commit 4cfe0998bf
3 changed files with 94 additions and 105 deletions

View File

@@ -31,7 +31,7 @@ from talib._ta_lib import (
from vectorbt import settings
from vectorbt.utils.config import merge_dicts
from vectorbt.utils.colors import adjust_opacity
from vectorbt.portfolio.enums import Direction, ConflictMode
from vectorbt.portfolio.enums import Direction, DirectionConflictMode
from vectorbt.portfolio.base import Portfolio
USE_CACHING = os.environ.get(
@@ -80,7 +80,7 @@ intervals = ['1m', '2m', '5m', '15m', '30m', '60m', '90m', '1d', '5d', '1wk', '1
patterns = talib.get_function_groups()['Pattern Recognition']
stats_table_columns = ["Metric", "Buy & Hold", "Random (Median)", "Strategy", "Z-Score"]
directions = Direction._fields
conflict_modes = ConflictMode._fields
conflict_modes = DirectionConflictMode._fields
plot_types = ['OHLC', 'Candlestick']
# Colors
@@ -1295,7 +1295,7 @@ def simulate_portfolio(df, interval, date_range, selected_data, entry_patterns,
price=df['Open'],
size=np.abs(size),
direction=direction,
conflict_mode=conflict_mode,
upon_dir_conflict=conflict_mode,
accumulate='allow_accumulate' in sim_options,
init_cash=init_cash,
fees=float(fees) / 100,

View File

@@ -3451,37 +3451,39 @@ class TestFromRandomSignals:
@njit
def order_func_nb(c, size):
return nb.order_nb(size if c.i % 2 == 0 else -size)
_size = nb.get_elem_nb(c, size)
return nb.order_nb(_size if c.i % 2 == 0 else -_size)
@njit
def log_order_func_nb(c, size):
return nb.order_nb(size if c.i % 2 == 0 else -size, log=True)
_size = nb.get_elem_nb(c, size)
return nb.order_nb(_size if c.i % 2 == 0 else -_size, log=True)
@njit
def flex_order_func_nb(c, size):
if c.call_idx < c.group_len:
return c.from_col + c.call_idx, nb.order_nb(size if c.i % 2 == 0 else -size)
_size = nb.get_col_elem_nb(c, c.from_col + c.call_idx, size)
return c.from_col + c.call_idx, nb.order_nb(_size if c.i % 2 == 0 else -_size)
return -1, nb.order_nothing_nb()
@njit
def log_flex_order_func_nb(c, size):
if c.call_idx < c.group_len:
return c.from_col + c.call_idx, nb.order_nb(size if c.i % 2 == 0 else -size, log=True)
_size = nb.get_col_elem_nb(c, c.from_col + c.call_idx, size)
return c.from_col + c.call_idx, nb.order_nb(_size if c.i % 2 == 0 else -_size, log=True)
return -1, nb.order_nothing_nb()
class TestFromOrderFunc:
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_one_column(self, test_row_wise, test_flexible):
order_func = flex_order_func_nb if test_flexible else order_func_nb
pf = vbt.Portfolio.from_order_func(
price.tolist(), order_func, np.inf, row_wise=test_row_wise, flexible=test_flexible)
price.tolist(), order_func, np.asarray(np.inf), row_wise=test_row_wise, flexible=test_flexible)
record_arrays_close(
pf.order_records,
np.array([
@@ -3491,7 +3493,7 @@ class TestFromOrderFunc:
], dtype=order_dt)
)
pf = vbt.Portfolio.from_order_func(
price, order_func, np.inf, row_wise=test_row_wise, flexible=test_flexible)
price, order_func, np.asarray(np.inf), row_wise=test_row_wise, flexible=test_flexible)
record_arrays_close(
pf.order_records,
np.array([
@@ -3512,40 +3514,34 @@ class TestFromOrderFunc:
assert pf.wrapper.freq == day_dt
assert pf.wrapper.grouper.group_by is None
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
def test_multiple_columns(self, test_row_wise, test_flexible):
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
@pytest.mark.parametrize("test_use_numba", [False, True])
def test_multiple_columns(self, test_row_wise, test_flexible, test_use_numba):
order_func = flex_order_func_nb if test_flexible else order_func_nb
pf = vbt.Portfolio.from_order_func(
price_wide, order_func, np.inf, row_wise=test_row_wise, flexible=test_flexible)
price_wide, order_func, vbt.Rep('size'), broadcast_named_args=dict(size=[0, 1, np.inf]),
row_wise=test_row_wise, flexible=test_flexible, use_numba=test_use_numba)
if test_row_wise:
record_arrays_close(
pf.order_records,
np.array([
(0, 0, 0, 100.0, 1.0, 0.0, 0), (1, 1, 0, 100.0, 1.0, 0.0, 0),
(2, 2, 0, 100.0, 1.0, 0.0, 0), (3, 0, 1, 200.0, 2.0, 0.0, 1),
(4, 1, 1, 200.0, 2.0, 0.0, 1), (5, 2, 1, 200.0, 2.0, 0.0, 1),
(6, 0, 2, 133.33333333333334, 3.0, 0.0, 0), (7, 1, 2, 133.33333333333334, 3.0, 0.0, 0),
(8, 2, 2, 133.33333333333334, 3.0, 0.0, 0), (9, 0, 3, 66.66666666666669, 4.0, 0.0, 1),
(10, 1, 3, 66.66666666666669, 4.0, 0.0, 1), (11, 2, 3, 66.66666666666669, 4.0, 0.0, 1),
(12, 0, 4, 53.33333333333335, 5.0, 0.0, 0), (13, 1, 4, 53.33333333333335, 5.0, 0.0, 0),
(14, 2, 4, 53.33333333333335, 5.0, 0.0, 0)
(0, 1, 0, 1.0, 1.0, 0.0, 0), (1, 2, 0, 100.0, 1.0, 0.0, 0),
(2, 1, 1, 1.0, 2.0, 0.0, 1), (3, 2, 1, 200.0, 2.0, 0.0, 1),
(4, 1, 2, 1.0, 3.0, 0.0, 0), (5, 2, 2, 133.33333333333334, 3.0, 0.0, 0),
(6, 1, 3, 1.0, 4.0, 0.0, 1), (7, 2, 3, 66.66666666666669, 4.0, 0.0, 1),
(8, 1, 4, 1.0, 5.0, 0.0, 0), (9, 2, 4, 53.33333333333335, 5.0, 0.0, 0)
], dtype=order_dt)
)
else:
record_arrays_close(
pf.order_records,
np.array([
(0, 0, 0, 100.0, 1.0, 0.0, 0), (1, 0, 1, 200.0, 2.0, 0.0, 1),
(2, 0, 2, 133.33333333333334, 3.0, 0.0, 0), (3, 0, 3, 66.66666666666669, 4.0, 0.0, 1),
(4, 0, 4, 53.33333333333335, 5.0, 0.0, 0), (5, 1, 0, 100.0, 1.0, 0.0, 0),
(6, 1, 1, 200.0, 2.0, 0.0, 1), (7, 1, 2, 133.33333333333334, 3.0, 0.0, 0),
(8, 1, 3, 66.66666666666669, 4.0, 0.0, 1), (9, 1, 4, 53.33333333333335, 5.0, 0.0, 0),
(10, 2, 0, 100.0, 1.0, 0.0, 0), (11, 2, 1, 200.0, 2.0, 0.0, 1),
(12, 2, 2, 133.33333333333334, 3.0, 0.0, 0), (13, 2, 3, 66.66666666666669, 4.0, 0.0, 1),
(14, 2, 4, 53.33333333333335, 5.0, 0.0, 0)
(0, 1, 0, 1.0, 1.0, 0.0, 0), (1, 1, 1, 1.0, 2.0, 0.0, 1),
(2, 1, 2, 1.0, 3.0, 0.0, 0), (3, 1, 3, 1.0, 4.0, 0.0, 1),
(4, 1, 4, 1.0, 5.0, 0.0, 0), (5, 2, 0, 100.0, 1.0, 0.0, 0),
(6, 2, 1, 200.0, 2.0, 0.0, 1), (7, 2, 2, 133.33333333333334, 3.0, 0.0, 0),
(8, 2, 3, 66.66666666666669, 4.0, 0.0, 1), (9, 2, 4, 53.33333333333335, 5.0, 0.0, 0)
], dtype=order_dt)
)
pd.testing.assert_index_equal(
@@ -3560,14 +3556,12 @@ class TestFromOrderFunc:
assert pf.wrapper.freq == day_dt
assert pf.wrapper.grouper.group_by is None
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_group_by(self, test_row_wise, test_flexible):
order_func = flex_order_func_nb if test_flexible else order_func_nb
pf = vbt.Portfolio.from_order_func(
price_wide, order_func, np.inf,
price_wide, order_func, np.asarray(np.inf),
group_by=np.array([0, 0, 1]), row_wise=test_row_wise, flexible=test_flexible)
if test_row_wise:
record_arrays_close(
@@ -3607,14 +3601,12 @@ class TestFromOrderFunc:
)
assert not pf.cash_sharing
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_cash_sharing(self, test_row_wise, test_flexible):
order_func = flex_order_func_nb if test_flexible else order_func_nb
pf = vbt.Portfolio.from_order_func(
price_wide, order_func, np.inf,
price_wide, order_func, np.asarray(np.inf),
group_by=np.array([0, 0, 1]), cash_sharing=True, row_wise=test_row_wise, flexible=test_flexible)
if test_row_wise:
record_arrays_close(
@@ -3654,7 +3646,7 @@ class TestFromOrderFunc:
)
def test_call_seq(self, test_row_wise):
pf = vbt.Portfolio.from_order_func(
price_wide, order_func_nb, np.inf, group_by=np.array([0, 0, 1]),
price_wide, order_func_nb, np.asarray(np.inf), group_by=np.array([0, 0, 1]),
cash_sharing=True, row_wise=test_row_wise)
if test_row_wise:
record_arrays_close(
@@ -3689,7 +3681,7 @@ class TestFromOrderFunc:
])
)
pf = vbt.Portfolio.from_order_func(
price_wide, order_func_nb, np.inf, group_by=np.array([0, 0, 1]),
price_wide, order_func_nb, np.asarray(np.inf), group_by=np.array([0, 0, 1]),
cash_sharing=True, call_seq='reversed', row_wise=test_row_wise)
if test_row_wise:
record_arrays_close(
@@ -3724,7 +3716,7 @@ class TestFromOrderFunc:
])
)
pf = vbt.Portfolio.from_order_func(
price_wide, order_func_nb, np.inf, group_by=np.array([0, 0, 1]),
price_wide, order_func_nb, np.asarray(np.inf), group_by=np.array([0, 0, 1]),
cash_sharing=True, call_seq='random', seed=seed, row_wise=test_row_wise)
if test_row_wise:
record_arrays_close(
@@ -3760,7 +3752,7 @@ class TestFromOrderFunc:
)
with pytest.raises(Exception):
_ = vbt.Portfolio.from_order_func(
price_wide, order_func_nb, np.inf, group_by=np.array([0, 0, 1]),
price_wide, order_func_nb, np.asarray(np.inf), group_by=np.array([0, 0, 1]),
cash_sharing=True, call_seq='auto', row_wise=test_row_wise
)
@@ -3809,10 +3801,8 @@ class TestFromOrderFunc:
target_hold_value
)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_target_value(self, test_row_wise, test_flexible):
@njit
def target_val_pre_segment_func_nb(c, val_price):
@@ -3870,10 +3860,8 @@ class TestFromOrderFunc:
], dtype=order_dt)
)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_target_percent(self, test_row_wise, test_flexible):
@njit
def target_pct_pre_segment_func_nb(c, val_price):
@@ -3931,10 +3919,8 @@ class TestFromOrderFunc:
], dtype=order_dt)
)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_update_value(self, test_row_wise, test_flexible):
if test_flexible:
@njit
@@ -4012,10 +3998,8 @@ class TestFromOrderFunc:
])
)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_states(self, test_row_wise, test_flexible):
close = np.array([
[1, 1, 1],
@@ -4289,10 +4273,8 @@ class TestFromOrderFunc:
pf.returns(in_sim_order=True, group_by=False).values
)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_post_sim_ctx(self, test_row_wise, test_flexible):
if test_flexible:
def order_func(c):
@@ -4579,10 +4561,8 @@ class TestFromOrderFunc:
assert c.log_records[c.last_lidx[1]]['col'] == 1
assert c.log_records[c.last_lidx[2]]['col'] == 2
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_free_cash(self, test_row_wise, test_flexible):
if test_flexible:
def order_func(c, size):
@@ -4730,14 +4710,12 @@ class TestFromOrderFunc:
pf.cash(free=True).values
)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_init_cash(self, test_row_wise, test_flexible):
order_func = flex_order_func_nb if test_flexible else order_func_nb
pf = vbt.Portfolio.from_order_func(
price_wide, order_func, 10., row_wise=test_row_wise,
price_wide, order_func, np.asarray(10.), row_wise=test_row_wise,
init_cash=[1., 10., np.inf], flexible=test_flexible)
if test_row_wise:
record_arrays_close(
@@ -4769,10 +4747,10 @@ class TestFromOrderFunc:
)
assert type(pf._init_cash) == np.ndarray
base_pf = vbt.Portfolio.from_order_func(
price_wide, order_func, 10., row_wise=test_row_wise,
price_wide, order_func, np.asarray(10.), row_wise=test_row_wise,
init_cash=np.inf, flexible=test_flexible)
pf = vbt.Portfolio.from_order_func(
price_wide, order_func, 10., row_wise=test_row_wise,
price_wide, order_func, np.asarray(10.), row_wise=test_row_wise,
init_cash=InitCashMode.Auto, flexible=test_flexible)
record_arrays_close(
pf.order_records,
@@ -4780,7 +4758,7 @@ class TestFromOrderFunc:
)
assert pf._init_cash == InitCashMode.Auto
pf = vbt.Portfolio.from_order_func(
price_wide, order_func, 10., row_wise=test_row_wise,
price_wide, order_func, np.asarray(10.), row_wise=test_row_wise,
init_cash=InitCashMode.AutoAlign, flexible=test_flexible)
record_arrays_close(
pf.order_records,
@@ -5456,33 +5434,35 @@ class TestFromOrderFunc:
assert list(order_lst) == [6, 8, 13, 15, 17, 22, 24, 26, 29, 31]
assert list(post_order_lst) == [7, 14, 16, 23, 25, 30]
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_max_orders(self, test_row_wise, test_flexible):
order_func = flex_order_func_nb if test_flexible else order_func_nb
_ = vbt.Portfolio.from_order_func(
price_wide, order_func, np.inf, row_wise=test_row_wise, flexible=test_flexible)
price_wide, order_func, np.asarray(np.inf),
row_wise=test_row_wise, flexible=test_flexible)
_ = vbt.Portfolio.from_order_func(
price_wide, order_func, np.inf, row_wise=test_row_wise, max_orders=15, flexible=test_flexible)
price_wide, order_func, np.asarray(np.inf),
row_wise=test_row_wise, max_orders=15, flexible=test_flexible)
with pytest.raises(Exception):
_ = vbt.Portfolio.from_order_func(
price_wide, order_func, np.inf, row_wise=test_row_wise, max_orders=14, flexible=test_flexible)
price_wide, order_func, np.asarray(np.inf),
row_wise=test_row_wise, max_orders=14, flexible=test_flexible)
@pytest.mark.parametrize(
"test_row_wise,test_flexible",
[[False, False], [False, True], [True, False], [True, True]],
)
@pytest.mark.parametrize("test_row_wise", [False, True])
@pytest.mark.parametrize("test_flexible", [False, True])
def test_max_logs(self, test_row_wise, test_flexible):
log_order_func = log_flex_order_func_nb if test_flexible else log_order_func_nb
_ = vbt.Portfolio.from_order_func(
price_wide, log_order_func, np.inf, row_wise=test_row_wise, flexible=test_flexible)
price_wide, log_order_func, np.asarray(np.inf),
row_wise=test_row_wise, flexible=test_flexible)
_ = vbt.Portfolio.from_order_func(
price_wide, log_order_func, np.inf, row_wise=test_row_wise, max_logs=15, flexible=test_flexible)
price_wide, log_order_func, np.asarray(np.inf),
row_wise=test_row_wise, max_logs=15, flexible=test_flexible)
with pytest.raises(Exception):
_ = vbt.Portfolio.from_order_func(
price_wide, log_order_func, np.inf, row_wise=test_row_wise, max_logs=14, flexible=test_flexible)
price_wide, log_order_func, np.asarray(np.inf),
row_wise=test_row_wise, max_logs=14, flexible=test_flexible)
# ############# Portfolio ############# #
@@ -7464,6 +7444,18 @@ class TestPortfolio:
'Trades with PnL over $20'
], name='a')
)
pd.testing.assert_frame_equal(
pf.stats(metrics='total_trades', agg_func=None, settings=dict(trades_type='entry_trades')),
pd.DataFrame([2, 2, 2], index=price_na.columns, columns=['Total Trades'])
)
pd.testing.assert_frame_equal(
pf.stats(metrics='total_trades', agg_func=None, settings=dict(trades_type='exit_trades')),
pd.DataFrame([2, 2, 2], index=price_na.columns, columns=['Total Trades'])
)
pd.testing.assert_frame_equal(
pf.stats(metrics='total_trades', agg_func=None, settings=dict(trades_type='positions')),
pd.DataFrame([2, 2, 2], index=price_na.columns, columns=['Total Trades'])
)
pd.testing.assert_series_equal(
pf['c'].stats(),
pf.stats(column='c')

View File

@@ -2067,7 +2067,7 @@ class Portfolio(Wrapping, StatsBuilderMixin, PlotsBuilderMixin, metaclass=MetaPo
You have three options to provide signals:
1) `entries` and `exits`: The direction of each pair of signals is taken from `direction` argument.
* `entries` and `exits`: The direction of each pair of signals is taken from `direction` argument.
Best to use when the direction doesn't change throughout time.
Uses `vectorbt.portfolio.nb.dir_enex_signal_func_nb` as `signal_func_nb`.
@@ -2078,12 +2078,14 @@ class Portfolio(Wrapping, StatsBuilderMixin, PlotsBuilderMixin, metaclass=MetaPo
* (True, True, 'longonly') -> True, True, False, False
* (True, True, 'shortonly') -> False, False, True, True
* (True, True, 'both') -> True, False, True, False
2) `entries` (acting as long), `exits` (acting as long), `short_entries`, and `short_exits`:
* `entries` (acting as long), `exits` (acting as long), `short_entries`, and `short_exits`:
The direction is already built into the arrays. Best to use when the direction changes frequently
(for example, if you have one indicator providing long signals and one providing short signals).
Uses `vectorbt.portfolio.nb.ls_enex_signal_func_nb` as `signal_func_nb`.
3) `signal_func_nb` and `signal_args`: Custom signal function that returns direction-aware signals.
* `signal_func_nb` and `signal_args`: Custom signal function that returns direction-aware signals.
Best to use when signals should be placed dynamically based on custom conditions.
Args:
@@ -2150,10 +2152,8 @@ class Portfolio(Wrapping, StatsBuilderMixin, PlotsBuilderMixin, metaclass=MetaPo
See `vectorbt.portfolio.enums.ConflictMode`. Will broadcast.
upon_short_conflict (ConflictMode or array_like): Conflict mode for short signals.
See `vectorbt.portfolio.enums.ConflictMode`. Will broadcast.
upon_dir_conflict (DirectionConflictMode or array_like):
See `vectorbt.portfolio.enums.DirectionConflictMode`. Will broadcast.
upon_opposite_entry (OppositeEntryMode or array_like):
See `vectorbt.portfolio.enums.OppositeEntryMode`. Will broadcast.
upon_dir_conflict (DirectionConflictMode or array_like): See `vectorbt.portfolio.enums.DirectionConflictMode`. Will broadcast.
upon_opposite_entry (OppositeEntryMode or array_like): See `vectorbt.portfolio.enums.OppositeEntryMode`. Will broadcast.
direction (Direction or array_like): See `Portfolio.from_orders`.
Takes only effect if `short_entries` and `short_exits` are not set.
@@ -3194,9 +3194,6 @@ class Portfolio(Wrapping, StatsBuilderMixin, PlotsBuilderMixin, metaclass=MetaPo
cash_sharing (bool): Whether to share cash within the same group.
If `group_by` is None, `group_by` becomes True to form a single group with cash sharing.
!!! warning
Introduces cross-asset dependencies.
call_seq (CallSeqType or array_like): Default sequence of calls per row and group.
* Use `vectorbt.portfolio.enums.CallSeqType` to select a sequence type.