增加vectorbt源码

This commit is contained in:
2025-11-01 09:32:26 +08:00
parent 63b2c3c06a
commit bbfda1446c
139 changed files with 176161 additions and 741 deletions

View File

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,297 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 65,
"id": "b8949d99-6bf5-4abb-a857-28c97f6667b9",
"metadata": {},
"outputs": [],
"source": [
"from datetime import datetime\n",
"import backtrader as bt\n",
"import pandas as pd\n",
"import numpy as np\n",
"import vectorbt as vbt\n",
"\n",
"df = pd.DataFrame(index=[datetime(2020, 1, i + 1) for i in range(9)])\n",
"df['open'] = [1, 1, 2, 3, 4, 5, 6, 7, 8]\n",
"df['high'] = df['open'] + 0.5\n",
"df['low'] = df['open'] - 0.5\n",
"df['close'] = df['open']\n",
"data = bt.feeds.PandasData(dataname=df)\n",
"size = np.array([5, 5, -5, -5, -5, -5, 5, 5, 0])\n",
"\n",
"\n",
"class CommInfoFloat(bt.CommInfoBase):\n",
" \"\"\"Commission schema that keeps size as float.\"\"\"\n",
" params = (\n",
" ('stocklike', True),\n",
" ('commtype', bt.CommInfoBase.COMM_PERC),\n",
" ('percabs', True),\n",
" )\n",
" \n",
" def getsize(self, price, cash):\n",
" if not self._stocklike:\n",
" return self.p.leverage * (cash / self.get_margin(price))\n",
"\n",
" return self.p.leverage * (cash / price)\n",
"\n",
"\n",
"class CashValueAnalyzer(bt.analyzers.Analyzer):\n",
" \"\"\"Analyzer to extract cash and value.\"\"\"\n",
" def create_analysis(self):\n",
" self.rets = {}\n",
"\n",
" def notify_cashvalue(self, cash, value):\n",
" self.rets[self.strategy.datetime.datetime()] = (cash, value)\n",
"\n",
" def get_analysis(self):\n",
" return self.rets\n",
"\n",
"\n",
"class TestStrategy(bt.Strategy):\n",
" def __init__(self):\n",
" self.i = 0\n",
" \n",
" def log(self, txt, dt=None):\n",
" dt = dt or self.data.datetime[0]\n",
" dt = bt.num2date(dt)\n",
" print('%s, %s' % (dt.isoformat(), txt))\n",
" \n",
" def notify_order(self, order):\n",
" if order.status in [bt.Order.Submitted, bt.Order.Accepted]:\n",
" return # Await further notifications\n",
"\n",
" if order.status == order.Completed:\n",
" if order.isbuy():\n",
" buytxt = 'BUY COMPLETE {}, size = {:.2f}, price = {:.2f}'.format(\n",
" order.data._name, order.executed.size, order.executed.price)\n",
" self.log(buytxt, order.executed.dt)\n",
" else:\n",
" selltxt = 'SELL COMPLETE {}, size = {:.2f}, price = {:.2f}'.format(\n",
" order.data._name, order.executed.size, order.executed.price)\n",
" self.log(selltxt, order.executed.dt)\n",
"\n",
" elif order.status in [order.Expired, order.Canceled, order.Margin]:\n",
" self.log('%s ,' % order.Status[order.status])\n",
" pass # Simply log\n",
"\n",
" # Allow new orders\n",
" self.orderid = None\n",
" \n",
" def next(self):\n",
" if size[self.i] > 0:\n",
" self.buy(size=size[self.i])\n",
" elif size[self.i] < 0:\n",
" self.sell(size=-size[self.i])\n",
" self.i += 1\n",
"\n",
"def bt_simulate(shortcash):\n",
" cerebro = bt.Cerebro()\n",
" comminfo = CommInfoFloat(commission=0.01)\n",
" cerebro.broker.addcommissioninfo(comminfo)\n",
" cerebro.addstrategy(TestStrategy)\n",
" cerebro.addanalyzer(CashValueAnalyzer)\n",
" cerebro.broker.setcash(100.)\n",
" cerebro.broker.set_checksubmit(False)\n",
" cerebro.broker.set_shortcash(shortcash)\n",
" cerebro.adddata(data)\n",
" return cerebro.run()[0]"
]
},
{
"cell_type": "code",
"execution_count": 66,
"id": "f3b0b9e6-c91e-4c1d-a587-b95266bc24b1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2020-01-02T00:00:00, BUY COMPLETE , size = 5.00, price = 1.00\n",
"2020-01-03T00:00:00, BUY COMPLETE , size = 5.00, price = 2.00\n",
"2020-01-04T00:00:00, SELL COMPLETE , size = -5.00, price = 3.00\n",
"2020-01-05T00:00:00, SELL COMPLETE , size = -5.00, price = 4.00\n",
"2020-01-06T00:00:00, SELL COMPLETE , size = -5.00, price = 5.00\n",
"2020-01-07T00:00:00, SELL COMPLETE , size = -5.00, price = 6.00\n",
"2020-01-08T00:00:00, BUY COMPLETE , size = 5.00, price = 7.00\n",
"2020-01-09T00:00:00, BUY COMPLETE , size = 5.00, price = 8.00\n"
]
},
{
"data": {
"text/plain": [
"{datetime.datetime(2020, 1, 1, 0, 0): (100.0, 100.0),\n",
" datetime.datetime(2020, 1, 2, 0, 0): (94.95, 99.95),\n",
" datetime.datetime(2020, 1, 3, 0, 0): (84.85000000000001, 104.85000000000001),\n",
" datetime.datetime(2020, 1, 4, 0, 0): (99.7, 114.7),\n",
" datetime.datetime(2020, 1, 5, 0, 0): (119.5, 119.5),\n",
" datetime.datetime(2020, 1, 6, 0, 0): (144.25, 119.25),\n",
" datetime.datetime(2020, 1, 7, 0, 0): (173.95, 113.94999999999999),\n",
" datetime.datetime(2020, 1, 8, 0, 0): (138.6, 103.6),\n",
" datetime.datetime(2020, 1, 9, 0, 0): (98.19999999999999, 98.19999999999999)}"
]
},
"execution_count": 66,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strategy = bt_simulate(True)\n",
"strategy.analyzers.cashvalueanalyzer.get_analysis()"
]
},
{
"cell_type": "code",
"execution_count": 67,
"id": "e91645b8-9efe-468a-9154-d0928c9518e6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2020-01-01 100.00\n",
"2020-01-02 94.95\n",
"2020-01-03 84.85\n",
"2020-01-04 99.70\n",
"2020-01-05 119.50\n",
"2020-01-06 144.25\n",
"2020-01-07 173.95\n",
"2020-01-08 138.60\n",
"2020-01-09 98.20\n",
"Name: close, dtype: float64\n",
"2020-01-01 100.00\n",
"2020-01-02 99.95\n",
"2020-01-03 104.85\n",
"2020-01-04 114.70\n",
"2020-01-05 119.50\n",
"2020-01-06 119.25\n",
"2020-01-07 113.95\n",
"2020-01-08 103.60\n",
"2020-01-09 98.20\n",
"Name: close, dtype: float64\n"
]
}
],
"source": [
"portfolio = vbt.Portfolio.from_orders(df.close, [np.nan] + size[:-1].tolist(), fees=0.01)\n",
"print(portfolio.cash(free=False))\n",
"print(portfolio.value())"
]
},
{
"cell_type": "code",
"execution_count": 68,
"id": "bf4257a3-511a-405a-a1fd-9d7250935f5f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2020-01-02T00:00:00, BUY COMPLETE , size = 5.00, price = 1.00\n",
"2020-01-03T00:00:00, BUY COMPLETE , size = 5.00, price = 2.00\n",
"2020-01-04T00:00:00, SELL COMPLETE , size = -5.00, price = 3.00\n",
"2020-01-05T00:00:00, SELL COMPLETE , size = -5.00, price = 4.00\n",
"2020-01-06T00:00:00, SELL COMPLETE , size = -5.00, price = 5.00\n",
"2020-01-07T00:00:00, SELL COMPLETE , size = -5.00, price = 6.00\n",
"2020-01-08T00:00:00, BUY COMPLETE , size = 5.00, price = 7.00\n",
"2020-01-09T00:00:00, BUY COMPLETE , size = 5.00, price = 8.00\n"
]
},
{
"data": {
"text/plain": [
"{datetime.datetime(2020, 1, 1, 0, 0): (100.0, 100.0),\n",
" datetime.datetime(2020, 1, 2, 0, 0): (94.95, 99.95),\n",
" datetime.datetime(2020, 1, 3, 0, 0): (84.85000000000001, 104.85000000000001),\n",
" datetime.datetime(2020, 1, 4, 0, 0): (99.7, 114.7),\n",
" datetime.datetime(2020, 1, 5, 0, 0): (119.5, 119.5),\n",
" datetime.datetime(2020, 1, 6, 0, 0): (94.25, 119.25),\n",
" datetime.datetime(2020, 1, 7, 0, 0): (63.95, 113.95),\n",
" datetime.datetime(2020, 1, 8, 0, 0): (83.60000000000001, 103.60000000000001),\n",
" datetime.datetime(2020, 1, 9, 0, 0): (98.2, 98.2)}"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"strategy = bt_simulate(False)\n",
"strategy.analyzers.cashvalueanalyzer.get_analysis()"
]
},
{
"cell_type": "code",
"execution_count": 69,
"id": "e936ecb7-4878-4d9f-97ba-675292e95a47",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2020-01-01 100.00\n",
"2020-01-02 94.95\n",
"2020-01-03 84.85\n",
"2020-01-04 99.70\n",
"2020-01-05 119.50\n",
"2020-01-06 94.25\n",
"2020-01-07 63.95\n",
"2020-01-08 83.60\n",
"2020-01-09 98.20\n",
"Name: close, dtype: float64\n",
"2020-01-01 100.00\n",
"2020-01-02 99.95\n",
"2020-01-03 104.85\n",
"2020-01-04 114.70\n",
"2020-01-05 119.50\n",
"2020-01-06 119.25\n",
"2020-01-07 113.95\n",
"2020-01-08 103.60\n",
"2020-01-09 98.20\n",
"Name: close, dtype: float64\n"
]
}
],
"source": [
"print(portfolio.cash(free=True))\n",
"print(portfolio.value())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6690767c-70f6-4ff2-859e-979b3bd83f2f",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

3293
vectorbt/tests/test_base.py Normal file

File diff suppressed because it is too large Load Diff

1066
vectorbt/tests/test_data.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,413 @@
from datetime import datetime
import numpy as np
import pandas as pd
import vectorbt as vbt
close_ts = pd.DataFrame({
'a': [1, 2, 1, 2, 3, 2],
'b': [3, 2, 3, 2, 1, 2]
}, index=pd.Index([
datetime(2020, 1, 1),
datetime(2020, 1, 2),
datetime(2020, 1, 3),
datetime(2020, 1, 4),
datetime(2020, 1, 5),
datetime(2020, 1, 6)
]))
pos_ths = [np.array([1, 1 / 2]), np.array([2, 1 / 2]), np.array([3, 1 / 2])]
neg_ths = [np.array([1 / 2, 1 / 3]), np.array([1 / 2, 2 / 3]), np.array([1 / 2, 3 / 4])]
# ############# Global ############# #
def setup_module():
vbt.settings.numba['check_func_suffix'] = True
vbt.settings.caching.enabled = False
vbt.settings.caching.whitelist = []
vbt.settings.caching.blacklist = []
def teardown_module():
vbt.settings.reset()
# ############# generators.py ############# #
class TestGenerators:
def test_FMEAN(self):
pd.testing.assert_frame_equal(
vbt.FMEAN.run(close_ts, window=(2, 3), ewm=False).fmean,
pd.DataFrame(
np.array([
[1.5, 2.5, 1.6666666666666667, 2.3333333333333335],
[1.5, 2.5, 2.0, 2.0],
[2.5, 1.5, 2.3333333333333335, 1.6666666666666667],
[2.5, 1.5, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, False, 'a'),
(2, False, 'b'),
(3, False, 'a'),
(3, False, 'b'),
], names=['fmean_window', 'fmean_ewm', None])
)
)
pd.testing.assert_frame_equal(
vbt.FMEAN.run(close_ts, window=(2, 3), ewm=True).fmean,
pd.DataFrame(
np.array([
[1.8024691358024691, 2.197530864197531, 1.8125, 2.1875],
[1.4074074074074074, 2.5925925925925926, 1.625, 2.375],
[2.2222222222222223, 1.7777777777777777, 2.25, 1.75],
[2.666666666666667, 1.3333333333333335, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, True, 'a'),
(2, True, 'b'),
(3, True, 'a'),
(3, True, 'b'),
], names=['fmean_window', 'fmean_ewm', None])
)
)
def test_FSTD(self):
pd.testing.assert_frame_equal(
vbt.FSTD.run(close_ts, window=(2, 3), ewm=False).fstd,
pd.DataFrame(
np.array([
[0.5, 0.5, 0.4714045207910384, 0.4714045207910183],
[0.5, 0.5, 0.816496580927726, 0.816496580927726],
[0.5, 0.5, 0.4714045207910183, 0.4714045207910384],
[0.5, 0.5, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, False, 'a'),
(2, False, 'b'),
(3, False, 'a'),
(3, False, 'b'),
], names=['fstd_window', 'fstd_ewm', None])
)
)
pd.testing.assert_frame_equal(
vbt.FSTD.run(close_ts, window=(2, 3), ewm=True).fstd,
pd.DataFrame(
np.array([
[0.64486716348143, 0.6448671634814303, 0.6462561866810479, 0.6462561866810479],
[0.8833005039168617, 0.8833005039168604, 0.8591246929842246, 0.8591246929842246],
[0.5916079783099623, 0.5916079783099623, 0.5477225575051662, 0.5477225575051662],
[0.7071067811865476, 0.7071067811865476, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, True, 'a'),
(2, True, 'b'),
(3, True, 'a'),
(3, True, 'b'),
], names=['fstd_window', 'fstd_ewm', None])
)
)
def test_FMIN(self):
pd.testing.assert_frame_equal(
vbt.FMIN.run(close_ts, window=(2, 3)).fmin,
pd.DataFrame(
np.array([
[1.0, 2.0, 1.0, 2.0],
[1.0, 2.0, 1.0, 1.0],
[2.0, 1.0, 2.0, 1.0],
[2.0, 1.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, 'a'),
(2, 'b'),
(3, 'a'),
(3, 'b'),
], names=['fmin_window', None])
)
)
def test_FMAX(self):
pd.testing.assert_frame_equal(
vbt.FMAX.run(close_ts, window=(2, 3)).fmax,
pd.DataFrame(
np.array([
[2.0, 3.0, 2.0, 3.0],
[2.0, 3.0, 3.0, 3.0],
[3.0, 2.0, 3.0, 2.0],
[3.0, 2.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, 'a'),
(2, 'b'),
(3, 'a'),
(3, 'b'),
], names=['fmax_window', None])
)
)
def test_FIXLB(self):
pd.testing.assert_frame_equal(
vbt.FIXLB.run(close_ts, n=(2, 3)).labels,
pd.DataFrame(
np.array([
[0.0, 0.0, 1.0, -0.3333333333333333],
[0.0, 0.0, 0.5, -0.5],
[2.0, -0.6666666666666666, 1.0, -0.3333333333333333],
[0.0, 0.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, 'a'),
(2, 'b'),
(3, 'a'),
(3, 'b'),
], names=['fixlb_n', None])
)
)
def test_MEANLB(self):
pd.testing.assert_frame_equal(
vbt.MEANLB.run(close_ts, window=(2, 3), ewm=False).labels,
pd.DataFrame(
np.array([
[0.5, -0.16666666666666666, 0.6666666666666667, -0.22222222222222218],
[-0.25, 0.25, 0.0, 0.0],
[1.5, -0.5, 1.3333333333333335, -0.4444444444444444],
[0.25, -0.25, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, False, 'a'),
(2, False, 'b'),
(3, False, 'a'),
(3, False, 'b'),
], names=['meanlb_window', 'meanlb_ewm', None])
)
)
pd.testing.assert_frame_equal(
vbt.MEANLB.run(close_ts, window=(2, 3), ewm=True).labels,
pd.DataFrame(
np.array([
[0.8024691358024691, -0.2674897119341564, 0.8125, -0.2708333333333333],
[-0.2962962962962963, 0.2962962962962963, -0.1875, 0.1875],
[1.2222222222222223, -0.40740740740740744, 1.25, -0.4166666666666667],
[0.3333333333333335, -0.33333333333333326, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, True, 'a'),
(2, True, 'b'),
(3, True, 'a'),
(3, True, 'b'),
], names=['meanlb_window', 'meanlb_ewm', None])
)
)
def test_LEXLB(self):
pd.testing.assert_frame_equal(
vbt.LEXLB.run(close_ts, pos_th=pos_ths, neg_th=neg_ths).labels,
pd.DataFrame(
np.array([
[-1, 1, -1, 1, 0, 0],
[1, -1, 0, 0, 0, 0],
[-1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[1, -1, 1, -1, 0, 0],
[0, 1, 0, 1, 0, 0]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
('array_0', 'array_0', 'a'),
('array_0', 'array_0', 'b'),
('array_1', 'array_1', 'a'),
('array_1', 'array_1', 'b'),
('array_2', 'array_2', 'a'),
('array_2', 'array_2', 'b')
], names=['lexlb_pos_th', 'lexlb_neg_th', None])
)
)
def test_TRENDLB(self):
pd.testing.assert_frame_equal(
vbt.TRENDLB.run(close_ts, pos_th=pos_ths, neg_th=neg_ths, mode='Binary').labels,
pd.DataFrame(
np.array([
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[0.0, 1.0, 1.0, 0.0, np.nan, np.nan],
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[np.nan, 1.0, np.nan, 1.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
('array_0', 'array_0', 0, 'a'),
('array_0', 'array_0', 0, 'b'),
('array_1', 'array_1', 0, 'a'),
('array_1', 'array_1', 0, 'b'),
('array_2', 'array_2', 0, 'a'),
('array_2', 'array_2', 0, 'b')
], names=['trendlb_pos_th', 'trendlb_neg_th', 'trendlb_mode', None])
)
)
pd.testing.assert_frame_equal(
vbt.TRENDLB.run(close_ts, pos_th=pos_ths, neg_th=neg_ths, mode='BinaryCont').labels,
pd.DataFrame(
np.array([
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[0.0, 1.0, 0.5, 0.5, np.nan, np.nan],
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[0.5, 0.5, 0.5, 0.5, np.nan, np.nan],
[np.nan, 1.0, np.nan, 1.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
('array_0', 'array_0', 1, 'a'),
('array_0', 'array_0', 1, 'b'),
('array_1', 'array_1', 1, 'a'),
('array_1', 'array_1', 1, 'b'),
('array_2', 'array_2', 1, 'a'),
('array_2', 'array_2', 1, 'b')
], names=['trendlb_pos_th', 'trendlb_neg_th', 'trendlb_mode', None])
)
)
pd.testing.assert_frame_equal(
vbt.TRENDLB.run(close_ts, pos_th=pos_ths, neg_th=neg_ths, mode='BinaryContSat').labels,
pd.DataFrame(
np.array([
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[0.0, 1.0, 0.5, 0.4999999999999999, np.nan, np.nan],
[1.0, 0.0, 1.0, 0.0, np.nan, np.nan],
[0.6666666666666667, 0.0, 0.5, 0.4999999999999999, np.nan, np.nan],
[np.nan, 1.0, np.nan, 1.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
('array_0', 'array_0', 2, 'a'),
('array_0', 'array_0', 2, 'b'),
('array_1', 'array_1', 2, 'a'),
('array_1', 'array_1', 2, 'b'),
('array_2', 'array_2', 2, 'a'),
('array_2', 'array_2', 2, 'b')
], names=['trendlb_pos_th', 'trendlb_neg_th', 'trendlb_mode', None])
)
)
pd.testing.assert_frame_equal(
vbt.TRENDLB.run(close_ts, pos_th=pos_ths, neg_th=neg_ths, mode='PctChange').labels,
pd.DataFrame(
np.array([
[1.0, -0.3333333333333333, 2.0, -0.6666666666666666, np.nan, np.nan],
[-0.5, 0.5, 0.5, -0.5, np.nan, np.nan],
[2.0, -0.6666666666666666, 2.0, -0.6666666666666666, np.nan, np.nan],
[0.5, -0.5, 0.5, -0.5, np.nan, np.nan],
[np.nan, 1.0, np.nan, 1.0, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
('array_0', 'array_0', 3, 'a'),
('array_0', 'array_0', 3, 'b'),
('array_1', 'array_1', 3, 'a'),
('array_1', 'array_1', 3, 'b'),
('array_2', 'array_2', 3, 'a'),
('array_2', 'array_2', 3, 'b')
], names=['trendlb_pos_th', 'trendlb_neg_th', 'trendlb_mode', None])
)
)
pd.testing.assert_frame_equal(
vbt.TRENDLB.run(close_ts, pos_th=pos_ths, neg_th=neg_ths, mode='PctChangeNorm').labels,
pd.DataFrame(
np.array([
[0.5, -0.3333333333333333, 0.6666666666666666, -0.6666666666666666, np.nan, np.nan],
[-0.5, 0.3333333333333333, 0.3333333333333333, -0.5, np.nan, np.nan],
[0.6666666666666666, -0.6666666666666666, 0.6666666666666666,
-0.6666666666666666, np.nan, np.nan],
[0.3333333333333333, -0.5, 0.3333333333333333, -0.5, np.nan, np.nan],
[np.nan, 0.5, np.nan, 0.5, np.nan, np.nan],
[np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
('array_0', 'array_0', 4, 'a'),
('array_0', 'array_0', 4, 'b'),
('array_1', 'array_1', 4, 'a'),
('array_1', 'array_1', 4, 'b'),
('array_2', 'array_2', 4, 'a'),
('array_2', 'array_2', 4, 'b')
], names=['trendlb_pos_th', 'trendlb_neg_th', 'trendlb_mode', None])
)
)
def test_BOLB(self):
pd.testing.assert_frame_equal(
vbt.BOLB.run(close_ts, window=1, pos_th=pos_ths, neg_th=neg_ths).labels,
pd.DataFrame(
np.array([
[1.0, -1.0, 0.0, 0.0, 0.0, 0.0],
[-1.0, 1.0, -1.0, 1.0, -1.0, 1.0],
[1.0, -1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, -1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(1, 'array_0', 'array_0', 'a'),
(1, 'array_0', 'array_0', 'b'),
(1, 'array_1', 'array_1', 'a'),
(1, 'array_1', 'array_1', 'b'),
(1, 'array_2', 'array_2', 'a'),
(1, 'array_2', 'array_2', 'b')
], names=['bolb_window', 'bolb_pos_th', 'bolb_neg_th', None])
)
)
pd.testing.assert_frame_equal(
vbt.BOLB.run(close_ts, window=2, pos_th=pos_ths, neg_th=neg_ths).labels,
pd.DataFrame(
np.array([
[1.0, -1.0, 0.0, 0.0, 0.0, 0.0],
[-1.0, 1.0, -1.0, 1.0, -1.0, 1.0],
[1.0, -1.0, 1.0, -1.0, 0.0, 0.0],
[0.0, -1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 1.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
]),
index=close_ts.index,
columns=pd.MultiIndex.from_tuples([
(2, 'array_0', 'array_0', 'a'),
(2, 'array_0', 'array_0', 'b'),
(2, 'array_1', 'array_1', 'a'),
(2, 'array_1', 'array_1', 'b'),
(2, 'array_2', 'array_2', 'a'),
(2, 'array_2', 'array_2', 'b')
], names=['bolb_window', 'bolb_pos_th', 'bolb_neg_th', None])
)
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,810 @@
from datetime import datetime
import numpy as np
import pandas as pd
import pytest
import vectorbt as vbt
from tests.utils import isclose
qs_available = True
try:
import quantstats as qs
except:
qs_available = False
day_dt = np.timedelta64(86400000000000)
ts = pd.DataFrame({
'a': [1, 2, 3, 4, 5],
'b': [5, 4, 3, 2, 1],
'c': [1, 2, 3, 2, 1]
}, index=pd.DatetimeIndex([
datetime(2018, 1, 1),
datetime(2018, 1, 2),
datetime(2018, 1, 3),
datetime(2018, 1, 4),
datetime(2018, 1, 5)
]))
rets = ts.pct_change()
seed = 42
np.random.seed(seed)
benchmark_rets = pd.DataFrame({
'a': rets['a'] * np.random.uniform(0.8, 1.2, rets.shape[0]),
'b': rets['b'] * np.random.uniform(0.8, 1.2, rets.shape[0]) * 2,
'c': rets['c'] * np.random.uniform(0.8, 1.2, rets.shape[0]) * 3
})
# ############# Global ############# #
def setup_module():
vbt.settings.numba['check_func_suffix'] = True
vbt.settings.caching.enabled = False
vbt.settings.caching.whitelist = []
vbt.settings.caching.blacklist = []
vbt.settings.returns.defaults = dict(
start_value=0.,
window=rets.shape[0],
minp=1,
ddof=1,
risk_free=0.01,
levy_alpha=2.,
required_return=0.1,
cutoff=0.05
)
def teardown_module():
vbt.settings.reset()
# ############# accessors.py ############# #
class TestAccessors:
def test_indexing(self):
assert rets.vbt.returns['a'].total() == rets['a'].vbt.returns.total()
def test_benchmark_rets(self):
ret_acc = rets.vbt.returns(benchmark_rets=benchmark_rets)
pd.testing.assert_frame_equal(ret_acc.benchmark_rets, benchmark_rets)
pd.testing.assert_series_equal(ret_acc['a'].benchmark_rets, benchmark_rets['a'])
def test_freq(self):
assert rets.vbt.returns.wrapper.freq == day_dt
assert rets['a'].vbt.returns.wrapper.freq == day_dt
assert rets.vbt.returns(freq='2D').wrapper.freq == day_dt * 2
assert rets['a'].vbt.returns(freq='2D').wrapper.freq == day_dt * 2
assert pd.Series([1, 2, 3]).vbt.returns.wrapper.freq is None
assert pd.Series([1, 2, 3]).vbt.returns(freq='3D').wrapper.freq == day_dt * 3
assert pd.Series([1, 2, 3]).vbt.returns(freq=np.timedelta64(4, 'D')).wrapper.freq == day_dt * 4
def test_ann_factor(self):
assert rets['a'].vbt.returns(year_freq='365 days').ann_factor == 365
assert rets.vbt.returns(year_freq='365 days').ann_factor == 365
with pytest.raises(Exception):
assert pd.Series([1, 2, 3]).vbt.returns(freq=None).ann_factor
def test_from_value(self):
pd.testing.assert_series_equal(pd.Series.vbt.returns.from_value(ts['a']).obj, ts['a'].pct_change())
pd.testing.assert_frame_equal(pd.DataFrame.vbt.returns.from_value(ts).obj, ts.pct_change())
assert pd.Series.vbt.returns.from_value(ts['a'], year_freq='365 days').year_freq == pd.to_timedelta('365 days')
assert pd.DataFrame.vbt.returns.from_value(ts, year_freq='365 days').year_freq == pd.to_timedelta('365 days')
def test_daily(self):
ret_12h = pd.DataFrame({
'a': [0.1, 0.1, 0.1, 0.1, 0.1],
'b': [-0.1, -0.1, -0.1, -0.1, -0.1],
'c': [0.1, -0.1, 0.1, -0.1, 0.1]
}, index=pd.DatetimeIndex([
datetime(2018, 1, 1, 0),
datetime(2018, 1, 1, 12),
datetime(2018, 1, 2, 0),
datetime(2018, 1, 2, 12),
datetime(2018, 1, 3, 0)
]))
pd.testing.assert_series_equal(
ret_12h['a'].vbt.returns.daily(),
pd.Series(
np.array([0.21, 0.21, 0.1]),
index=pd.DatetimeIndex([
'2018-01-01',
'2018-01-02',
'2018-01-03'
], dtype='datetime64[ns]', freq='D'),
name=ret_12h['a'].name
)
)
pd.testing.assert_frame_equal(
ret_12h.vbt.returns.daily(),
pd.DataFrame(
np.array([
[0.21, -0.19, -0.01],
[0.21, -0.19, -0.01],
[0.1, -0.1, 0.1]
]),
index=pd.DatetimeIndex([
'2018-01-01',
'2018-01-02',
'2018-01-03'
], dtype='datetime64[ns]', freq='D'),
columns=ret_12h.columns
)
)
def test_annual(self):
pd.testing.assert_series_equal(
rets['a'].vbt.returns.annual(),
pd.Series(
np.array([4.]),
index=pd.DatetimeIndex(['2018-01-01'], dtype='datetime64[ns]', freq='365D'),
name=rets['a'].name
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.annual(),
pd.DataFrame(
np.array([[4., -0.8, 0.]]),
index=pd.DatetimeIndex(['2018-01-01'], dtype='datetime64[ns]', freq='365D'),
columns=rets.columns
)
)
def test_cumulative(self):
pd.testing.assert_series_equal(
rets['a'].vbt.returns.cumulative(),
pd.Series(
[0.0, 1.0, 2.0, 3.0, 4.0],
index=rets.index,
name='a'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.cumulative(),
pd.DataFrame(
[
[0.0, 0.0, 0.0],
[1.0, -0.19999999999999996, 1.0],
[2.0, -0.3999999999999999, 2.0],
[3.0, -0.6, 1.0],
[4.0, -0.8, 0.0]
],
index=rets.index,
columns=rets.columns
)
)
def test_total_return(self):
assert isclose(rets['a'].vbt.returns.total(), 4.0)
pd.testing.assert_series_equal(
rets.vbt.returns.total(),
pd.Series(
[4.0, -0.8, 0.0],
index=rets.columns,
name='total_return'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_total(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[1.0, -0.19999999999999996, 1.0],
[2.0, -0.3999999999999999, 2.0],
[3.0, -0.6, 1.0],
[4.0, -0.8, 0.0]
],
index=rets.index,
columns=rets.columns
)
)
def test_annualized_return(self):
assert isclose(rets['a'].vbt.returns.annualized(), 1.0587911840678754e+51)
pd.testing.assert_series_equal(
rets.vbt.returns.annualized(),
pd.Series(
[1.0587911840678754e+51, -1.0, 0.0],
index=rets.columns,
name='annualized_return'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_annualized(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[8.669103912675328e+54, -1.0, 8.669103912675328e+54],
[1.1213796164129035e+58, -1.0, 1.1213796164129035e+58],
[8.669103912675328e+54, -1.0, 2.9443342053298444e+27],
[1.0587911840678754e+51, -1.0, 0.0]
],
index=rets.index,
columns=rets.columns
)
)
def test_annualized_volatility(self):
assert isclose(rets['a'].vbt.returns.annualized_volatility(), 6.417884083645567)
pd.testing.assert_series_equal(
rets.vbt.returns.annualized_volatility(),
pd.Series(
[6.417884083645567, 2.5122615973129334, 13.509256086106296],
index=rets.columns,
name='annualized_volatility'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_annualized_volatility(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan],
[6.754628043053148, 0.6754628043053155, 6.754628043053148],
[6.62836217969305, 1.2868638306046682, 12.868638306046675],
[6.417884083645567, 2.5122615973129334, 13.509256086106296]
],
index=rets.index,
columns=rets.columns
)
)
def test_calmar_ratio(self):
assert isclose(rets['a'].vbt.returns.calmar_ratio(), np.nan)
pd.testing.assert_series_equal(
rets.vbt.returns.calmar_ratio(),
pd.Series(
[np.nan, -1.25, 0.0],
index=rets.columns,
name='calmar_ratio'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_calmar_ratio(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, -5.000000000000001, np.nan],
[np.nan, -2.5000000000000004, np.nan],
[np.nan, -1.6666666666666667, 8.833002615989533e+27],
[np.nan, -1.25, 0.0]
],
index=rets.index,
columns=rets.columns
)
)
def test_omega_ratio(self):
assert isclose(rets['a'].vbt.returns.omega_ratio(risk_free=0.01, required_return=0.1), np.inf)
pd.testing.assert_series_equal(
rets.vbt.returns.omega_ratio(risk_free=0.01, required_return=0.1),
pd.Series(
[np.inf, 0.0, 1.7327023435781848],
index=rets.columns,
name='omega_ratio'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_omega_ratio(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.inf, 0.0, np.inf],
[np.inf, 0.0, np.inf],
[np.inf, 0.0, 4.305883016460259],
[np.inf, 0.0, 1.7327023435781848]
],
index=rets.index,
columns=rets.columns
)
)
def test_sharpe_ratio(self):
assert isclose(rets['a'].vbt.returns.sharpe_ratio(risk_free=0.01), 29.052280196490333)
pd.testing.assert_series_equal(
rets.vbt.returns.sharpe_ratio(risk_free=0.01),
pd.Series(
[29.052280196490333, -48.06592068111974, 4.232900240313306],
index=rets.columns,
name='sharpe_ratio'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_sharpe_ratio(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan],
[39.98739801487463, -126.98700720939904, 39.98739801487463],
[33.101020977359426, -76.89667951041766, 10.746626111906732],
[29.052280196490333, -48.06592068111974, 4.232900240313306]
],
index=rets.index,
columns=rets.columns
)
)
def test_deflated_sharpe_ratio(self):
pd.testing.assert_series_equal(
rets.vbt.returns.deflated_sharpe_ratio(risk_free=0.01),
pd.Series([np.nan, np.nan, 0.0005355605507117676], index=rets.columns, name='deflated_sharpe_ratio')
)
pd.testing.assert_series_equal(
rets.vbt.returns.deflated_sharpe_ratio(risk_free=0.03),
pd.Series([np.nan, np.nan, 0.0003423112350834066], index=rets.columns, name='deflated_sharpe_ratio')
)
def test_downside_risk(self):
assert isclose(rets['a'].vbt.returns.downside_risk(required_return=0.1), 0.0)
pd.testing.assert_series_equal(
rets.vbt.returns.downside_risk(required_return=0.1),
pd.Series(
[0.0, 8.329186468210578, 7.069987427302981],
index=rets.columns,
name='downside_risk'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_downside_risk(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[0.0, 5.7314919523628385, 0.0],
[0.0, 6.227459353540574, 0.0],
[0.0, 6.978571699349585, 4.779779942245908],
[0.0, 8.329186468210578, 7.069987427302981]
],
index=rets.index,
columns=rets.columns
)
)
def test_sortino_ratio(self):
assert isclose(rets['a'].vbt.returns.sortino_ratio(required_return=0.1), np.inf)
pd.testing.assert_series_equal(
rets.vbt.returns.sortino_ratio(required_return=0.1),
pd.Series(
[np.inf, -18.441677017667562, 3.4417788692752858],
index=rets.columns,
name='sortino_ratio'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_sortino_ratio(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.inf, -19.1049731745428, np.inf],
[np.inf, -19.04869919906529, np.inf],
[np.inf, -18.887182253617894, 22.06052281036573],
[np.inf, -18.441677017667562, 3.4417788692752858]
],
index=rets.index,
columns=rets.columns
)
)
def test_information_ratio(self):
assert isclose(rets['a'].vbt.returns.information_ratio(benchmark_rets['a']), -0.5575108215121097)
pd.testing.assert_series_equal(
rets.vbt.returns.information_ratio(benchmark_rets),
pd.Series(
[-0.5575108215121097, 1.8751745305884349, -0.3791876496995291],
index=rets.columns,
name='information_ratio'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_information_ratio(benchmark_rets),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan],
[-1.1972053570548309, 1.6499071488151926, -1.9503403469059444],
[-0.9036343476254122, 2.183905200180643, -0.6855076064440647],
[-0.5575108215121097, 1.8751745305884349, -0.3791876496995291]
],
index=rets.index,
columns=rets.columns
)
)
def test_beta(self):
assert isclose(rets['a'].vbt.returns.beta(benchmark_rets['a']), 0.7853755858374825)
pd.testing.assert_series_equal(
rets.vbt.returns.beta(benchmark_rets),
pd.Series(
[0.7853755858374825, 0.4123019930790345, 0.30840682076341036],
index=rets.columns,
name='beta'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_beta(benchmark_rets),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan],
[0.7887842027059571, 0.2049668794115673, 0.2681790192492397],
[0.7969484728140032, 0.34249231546013587, 0.30111751528469777],
[0.7853755858374825, 0.4123019930790345, 0.30840682076341036]
],
index=rets.index,
columns=rets.columns
)
)
def test_alpha(self):
assert isclose(rets['a'].vbt.returns.alpha(benchmark_rets['a'], risk_free=0.01), 41819510790.213036)
pd.testing.assert_series_equal(
rets.vbt.returns.alpha(benchmark_rets, risk_free=0.01),
pd.Series(
[41819510790.213036, -0.9999999939676926, -0.999999999999793],
index=rets.columns,
name='alpha'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_alpha(benchmark_rets),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, np.nan, np.nan],
[18396133022.071487, -1.0, 558643.320341666],
[974350522.6315696, -0.9999999999999931, -0.9999999996015246],
[41819510790.213036, -0.9999999939676926, -0.999999999999793]
],
index=rets.index,
columns=rets.columns
)
)
def test_tail_ratio(self):
assert isclose(rets['a'].vbt.returns.tail_ratio(), 3.5238095238095237)
pd.testing.assert_series_equal(
rets.vbt.returns.tail_ratio(),
pd.Series(
[3.5238095238095237, 0.43684210526315786, 1.947368421052631],
index=rets.columns,
name='tail_ratio'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_tail_ratio(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[1.0, 1.0, 1.0],
[1.857142857142857, 0.818181818181818, 1.857142857142857],
[2.714285714285715, 0.6307692307692306, 3.8000000000000007],
[3.5238095238095237, 0.43684210526315786, 1.947368421052631]
],
index=rets.index,
columns=rets.columns
)
)
def test_value_at_risk(self):
assert isclose(rets['a'].vbt.returns.value_at_risk(cutoff=0.05), 0.26249999999999996)
pd.testing.assert_series_equal(
rets.vbt.returns.value_at_risk(cutoff=0.05),
pd.Series(
[0.26249999999999996, -0.47500000000000003, -0.47500000000000003],
index=rets.columns,
name='value_at_risk'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_value_at_risk(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[1.0, -0.19999999999999996, 1.0],
[0.525, -0.2475, 0.525],
[0.3499999999999999, -0.325, -0.24999999999999994],
[0.26249999999999996, -0.47500000000000003, -0.47500000000000003]
],
index=rets.index,
columns=rets.columns
)
)
def test_cond_value_at_risk(self):
assert isclose(rets['a'].vbt.returns.cond_value_at_risk(cutoff=0.05), 0.25)
pd.testing.assert_series_equal(
rets.vbt.returns.cond_value_at_risk(cutoff=0.05),
pd.Series(
[0.25, -0.5, -0.5],
index=rets.columns,
name='cond_value_at_risk'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_cond_value_at_risk(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[1.0, -0.19999999999999996, 1.0],
[0.5, -0.25, 0.5],
[0.33333333333333326, -0.33333333333333337, -0.33333333333333337],
[0.25, -0.5, -0.5]
],
index=rets.index,
columns=rets.columns
)
)
def test_capture(self):
assert isclose(rets['a'].vbt.returns.capture(benchmark_rets['a']), 0.0007435597416888084)
pd.testing.assert_series_equal(
rets.vbt.returns.capture(benchmark_rets),
pd.Series(
[0.0007435597416888084, 1.0, -0.0],
index=rets.columns,
name='capture'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_capture(benchmark_rets),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[1.443034545422564e-07, 1.0, 4.0670014163453153e-66],
[6.758314743559073e-07, 1.0, 2.2829041301869233e-75],
[9.623155594782632e-06, 1.0, 43620380068493.234],
[0.0007435597416888084, 1.0, -0.0]
],
index=rets.index,
columns=rets.columns
)
)
def test_up_capture(self):
assert isclose(rets['a'].vbt.returns.up_capture(benchmark_rets['a']), 0.0001227848643711666)
pd.testing.assert_series_equal(
rets.vbt.returns.up_capture(benchmark_rets),
pd.Series(
[0.0001227848643711666, np.nan, 1.0907657953912082e-112],
index=rets.columns,
name='up_capture'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_up_capture(benchmark_rets),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[2.0823486992829062e-14, np.nan, 1.6540500520554797e-131],
[5.555940938023189e-10, np.nan, 1.0907657953912082e-112],
[2.0468688202710215e-07, np.nan, 1.0907657953912082e-112],
[0.0001227848643711666, np.nan, 1.0907657953912082e-112]
],
index=rets.index,
columns=rets.columns
)
)
def test_down_capture(self):
assert isclose(rets['a'].vbt.returns.down_capture(benchmark_rets['a']), np.nan)
pd.testing.assert_series_equal(
rets.vbt.returns.down_capture(benchmark_rets),
pd.Series(
[np.nan, np.nan, np.nan],
index=rets.columns,
name='down_capture'
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_down_capture(benchmark_rets),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[np.nan, 1.0, np.nan],
[np.nan, 1.0, np.nan],
[np.nan, 1.0, 1.0],
[np.nan, np.nan, np.nan]
],
index=rets.index,
columns=rets.columns
)
)
def test_drawdown(self):
pd.testing.assert_series_equal(
rets['a'].vbt.returns.drawdown(),
pd.Series(
np.array([0., 0., 0., 0., 0.]),
index=rets['a'].index,
name=rets['a'].name
)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.drawdown(),
pd.DataFrame(
np.array([
[0., 0., 0.],
[0., -0.2, 0.],
[0., -0.4, 0.],
[0., -0.6, -0.33333333],
[0., -0.8, -0.66666667]
]),
index=pd.DatetimeIndex([
'2018-01-01',
'2018-01-02',
'2018-01-03',
'2018-01-04',
'2018-01-05'
], dtype='datetime64[ns]', freq=None),
columns=rets.columns
)
)
def test_max_drawdown(self):
assert isclose(
rets['a'].vbt.returns.max_drawdown(),
rets['a'].vbt.returns.drawdowns.max_drawdown(fill_value=0.)
)
pd.testing.assert_series_equal(
rets.vbt.returns.max_drawdown(),
rets.vbt.returns.drawdowns.max_drawdown(fill_value=0.)
)
pd.testing.assert_frame_equal(
rets.vbt.returns.rolling_max_drawdown(),
pd.DataFrame(
[
[np.nan, np.nan, np.nan],
[0.0, -0.19999999999999996, 0.0],
[0.0, -0.3999999999999999, 0.0],
[0.0, -0.6, -0.33333333333333337],
[0.0, -0.8, -0.6666666666666667]
],
index=rets.index,
columns=rets.columns
)
)
def test_drawdowns(self):
assert type(rets['a'].vbt.returns.drawdowns) is vbt.Drawdowns
assert rets['a'].vbt.returns.drawdowns.wrapper.freq == rets['a'].vbt.wrapper.freq
assert rets['a'].vbt.returns.drawdowns.wrapper.ndim == rets['a'].ndim
assert rets.vbt.returns.drawdowns.wrapper.ndim == rets.ndim
assert isclose(rets['a'].vbt.returns.drawdowns.max_drawdown(), rets['a'].vbt.returns.max_drawdown())
pd.testing.assert_series_equal(
rets.vbt.returns.drawdowns.max_drawdown(fill_value=0.),
rets.vbt.returns.max_drawdown()
)
def test_stats(self):
stats_index = pd.Index([
'Start',
'End',
'Period',
'Total Return [%]',
'Annualized Return [%]',
'Annualized Volatility [%]',
'Max Drawdown [%]',
'Max Drawdown Duration',
'Sharpe Ratio',
'Calmar Ratio',
'Omega Ratio',
'Sortino Ratio',
'Skew',
'Kurtosis',
'Tail Ratio',
'Common Sense Ratio',
'Value at Risk'
], dtype='object')
pd.testing.assert_series_equal(
rets.vbt.returns.stats(),
pd.Series([
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
pd.Timedelta('5 days 00:00:00'), 106.66666666666667, 3.529303946892918e+52,
747.9800589021598, 73.33333333333333, pd.Timedelta('3 days 00:00:00'),
-4.926913414772034, -0.625, np.inf, np.inf, 0.25687104876585726, -0.25409565813913854,
1.9693400167084374, 1.2436594860479807e+51, -0.2291666666666667
],
index=stats_index,
name='agg_func_mean'
)
)
pd.testing.assert_series_equal(
rets.vbt.returns.stats(column='a'),
pd.Series([
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
pd.Timedelta('5 days 00:00:00'), 400.0, 1.0587911840678753e+53, 641.7884083645566,
np.nan, pd.NaT, 29.052280196490333, np.nan, np.inf, np.inf, 1.4693345482106241,
2.030769230769236, 3.5238095238095237, 3.730978458143942e+51, 0.26249999999999996
],
index=stats_index,
name='a'
)
)
pd.testing.assert_series_equal(
rets.vbt.returns.stats(column='a', settings=dict(freq='10 days', year_freq='200 days')),
pd.Series([
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
pd.Timedelta('50 days 00:00:00'), 400.0, 62400.0, 150.23130314433288, np.nan, pd.NaT,
6.800624405721308, np.nan, np.inf, np.inf, 1.4693345482106241, 2.030769230769236,
3.5238095238095237, 2202.3809523809523, 0.26249999999999996
],
index=stats_index,
name='a'
)
)
pd.testing.assert_series_equal(
rets.vbt.returns.stats(column='a', settings=dict(benchmark_rets=benchmark_rets)),
pd.Series([
pd.Timestamp('2018-01-01 00:00:00'), pd.Timestamp('2018-01-05 00:00:00'),
pd.Timedelta('5 days 00:00:00'), 400.0, 451.8597134178033, 1.0587911840678753e+53,
641.7884083645566, np.nan, pd.NaT, 29.052280196490333, np.nan, np.inf, np.inf,
1.4693345482106241, 2.030769230769236, 3.5238095238095237, 3.730978458143942e+51,
0.26249999999999996, 41819510790.213036, 0.7853755858374825
],
index=pd.Index([
'Start',
'End',
'Period',
'Total Return [%]',
'Benchmark Return [%]',
'Annualized Return [%]',
'Annualized Volatility [%]',
'Max Drawdown [%]',
'Max Drawdown Duration',
'Sharpe Ratio',
'Calmar Ratio',
'Omega Ratio',
'Sortino Ratio',
'Skew',
'Kurtosis',
'Tail Ratio',
'Common Sense Ratio',
'Value at Risk',
'Alpha',
'Beta'
], dtype='object'),
name='a'
)
)
pd.testing.assert_series_equal(
rets.vbt.returns.stats(column='a', settings=dict(benchmark_rets=benchmark_rets)),
rets.vbt.returns(benchmark_rets=benchmark_rets).stats(column='a'),
)
pd.testing.assert_series_equal(
rets['c'].vbt.returns.stats(),
rets.vbt.returns.stats(column='c')
)
pd.testing.assert_series_equal(
rets['c'].vbt.returns.stats(),
rets.vbt.returns.stats(column='c', group_by=False)
)
pd.testing.assert_series_equal(
rets.vbt.returns(freq='10d').stats(),
rets.vbt.returns.stats(settings=dict(freq='10d'))
)
pd.testing.assert_series_equal(
rets.vbt.returns(freq='d', year_freq='400d').stats(),
rets.vbt.returns.stats(settings=dict(freq='d', year_freq='400d'))
)
stats_df = rets.vbt.returns.stats(agg_func=None)
assert stats_df.shape == (3, 17)
pd.testing.assert_index_equal(stats_df.index, rets.vbt.returns.wrapper.columns)
pd.testing.assert_index_equal(stats_df.columns, stats_index)
def test_qs(self):
if qs_available:
pd.testing.assert_series_equal(
rets.vbt.returns.qs.sharpe(),
qs.stats.sharpe(rets.dropna(), periods=365, rf=0.01)
)
pd.testing.assert_series_equal(
rets.vbt.returns(freq='h', year_freq='252d').qs.sharpe(),
qs.stats.sharpe(rets.dropna(), periods=252 * 24, rf=0.01)
)
pd.testing.assert_series_equal(
rets.vbt.returns(freq='h', year_freq='252d').qs.sharpe(periods=252, periods_per_year=252, rf=0),
qs.stats.sharpe(rets.dropna())
)
assert rets['a'].vbt.returns(benchmark_rets=benchmark_rets['a']).qs.r_squared() == 0.6321016849785153

View File

@ -0,0 +1,18 @@
import vectorbt as vbt
# ############# Global ############# #
def teardown_module():
vbt.settings.reset()
# ############# settings.py ############# #
class TestSettings:
def test_save_and_load(self, tmp_path):
vbt.settings.set_theme('seaborn')
vbt.settings.save(tmp_path / "settings")
new_settings = vbt.settings.load(tmp_path / "settings")
assert vbt.settings == new_settings
assert vbt.settings.__dict__ == new_settings.__dict__

File diff suppressed because it is too large Load Diff

2604
vectorbt/tests/test_utils.py Normal file

File diff suppressed because it is too large Load Diff

21
vectorbt/tests/utils.py Normal file
View File

@ -0,0 +1,21 @@
import hashlib
import numpy as np
# non-randomized hash function
hash = lambda s: int(hashlib.sha512(s.encode('utf-8')).hexdigest()[:16], 16)
def isclose(a, b, rel_tol=1e-06, abs_tol=0.0):
if np.isnan(a) == np.isnan(b):
return True
if np.isinf(a) == np.isinf(b):
return True
if a == b:
return True
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
def record_arrays_close(x, y):
for field in x.dtype.names:
np.testing.assert_allclose(x[field], y[field], rtol=1e-06)