Exemple #1
0
    def trim_list(cls,
                  quotes: List['Quote'],
                  domain: Any = None) -> List['Quote']:
        """
        Removes empty quotes from the start and end of the list.

        If a domain is specified, only empty quotes outside of the
        domain are removed.
        """
        if len(quotes) == 0:
            return []
        i0: Optional[int] = None
        i1: Optional[int] = None
        r = range(len(quotes))
        if domain is not None:
            domain = Interval.parse(domain)

        for i in r:
            q = quotes[i]
            if not q.is_empty or (domain is not None
                                  and domain.intersects(q.domain)):
                i0 = i
                break
        if i0 is None:
            return []

        for i in reversed(r):
            q = quotes[i]
            if not q.is_empty or (domain is not None
                                  and domain.intersects(q.domain)):
                i1 = i
                break
        assert i1 is not None
        return quotes[i0:i1 + 1]
Exemple #2
0
    def span_interval(self, interval, start_open=False) -> Interval:
        """
        Returns the time interval which fully contains the specified interval.
        """
        interval = Interval.parse(interval, default_inf=True)
        if interval.is_empty:
            return Interval.empty()
        elif interval.start == interval.end:
            return self.span_date(interval.start, start_open=start_open)

        end_open = not start_open

        if interval.is_negative_infinite:
            start = -math.inf
        else:
            # Move outward if interval is closed but should be open
            o = not interval.start_open and start_open
            span = self.span_date(interval.start, start_open=o)
            start = span.start

        if interval.is_positive_infinite:
            end = math.inf
        else:
            # Move outward if interval is closed but should be open
            o = not interval.end_open and end_open
            span = self.span_date(interval.end, start_open=not o)
            end = span.end

        return Interval(start,
                        end,
                        start_open=start_open,
                        end_open=not start_open)
Exemple #3
0
 def __init__(self, y, domain=None):
     super().__init__(min_step=math.inf)
     self._value = y
     if domain is not None:
         domain = Interval.parse(domain)
     else:
         domain = Interval.infinite()
     self._const_interval = domain
Exemple #4
0
def test_parse():
    d = Interval.parse(Interval(0, 1, start_open=True, end_open=True))
    assert d.start == 0
    assert d.end == 1
    assert d.start_open
    assert d.end_open

    d = Interval.parse((0, 1))
    assert d.start == 0
    assert d.end == 1
    assert not d.start_open
    assert not d.end_open

    d = Interval.parse(1)
    assert d.start == 1
    assert d.end == 1
    assert not d.start_open
    assert not d.end_open

    with pytest.raises(Exception):
        _ = Interval.parse(None)
    with pytest.raises(Exception):
        _ = Interval.parse(None, default_inf=False)
    assert Interval.parse(None, default_inf=True) == Interval.infinite()

    d = Interval.parse(math.inf)
    assert math.isinf(d.start)
    assert math.isinf(d.end)
    assert d.start > 0
    assert d.end > 0
    assert not d.is_negative_infinite
    assert not d.is_positive_infinite

    d = Interval.parse(-math.inf)
    assert math.isinf(d.start)
    assert math.isinf(d.end)
    assert d.start < 0
    assert d.end < 0
    assert not d.is_negative_infinite
    assert not d.is_positive_infinite

    d = Interval.parse([])
    assert d.is_empty
Exemple #5
0
    def reset(self, domain=None):
        points_len = len(self._points)
        if points_len == 0:
            return
        if domain is None:
            return self.set([])
        domain = Interval.parse(domain)

        if not self.domain.intersects(domain):
            return

        if domain.is_superset_of(self.domain):
            self.set([])
            return

        remove_start_i, remove_end_i = self._domain_indexes(domain)
        if remove_start_i == remove_end_i:
            # Nothing to remove
            return
        head = self._points[:remove_start_i]
        tail = self._points[remove_end_i:]
        if len(head) != 0 and domain.contains(head[-1][0]):
            del head[-1]
        if len(tail) != 0 and domain.contains(tail[0][0]):
            del tail[0]
        head_len = len(head)
        tail_len = len(tail)
        if head_len + tail_len == 0:
            self.set([])
            return
        points = head + tail

        update_start = self._points[0][0]
        update_start_open = False
        if head_len != 0:
            update_start = head[-1][0]
            update_start_open = True

        update_end = self._points[-1][0]
        update_end_open = False
        if tail_len != 0:
            update_end = tail[0][0]
            update_end_open = True

        update_domain = Interval(update_start,
                                 update_end,
                                 start_open=update_start_open,
                                 end_open=update_end_open)

        self.set(points, update_domain=update_domain)
Exemple #6
0
 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
Exemple #7
0
    def add_observer(self,
                     *obj,
                     domain=None,
                     begin=None,
                     end=None,
                     autoremove=False,
                     prioritize=False):
        if begin is None and end is None:
            return 0

        Curve._token_counter += 1
        token = Curve._token_counter
        domain = Interval.parse(domain, default_inf=True)
        obj_ref = None

        if len(obj) != 0:
            if autoremove:
                # Remove observer automatically
                obj_ref = weakref.ref(obj[0],
                                      lambda _: self.remove_observer(token))
            else:
                # Calling remove_observer() is required
                obj_ref = weakref.ref(obj[0])
        elif autoremove:
            raise Exception('Autoremoving an observer requires an object')

        # Do the callback functions require the domain?
        begin_with_interval = False
        end_with_interval = False
        if begin:
            begin_with_interval = util.count_positional_args(begin) == 1
        if end:
            end_with_interval = util.count_positional_args(end) == 1

        # TODO: does saving strong references to callbacks create a retain cycle?
        self._observer_data[token] = (obj_ref, domain, begin, end,
                                      begin_with_interval, end_with_interval)
        if prioritize:
            self._ordered_observer_tokens.insert(0, token)
        else:
            self._ordered_observer_tokens.append(token)
        return token
Exemple #8
0
    def missing_domains(cls, quotes, domain=None):
        # TODO: optimise by recursively dividing quotes
        # in half and checking if the quotes are
        # contiguous. Then collect domains between
        # contiguous quotes.
        quotes_len = len(quotes)
        if quotes_len == 0:
            if domain is None:
                return []
            else:
                return [domain]

        if domain is None:
            domain = cls.list_domain(quotes)
        else:
            domain = Interval.parse(domain)
        if domain.is_empty:
            return []

        missing_list = []
        head = Interval.intersection([domain, quotes[0].domain.get_lt()])
        if not head.is_empty:
            missing_list.append(head)

        for i in range(1, quotes_len):
            q0 = quotes[i - 1]
            q1 = quotes[i]
            if q0.end_date != q1.start_date:
                if q0.end_date > q1.start_date:
                    continue
                missing = Interval(q0.domain.end,
                                   q1.domain.start,
                                   start_open=not q0.domain.end_open,
                                   end_open=not q1.domain.start_open)
                missing_list.append(missing)

        tail = Interval.intersection([domain, quotes[-1].domain.get_gt()])
        if not tail.is_empty:
            missing_list.append(tail)

        return missing_list
Exemple #9
0
    def count(self, interval, start_open=False) -> int:
        """
        Returns the number of intervals in the specified interval.
        """
        interval = Interval.parse(interval, default_inf=True)
        if interval.is_empty:
            return 0
        if not interval.is_finite:
            raise Exception('Cannot count intervals on infinite interval')
        interval = self.span_interval(interval, start_open=start_open)

        if not self.is_uniform:
            # Walk spans
            # TODO: this can be optimised to avoid walking
            count = 0
            for _ in self.iterate(interval):
                count += 1
            return count
        else:
            # Use seconds
            return int(math.ceil(interval.length / self.ave_seconds))
Exemple #10
0
    def inside_domain(quotes, domain):
        """
        Returns quotes inside the domain.
        """
        domain = Interval.parse(domain)
        if domain.is_empty:
            return []
        if domain.is_infinite:
            return list(quotes)
        quotes_len = len(quotes)
        if quotes_len == 0:
            return []
        i0 = max(0, Quote.bisect(quotes, domain.start) - 1)
        i1 = min(quotes_len, Quote.bisect(quotes, domain.end) + 1)
        matching_quotes = quotes[i0:i1]

        while len(matching_quotes) != 0 and not domain.intersects(
                matching_quotes[0].domain):
            del matching_quotes[0]
        while len(matching_quotes) != 0 and not domain.intersects(
                matching_quotes[-1].domain):
            del matching_quotes[-1]

        return matching_quotes
Exemple #11
0
    def sample_points(self, domain=None, min_step=None, step=None):
        domain = Interval.parse(domain, default_inf=True)

        if self.domain.is_empty:
            return []

        if self.interval is not None and (
            (min_step is not None and min_step > self.interval) or
            (step is not None and step != self.interval)):
            # Irregular sampling
            return super().sample_points(domain=domain,
                                         min_step=min_step,
                                         step=step)

        if domain is None or domain.is_superset_of(self.domain):
            # Sample all
            return list(self._points)

        # Sample some
        domain = Interval.intersection([self.domain, domain])
        if domain.is_empty:
            return []
        i0, i1 = self._domain_indexes(domain)
        return self._points[i0:i1]
Exemple #12
0
    def iterate(self,
                interval,
                size=1,
                backward=False,
                start_open=False) -> Iterator[Optional[Interval]]:
        """
        Iterates time-spans inside a interval.
        Does not trim timespans inside the interval.
        """
        interval = Interval.parse(interval, default_inf=True)
        if interval.is_empty:
            return iter([])
        start = interval.start if not backward else interval.end
        if math.isinf(start):
            raise ValueError('Cannot iterate intervals from infinity')
        if size < 1:
            raise ValueError('Size must ba a positive integer')

        if self.is_uniform:
            interval_span = self.span_interval(interval, start_open=start_open)
            start = interval_span.start if not backward else interval_span.end
            count = math.ceil(
                self.count(interval_span, start_open=start_open) / size)
            step = size * self.ave_seconds
            if backward:
                step = -step
            i = 0

            def next_uniform_span():
                nonlocal start, step, i, count

                if i == count:
                    return None
                a = start + i * step
                b = a + step
                i += 1
                return Interval(min(a, b),
                                max(a, b),
                                start_open=start_open,
                                end_open=not start_open)

            return iter(next_uniform_span, None)

        walker = self.walk(start,
                           size=size,
                           backward=backward,
                           start_open=start_open)

        def next_span_in_interval():
            nonlocal start

            while True:
                span = next(walker)
                if span is None or span.is_empty:
                    return None
                elif span.intersects(interval):
                    return span
                elif span.contains(start):
                    # Keep looking for interval
                    continue
                else:
                    # Went outside interval
                    return None

            return None

        return iter(next_span_in_interval, None)
Exemple #13
0
 def is_in_extension(self, x):
     domain = Interval.parse(x)
     return (self.start and domain <= self.extension_interval) or \
         (self.end and domain >= self.extension_interval)
Exemple #14
0
 def __init__(self, y_func, domain=None, min_step=MIN_STEP):
     super().__init__(min_step=min_step)
     self.y_func = y_func
     self._domain = Interval.parse(domain) if domain is not None else Interval.infinite()