<commit_before># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2013 <NAME>
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import collections
import itertools
import numbers
import operator

import numpy as np
from zipline.transforms import utils as transforms
from zl.indicators import utils


BUY = 'Buy'
SELL = 'Sell'


def countdown(direction, events, period, lookback, field):
    event_iter = itertools.izip(events[lookback:], events[:-lookback])
    signals = []
    compare_field = 'low'
    compare_op = operator.le

    if direction == SELL:
        compare_field = 'high'
        compare_op = operator.ge

    for position, (event, prior) in enumerate(event_iter):
        if compare_op(event[field], prior[compare_field]):
            signals.append(position)

    if len(signals) >= period:
        bars = [bar for bar in events[lookback:]]

        # TODO(jkoelker) Figure out if bar "8" should be determined by
        #                the period length
        qualifier = bars[signals[7]]
        ending = bars[signals[-1]]

        if compare_op(ending[compare_field], qualifier[field]):
            lowes = [bar['low'] for bar in bars]
            highs = [bar['high'] for bar in bars]

            high = np.max(highs)
            low = np.max(lowes)
            return Signal(direction, high, low, bars, signals)


class Signal(object):
    def __init__(self, direction, high, low, bars, signals):
        self.directin = direction
        self.high = high
        self.low = low
        self.bars = bars
        self.signals = signals

    @utils.cached_property
    def risk_level(self):
        for bar in self.bars:
            if self.direction == BUY:
                if bar['low'] == self.low:
                    return self.low - (bar['high'] - bar['low'])

            elif self.direction == SELL:
                if bar['high'] == self.hight:
                    return self.high + (bar['high'] - bar['low'])

        assert False, "Signal extreme not found in bars!"


class Countdown(object):
    __metaclass__ = transforms.TransformMeta

    def __init__(self, period=13, lookback=2, field='close'):
        self.period = period
        self.lookback = lookback
        self.field = field
        self.sid_windows = collections.defaultdict(self.create_window)

    def create_window(self):
        return CountdownWindow(self.period, self.lookback, self.field)

    def update(self, event):
        window = self.sid_windows[event.sid]
        window.update(event)
        return window()


class CountdownWindow(transforms.EventWindow):
    def __init__(self, period, lookback, field, setup_signal=None):
        window_length = period + lookback
        transforms.EventWindow.__init__(self, window_length=window_length)

        self.period = period
        self.lookback = lookback
        self.field = field
        self.setup_signal = setup_signal
        self.signal = None

        # NOTE(jkoelker) Prime the window with the last event + lookback
        #                from the setup_signal
        if self.setup_signal is not None:
            for bar in self.setup_signal.bars[-(lookback + 1):]:
                self.update(bar)

    def handle_add(self, event):
        for field in (self.field, 'high', 'low'):
            assert field in event
            assert isinstance(event[field], numbers.Number)

        if len(self.ticks) >= self.window_length and self.setup_signal:
            self.signal = countdown(self.setup_signal.direct, self.ticks,
                                    self.period, self.lookback, self.field)

        # NOTE(jkoelker) Check if we need to expand the window
        if len(self.ticks) == self.window_length:
            self.window_length = self.window_length + 1

    def handle_remove(self, event):
        pass

    def _reset_window(self):
        self.window_length = self.period + self.lookback

    def __call__(self):<commit_msg>Add TODO about possible bug<commit_after># vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2013 <NAME>
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import collections
import itertools
import numbers
import operator

import numpy as np
from zipline.transforms import utils as transforms
from zl.indicators import utils


BUY = 'Buy'
SELL = 'Sell'


def countdown(direction, events, period, lookback, field):
    event_iter = itertools.izip(events[lookback:], events[:-lookback])
    signals = []
    compare_field = 'low'
    compare_op = operator.le

    if direction == SELL:
        compare_field = 'high'
        compare_op = operator.ge

    for position, (event, prior) in enumerate(event_iter):
        if compare_op(event[field], prior[compare_field]):
            signals.append(position)

    if len(signals) >= period:
        bars = [bar for bar in events[lookback:]]

        # TODO(jkoelker) Figure out if bar "8" should be determined by
        #                the period length
        qualifier = bars[signals[7]]
        ending = bars[signals[-1]]

        if compare_op(ending[compare_field], qualifier[field]):
            lowes = [bar['low'] for bar in bars]
            highs = [bar['high'] for bar in bars]

            high = np.max(highs)
            low = np.max(lowes)
            return Signal(direction, high, low, bars, signals)


class Signal(object):
    def __init__(self, direction, high, low, bars, signals):
        self.directin = direction
        self.high = high
        self.low = low
        self.bars = bars
        self.signals = signals

    @utils.cached_property
    def risk_level(self):
        for bar in self.bars:
            if self.direction == BUY:
                if bar['low'] == self.low:
                    return self.low - (bar['high'] - bar['low'])

            elif self.direction == SELL:
                if bar['high'] == self.hight:
                    return self.high + (bar['high'] - bar['low'])

        assert False, "Signal extreme not found in bars!"


class Countdown(object):
    __metaclass__ = transforms.TransformMeta

    def __init__(self, period=13, lookback=2, field='close'):
        self.period = period
        self.lookback = lookback
        self.field = field
        self.sid_windows = collections.defaultdict(self.create_window)

    def create_window(self):
        return CountdownWindow(self.period, self.lookback, self.field)

    def update(self, event):
        window = self.sid_windows[event.sid]
        window.update(event)
        return window()


class CountdownWindow(transforms.EventWindow):
    def __init__(self, period, lookback, field, setup_signal=None):
        window_length = period + lookback
        transforms.EventWindow.__init__(self, window_length=window_length)

        self.period = period
        self.lookback = lookback
        self.field = field
        self.setup_signal = setup_signal
        self.signal = None

        # NOTE(jkoelker) Prime the window with the last event + lookback
        #                from the setup_signal
        if self.setup_signal is not None:
            for bar in self.setup_signal.bars[-(lookback + 1):]:
                self.update(bar)

    # TODO(jkoelker) There is probably a bug in the window expansion. Need
    #                to think aboot this more
    def handle_add(self, event):
        for field in (self.field, 'high', 'low'):
            assert field in event
            assert isinstance(event[field], numbers.Number)

        if len(self.ticks) >= self.window_length and self.setup_signal:
            self.signal = countdown(self.setup_signal.direct, self.ticks,
                                    self.period, self.lookback, self.field)

        # NOTE(jkoelker) Check if we need to expand the window
        if len(self.ticks) == self.window_length:
            self.window_length = self.window_length + 1

    def handle_remove(self, event):
        pass

    def _reset_window(self):
        self.window_length = self.period + self.lookback

    def __call__(self):
{'fraction_non_alphanumeric': 0.09532793252691155, 'fraction_numerical': 0.00421706802796582, 'mean_word_length': 2.4489092996555684, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4091648', 'n_tokens_mistral': 2599, 'n_tokens_neox': 2513, 'n_words': 775}