Skip to content

Commit

Permalink
Specify size granularity
Browse files Browse the repository at this point in the history
  • Loading branch information
polakowo committed Dec 11, 2021
1 parent 57f1d4b commit 3f7f3d1
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 200 deletions.
30 changes: 16 additions & 14 deletions tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
import pandas as pd
from datetime import datetime, timedelta, timezone
import pytest
import pytz

import vectorbt as vbt
from vectorbt.utils.config import merge_dicts
from vectorbt.utils.datetime_ import to_timezone

seed = 42

Expand Down Expand Up @@ -210,15 +212,15 @@ def test_download(self):
)
index2 = pd.DatetimeIndex(
[
'2020-01-01 02:00:00',
'2020-01-02 02:00:00',
'2020-01-03 02:00:00',
'2020-01-04 02:00:00',
'2020-01-05 02:00:00'
'2020-01-01 00:00:00',
'2020-01-02 00:00:00',
'2020-01-03 00:00:00',
'2020-01-04 00:00:00',
'2020-01-05 00:00:00'
],
freq='D',
tz=timezone(timedelta(hours=2))
)
tz=pytz.utc
).tz_convert(to_timezone('Europe/Berlin'))
pd.testing.assert_series_equal(
MyData.download(0, shape=(5,), tz_localize='UTC', tz_convert='Europe/Berlin').data[0],
pd.Series(
Expand Down Expand Up @@ -458,15 +460,15 @@ def test_update(self):
)
index2 = pd.DatetimeIndex(
[
'2020-01-01 02:00:00',
'2020-01-02 02:00:00',
'2020-01-03 02:00:00',
'2020-01-04 02:00:00',
'2020-01-05 02:00:00'
'2020-01-01 00:00:00',
'2020-01-02 00:00:00',
'2020-01-03 00:00:00',
'2020-01-04 00:00:00',
'2020-01-05 00:00:00'
],
freq='D',
tz=timezone(timedelta(hours=2))
)
tz=pytz.utc
).tz_convert(to_timezone('Europe/Berlin'))
pd.testing.assert_series_equal(
MyData.download(0, shape=(5,), tz_localize='UTC', tz_convert='Europe/Berlin')
.update(tz_localize=None).data[0],
Expand Down
320 changes: 170 additions & 150 deletions tests/test_portfolio.py

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions tests/test_records.py
Original file line number Diff line number Diff line change
Expand Up @@ -3365,13 +3365,29 @@ def test_records_readable(self):
np.inf, np.inf, np.inf, np.inf, np.inf, np.inf
])
)
np.testing.assert_array_equal(
records_readable['Request Size Granularity'].values,
np.array([
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan
])
)
np.testing.assert_array_equal(
records_readable['Request Rejection Prob'].values,
np.array([
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
])
)
np.testing.assert_array_equal(
records_readable['Request Lock Cash'].values,
np.array([
False, False, False, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False, False, False, False,
False, False
])
)
np.testing.assert_array_equal(
records_readable['Request Allow Partial'].values,
np.array([
Expand Down
1 change: 1 addition & 0 deletions vectorbt/_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ def reset_theme(self) -> None:
reject_prob=0.,
min_size=1e-8,
max_size=np.inf,
size_granularity=np.nan,
lock_cash=False,
allow_partial=True,
raise_reject=False,
Expand Down
11 changes: 11 additions & 0 deletions vectorbt/portfolio/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,7 @@ def from_orders(cls: tp.Type[PortfolioT],
slippage: tp.Optional[tp.ArrayLike] = None,
min_size: tp.Optional[tp.ArrayLike] = None,
max_size: tp.Optional[tp.ArrayLike] = None,
size_granularity: tp.Optional[tp.ArrayLike] = None,
reject_prob: tp.Optional[tp.ArrayLike] = None,
lock_cash: tp.Optional[tp.ArrayLike] = None,
allow_partial: tp.Optional[tp.ArrayLike] = None,
Expand Down Expand Up @@ -1670,6 +1671,8 @@ def from_orders(cls: tp.Type[PortfolioT],
See `vectorbt.portfolio.enums.Order.max_size`. Will broadcast.
Will be partially filled if exceeded.
size_granularity (float or array_like): Granularity of the size.
See `vectorbt.portfolio.enums.Order.size_granularity`. Will broadcast.
reject_prob (float or array_like): Order rejection probability.
See `vectorbt.portfolio.enums.Order.reject_prob`. Will broadcast.
lock_cash (bool or array_like): Whether to lock cash when shorting.
Expand Down Expand Up @@ -1887,6 +1890,8 @@ def from_orders(cls: tp.Type[PortfolioT],
min_size = portfolio_cfg['min_size']
if max_size is None:
max_size = portfolio_cfg['max_size']
if size_granularity is None:
size_granularity = portfolio_cfg['size_granularity']
if reject_prob is None:
reject_prob = portfolio_cfg['reject_prob']
if lock_cash is None:
Expand Down Expand Up @@ -1952,6 +1957,7 @@ def from_orders(cls: tp.Type[PortfolioT],
slippage,
min_size,
max_size,
size_granularity,
reject_prob,
lock_cash,
allow_partial,
Expand Down Expand Up @@ -2030,6 +2036,7 @@ def from_signals(cls: tp.Type[PortfolioT],
slippage: tp.Optional[tp.ArrayLike] = None,
min_size: tp.Optional[tp.ArrayLike] = None,
max_size: tp.Optional[tp.ArrayLike] = None,
size_granularity: tp.Optional[tp.ArrayLike] = None,
reject_prob: tp.Optional[tp.ArrayLike] = None,
lock_cash: tp.Optional[tp.ArrayLike] = None,
allow_partial: tp.Optional[tp.ArrayLike] = None,
Expand Down Expand Up @@ -2151,6 +2158,7 @@ def from_signals(cls: tp.Type[PortfolioT],
Will be partially filled if exceeded. You might not be able to properly close
the position if accumulation is enabled and `max_size` is too low.
size_granularity (float or array_like): See `Portfolio.from_orders`.
reject_prob (float or array_like): See `Portfolio.from_orders`.
lock_cash (bool or array_like): See `Portfolio.from_orders`.
allow_partial (bool or array_like): See `Portfolio.from_orders`.
Expand Down Expand Up @@ -2720,6 +2728,8 @@ def from_signals(cls: tp.Type[PortfolioT],
min_size = portfolio_cfg['min_size']
if max_size is None:
max_size = portfolio_cfg['max_size']
if size_granularity is None:
size_granularity = portfolio_cfg['size_granularity']
if reject_prob is None:
reject_prob = portfolio_cfg['reject_prob']
if lock_cash is None:
Expand Down Expand Up @@ -2844,6 +2854,7 @@ def from_signals(cls: tp.Type[PortfolioT],
slippage=slippage,
min_size=min_size,
max_size=max_size,
size_granularity=size_granularity,
reject_prob=reject_prob,
lock_cash=lock_cash,
allow_partial=allow_partial,
Expand Down
38 changes: 24 additions & 14 deletions vectorbt/portfolio/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,7 @@ class Order(tp.NamedTuple):
slippage: float = 0.0
min_size: float = 0.0
max_size: float = np.inf
size_granularity: float = np.nan
reject_prob: float = 0.0
lock_cash: bool = False
allow_partial: bool = True
Expand Down Expand Up @@ -1452,6 +1453,13 @@ class Order(tp.NamedTuple):
__pdoc__['Order.max_size'] = """Maximum size in both directions.
Higher than that will be partly filled."""
__pdoc__['Order.size_granularity'] = """Granularity of the size.
For example, granularity of 1.0 makes the quantity to behave like an integer.
Placing an order of 12.5 shares (in any direction) will order exactly 12.0 shares.
!!! note
The filled size remains a floating number."""
__pdoc__['Order.reject_prob'] = """Probability of rejecting this order to simulate a random rejection event.
Not everything goes smoothly in real life. Use random rejections to test your order management for robustness."""
Expand All @@ -1472,20 +1480,21 @@ class Order(tp.NamedTuple):
Remember to increase `max_logs`."""

NoOrder = Order(
np.nan,
np.nan,
-1,
-1,
np.nan,
np.nan,
np.nan,
np.nan,
np.nan,
np.nan,
False,
False,
False,
False
size=np.nan,
price=np.nan,
size_type=-1,
direction=-1,
fees=np.nan,
fixed_fees=np.nan,
slippage=np.nan,
min_size=np.nan,
max_size=np.nan,
size_granularity=np.nan,
reject_prob=np.nan,
lock_cash=False,
allow_partial=False,
raise_reject=False,
log=False
)
"""_"""

Expand Down Expand Up @@ -1658,6 +1667,7 @@ class SignalContext(tp.NamedTuple):
('req_slippage', np.float_),
('req_min_size', np.float_),
('req_max_size', np.float_),
('req_size_granularity', np.float_),
('req_reject_prob', np.float_),
('req_lock_cash', np.bool_),
('req_allow_partial', np.bool_),
Expand Down
3 changes: 3 additions & 0 deletions vectorbt/portfolio/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@
req_max_size=dict(
title='Request Max Size'
),
req_size_granularity=dict(
title='Request Size Granularity'
),
req_reject_prob=dict(
title='Request Rejection Prob'
),
Expand Down
Loading

0 comments on commit 3f7f3d1

Please sign in to comment.