def test_ceil(): assert Duration('1h').ceil('2018-12-07 13:12') == t_('2018-12-07 14:00') assert Duration('1d').ceil('2018-12-07 13:12') == t_('2018-12-08') assert Duration('1d').ceil('2018-12-07') == t_('2018-12-07') assert Duration('1d').ceil('2018-12-31') == t_('2018-12-31') assert Duration('1d').ceil('2019-01-01') == t_('2019-01-01')
def test_iterate_non_uniform(): spans = list( Duration('1M').iterate(util.timespan('2018-05-02', '2018-06-03', start_open=False), start_open=False)) assert len(spans) == 2 assert spans[0] == util.timespan('2018-05-01', '2018-06-01', start_open=False) assert spans[1] == util.timespan('2018-06-01', '2018-07-01', start_open=False) spans = list( Duration('1M').iterate(util.timespan('2018-05-02', '2018-06-03', start_open=False), backward=True, start_open=False)) assert len(spans) == 2 assert spans[0] == util.timespan('2018-06-01', '2018-07-01', start_open=False) assert spans[1] == util.timespan('2018-05-01', '2018-06-01', start_open=False)
def test_next(): assert Duration('1h').next('2018-12-07 13:12') == t_('2018-12-07 14:00') assert Duration('1d').next('2018-12-07 13:12') == t_('2018-12-08') assert Duration('1d').next('2018-12-07') == t_('2018-12-08') assert Duration('1d').next('2018-12-31') == t_('2019-01-01') assert Duration('1d').next('2019-01-01') == t_('2019-01-02')
def test_floor(): assert Duration('1h').floor('2018-12-07 13:12') == t_('2018-12-07 13:00') assert Duration('1d').floor('2018-12-07 13:12') == t_('2018-12-07') assert Duration('1d').floor('2018-12-07') == t_('2018-12-07') assert Duration('1d').floor('2018-12-31') == t_('2018-12-31') assert Duration('1d').floor('2019-01-01') == t_('2019-01-01')
def test_previous(): assert Duration('1h').previous('2018-12-07 13:12') == t_( '2018-12-07 13:00') assert Duration('1d').previous('2018-12-07 13:12') == t_('2018-12-07') assert Duration('1d').previous('2018-12-07') == t_('2018-12-06') assert Duration('1d').previous('2018-12-31') == t_('2018-12-30') assert Duration('1d').previous('2019-01-01') == t_('2018-12-31')
def test_span_date_non_calendar_non_uniform(): assert Duration('200d').span_date('2018-04-07 13:12') == span( '2018-01-01', '2018-07-20') assert Duration('200d').span_date('2018-12-07 20:01') == span( '2018-07-20', '2019-01-01') assert Duration('200d').span_date('2018-07-20', start_open=False) == span( '2018-07-20', '2019-01-01', start_open=False) assert Duration('200d').span_date('2018-07-20', start_open=True) == span('2018-01-01', '2018-07-20', start_open=True)
def test_span_interval(): assert Duration('1h').span_interval(Interval.open(3600, 2 * 3600), start_open=False) == span( 3600, 2 * 3600, start_open=False) assert Duration('1h').span_interval(Interval.open(3600, 2 * 3600), start_open=True) == span( 3600, 2 * 3600, start_open=True) assert Duration('1h').span_interval(Interval.closed(3600, 2 * 3600), start_open=False) == span( 3600, 3 * 3600, start_open=False) assert Duration('1h').span_interval(Interval.closed(3600, 2 * 3600), start_open=True) == span( 0, 2 * 3600, start_open=True)
def test_span_date_calendar_uniform(): t = t_('2018-12-07 13:12') assert Duration('1d').span_date(t) == span('2018-12-07', '2018-12-08') assert Duration('2d').span_date(t) == span('2018-12-07', '2018-12-09') assert Duration('100d').span_date(t) == span('2018-10-28', '2019-01-01') assert Duration('1w').span_date(t) == span('2018-12-03', '2018-12-10') assert Duration('2w').span_date(t) == span('2018-12-03', '2018-12-17') # The last day of the last week of 2018 is 30/12/2018 assert Duration('20w').span_date(t) == span('2018-10-08', '2018-12-31') assert Duration('1M').span_date(t) == span('2018-12-01', '2019-01-01') assert Duration('2M').span_date(t) == span('2018-11-01', '2019-01-01') assert Duration('1y').span_date(t) == span('2018-01-01', '2019-01-01') assert Duration('2y').span_date(t) == span('2018-01-01', '2020-01-01')
def test_walk(): spans = list(Duration('1h').walk(2 * 3600, limit=2, start_open=False)) assert spans == [ span(2 * 3600, 3 * 3600, start_open=False), span(3 * 3600, 4 * 3600, start_open=False) ] spans = list(Duration('1h').walk(2 * 3600, limit=2, start_open=True)) assert spans == [ span(1 * 3600, 2 * 3600, start_open=True), span(2 * 3600, 3 * 3600, start_open=True) ] spans = list( Duration('1h').walk(2 * 3600, limit=2, backward=True, start_open=False)) assert spans == [ span(2 * 3600, 3 * 3600, start_open=False), span(1 * 3600, 2 * 3600, start_open=False) ] spans = list( Duration('1h').walk(2 * 3600, limit=2, backward=True, start_open=True)) assert spans == [ span(1 * 3600, 2 * 3600, start_open=True), span(0 * 3600, 1 * 3600, start_open=True) ] r = Duration('1h') for s in r.walk(2 * 3600, limit=10): assert s == span(2 * 3600, 3 * 3600) break for s in r.walk(2 * 3600, limit=10): assert s == span(2 * 3600, 3 * 3600) break
def __init__(self, *ohlc, date=None, volume=None, resolution=None, res=None): ohlc = test_util.flatten(ohlc) assert len(ohlc) >= 4 if volume is None and len(ohlc) > 4: volume = float(ohlc[4]) resolution = resolution or res assert date >= 0 assert volume >= 0 assert resolution is not None super().__init__() self._start_date = date self._resolution = Duration.parse(resolution) self.open = float(ohlc[0]) self.high = float(ohlc[1]) self.low = float(ohlc[2]) self.close = float(ohlc[3]) assert self.open > 0 assert self.high > 0 assert self.low > 0 assert self.close > 0 self.volume = volume self._price_domain = None self.reset_transient_time()
def aggregate(cls, quotes, resolution=None) -> List['Quote']: if len(quotes) == 0: return [] resolution = Duration.parse(resolution) if resolution == quotes[0].resolution: # No aggregation necessary return quotes elif resolution < quotes[0].resolution: raise Exception( 'Aggregate resolution is smaller than the quote resolution') agr_quotes: List[Quote] = [] current_start_date = None span_quotes = [] for quote in quotes: start_date = resolution.floor(quote.date) if start_date != current_start_date: if len(span_quotes) != 0: agr_quote = cls.aggregate_single(span_quotes, resolution=resolution) agr_quotes.append(agr_quote) span_quotes.clear() current_start_date = start_date if start_date == current_start_date: span_quotes.append(quote) if len(span_quotes) != 0: agr_quote = cls.aggregate_single(span_quotes, resolution=resolution) agr_quotes.append(agr_quote) return agr_quotes
def test_iterate_uniform(): spans = list( Duration('1h').iterate(Interval.closed(2 * 3600, 4 * 3600), start_open=False)) assert len(spans) == 3 assert spans[0] == span(2 * 3600, 3 * 3600, start_open=False) assert spans[1] == span(3 * 3600, 4 * 3600, start_open=False) assert spans[2] == span(4 * 3600, 5 * 3600, start_open=False) spans = list( Duration('1h').iterate(Interval.closed(2 * 3600, 4 * 3600), backward=True, start_open=False)) assert len(spans) == 3 assert spans[0] == span(4 * 3600, 5 * 3600, start_open=False) assert spans[1] == span(3 * 3600, 4 * 3600, start_open=False) assert spans[2] == span(2 * 3600, 3 * 3600, start_open=False)
def test_span_date_non_calendar_uniform(): t = t_('2018-12-07 13:12') assert Duration('1S').span_date(1) == span(1, 1 + 1e-6) assert Duration('1s').span_date(1) == span(1, 2) assert Duration('1s').span_date(1) == span(1, 2) assert Duration('1m').span_date(1) == span(0, 60) assert Duration('1h').span_date(1) == span(0, 3600) assert Duration('1h').span_date(3600, start_open=False) == span(3600, 2 * 3600, start_open=False) assert Duration('1h').span_date(3600, start_open=True) == span(0, 3600, start_open=True) assert Duration('2h').span_date(1) == span(0, 2 * 3600) assert Duration('4h').span_date(t) == span('2018-12-07 12:00', '2018-12-07 16:00') assert Duration('1d').span_date(t) == span('2018-12-07', '2018-12-08')
def parse_many(cls, data) -> List['Quote']: # Get dicts in case there is missing # resolution data dicts = [cls.normalize_data(x) for x in data] # Find resolution res = None for i, d in enumerate(dicts): if d['res'] is not None: res = d['res'] break elif i != 0: # Compare dates d_prev = dicts[i - 1] if d['date'] is not None and d_prev['date'] is not None: res = Duration(d['date'] - d_prev['date']) break if res is None: raise ValueError('Unable to infer quote resolution') # Apply resolution, dates and create quotes quotes = [] last_quote = None for i, d in enumerate(dicts): if d['res'] is None: d['res'] = res elif d['res'] != res: raise ValueError('Mixed resolution') if d['date'] is None: if i == 0: raise ValueError('Unable to infer quote date') prev_date = dicts[i - 1]['date'] d['date'] = res.next(prev_date) q = cls._with_normalized_data(d) if last_quote is not None: if q.domain.intersects(last_quote.domain): raise ValueError('Quote dates are overlapping') quotes.append(q) last_quote = q return quotes
def test_iterate_non_uniform_large_size(): M = Duration('1M') spans = list( M.iterate(util.timespan('2018-05-02', '2018-08-10', start_open=False), size=2, start_open=False)) assert len(spans) == 2 assert M.count(spans[0], start_open=False) == 2 assert M.count(spans[1], start_open=False) == 2 assert spans[0] == util.timespan('2018-05-01', '2018-07-01', start_open=False) assert spans[1] == util.timespan('2018-07-01', '2018-09-01', start_open=False) spans = list( M.iterate(util.timespan('2018-05-02', '2018-08-10', start_open=False), size=2, backward=True, start_open=False)) assert len(spans) == 2 assert M.count(spans[0], start_open=False) == 2 assert M.count(spans[1], start_open=False) == 2 assert spans[0] == util.timespan('2018-07-01', '2018-09-01', start_open=False) assert spans[1] == util.timespan('2018-05-01', '2018-07-01', start_open=False)
def test_iterate_uniform_large_size(): d = Duration('1d') spans = list( d.iterate(util.timespan('2018-05-01', '2018-05-10', start_open=False), size=8, start_open=False)) assert len(spans) == 2 assert d.count(spans[0], start_open=False) == 8 assert d.count(spans[1], start_open=False) == 8 assert spans[0] == util.timespan('2018-05-01', '2018-05-09', start_open=False) assert spans[1] == util.timespan('2018-05-09', '2018-05-17', start_open=False) spans = list( d.iterate(util.timespan('2018-05-20', '2018-05-30', start_open=False), size=8, backward=True, start_open=False)) assert len(spans) == 2 assert d.count(spans[0], start_open=False) == 8 assert d.count(spans[1], start_open=False) == 8 assert spans[0] == util.timespan('2018-05-22', '2018-05-30', start_open=False) assert spans[1] == util.timespan('2018-05-14', '2018-05-22', start_open=False)
def __init__(self, func, offset, duration=None): super().__init__() self.curve = Curve.parse(func) self.offset = offset self.duration = None if duration is not None: self.duration = Duration.parse(duration) if type(self.offset) != int: raise Exception( 'Offset must be an interger when duration is defined') self._observer_token = self.curve.add_observer( begin=self.begin_offset_update, end=self.end_offset_update, prioritize=True)
def test_walk_sized(): spans = list( Duration('1h').walk(2 * 3600, size=2, limit=2, start_open=False)) assert spans == [ span(2 * 3600, 4 * 3600, start_open=False), span(4 * 3600, 6 * 3600, start_open=False) ] spans = list( Duration('1h').walk(2 * 3600, size=2, limit=2, start_open=True)) assert spans == [ span(1 * 3600, 3 * 3600, start_open=True), span(3 * 3600, 5 * 3600, start_open=True) ] spans = list( Duration('1h').walk(2 * 3600, size=2, limit=2, backward=True, start_open=False)) assert spans == [ span(1 * 3600, 3 * 3600, start_open=False), span(-1 * 3600, 1 * 3600, start_open=False) ] spans = list( Duration('1h').walk(2 * 3600, size=2, limit=2, backward=True, start_open=True)) assert spans == [ span(0 * 3600, 2 * 3600, start_open=True), span(-2 * 3600, 0 * 3600, start_open=True) ]
def test_offset_with_duration_non_uniform(): duration = Duration('20h') raw_points = [(ts.start, float(i + 1)) for i, ts in enumerate(duration.walk(0, limit=3))] assert np.allclose(raw_points, [(0, 1), (20 * HOUR, 2), (DAY, 3)]) points = Points(raw_points, uniform=False) f = points.offset(1, duration=duration) assert f.domain.start == 20 * HOUR assert f.domain.end == DAY + 20 * HOUR assert np.allclose(f.sample_points(), [(20 * HOUR, 1), (DAY, 2), (DAY + 20 * HOUR, 3)]) assert f(19 * HOUR) is None assert f(22 * HOUR) == 1.5 assert f(DAY + 10 * HOUR) == 2.5 assert f(DAY + 20.5 * HOUR) is None assert f.x_next(19 * HOUR) == 20 * HOUR assert f.x_next(22 * HOUR) == DAY assert f.x_next(DAY + 10 * HOUR) == DAY + 20 * HOUR assert f.x_next(DAY + 20.5 * HOUR) is None assert f.x_previous(19 * HOUR) is None assert f.x_previous(22 * HOUR) == 20 * HOUR assert f.x_previous(DAY + 10 * HOUR) == DAY assert f.x_previous(DAY + 20.5 * HOUR) == DAY + 20 * HOUR
def empty_list(cls, price: float, resolution: Any, domain: Any) -> List['Quote']: domain = Interval.parse(domain) quotes: List[Quote] = [] if not domain.is_empty: if not domain.is_finite: raise ValueError('Must specify a finite domain') resolution = Duration.parse(resolution) ohlc = [price] * 4 for span in resolution.iterate(domain, start_open=DOMAIN_START_OPEN): q = Quote(ohlc, date=span.start, volume=0.0, resolution=resolution) quotes.append(q) return quotes
def aggregate_single(cls, quotes, resolution=None) -> 'Quote': if len(quotes) == 0: raise Exception('Cannot aggregate quote from empty list') o = quotes[0].open c = quotes[-1].close l = o h = o v = 0 for q in quotes: if q.low < l: l = q.low if q.high > h: h = q.high v += q.volume if resolution is not None: resolution = Duration.parse(resolution) else: resolution = quotes[0].resolution.aggregate(len(quotes)) date = resolution.floor(quotes[0].date) return Quote(o, h, l, c, date=date, volume=v, resolution=resolution)
def mock(cls, quotes, t_start=0, t_step=86400.0, volume=1): dicts = [] t_start = test_util.timestamp(t_start) t_step = Duration.parse(t_step) t = t_step.floor(t_start) for q in quotes: d = cls.normalize_data(q) if d['volume'] is None: d['volume'] = volume if d['date'] is None: d['date'] = t else: t = d['date'] if d['res'] is None: d['res'] = t_step dicts.append(d) t = t_step.next(t) return cls.parse_many(dicts)
def test_is_uniform(): assert Duration('1S').is_uniform is True assert Duration('2S').is_uniform is True assert Duration('1s').is_uniform is True assert Duration('2s').is_uniform is True assert Duration('1m').is_uniform is True assert Duration('2m').is_uniform is True assert Duration('3m').is_uniform is True assert Duration('5m').is_uniform is True assert Duration('6m').is_uniform is True assert Duration('7m').is_uniform is False assert Duration('8m').is_uniform is True assert Duration('9m').is_uniform is True assert Duration('12m').is_uniform is True assert Duration('15m').is_uniform is True assert Duration('30m').is_uniform is True assert Duration('1h').is_uniform is True assert Duration('2h').is_uniform is True assert Duration('3h').is_uniform is True assert Duration('5h').is_uniform is False assert Duration('6h').is_uniform is True assert Duration('7h').is_uniform is False assert Duration('8h').is_uniform is True assert Duration('9h').is_uniform is False assert Duration('12h').is_uniform is True assert Duration('1d').is_uniform is True assert Duration('2d').is_uniform is False assert Duration('1w').is_uniform is True assert Duration('2w').is_uniform is False assert Duration('1M').is_uniform is False assert Duration('2M').is_uniform is False assert Duration('1y').is_uniform is False assert Duration('2y').is_uniform is False
def test_parent(): assert Duration('1S').parent == Duration('1s') assert Duration('1s').parent == Duration('1d') assert Duration('1m').parent == Duration('1d') assert Duration('1h').parent == Duration('1d') assert Duration('1m').parent == Duration('1d') assert Duration('1d').parent == Duration('1y') assert Duration('1w').parent == Duration('1y') assert Duration('1y').parent is None
def test_with_degree(): assert Duration('1h').with_degree(4) == Duration('4h')
def test_eq(): assert Duration('1h') == Duration('1h') assert Duration('1h') != Duration('2h') assert Duration('1h') != Duration('1d')
def test_implicit_arithmetic(): assert Duration('1h') + 1 == 3601 assert Duration('1h') - 1 == 3599 assert Duration('1h') * 10 == 36000 assert Duration('1h') / 10 == 360 assert 1 + Duration('1h') == 3601 assert 1 - Duration('1h') == -3599 assert 10 * Duration('1h') == 36000 assert 10 / Duration('1h') == 10 / 3600 assert -Duration('1h') == -3600 assert +Duration('1h') == 3600 assert abs(Duration('1h')) == 3600 with pytest.raises(Exception): _ = Duration('23h') / 10
def test_pad(): assert Duration('1d').pad(span('2018-03-10', '2019-03-20'), start=2, end=3) == span('2018-03-08', '2019-03-23')
def test_count(): assert Duration('1h').count(Interval.open(2 * 3600, 4 * 3600), start_open=False) == 2
def test_step_non_uniform(): assert Duration('1M').step('2018-06-07 13:12', count=0) == t_('2018-06-07 13:12') assert Duration('1M').step('2018-06-07 13:12', count=1) == t_('2018-07-01') assert Duration('1M').step('2018-06-01', count=1) == t_('2018-07-01') assert Duration('1M').step('2018-06-07 13:12', count=0, backward=True) == t_('2018-06-07 13:12') assert Duration('1M').step('2018-06-07 13:12', count=1, backward=True) == t_('2018-06-01') assert Duration('1M').step('2018-06-01', count=1, backward=True) == t_('2018-05-01') assert Duration('1M').step('2018-06-07 13:12', count=-1) == t_('2018-06-01') assert Duration('1M').step('2018-06-01', count=-1) == t_('2018-05-01') assert Duration('1M').step('2018-06-07 13:12', count=3) == t_('2018-09-01') assert Duration('1M').step('2018-06-01', count=3) == t_('2018-09-01') assert Duration('1M').step('2018-06-07 13:12', count=-3) == t_('2018-04-01') assert Duration('1M').step('2018-06-01', count=-3) == t_('2018-03-01')