Added tests for filter. Added pytest dependency.

This commit is contained in:
Juan Pablo Amoroso
2019-05-30 11:42:52 -03:00
parent 5a4085a816
commit 16436dc124
8 changed files with 154 additions and 138 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
.PHONY: image init env testdata test test_scraper scrape aggregate backup bench
.PHONY: image init env testdata test_backtester test_scraper scrape aggregate backup bench
image:
docker build -t data_scraper -f ./docker/data_scraper/Dockerfile .
@@ -18,8 +18,8 @@ env:
testdata:
pipenv run python backtester/test/create_test_data.py
test:
pipenv run python -m unittest discover -s backtester/test
test_backtester:
pipenv run python -m pytest backtester/test
test_scraper:
pipenv run python -m unittest discover -s data_scraper
+1
View File
@@ -12,6 +12,7 @@ jupyter = "*"
pandas-datareader = "*"
pandas-market-calendars = "*"
boto3 = "*"
pytest = "*"
[dev-packages]
yapf = "*"
Generated
+97 -46
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "cd6cfed74640668b74dcbedc2cdf4426f24c7e71ab3351bfc4c331558994685c"
"sha256": "e6dd21683a090d52f61e0ae4c3be50e9906e212d12603846c4d70209886216cd"
},
"pipfile-spec": 6,
"requires": {},
@@ -30,6 +30,13 @@
"markers": "sys_platform == 'darwin'",
"version": "==0.1.0"
},
"atomicwrites": {
"hashes": [
"sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
"sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
],
"version": "==1.3.0"
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
@@ -62,18 +69,18 @@
},
"boto3": {
"hashes": [
"sha256:a653dd7e155f0fb7d950cef7e2be7fa12b7787f4c8835f324abcfd1dbe4bb73d",
"sha256:f2054b788e19a2d91a3cc76814ca4e44050da7c89781a50924a132acf24fad56"
"sha256:98516650f72c02d0c7b47e6f0df69c19a0d1046d3973783ed7b0f093e8e81adb",
"sha256:aa06b240e49dbc8443bd96086c92baa0275281e0f7cc0439a328fef9ac254253"
],
"index": "pypi",
"version": "==1.9.147"
"version": "==1.9.157"
},
"botocore": {
"hashes": [
"sha256:57d351535ffe0f19a80cb408df4dfc43fe66815cad3320e6a02d2261b498a8f6",
"sha256:e49db28173955bd3674b3280911446e6bd4458a9c40abfec46bf7c378fd30b2b"
"sha256:2ded06af31f9e423fcf549e35f2fa0fd618e12995e37ebc52186dbaa870316c6",
"sha256:473955cd4eda6121047205fc43c56ab0e0616b93651bac5e9c747fc180603fe2"
],
"version": "==1.12.147"
"version": "==1.12.157"
},
"certifi": {
"hashes": [
@@ -125,12 +132,19 @@
],
"version": "==2.8"
},
"importlib-metadata": {
"hashes": [
"sha256:027cfc6524613de726789072f95d2e4cc64dd1dee8096d42d13f2ead5bd302f5",
"sha256:0d05199e1f0b1a8707a1b9c46476d4a49807fb56cb1b0737db1d37feb42fe31d"
],
"version": "==0.15"
},
"ipykernel": {
"hashes": [
"sha256:0aeb7ec277ac42cc2b59ae3d08b10909b2ec161dc6908096210527162b53675d",
"sha256:0fc0bf97920d454102168ec2008620066878848fcfca06c22b669696212e292f"
"sha256:346189536b88859937b5f4848a6fd85d1ad0729f01724a411de5cae9b618819c",
"sha256:f0e962052718068ad3b1d8bcc703794660858f58803c3798628817f492a8769c"
],
"version": "==5.1.0"
"version": "==5.1.1"
},
"ipython": {
"hashes": [
@@ -283,6 +297,14 @@
],
"version": "==0.8.4"
},
"more-itertools": {
"hashes": [
"sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
"sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
],
"markers": "python_version > '2.7'",
"version": "==7.0.0"
},
"nbconvert": {
"hashes": [
"sha256:138381baa41d83584459b5cfecfc38c800ccf1f37d9ddd0bd440783346a4c39c",
@@ -306,31 +328,31 @@
},
"numpy": {
"hashes": [
"sha256:0e2eed77804b2a6a88741f8fcac02c5499bba3953ec9c71e8b217fad4912c56c",
"sha256:1c666f04553ef70fda54adf097dbae7080645435fc273e2397f26bbf1d127bbb",
"sha256:1f46532afa7b2903bfb1b79becca2954c0a04389d19e03dc73f06b039048ac40",
"sha256:315fa1b1dfc16ae0f03f8fd1c55f23fd15368710f641d570236f3d78af55e340",
"sha256:3d5fcea4f5ed40c3280791d54da3ad2ecf896f4c87c877b113576b8280c59441",
"sha256:48241759b99d60aba63b0e590332c600fc4b46ad597c9b0a53f350b871ef0634",
"sha256:4b4f2924b36d857cf302aec369caac61e43500c17eeef0d7baacad1084c0ee84",
"sha256:54fe3b7ed9e7eb928bbc4318f954d133851865f062fa4bbb02ef8940bc67b5d2",
"sha256:5a8f021c70e6206c317974c93eaaf9bc2b56295b6b1cacccf88846e44a1f33fc",
"sha256:754a6be26d938e6ca91942804eb209307b73f806a1721176278a6038869a1686",
"sha256:771147e654e8b95eea1293174a94f34e2e77d5729ad44aefb62fbf8a79747a15",
"sha256:78a6f89da87eeb48014ec652a65c4ffde370c036d780a995edaeb121d3625621",
"sha256:7fde5c2a3a682a9e101e61d97696687ebdba47637611378b4127fe7e47fdf2bf",
"sha256:80d99399c97f646e873dd8ce87c38cfdbb668956bbc39bc1e6cac4b515bba2a0",
"sha256:88a72c1e45a0ae24d1f249a529d9f71fe82e6fa6a3fd61414b829396ec585900",
"sha256:a4f4460877a16ac73302a9c077ca545498d9fe64e6a81398d8e1a67e4695e3df",
"sha256:a61255a765b3ac73ee4b110b28fccfbf758c985677f526c2b4b39c48cc4b509d",
"sha256:ab4896a8c910b9a04c0142871d8800c76c8a2e5ff44763513e1dd9d9631ce897",
"sha256:abbd6b1c2ef6199f4b7ca9f818eb6b31f17b73a6110aadc4e4298c3f00fab24e",
"sha256:b16d88da290334e33ea992c56492326ea3b06233a00a1855414360b77ca72f26",
"sha256:b78a1defedb0e8f6ae1eb55fa6ac74ab42acc4569c3a2eacc2a407ee5d42ebcb",
"sha256:cfef82c43b8b29ca436560d51b2251d5117818a8d1fb74a8384a83c096745dad",
"sha256:d160e57731fcdec2beda807ebcabf39823c47e9409485b5a3a1db3a8c6ce763e"
"sha256:0778076e764e146d3078b17c24c4d89e0ecd4ac5401beff8e1c87879043a0633",
"sha256:141c7102f20abe6cf0d54c4ced8d565b86df4d3077ba2343b61a6db996cefec7",
"sha256:14270a1ee8917d11e7753fb54fc7ffd1934f4d529235beec0b275e2ccf00333b",
"sha256:27e11c7a8ec9d5838bc59f809bfa86efc8a4fd02e58960fa9c49d998e14332d5",
"sha256:2a04dda79606f3d2f760384c38ccd3d5b9bb79d4c8126b67aff5eb09a253763e",
"sha256:3c26010c1b51e1224a3ca6b8df807de6e95128b0908c7e34f190e7775455b0ca",
"sha256:52c40f1a4262c896420c6ea1c6fda62cf67070e3947e3307f5562bd783a90336",
"sha256:6e4f8d9e8aa79321657079b9ac03f3cf3fd067bf31c1cca4f56d49543f4356a5",
"sha256:7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7",
"sha256:7dc253b542bfd4b4eb88d9dbae4ca079e7bf2e2afd819ee18891a43db66c60c7",
"sha256:94f5bd885f67bbb25c82d80184abbf7ce4f6c3c3a41fbaa4182f034bba803e69",
"sha256:a89e188daa119ffa0d03ce5123dee3f8ffd5115c896c2a9d4f0dbb3d8b95bfa3",
"sha256:ad3399da9b0ca36e2f24de72f67ab2854a62e623274607e37e0ce5f5d5fa9166",
"sha256:b0348be89275fd1d4c44ffa39530c41a21062f52299b1e3ee7d1c61f060044b8",
"sha256:b5554368e4ede1856121b0dfa35ce71768102e4aa55e526cb8de7f374ff78722",
"sha256:cbddc56b2502d3f87fda4f98d948eb5b11f36ff3902e17cb6cc44727f2200525",
"sha256:d79f18f41751725c56eceab2a886f021d70fd70a6188fd386e29a045945ffc10",
"sha256:dc2ca26a19ab32dc475dbad9dfe723d3a64c835f4c23f625c2b6566ca32b9f29",
"sha256:dd9bcd4f294eb0633bb33d1a74febdd2b9018b8b8ed325f861fffcd2c7660bb8",
"sha256:e8baab1bc7c9152715844f1faca6744f2416929de10d7639ed49555a85549f52",
"sha256:ec31fe12668af687b99acf1567399632a7c47b0e17cfb9ae47c098644ef36797",
"sha256:f12b4f7e2d8f9da3141564e6737d79016fe5336cc92de6814eba579744f65b0a",
"sha256:f58ac38d5ca045a377b3b377c84df8175ab992c970a53332fa8ac2373df44ff7"
],
"version": "==1.16.3"
"version": "==1.16.4"
},
"pandas": {
"hashes": [
@@ -402,6 +424,13 @@
],
"version": "==0.7.5"
},
"pluggy": {
"hashes": [
"sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc",
"sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"
],
"version": "==0.12.0"
},
"prometheus-client": {
"hashes": [
"sha256:1b38b958750f66f208bcd9ab92a633c0c994d8859c831f7abc1f46724fcee490"
@@ -424,12 +453,19 @@
"markers": "os_name != 'nt'",
"version": "==0.6.0"
},
"py": {
"hashes": [
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
],
"version": "==1.8.0"
},
"pygments": {
"hashes": [
"sha256:31cba6ffb739f099a85e243eff8cb717089fdd3c7300767d9fc34cb8e1b065f5",
"sha256:5ad302949b3c98dd73f8d9fcdc7e9cb592f120e32a18e23efd7f3dc51194472b"
"sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127",
"sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"
],
"version": "==2.4.0"
"version": "==2.4.2"
},
"pyrsistent": {
"hashes": [
@@ -437,6 +473,14 @@
],
"version": "==0.15.2"
},
"pytest": {
"hashes": [
"sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24",
"sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"
],
"index": "pypi",
"version": "==4.5.0"
},
"python-dateutil": {
"hashes": [
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
@@ -484,18 +528,18 @@
},
"qtconsole": {
"hashes": [
"sha256:a667558c7b1e1442a2e5bcef1686c55e096efd0b58d8b2a0a8415f4579991ee3",
"sha256:fdfc6002d9d2834c88f9c92e0f6f590284ff3740fa53016f188a62d58bcca6d8"
"sha256:4af84facdd6f00a6b9b2927255f717bb23ae4b7a20ba1d9ef0a5a5a8dbe01ae2",
"sha256:60d61d93f7d67ba2b265c6d599d413ffec21202fec999a952f658ff3a73d252b"
],
"version": "==4.4.4"
"version": "==4.5.1"
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"index": "pypi",
"version": "==2.21.0"
"version": "==2.22.0"
},
"s3transfer": {
"hashes": [
@@ -566,11 +610,11 @@
},
"urllib3": {
"hashes": [
"sha256:2393a695cd12afedd0dcb26fe5d50d0cf248e5a66f75dbd89a3d4eb333a61af4",
"sha256:a637e5fae88995b256e3409dc4d52c2e2e0ba32c42a6365fee8bbd2238de3cfb"
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
],
"markers": "python_version >= '3.4'",
"version": "==1.24.3"
"version": "==1.25.3"
},
"wcwidth": {
"hashes": [
@@ -598,6 +642,13 @@
"sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"
],
"version": "==1.11.1"
},
"zipp": {
"hashes": [
"sha256:8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d",
"sha256:ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3"
],
"version": "==0.5.1"
}
},
"develop": {
@@ -5,7 +5,7 @@ from .schema import Schema
class HistoricalOptionsData:
"""Historical Options Data container class."""
def __init__(self, file, key=None, query=None, schema=None):
def __init__(self, file, schema=None, **params):
if schema:
assert isinstance(schema, Schema)
else:
@@ -13,7 +13,7 @@ class HistoricalOptionsData:
schema.update({"contract": "optionroot", "date": "quotedate"})
self.schema = schema
self._data = pd.read_hdf(file, key=key, where=query)
self._data = pd.read_hdf(file, **params)
columns = self._data.columns
assert all((col in columns for col in schema))
+2
View File
@@ -59,6 +59,8 @@ class Field:
return self._create_filter(">=", value)
def __eq__(self, value):
if isinstance(value, str):
value = "'{}'".format(value)
return self._create_filter("==", value)
def __ne__(self, value):
@@ -0,0 +1,49 @@
from backtester.datahandler import Field
def test_strike_eq_100():
"""Test filter for 'strike' == 100"""
strike_field = Field("strike", "strike")
ft = strike_field == 100
assert ft.query == "strike == 100"
def test_strike_lt_100():
"""Test filter for 'strike' < 100"""
strike_field = Field("strike", "strike")
ft = strike_field < 100
assert ft.query == "strike < 100"
def test_strike_ge_100():
"""Test filter for 'strike' >= 100"""
strike_field = Field("strike", "strike")
ft = strike_field >= 100
assert ft.query == "strike >= 100"
def test_negate_filter():
"""Test negations of a filter"""
symbol_field = Field("underlying", "underlying")
ft = symbol_field == "SPX"
negated = ~ft
assert negated.query == "!(underlying == 'SPX')"
def test_compose_filters_with_and():
"""Test composition of two filters with and"""
symbol_field = Field("underlying", "underlying")
strike_field = Field("strike", "strike")
ft1 = symbol_field == "SPX"
ft2 = strike_field < 200
composed = ft1 & ft2
assert composed.query == "(underlying == 'SPX') & (strike < 200)"
def test_compose_filters_with_or():
"""Test composition of two filters with or"""
strike_field = Field("strike", "strike")
ft1 = strike_field >= 100
ft2 = strike_field < 200
composed = ft1 | ft2
assert composed.query == "(strike >= 100) | (strike < 200)"
-55
View File
@@ -1,55 +0,0 @@
import unittest
import os
import backtester as bt
from backtester.utils import get_data_dir
class TestBalancedPortfolio(unittest.TestCase):
"""Tests benchmark strategy using synthetic data"""
@classmethod
def setUpClass(cls):
data_dir = get_data_dir()
balanced_file = os.path.join(data_dir, "balanced_2015.csv")
cls.port = bt.run(balanced_file)
cls.report = cls.port.create_report()
cls.initial_capital = 1000000
cls.symbols = [
"VOO", "GLD", "VNQ", "VNQI", "TLT", "TIP", "BNDX", "EEM", "RJI"
]
def test_first_day_allocation(self):
"""First day allocation of cash should be equal to initial capital"""
self.assertEqual(TestBalancedPortfolio.report["Cash"].iloc[0],
TestBalancedPortfolio.initial_capital)
def test_total_return(self):
self.assertAlmostEqual(
TestBalancedPortfolio.report["% Price"][-1], -0.041, delta=0.001)
def test_voo_allocation(self):
"""Default VOO allocation should equal 30%"""
self.assertAlmostEqual(
TestBalancedPortfolio.report["VOO Exposure"].iloc[1],
TestBalancedPortfolio.initial_capital * 0.3,
delta=TestBalancedPortfolio.initial_capital * 0.01)
def test_voo_amount_constant(self):
"""Default VOO amount should remain constant throughout backtest"""
voo_amounts = TestBalancedPortfolio.report["VOO Amount"]
self.assertTrue(all(voo_amounts[1:] == voo_amounts.iloc[1]))
def test_sum_of_all_symbols(self):
"""Sum of all allocations should equal initial capital"""
total_allocations = sum([
TestBalancedPortfolio.report[symbol + " Exposure"].iloc[2]
for symbol in TestBalancedPortfolio.symbols
])
self.assertAlmostEqual(
total_allocations,
TestBalancedPortfolio.initial_capital,
delta=TestBalancedPortfolio.initial_capital * 0.1)
if __name__ == "__main__":
unittest.main()
-32
View File
@@ -1,32 +0,0 @@
import unittest
import os
import backtester as bt
from backtester.utils import get_data_dir
class TestPortfolio(unittest.TestCase):
"""Tests benchmark strategy using synthetic data"""
def setUp(self):
data_dir = get_data_dir()
synth_file = os.path.join(data_dir, "synthetic_data.csv")
self.port = bt.run(synth_file)
self.report = self.port.create_report()
def test_last_pct_price(self):
self.assertAlmostEqual(self.report["% Price"].iloc[-1], 1999)
def test_mean_10_day_return(self):
self.assertAlmostEqual(
self.report["Interval Change"][:10].mean(), 0.31432, delta=0.0001)
def test_mean_100_day_return(self):
self.assertAlmostEqual(
self.report["Interval Change"][:100].mean(), 0.05229, delta=0.0001)
def test_last_total(self):
self.assertEqual(self.report["Total Portfolio"].iloc[-1], 2000000000)
if __name__ == "__main__":
unittest.main()