diff options
-rw-r--r-- | portfolio.py | 23 | ||||
-rw-r--r-- | test.py | 53 |
2 files changed, 66 insertions, 10 deletions
diff --git a/portfolio.py b/portfolio.py index 21a9834..b77f975 100644 --- a/portfolio.py +++ b/portfolio.py | |||
@@ -1,5 +1,5 @@ | |||
1 | import time | 1 | import time |
2 | from datetime import datetime | 2 | from datetime import datetime, timedelta |
3 | from decimal import Decimal as D, ROUND_DOWN | 3 | from decimal import Decimal as D, ROUND_DOWN |
4 | # Put your poloniex api key in market.py | 4 | # Put your poloniex api key in market.py |
5 | from json import JSONDecodeError | 5 | from json import JSONDecodeError |
@@ -14,12 +14,19 @@ class Portfolio: | |||
14 | URL = "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json" | 14 | URL = "https://cryptoportfolio.io/wp-content/uploads/portfolio/json/cryptoportfolio.json" |
15 | liquidities = {} | 15 | liquidities = {} |
16 | data = None | 16 | data = None |
17 | last_date = None | ||
17 | 18 | ||
18 | @classmethod | 19 | @classmethod |
19 | def repartition(cls, liquidity="medium"): | 20 | def wait_for_recent(cls, delta=4): |
20 | cls.parse_cryptoportfolio() | 21 | cls.repartition(refetch=True) |
22 | while cls.last_date is None or datetime.now() - cls.last_date > timedelta(delta): | ||
23 | time.sleep(30) | ||
24 | cls.repartition(refetch=True) | ||
25 | |||
26 | @classmethod | ||
27 | def repartition(cls, liquidity="medium", refetch=False): | ||
28 | cls.parse_cryptoportfolio(refetch=refetch) | ||
21 | liquidities = cls.liquidities[liquidity] | 29 | liquidities = cls.liquidities[liquidity] |
22 | cls.last_date = sorted(liquidities.keys())[-1] | ||
23 | return liquidities[cls.last_date] | 30 | return liquidities[cls.last_date] |
24 | 31 | ||
25 | @classmethod | 32 | @classmethod |
@@ -34,8 +41,8 @@ class Portfolio: | |||
34 | cls.data = None | 41 | cls.data = None |
35 | 42 | ||
36 | @classmethod | 43 | @classmethod |
37 | def parse_cryptoportfolio(cls): | 44 | def parse_cryptoportfolio(cls, refetch=False): |
38 | if cls.data is None: | 45 | if refetch or cls.data is None: |
39 | cls.get_cryptoportfolio() | 46 | cls.get_cryptoportfolio() |
40 | 47 | ||
41 | def filter_weights(weight_hash): | 48 | def filter_weights(weight_hash): |
@@ -57,7 +64,8 @@ class Portfolio: | |||
57 | weights_hash = portfolio_hash["weights"] | 64 | weights_hash = portfolio_hash["weights"] |
58 | weights = {} | 65 | weights = {} |
59 | for i in range(len(weights_hash["_row"])): | 66 | for i in range(len(weights_hash["_row"])): |
60 | weights[weights_hash["_row"][i]] = dict(filter( | 67 | date = datetime.strptime(weights_hash["_row"][i], "%Y-%m-%d") |
68 | weights[date] = dict(filter( | ||
61 | filter_weights, | 69 | filter_weights, |
62 | map(clean_weights(i), weights_hash.items()))) | 70 | map(clean_weights(i), weights_hash.items()))) |
63 | return weights | 71 | return weights |
@@ -69,6 +77,7 @@ class Portfolio: | |||
69 | "medium": medium_liquidity, | 77 | "medium": medium_liquidity, |
70 | "high": high_liquidity, | 78 | "high": high_liquidity, |
71 | } | 79 | } |
80 | cls.last_date = max(max(medium_liquidity.keys()), max(high_liquidity.keys())) | ||
72 | 81 | ||
73 | class Computation: | 82 | class Computation: |
74 | computations = { | 83 | computations = { |
@@ -32,7 +32,7 @@ class WebMockTestCase(unittest.TestCase): | |||
32 | mock.patch.multiple(portfolio.TradeStore, | 32 | mock.patch.multiple(portfolio.TradeStore, |
33 | all=[], | 33 | all=[], |
34 | debug=False), | 34 | debug=False), |
35 | mock.patch.multiple(portfolio.Portfolio, data=None, liquidities={}), | 35 | mock.patch.multiple(portfolio.Portfolio, last_date=None, data=None, liquidities={}), |
36 | mock.patch.multiple(portfolio.Computation, | 36 | mock.patch.multiple(portfolio.Computation, |
37 | computations=portfolio.Computation.computations), | 37 | computations=portfolio.Computation.computations), |
38 | mock.patch.multiple(helper, | 38 | mock.patch.multiple(helper, |
@@ -102,7 +102,8 @@ class PortfolioTest(WebMockTestCase): | |||
102 | 'SC': (D("0.0623"), "long"), | 102 | 'SC': (D("0.0623"), "long"), |
103 | 'ZEC': (D("0.3701"), "long"), | 103 | 'ZEC': (D("0.3701"), "long"), |
104 | } | 104 | } |
105 | self.assertDictEqual(expected, liquidities["high"]['2018-01-08']) | 105 | date = portfolio.datetime(2018, 1, 8) |
106 | self.assertDictEqual(expected, liquidities["high"][date]) | ||
106 | 107 | ||
107 | expected = { | 108 | expected = { |
108 | 'BTC': (D("1.1102e-16"), "long"), | 109 | 'BTC': (D("1.1102e-16"), "long"), |
@@ -117,13 +118,17 @@ class PortfolioTest(WebMockTestCase): | |||
117 | 'VIA': (D("0.1"), "long"), | 118 | 'VIA': (D("0.1"), "long"), |
118 | 'XCP': (D("0.1"), "long"), | 119 | 'XCP': (D("0.1"), "long"), |
119 | } | 120 | } |
120 | self.assertDictEqual(expected, liquidities["medium"]['2018-01-08']) | 121 | self.assertDictEqual(expected, liquidities["medium"][date]) |
122 | self.assertEqual(portfolio.datetime(2018, 1, 15), portfolio.Portfolio.last_date) | ||
121 | 123 | ||
122 | # It doesn't refetch the data when available | 124 | # It doesn't refetch the data when available |
123 | portfolio.Portfolio.parse_cryptoportfolio() | 125 | portfolio.Portfolio.parse_cryptoportfolio() |
124 | 126 | ||
125 | self.assertEqual(1, self.wm.call_count) | 127 | self.assertEqual(1, self.wm.call_count) |
126 | 128 | ||
129 | portfolio.Portfolio.parse_cryptoportfolio(refetch=True) | ||
130 | self.assertEqual(2, self.wm.call_count) | ||
131 | |||
127 | def test_repartition(self): | 132 | def test_repartition(self): |
128 | expected_medium = { | 133 | expected_medium = { |
129 | 'BTC': (D("1.1102e-16"), "long"), | 134 | 'BTC': (D("1.1102e-16"), "long"), |
@@ -151,6 +156,48 @@ class PortfolioTest(WebMockTestCase): | |||
151 | self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium")) | 156 | self.assertEqual(expected_medium, portfolio.Portfolio.repartition(liquidity="medium")) |
152 | self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high")) | 157 | self.assertEqual(expected_high, portfolio.Portfolio.repartition(liquidity="high")) |
153 | 158 | ||
159 | self.assertEqual(1, self.wm.call_count) | ||
160 | |||
161 | portfolio.Portfolio.repartition() | ||
162 | self.assertEqual(1, self.wm.call_count) | ||
163 | |||
164 | portfolio.Portfolio.repartition(refetch=True) | ||
165 | self.assertEqual(2, self.wm.call_count) | ||
166 | |||
167 | @mock.patch.object(portfolio.time, "sleep") | ||
168 | @mock.patch.object(portfolio.Portfolio, "repartition") | ||
169 | def test_wait_for_recent(self, repartition, sleep): | ||
170 | self.call_count = 0 | ||
171 | def _repartition(refetch): | ||
172 | self.assertTrue(refetch) | ||
173 | self.call_count += 1 | ||
174 | portfolio.Portfolio.last_date = portfolio.datetime.now()\ | ||
175 | - portfolio.timedelta(10)\ | ||
176 | + portfolio.timedelta(self.call_count) | ||
177 | repartition.side_effect = _repartition | ||
178 | |||
179 | portfolio.Portfolio.wait_for_recent() | ||
180 | sleep.assert_called_with(30) | ||
181 | self.assertEqual(6, sleep.call_count) | ||
182 | self.assertEqual(7, repartition.call_count) | ||
183 | |||
184 | sleep.reset_mock() | ||
185 | repartition.reset_mock() | ||
186 | portfolio.Portfolio.last_date = None | ||
187 | self.call_count = 0 | ||
188 | portfolio.Portfolio.wait_for_recent(delta=15) | ||
189 | sleep.assert_not_called() | ||
190 | self.assertEqual(1, repartition.call_count) | ||
191 | |||
192 | sleep.reset_mock() | ||
193 | repartition.reset_mock() | ||
194 | portfolio.Portfolio.last_date = None | ||
195 | self.call_count = 0 | ||
196 | portfolio.Portfolio.wait_for_recent(delta=1) | ||
197 | sleep.assert_called_with(30) | ||
198 | self.assertEqual(9, sleep.call_count) | ||
199 | self.assertEqual(10, repartition.call_count) | ||
200 | |||
154 | @unittest.skipUnless("unit" in limits, "Unit skipped") | 201 | @unittest.skipUnless("unit" in limits, "Unit skipped") |
155 | class AmountTest(WebMockTestCase): | 202 | class AmountTest(WebMockTestCase): |
156 | def test_values(self): | 203 | def test_values(self): |