Пример #1
0
    def _subscribe_core(self, observer):
        clock = self.scheduler.to_relative(self.scheduler.now)
        self.subscriptions.append(Subscription(clock))
        index = len(self.subscriptions) - 1
        disposable = CompositeDisposable()

        def get_action(notification):
            def action(scheduler, state):
                notification.accept(observer)
                return Disposable.empty()
            return action

        for message in self.messages:
            notification = message.value

            # Don't make closures within a loop
            action = get_action(notification)
            disposable.add(self.scheduler.schedule_relative(message.time, action))

        def dispose():
            start = self.subscriptions[index].subscribe
            end = self.scheduler.to_relative(self.scheduler.now)
            self.subscriptions[index] = Subscription(start, end)
            disposable.dispose()

        return Disposable.create(dispose)
Пример #2
0
def test_groupdisposable_clear():
    disp1 = False
    disp2 = False
    def action1():
        nonlocal disp1
        disp1 = True
    d1 = Disposable(action1)

    def action2():
        nonlocal disp2
        disp2 = True
    d2 = Disposable(action2)

    g = CompositeDisposable(d1, d2)
    assert g.length == 2

    g.clear()
    assert disp1
    assert disp2
    assert not g.length

    disp3 = False
    def action3():
        nonlocal disp3
        disp3 = True
    d3 = Disposable(action3)
    g.add(d3);
    assert not disp3
    assert g.length == 1
Пример #3
0
        def subscribe(observer):
            m = SingleAssignmentDisposable()
            group = CompositeDisposable()
            is_stopped = [False]
            group.add(m)

            def on_next(inner_source):
                inner_subscription = SingleAssignmentDisposable()
                group.add(inner_subscription)

                inner_source = Observable.from_future(inner_source)

                def on_complete():
                    group.remove(inner_subscription)
                    if is_stopped[0] and group.length == 1:
                        observer.on_completed()

                disposable = inner_source.subscribe(
                    observer.on_next,
                    observer.on_error,
                    on_complete)

                inner_subscription.disposable = disposable

            def on_complete():
                is_stopped[0] = True
                if group.length == 1:
                    observer.on_completed()

            m.disposable = sources.subscribe(on_next, observer.on_error,
                                             on_complete)
            return group
Пример #4
0
    def subscribe(observer, scheduler=None):
        group = CompositeDisposable()
        is_stopped = [False]
        m = SingleAssignmentDisposable()
        group.add(m)

        def on_next(inner_source):
            inner_subscription = SingleAssignmentDisposable()
            group.add(inner_subscription)

            inner_source = Observable.from_future(inner_source) if is_future(
                inner_source) else inner_source

            @synchronized(source.lock)
            def on_completed():
                group.remove(inner_subscription)
                if is_stopped[0] and len(group) == 1:
                    observer.on_completed()

            on_next = synchronized(source.lock)(observer.on_next)
            on_error = synchronized(source.lock)(observer.on_error)
            disposable = inner_source.subscribe_(on_next, on_error,
                                                 on_completed, scheduler)
            inner_subscription.disposable = disposable

        def on_completed():
            is_stopped[0] = True
            if len(group) == 1:
                observer.on_completed()

        m.disposable = source.subscribe_(on_next, observer.on_error,
                                         on_completed, scheduler)
        return group
Пример #5
0
    def subscribe(self, on_next, on_error=None, on_completed=None):
        print ("ColdObservable:subscribe()")

        if isinstance(on_next, AbstractObserver):
            observer = on_next
        else: 
            observer = Observer(on_next, on_error, on_completed)
            
        self.subscriptions.append(Subscription(self.scheduler.clock))
        index = len(self.subscriptions) - 1
        disposable = CompositeDisposable()
        
        def get_action(notification):
            def action(scheduler, state):
                notification.accept(observer)
                return Disposable.empty()
            return action

        for message in self.messages:
            notification = message.value
            print ("Notification: ", notification)
            
            # Don't make closures within a loop
            action = get_action(notification)
            disposable.add(self.scheduler.schedule_relative(message.time, action))

        def dispose():
            print ("ColdObservable:dispose()")
            start = self.subscriptions[index].subscribe
            end = self.scheduler.clock
            self.subscriptions[index] = Subscription(start, end)
            disposable.dispose()

        return Disposable(dispose)
Пример #6
0
        def subscribe(observer):
            is_open = [False]

            def on_next(left):
                if is_open[0]:
                    observer.on_next(left)

            def on_completed():
                if is_open[0]:
                    observer.on_completed()

            disposables = CompositeDisposable(
                source.subscribe(on_next, observer.on_error, on_completed))

            right_subscription = SingleAssignmentDisposable()
            disposables.add(right_subscription)

            def on_next2(x):
                is_open[0] = True
                right_subscription.dispose()

            def on_completed2():
                right_subscription.dispose()

            right_subscription.disposable = other.subscribe(
                on_next2, observer.on_error, on_completed2)

            return disposables
        def subscribe(observer):
            window = Subject()
            d = CompositeDisposable()
            r = RefCountDisposable(d)

            observer.on_next(add_ref(window, r))

            def on_next_window(x):
                window.on_next(x)
            
            def on_error(err):
                window.on_error(err)
                observer.on_error(err)
            
            def on_completed():
                window.on_completed()
                observer.on_completed()

            d.add(source.subscribe(on_next_window, on_error, on_completed))

            def on_next_observer(w):
                window.on_completed()
                window = Subject()
                observer.on_next(add_ref(window, r))
            
            d.add(window_boundaries.subscribe(on_next_observer, on_error, on_copleted))
            return r
        def subscribe(observer):
            m = SingleAssignmentDisposable()
            group = CompositeDisposable()
            is_stopped = False
            group.add(m)
            
            def on_next(inner_source):
                inner_subscription = SingleAssignmentDisposable()
                group.add(inner_subscription)

                def on_complete():
                    nonlocal group
                    
                    group.remove(inner_subscription)
                    if is_stopped and group.length == 1:
                        observer.on_completed()
                    
                disposable = inner_source.subscribe(
                    observer.on_next,
                    observer.on_error, 
                    on_complete)
                
                inner_subscription.disposable = disposable
            
            def on_complete():
                nonlocal is_stopped

                is_stopped = True
                if group.length == 1:
                    observer.on_completed()
            
            m.disposable = sources.subscribe(on_next, observer.on_error, on_complete)
            return group
Пример #9
0
    def subscribe(observer):
        active_plans = []
        external_subscriptions = {}

        def on_error(err):
            for v in external_subscriptions.values():
                v.on_error(err)
            observer.on_error(err)

        out_observer = AnonymousObserver(observer.on_next, on_error,
                                         observer.on_completed)

        def deactivate(active_plan):
            active_plans.remove(active_plan)
            if not len(active_plans):
                observer.on_completed()

        try:
            for plan in plans:
                active_plans.append(
                    plan.activate(external_subscriptions, out_observer,
                                  deactivate))
        except Exception as ex:
            Observable.throw(ex).subscribe(observer)

        group = CompositeDisposable()
        for join_observer in external_subscriptions.values():
            join_observer.subscribe()
            group.add(join_observer)

        return group
Пример #10
0
    def subscribe(observer):
        active_plans = []
        external_subscriptions = {}

        def on_error(err):
            for v in external_subscriptions.values():
                v.on_error(err)
            observer.on_error(err)

        out_observer = AnonymousObserver(observer.on_next, on_error, observer.on_completed)

        def deactivate(active_plan):
            active_plans.remove(active_plan)
            if not len(active_plans):
                observer.on_completed()
        try:
            for plan in plans:
                active_plans.append(plan.activate(external_subscriptions, 
                                                  out_observer, deactivate))
        except Exception as ex:
            Observable.throw(ex).subscribe(observer)

        group = CompositeDisposable()
        for join_observer in external_subscriptions.values():
            join_observer.subscribe()
            group.add(join_observer)

        return group
Пример #11
0
def test_groupdisposable_clear():
    disp1 = [False]
    disp2 = [False]
    def action1():
        disp1[0] = True
    d1 = Disposable.create(action1)

    def action2():
        disp2[0] = True
    d2 = Disposable.create(action2)

    g = CompositeDisposable(d1, d2)
    assert g.length == 2

    g.clear()
    assert disp1[0]
    assert disp2[0]
    assert not g.length

    disp3 = [False]
    def action3():
        disp3[0] = True
    d3 = Disposable.create(action3)
    g.add(d3);
    assert not disp3[0]
    assert g.length == 1
Пример #12
0
    def subscribe(observer):
        has_current = [False]
        is_stopped = [False]
        m = SingleAssignmentDisposable()
        g = CompositeDisposable()

        g.add(m)

        def on_next(inner_source):
            if not has_current[0]:
                has_current[0] = True

                inner_source = Observable.from_future(inner_source)

                inner_subscription = SingleAssignmentDisposable()
                g.add(inner_subscription)

                def on_completed_inner():
                    g.remove(inner_subscription)
                    has_current[0] = False
                    if is_stopped[0] and len(g) == 1:
                        observer.on_completed()

                inner_subscription.disposable = inner_source.subscribe(
                    observer.on_next, observer.on_error, on_completed_inner)

        def on_completed():
            is_stopped[0] = True
            if not has_current[0] and len(g) == 1:
                observer.on_completed()

        m.disposable = sources.subscribe(on_next, observer.on_error,
                                         on_completed)
        return g
Пример #13
0
        def subscribe(observer):
            m = SingleAssignmentDisposable()
            group = CompositeDisposable()
            is_stopped = [False]
            group.add(m)

            def on_next(inner_source):
                inner_subscription = SingleAssignmentDisposable()
                group.add(inner_subscription)

                inner_source = Observable.from_future(inner_source)

                def on_complete():
                    group.remove(inner_subscription)
                    if is_stopped[0] and group.length == 1:
                        observer.on_completed()

                disposable = inner_source.subscribe(
                    observer.on_next,
                    observer.on_error,
                    on_complete)

                inner_subscription.disposable = disposable

            def on_complete():
                is_stopped[0] = True
                if group.length == 1:
                    observer.on_completed()

            m.disposable = sources.subscribe(on_next, observer.on_error, on_complete)
            return group
Пример #14
0
        def subscribe(observer):
            is_open = [False]

            def on_next(left):
                if is_open[0]:
                    observer.on_next(left)

            def on_completed():
                if is_open[0]:
                    observer.on_completed()

            disposables = CompositeDisposable(source.subscribe(on_next, observer.on_error, on_completed))

            right_subscription = SingleAssignmentDisposable()
            disposables.add(right_subscription)

            def on_next2(x):
                is_open[0] = True
                right_subscription.dispose()

            def on_completed2():
                right_subscription.dispose()

            right_subscription.disposable = other.subscribe(on_next2, observer.on_error, on_completed2)

            return disposables
        def subscribe(observer):
            m = SingleAssignmentDisposable()
            group = CompositeDisposable()
            is_stopped = False
            group.add(m)

            def on_next(inner_source):
                inner_subscription = SingleAssignmentDisposable()
                group.add(inner_subscription)

                def on_complete():
                    nonlocal group

                    group.remove(inner_subscription)
                    if is_stopped and group.length == 1:
                        observer.on_completed()

                disposable = inner_source.subscribe(observer.on_next,
                                                    observer.on_error,
                                                    on_complete)

                inner_subscription.disposable = disposable

            def on_complete():
                nonlocal is_stopped

                is_stopped = True
                if group.length == 1:
                    observer.on_completed()

            m.disposable = sources.subscribe(on_next, observer.on_error,
                                             on_complete)
            return group
Пример #16
0
        def subscribe(observer):
            n = [0]
            s = [None]
            timer_d = SerialDisposable()
            window_id = [0]
            group_disposable = CompositeDisposable(timer_d)
            ref_count_disposable = RefCountDisposable(group_disposable)

            def create_timer(_id):
                m = SingleAssignmentDisposable()
                timer_d.disposable = m

                def action(scheduler, state):
                    if _id != window_id[0]:
                        return

                    n[0] = 0
                    window_id[0] += 1
                    new_id = window_id[0]
                    s[0].on_completed()
                    s[0] = Subject()
                    observer.on_next(add_ref(s[0], ref_count_disposable))
                    create_timer(new_id)

                m.disposable = scheduler.schedule_relative(timespan, action)

            s[0] = Subject()
            observer.on_next(add_ref(s[0], ref_count_disposable))
            create_timer(0)

            def on_next(x):
                new_window = False
                new_id = 0

                s[0].on_next(x)
                n[0] += 1
                if n[0] == count:
                    new_window = True
                    n[0] = 0
                    window_id[0] += 1
                    new_id = window_id[0]
                    s[0].on_completed()
                    s[0] = Subject()
                    observer.on_next(add_ref(s[0], ref_count_disposable))

                if new_window:
                    create_timer(new_id)

            def on_error(e):
                s[0].on_error(e)
                observer.on_error(e)

            def on_completed():
                s[0].on_completed()
                observer.on_completed()

            group_disposable.add(
                source.subscribe(on_next, on_error, on_completed))
            return ref_count_disposable
Пример #17
0
        def subscribe(observer):
            n = [0]
            s = [None]
            timer_d = SerialDisposable()
            window_id = [0]
            group_disposable = CompositeDisposable(timer_d)
            ref_count_disposable = RefCountDisposable(group_disposable)

            def create_timer(_id):
                m = SingleAssignmentDisposable()
                timer_d.disposable = m

                def action(scheduler, state):
                    if _id != window_id[0]:
                        return

                    n[0] = 0
                    window_id[0] += 1
                    new_id = window_id[0]
                    s[0].on_completed()
                    s[0] = Subject()
                    observer.on_next(add_ref(s[0], ref_count_disposable))
                    create_timer(new_id)

                m.disposable = scheduler.schedule_relative(timespan, action)

            s[0] = Subject()
            observer.on_next(add_ref(s[0], ref_count_disposable))
            create_timer(0)

            def on_next(x):
                new_window = False
                new_id = 0

                s[0].on_next(x)
                n[0] += 1
                if n[0] == count:
                    new_window = True
                    n[0] = 0
                    window_id[0] += 1
                    new_id = window_id[0]
                    s[0].on_completed()
                    s[0] = Subject()
                    observer.on_next(add_ref(s[0], ref_count_disposable))

                if new_window:
                    create_timer(new_id)

            def on_error(e):
                s[0].on_error(e)
                observer.on_error(e)

            def on_completed():
                s[0].on_completed()
                observer.on_completed()

            group_disposable.add(source.subscribe(on_next, on_error, on_completed))
            return ref_count_disposable
Пример #18
0
def test_groupdisposable_contains():
    d1 = Disposable.empty()
    d2 = Disposable.empty()

    g = CompositeDisposable(d1, d2)

    assert g.length == 2
    assert g.contains(d1)
    assert g.contains(d2)
Пример #19
0
    def _subscribe_core(self, observer, scheduler=None):
        g = CompositeDisposable()

        def action(scheduler, state):
            observer.on_next(self.head)
            g.add(self.tail.merge_all().subscribe(observer, scheduler))

        g.add(scheduler.schedule(action))
        return g
Пример #20
0
    def _subscribe(self, observer):
        g = CompositeDisposable()

        def action(scheduler, state):
            observer.on_next(self.head)
            g.add(self.tail.merge_observable().subscribe(observer))

        g.add(current_thread_scheduler.schedule(action))
        return g
Пример #21
0
    def _subscribe(self, observer):
        g = CompositeDisposable()

        def action(scheduler, state):
            observer.on_next(self.head)
            g.add(self.tail.merge_observable().subscribe(observer))

        g.add(current_thread_scheduler.schedule(action))
        return g
Пример #22
0
def test_groupdisposable_contains():
    d1 = Disposable.empty()
    d2 = Disposable.empty()

    g = CompositeDisposable(d1, d2)

    assert g.length == 2
    assert g.contains(d1)
    assert g.contains(d2)
Пример #23
0
        def subscribe(observer):
            delays = CompositeDisposable()
            at_end = False

            def done():
                if (at_end and delays.length == 0):
                    observer.on_completed()

            subscription = SerialDisposable()

            def start():
                def on_next(x):
                    try:
                        delay = selector(x)
                    except Exception as error:
                        observer.on_error(error)
                        return

                    d = SingleAssignmentDisposable()
                    delays.add(d)

                    def on_next(_):
                        observer.on_next(x)
                        delays.remove(d)
                        done()

                    def on_completed():
                        observer.on_next(x)
                        delays.remove(d)
                        done()

                    d.disposable = delay.subscribe(on_next, observer.on_error,
                                                   on_completed)

                def on_completed():
                    nonlocal at_end

                    at_end = True
                    subscription.dispose()
                    done()

                subscription.disposable = source.subscribe(
                    on_next, observer.on_error, on_completed)

            if not sub_delay:
                start()
            else:
                subscription.disposable(
                    sub_delay.subscribe(lambda _: start(), observer.on_error,
                                        lambda: start()))

            return CompositeDisposable(subscription, delays)
Пример #24
0
        def subscribe(observer):
            disposable = Disposable.empty()
            try:
                resource = resource_factory()
                if resource:
                    disposable = resource

                source = observable_factory(resource)
            except Exception as exception:
                d = Observable.throw_exception(exception).subscribe(observer)
                return CompositeDisposable(d, disposable)

            return CompositeDisposable(source.subscribe(observer), disposable)
Пример #25
0
  def create_observer(observer):
    subscription = CompositeDisposable()
    for name in observables.keys():
      if not subjects[name].is_disposed:
        subscription.add(observables[name].subscribe(observer=subjects[name], on_error=log_error))
    observer.on_next(subscription)

    def dispose():
      subscription.dispose()
      for x in subjects:
        if hasattr(subjects, x):
          subjects[x].dispose()

    return dispose
Пример #26
0
    def subscribe(observer, scheduler=None):
        delays = CompositeDisposable()
        at_end = [False]

        def done():
            if (at_end[0] and delays.length == 0):
                observer.on_completed()

        subscription = SerialDisposable()

        def start():
            def on_next(x):
                try:
                    delay = mapper(x)
                except Exception as error:
                    observer.on_error(error)
                    return

                d = SingleAssignmentDisposable()
                delays.add(d)

                def on_next(_):
                    observer.on_next(x)
                    delays.remove(d)
                    done()

                def on_completed():
                    observer.on_next(x)
                    delays.remove(d)
                    done()

                d.disposable = delay.subscribe_(on_next, observer.on_error,
                                                on_completed, scheduler)

            def on_completed():
                at_end[0] = True
                subscription.dispose()
                done()

            subscription.disposable = source.subscribe_(
                on_next, observer.on_error, on_completed, scheduler)

        if not sub_delay:
            start()
        else:
            subscription.disposable(
                sub_delay.subscribe_(lambda _: start(), observer.on_error,
                                     start))

        return CompositeDisposable(subscription, delays)
Пример #27
0
def test_groupdisposable_remove():
    disp1 = [False]
    disp2 = [False]

    def action1():
        disp1[0] = True
    d1 = Disposable.create(action1)

    def action2():
        disp2[0] = True
    d2 = Disposable.create(action2)

    g = CompositeDisposable(d1, d2)

    assert g.length == 2
    assert g.contains(d1)
    assert g.contains(d2)
    assert g.remove(d1)
    assert g.length == 1
    assert not g.contains(d1)
    assert g.contains(d2)
    assert disp1[0]
    assert g.remove(d2)
    assert not g.contains(d1)
    assert not g.contains(d2)
    assert disp2[0]

    disp3 = [False]

    def action3():
        disp3[0] = True
    d3 = Disposable.create(action3)
    assert not g.remove(d3)
    assert not disp3[0]
Пример #28
0
    def schedule_relative(self, duetime, action, state=None):
        """Schedules an action to be executed after duetime.

        Keyword arguments:
        duetime -- {timedelta} Relative time after which to execute the action.
        action -- {Function} Action to be executed.

        Returns {Disposable} The disposable object used to cancel the scheduled
        action (best effort)."""

        scheduler = self
        msecs = self.to_relative(duetime)
        if msecs == 0:
            return scheduler.schedule(action, state)

        disposable = SingleAssignmentDisposable()

        def interval():
            disposable.disposable = action(scheduler, state)

        log.debug("timeout: %s", msecs)
        alarm = self.master.after(msecs, interval)

        def dispose():
            # nonlocal alarm
            self.master.after_cancel(alarm)

        return CompositeDisposable(disposable, Disposable(dispose))
Пример #29
0
    def subscribe(observer):
        subscription = SerialDisposable()
        cancelable = SerialDisposable()
        enum = iter(sources)
        is_disposed = []

        def action(action1, state=None):
            if is_disposed:
                return

            def on_completed():
                cancelable.disposable = scheduler.schedule(action)

            try:
                current = next(enum)
            except StopIteration:
                observer.on_completed()
            except Exception as ex:
                observer.on_error(ex)
            else:
                d = SingleAssignmentDisposable()
                subscription.disposable = d
                d.disposable = current.subscribe(observer.on_next,
                                                 observer.on_error,
                                                 on_completed)

        cancelable.disposable = scheduler.schedule(action)

        def dispose():
            is_disposed.append(True)

        return CompositeDisposable(subscription, cancelable,
                                   Disposable.create(dispose))
Пример #30
0
        def subscribe(observer):
            at_end = None
            has_value = None
            value = None

            def sample_subscribe(x):
                nonlocal has_value

                if has_value:
                    has_value = False
                    observer.on_next(value)

                if at_end:
                    observer.on_completed()

            def on_next(new_value):
                nonlocal value, has_value

                has_value = True
                value = new_value

            def on_completed():
                nonlocal at_end

                at_end = True

            return CompositeDisposable(
                source.subscribe(on_next, observer.on_error, on_completed),
                sampler.subscribe(sample_subscribe, observer.on_error,
                                  sample_subscribe))
        def subscribe(observer):
            def on_completed(x):
                observer.on_completed()

            return CompositeDisposable(
                source.subscribe(observer),
                other.subscribe(on_completed, observer.on_error, noop))
Пример #32
0
    def _gtk_schedule(self, time, action, state, periodic=False):
        # Do not import GLib into global scope because Qt and GLib
        # don't like each other there
        from gi.repository import GLib

        scheduler = self
        msecs = self.to_relative(time)

        disposable = SingleAssignmentDisposable()

        periodic_state = [state]
        stopped = [False]

        def timer_handler(_):
            if stopped[0]:
                return False

            if periodic:
                periodic_state[0] = action(periodic_state[0])
            else:
                disposable.disposable = action(scheduler, state)

            return periodic

        GLib.timeout_add(msecs, timer_handler, None)

        def dispose():
            stopped[0] = True

        return CompositeDisposable(disposable, Disposable.create(dispose))
Пример #33
0
    def schedule_relative(self, duetime, action, state=None):
        """Schedules an action to be executed after duetime.

        Keyword arguments:
        duetime -- {timedelta} Relative time after which to execute the action.
        action -- {Function} Action to be executed.

        Returns {Disposable} The disposable object used to cancel the scheduled
        action (best effort)."""

        from twisted.internet.task import deferLater

        scheduler = self
        seconds = self.to_relative(duetime) / 1000.0

        disposable = SingleAssignmentDisposable()

        def interval():
            disposable.disposable = action(scheduler, state)

        log.debug("timeout: %s", seconds)
        handle = deferLater(self.reactor, seconds,
                            interval).addErrback(lambda _: None)

        def dispose():
            if not handle.called:
                handle.cancel()

        return CompositeDisposable(disposable, Disposable.create(dispose))
Пример #34
0
    def invoke_rec_date(scheduler, pair, method):
        state = pair.get('first')
        action = pair.get('second')
        group = CompositeDisposable()

        def inner_action(state2, duetime):
            is_added = False
            is_done = [False]

            def schedule_work(_, state3):
                action(state3, inner_action)
                if is_added:
                    group.remove(d)
                else:
                    is_done[0] = True

                return Disposable.empty()

            d = getattr(scheduler, method)(duetime=duetime,
                                           action=schedule_work,
                                           state=state2)
            if not is_done[0]:
                group.add(d)
                is_added = True

        action(state, inner_action)
        return group
Пример #35
0
    def schedule_relative(self, duetime, action, state=None):
        """Schedules an action to be executed after duetime.

        Keyword arguments:
        duetime -- {timedelta} Relative time after which to execute the action.
        action -- {Function} Action to be executed.

        Returns {Disposable} The disposable object used to cancel the scheduled
        action (best effort)."""

        scheduler = self
        seconds = self.to_relative(duetime) / 1000.0

        disposable = SingleAssignmentDisposable()

        def interval():
            disposable.disposable = action(scheduler, state)

        log.debug("timeout: %s", seconds)
        handle = [self.reactor.callLater(seconds, interval)]

        def dispose():
            handle[0].cancel()

        return CompositeDisposable(disposable, Disposable(dispose))
Пример #36
0
    def schedule_relative(self, duetime, action, state=None):
        """Schedules an action to be executed at duetime.

        Keyword arguments:
        duetime -- {timedelta} Relative time after which to execute the action.
        action -- {Function} Action to be executed.

        Returns {Disposable} The disposable object used to cancel the scheduled
        action (best effort)."""

        scheduler = self
        seconds = self.to_relative(duetime) / 1000.0
        if seconds == 0:
            return scheduler.schedule(action, state)

        disposable = SingleAssignmentDisposable()

        def interval():
            disposable.disposable = self.invoke_action(action, state)

        handle = [self.loop.call_later(seconds, interval)]

        def dispose():
            # nonlocal handle
            handle[0].cancel()

        return CompositeDisposable(disposable, Disposable.create(dispose))
Пример #37
0
    def subscribe(observer):
        enum = iter(sources)
        is_disposed = [False]
        subscription = SerialDisposable()

        def action(action1, state=None):
            if is_disposed[0]:
                return
            try:
                current = next(enum)
            except StopIteration:
                observer.on_completed()
            except Exception as ex:
                observer.on_error(ex)
            else:
                d = SingleAssignmentDisposable()
                subscription.disposable = d
                d.disposable = current.subscribe(
                    observer.on_next,
                    observer.on_error,
                    lambda: action1()
                )

        cancelable = immediate_scheduler.schedule_recursive(action)

        def dispose():
            is_disposed[0] = True
        return CompositeDisposable(subscription, cancelable, Disposable.create(dispose))
Пример #38
0
    def _wxtimer_schedule(self, time, action, state, periodic=False):
        scheduler = self
        msecs = self.to_relative(time)

        disposable = SingleAssignmentDisposable()

        periodic_state = [state]

        def interval():
            if periodic:
                periodic_state[0] = action(periodic_state[0])
            else:
                disposable.disposable = action(scheduler, state)

        log.debug("timeout: %s", msecs)

        if msecs == 0:
            msecs = 1  # wx.Timer doesn't support zero.

        timer = self._timer_class(interval)
        timer.Start(
            msecs,
            self.wx.TIMER_CONTINUOUS if periodic else self.wx.TIMER_ONE_SHOT)
        self._timers.add(timer)

        def dispose():
            timer.Stop()
            self._timers.remove(timer)

        return CompositeDisposable(disposable, Disposable.create(dispose))
Пример #39
0
    def schedule_relative(self, duetime, action, state=None):
        """Schedules an action to be executed after duetime.

        Keyword arguments:
        duetime -- {timedelta} Relative time after which to execute the action.
        action -- {Function} Action to be executed.

        Returns {Disposable} The disposable object used to cancel the scheduled
        action (best effort)."""

        scheduler = self
        seconds = scheduler.to_relative(duetime) / 1000.0
        if not seconds:
            return scheduler.schedule(action, state)

        disposable = SingleAssignmentDisposable()

        def interval():
            disposable.disposable = self.invoke_action(action, state)

        log.debug("timeout: %s", seconds)
        handle = self.loop.call_later(seconds, interval)

        def dispose():
            self.loop.remove_timeout(handle)

        return CompositeDisposable(disposable, Disposable.create(dispose))
Пример #40
0
    def subscribe(observer):
        subscription = SerialDisposable()
        cancelable = SerialDisposable()

        def action(scheduler, state=None):
            try:
                source = next(sources)
            except StopIteration:
                observer.on_completed()
                return

            # Allow source to be a factory method taking an error
            source = source(state) if callable(source) else source
            current = Observable.from_future(source)

            d = SingleAssignmentDisposable()
            subscription.disposable = d

            def on_resume(state=None):
                scheduler.schedule(action, state)

            d.disposable = current.subscribe(observer.on_next, on_resume, on_resume)

        cancelable.disposable = scheduler.schedule(action)
        return CompositeDisposable(subscription, cancelable)
Пример #41
0
    def invoke_rec_immediate(scheduler, pair):
        state = pair.get('state')
        action = pair.get('action')
        group = CompositeDisposable()

        def inner_action(state2=None):
            is_added = False
            is_done = [False]

            def schedule_work(_, state3):
                action(inner_action, state3)
                if is_added:
                    group.remove(d)
                else:
                    is_done[0] = True

                return Disposable.empty()

            d = scheduler.schedule(schedule_work, state2)
            if not is_done[0]:
                group.add(d)
                is_added = True

        action(inner_action, state)
        return group
Пример #42
0
    def _qtimer_schedule(self, time, action, state, periodic=False):
        scheduler = self
        msecs = self.to_relative(time)

        disposable = SingleAssignmentDisposable()

        periodic_state = [state]

        def interval():
            if periodic:
                periodic_state[0] = action(periodic_state[0])
            else:
                disposable.disposable = action(scheduler, state)

        log.debug("timeout: %s", msecs)

        timer = self.qtcore.QTimer()
        timer.setSingleShot(not periodic)
        timer.timeout.connect(interval)
        timer.setInterval(msecs)
        timer.start()
        self._timers.add(timer)

        def dispose():
            timer.stop()
            self._timers.remove(timer)

        return CompositeDisposable(disposable, Disposable.create(dispose))
        def subscribe(observer):
            active_count = 0
            group = CompositeDisposable()
            is_stopped = False
            q = []
            
            def subscribe(xs):
                subscription = SingleAssignmentDisposable()
                group.add(subscription)
                
                def on_completed():
                    nonlocal active_count
                    
                    group.remove(subscription)
                    if q.length > 0:
                        s = q.shift()
                        subscribe(s)
                    else:
                        active_count -= 1
                        if is_stopped and active_count == 0:
                            observer.on_completed()
                        
                subscription.disposable = xs.subscribe(observer.on_next, observer.on_error, on_completed)
            
            def on_next(inner_source):
                nonlocal active_count

                if active_count < max_concurrent_or_other:
                    active_count += 1
                    subscribe(inner_source)
                else:
                    q.append(inner_source)

            def on_completed():
                nonlocal is_stopped

                is_stopped = True
                if active_count == 0:
                    observer.on_completed()
            
            group.add(sources.subscribe(on_next, observer.on_error, on_completed))
            return group
Пример #44
0
    def subscribe(observer):
        n = len(sources)
        queues = [[] for _ in range(n)]
        is_done = [False] * n

        def next(i):
            if all([len(q) for q in queues]):
                res = [x.pop(0) for x in queues]
                observer.on_next(res)
            elif all([x for j, x in enumerate(is_done) if j != i]):
                observer.on_completed()
                return

        def done(i):
            is_done[i] = True
            if all(is_done):
                observer.on_completed()
                return

        subscriptions = [None]*n

        def func(i):
            subscriptions[i] = SingleAssignmentDisposable()

            def on_next(x):
                queues[i].append(x)
                next(i)

            subscriptions[i].disposable = sources[i].subscribe(on_next, observer.on_error, lambda: done(i))
        for idx in range(n):
            func(idx)

        composite_disposable = CompositeDisposable(subscriptions)

        def action():
            for _ in queues:
                queues[n] = []

        composite_disposable.add(Disposable.create(action))

        return composite_disposable
        def subscribe(observer):
            m = SerialDisposable()
            d = CompositeDisposable(m)
            r = RefCountDisposable(d)
            window = Subject()
            
            observer.on_next(add_ref(window, r))

            def on_next(x):
                window.on_next(x)
            
            def on_error(ex):
                window.on_error(ex)
                observer.on_error(ex)
            
            def on_completed():
                window.on_completed()
                observer.on_completed()
            
            d.add(source.subscribe(on_next, on_error, on_completed))
            
            def create_window_close():
                try:
                    window_close = window_closing_selector()
                except Exception as exception:
                    log.error("*** Exception: %s" % exception)
                    observer.on_error(exception)
                    return
                
                def on_completed():
                    window.on_completed()
                    window = Subject()
                    observer.on_next(add_ref(window, r))
                    create_window_close()
                
                m1 = SingleAssignmentDisposable()
                m.disposable(m1)
                m1.disposable(window_close.take(1).subscribe(noop, on_error, on_completed))
            
            create_window_close()
            return r        
Пример #46
0
 def init(self):
     logger.info('Starting %s', self)
     self._subscription = CompositeDisposable(
         self._subscribe_for_get_server_time_command(),
         self._subscribe_for_get_price_command(),
         self._subscribe_for_get_balance_command(),
         self._subscribe_for_get_active_orders_command(),
         self._subscribe_for_get_completed_orders_command(),
         self._subscribe_for_create_sell_order_command(),
         self._subscribe_for_create_buy_order_command(),
         self._subscribe_for_cancel_order_command(),
     )
Пример #47
0
def test_groupdisposable_addafterdispose():
    disp1 = [False]
    disp2 = [False]

    def action1():
        disp1[0] = True

    d1 = Disposable.create(action1)

    def action2():
        disp2[0] = True

    d2 = Disposable.create(action2)

    g = CompositeDisposable(d1)
    assert g.length == 1
    g.dispose()
    assert disp1[0]
    assert g.length == 0
    g.add(d2)
    assert disp2[0]
    assert g.length == 0
Пример #48
0
        def subscribe(observer):
            active_count = [0]
            group = CompositeDisposable()
            is_stopped = [False]
            q = []

            def subscribe(xs):
                subscription = SingleAssignmentDisposable()
                group.add(subscription)

                def on_completed():
                    group.remove(subscription)
                    if len(q):
                        s = q.pop(0)
                        subscribe(s)
                    else:
                        active_count[0] -= 1
                        if is_stopped[0] and active_count[0] == 0:
                            observer.on_completed()

                subscription.disposable = xs.subscribe(observer.on_next,
                                                       observer.on_error,
                                                       on_completed)

            def on_next(inner_source):
                if active_count[0] < max_concurrent:
                    active_count[0] += 1
                    subscribe(inner_source)
                else:
                    q.append(inner_source)

            def on_completed():
                is_stopped[0] = True
                if active_count[0] == 0:
                    observer.on_completed()

            group.add(sources.subscribe(on_next, observer.on_error,
                                        on_completed))
            return group
Пример #49
0
 def init(self):
     logger.info('Starting %s', self)
     self._subscription = CompositeDisposable(
         self._subscribe_for_poll_server_time(),
         self._subscribe_for_poll_price(),
         self._subscribe_for_poll_balance(),
         self._subscribe_for_poll_active_orders(),
         self._subscribe_for_poll_completed_orders(),
         self._subscribe_for_time_and_price(),
         self._subscribe_for_balance(),
         self._subscribe_for_active_orders(),
         self._subscribe_for_completed_orders(),
         self._subscribe_for_jumping_price()
     )
Пример #50
0
    def subscribe(observer):
        has_current = [False]
        is_stopped = [False]
        m = SingleAssignmentDisposable()
        g = CompositeDisposable()

        g.add(m)

        def on_next(inner_source):
            if not has_current[0]:
                has_current[0] = True

                inner_source = Observable.from_future(inner_source)

                inner_subscription = SingleAssignmentDisposable()
                g.add(inner_subscription)

                def on_completed_inner():
                    g.remove(inner_subscription)
                    has_current[0] = False
                    if is_stopped[0] and len(g) == 1:
                        observer.on_completed()

                inner_subscription.disposable = inner_source.subscribe(
                    observer.on_next,
                    observer.on_error,
                    on_completed_inner
                )

        def on_completed():
            is_stopped[0] = True
            if not has_current[0] and len(g) == 1:
                observer.on_completed()

        m.disposable = sources.subscribe(on_next, observer.on_error, on_completed)
        return g
Пример #51
0
def test_groupdisposable_addafterdispose():
    disp1 = False
    disp2 = False

    def action1():
        nonlocal disp1
        disp1 = True

    d1 = Disposable(action1)

    def action2():
        nonlocal disp2
        disp2 = True

    d2 = Disposable(action2)

    g = CompositeDisposable(d1)
    assert g.length == 1
    g.dispose()
    assert disp1
    assert g.length == 0
    g.add(d2)
    assert disp2
    assert g.length == 0
Пример #52
0
def test_groupdisposable_remove():
    disp1 = False
    disp2 = False
    
    def action1():
        nonlocal disp1
        disp1 = True
    d1 = Disposable(action1)

    def action2():
        nonlocal disp2
        disp2 = True
    d2 = Disposable(action2)

    g = CompositeDisposable(d1, d2)

    assert g.length == 2
    assert g.contains(d1)
    assert g.contains(d2)
    assert g.remove(d1)
    assert g.length == 1
    assert not g.contains(d1)
    assert g.contains(d2)
    assert disp1
    assert g.remove(d2)
    assert not g.contains(d1)
    assert not g.contains(d2)
    assert disp2

    disp3 = False;

    def action3():
        nonlocal disp3
        disp3 = True
    d3 = Disposable(action3)
    assert not g.remove(d3)
    assert not disp3
Пример #53
0
        def subscribe(observer):
            mapping = OrderedDict()
            group_disposable = CompositeDisposable()
            ref_count_disposable = RefCountDisposable(group_disposable)

            def on_next(x):
                writer = None
                element = None
                duration = None
                key = None

                try:
                    key = key_selector(x)
                except Exception as e:
                    for w in mapping.values():
                        w.on_error(e)

                    observer.on_error(e)
                    return

                fire_new_map_entry = False
                writer = mapping.get(key)
                if not writer:
                    writer = Subject()
                    mapping[key] = writer
                    fire_new_map_entry = True

                if fire_new_map_entry:
                    group = GroupedObservable(key, writer, ref_count_disposable)
                    duration_group = GroupedObservable(key, writer)
                    try:
                        duration = duration_selector(duration_group)
                    except Exception as e:
                        for w in mapping.values():
                            w.on_error(e)

                        observer.on_error(e)
                        return

                    observer.on_next(group)
                    md = SingleAssignmentDisposable()
                    group_disposable.add(md)

                    def expire():
                        if mapping[serialized_key]:
                            del mapping[serialized_key]
                            writer.on_completed()

                        group_disposable.remove(md)

                    def on_next(value):
                        pass

                    def on_error(exn):
                        for wr in mapping.values():
                            wr.on_error(exn)
                        observer.on_error(exn)

                    def on_completed():
                        expire()

                    md.disposable = duration.take(1).subscribe(on_next, on_error, on_completed)

                try:
                    element = element_selector(x)
                except Exception as e:
                    for w in mapping.values():
                        w.on_error(e)

                    observer.on_error(e)
                    return

                writer.on_next(element)

            def on_error(ex):
                for w in mapping.values():
                    w.on_error(ex)

                observer.on_error(ex)

            def on_completed():
                for w in mapping.values():
                    w.on_completed()

                observer.on_completed()

            group_disposable.add(source.subscribe(on_next, on_error, on_completed))
            return ref_count_disposable
Пример #54
0
        def subscribe(observer):
            group = CompositeDisposable()
            left_done = [False]
            left_map = OrderedDict()
            left_id = [0]
            right_done = [False]
            right_map = OrderedDict()
            right_id = [0]

            def on_next_left(value):
                duration = None
                current_id = left_id[0]
                left_id[0] += 1
                md = SingleAssignmentDisposable()

                left_map[current_id] = value
                group.add(md)

                def expire():
                    if current_id in left_map:
                        del left_map[current_id]
                    if not len(left_map) and left_done[0]:
                        observer.on_completed()

                    return group.remove(md)

                try:
                    duration = left_duration_selector(value)
                except Exception as exception:
                    log.error("*** Exception: %s" % exception)
                    observer.on_error(exception)
                    return

                md.disposable = duration.take(1).subscribe(noop, observer.on_error, lambda: expire())

                for val in right_map.values():
                    try:
                        result = result_selector(value, val)
                    except Exception as exception:
                        log.error("*** Exception: %s" % exception)
                        observer.on_error(exception)
                        return

                    observer.on_next(result)

            def on_completed_left():
                left_done[0] = True
                if right_done[0] or not len(left_map):
                    observer.on_completed()

            group.add(left.subscribe(on_next_left, observer.on_error, on_completed_left))

            def on_next_right(value):
                duration = None
                current_id = right_id[0]
                right_id[0] += 1
                md = SingleAssignmentDisposable()
                right_map[current_id] = value
                group.add(md)

                def expire():
                    if current_id in right_map:
                        del right_map[current_id]
                    if not len(right_map) and right_done[0]:
                        observer.on_completed()

                    return group.remove(md)

                try:
                    duration = right_duration_selector(value)
                except Exception as exception:
                    log.error("*** Exception: %s" % exception)
                    observer.on_error(exception)
                    return

                md.disposable = duration.take(1).subscribe(noop, observer.on_error, lambda: expire())

                for val in left_map.values():
                    try:
                        result = result_selector(val, value)
                    except Exception as exception:
                        log.error("*** Exception: %s" % exception)
                        observer.on_error(exception)
                        return

                    observer.on_next(result)

            def on_completed_right():
                right_done[0] = True
                if left_done[0] or not len(right_map):
                    observer.on_completed()

            group.add(right.subscribe(on_next_right, observer.on_error, on_completed_right))
            return group
Пример #55
0
        def subscribe(observer):
            nothing = lambda _: None
            group = CompositeDisposable()
            r = RefCountDisposable(group)
            left_map = OrderedDict()
            right_map = OrderedDict()
            left_id = [0]
            right_id = [0]

            def on_next_left(value):
                s = Subject()
                _id = left_id[0]
                left_id[0] += 1
                left_map[_id] = s

                try:
                    result = result_selector(value, add_ref(s, r))
                except Exception as e:
                    log.error("*** Exception: %s" % e)
                    for left_value in left_map.values():
                        left_value.on_error(e)

                    observer.on_error(e)
                    return

                observer.on_next(result)

                for right_value in right_map.values():
                    s.on_next(right_value)

                md = SingleAssignmentDisposable()
                group.add(md)

                def expire():
                    if _id in left_map:
                        del left_map[_id]
                        s.on_completed()

                    group.remove(md)

                try:
                    duration = left_duration_selector(value)
                except Exception as e:
                    for left_value in left_map.values():
                        left_value.on_error(e)

                    observer.on_error(e)
                    return

                def on_error(e):
                    for left_value in left_map.values():
                        left_value.on_error(e)

                    observer.on_error(e)

                md.disposable = duration.take(1).subscribe(
                    nothing,
                    on_error,
                    expire)

            def on_error_left(e):
                for left_value in left_map.values():
                    left_value.on_error(e)

                observer.on_error(e)

            group.add(left.subscribe(on_next_left, on_error_left, observer.on_completed))

            def on_next_right(value):
                _id = right_id[0]
                right_id[0] += 1
                right_map[_id] = value

                md = SingleAssignmentDisposable()
                group.add(md)

                def expire():
                    del right_map[_id]
                    group.remove(md)

                try:
                    duration = right_duration_selector(value)
                except Exception as e:
                    for left_value in left_map.values():
                        left_value.on_error(e)

                    observer.on_error(e)
                    return

                def on_error(e):
                    for left_value in left_map.values():
                        left_value.on_error(e)

                    observer.on_error(e)

                md.disposable = duration.take(1).subscribe(
                    nothing,
                    on_error,
                    expire)

                for left_value in left_map.values():
                    left_value.on_next(value)

            def on_error_right(e):
                for left_value in left_map.values():
                    left_value.on_error(e)

                observer.on_error(e)

            group.add(right.subscribe(on_next_right, on_error_right))
            return r
Пример #56
0
class Trader:

    POLL_IMMEDIATELY = 1
    POLL_SERVER_TIME_INTERVAL = 1000
    POLL_PRICE_INTERVAL = 10000
    POLL_BALANCE_INTERVAL = 600000
    POLL_ACTIVE_ORDERS_INTERVAL = 3600000
    POLL_COMPLETED_ORDERS_INTERVAL = 10000
    SHOW_TIME_AND_PRICE_INTERVAL = 600000

    REASON_PRICE_JUMP = 0
    REASON_ORDER_COMPLETED = 1

    def __init__(self, options: TradingOptions, events: Observable, commands: Observable):
        self._subscription = None
        self._options = options
        self._events = events
        self._commands = commands

    def __repr__(self):
        return 'Trader(pair=%s)' % self._options.pair

    def _get_time(self):
        return (self._events
            .filter(lambda event: isinstance(event, events.TimeEvent))
            .map(lambda event: event.value))

    def _get_price(self):
        return (self._events
            .filter(lambda event: isinstance(event, events.PriceEvent))
            .filter(lambda event: event.pair == self._options.pair)
            .map(lambda event: event.value))

    def _get_balance(self, event_stream, currency):
        return (event_stream
            .filter(lambda event: isinstance(event, events.BalanceEvent))
            .filter(lambda event: event.currency == currency)
            .map(lambda event: event.value)
            .scan(lambda p, balance: d(balance=balance, change=(Decimal(0) if p.balance is None else balance - p.balance)),
                  d(balance=None, change=None)))

    def _get_first_currency_balance(self):
        return self._get_balance(self._events, self._options.pair.first)

    def _get_second_currency_balance(self):
        return self._get_balance(self._events, self._options.pair.second)

    def _get_active_orders(self):
        return (self._events
            .filter(lambda event: isinstance(event, events.ActiveOrdersEvent))
            .filter(lambda event: event.pair == self._options.pair)
            .map(lambda event: event.orders))

    def _get_completed_orders_singly(self, event_stream, pair):
        return (event_stream
            .filter(lambda event: isinstance(event, events.CompletedOrdersEvent))
            .filter(lambda event: event.pair == pair)
            .map(lambda event: event.orders)
            .scan(lambda p, orders: d(orders=orders, change=(set() if p.orders is None else set(orders) - set(p.orders))),
                  d(orders=None, change=None))
            .map(lambda p: p.change)
            .switch_map(Observable.from_iterable))

    def _get_jumping_price(self):
        return (self._get_price()
            .scan(lambda prev, price: prev if prev and abs(price - prev) / prev < self._options.price_jump_value
                                      else price)
            .distinct_until_changed()
            .skip(1))

    def init(self):
        logger.info('Starting %s', self)
        self._subscription = CompositeDisposable(
            self._subscribe_for_poll_server_time(),
            self._subscribe_for_poll_price(),
            self._subscribe_for_poll_balance(),
            self._subscribe_for_poll_active_orders(),
            self._subscribe_for_poll_completed_orders(),
            self._subscribe_for_time_and_price(),
            self._subscribe_for_balance(),
            self._subscribe_for_active_orders(),
            self._subscribe_for_completed_orders(),
            self._subscribe_for_jumping_price()
        )

    def _subscribe_for_poll_server_time(self):
        return (Observable
            .timer(self.POLL_IMMEDIATELY, self.POLL_SERVER_TIME_INTERVAL, MAIN_THREAD)
            .subscribe(lambda count: self._commands.on_next(commands.GetServerTimeCommand())))

    def _subscribe_for_poll_price(self):
        return (Observable
            .timer(self.POLL_IMMEDIATELY, self.POLL_PRICE_INTERVAL, MAIN_THREAD)
            .subscribe(lambda count: self._commands.on_next(commands.GetPriceCommand(self._options.pair))))

    def _subscribe_for_poll_balance(self):
        return CompositeDisposable(
            (Observable
                .timer(self.POLL_IMMEDIATELY, self.POLL_BALANCE_INTERVAL, MAIN_THREAD)
                .subscribe(lambda count: self._commands.on_next(commands.GetBalanceCommand(self._options.pair.first)))),
            (Observable
                .timer(self.POLL_IMMEDIATELY, self.POLL_BALANCE_INTERVAL, MAIN_THREAD)
                .subscribe(lambda count: self._commands.on_next(commands.GetBalanceCommand(self._options.pair.second))))
        )

    def _subscribe_for_poll_active_orders(self):
        return (Observable
            .timer(self.POLL_IMMEDIATELY, self.POLL_ACTIVE_ORDERS_INTERVAL, MAIN_THREAD)
            .subscribe(lambda count: self._commands.on_next(commands.GetActiveOrdersCommand(self._options.pair))))

    def _subscribe_for_poll_completed_orders(self):
        return (Observable
            .timer(self.POLL_IMMEDIATELY, self.POLL_COMPLETED_ORDERS_INTERVAL, MAIN_THREAD)
            .subscribe(lambda count: self._commands.on_next(commands.GetCompletedOrdersCommand(self._options.pair))))

    def _subscribe_for_time_and_price(self):
        return (Observable
            .combine_latest(
                self._get_time(),
                self._get_price(),
                d('time', 'price')
            )
            .throttle_first(self.SHOW_TIME_AND_PRICE_INTERVAL, MAIN_THREAD)
            .subscribe(lambda p: logger.info('[%s] Time now is %s, price is %s', self._options.pair, p.time, p.price)))

    def _subscribe_for_balance(self):
        return (Observable
            .combine_latest(
                self._get_first_currency_balance().map(lambda p: d(balance1=p.balance, change1=p.change)),
                self._get_second_currency_balance().map(lambda p: d(balance2=p.balance, change2=p.change)),
                d('balance1', 'change1', 'balance2', 'change2')
            )
            .distinct_until_changed(lambda p: (p.balance1, p.balance2))
            .subscribe(lambda p: logger.info('[%s] Balance is %s %s (%s) and %s %s (%s)', self._options.pair,
                                             p.balance1, self._options.pair.first, p.change1, p.balance2,
                                             self._options.pair.second, p.change2)))

    def _subscribe_for_active_orders(self):
        return CompositeDisposable(
            (self._get_active_orders()
                .subscribe(lambda orders: logger.info('[%s] Active orders: %s', self._options.pair, ', '.join(map(repr, orders))) if orders
                                          else logger.info('[%s] No active orders found', self._options.pair))),
            (self._get_active_orders()
                .switch_map(Observable.from_iterable)
                .filter(lambda order: datetime.utcnow() - order.created > config.ORDER_OUTDATE_PERIOD)
                .subscribe(self._cancel_order))
        )

    def _get_new_orders(self, completed_orders, min_amount):
        return (completed_orders
            .map(self._get_type_and_amount_and_price_for_new_order)
            .map(d('order_type', 'amount', 'price'))
            .filter(lambda p: p.amount >= min_amount))

    def _get_new_sell_orders(self, event_stream, pair, min_amount):
        return (Observable
            .combine_latest(
                (self._get_new_orders(self._get_completed_orders_singly(event_stream, pair), min_amount)
                    .filter(lambda p: p.order_type == Order.TYPE_SELL)),
                self._get_first_currency_balance().map(lambda p: p.balance),
                d('amount', 'price', 'balance')
            )
            .distinct_until_changed(lambda p: (p.amount, p.price))
            .filter(lambda p: p.amount <= p.balance))

    def _get_new_buy_orders(self, event_stream, pair, min_amount):
        return (Observable
            .combine_latest(
                (self._get_new_orders(self._get_completed_orders_singly(event_stream, pair), min_amount)
                    .filter(lambda p: p.order_type == Order.TYPE_BUY)),
                self._get_second_currency_balance().map(lambda p: p.balance),
                d('amount', 'price', 'balance')
            )
            .distinct_until_changed(lambda p: (p.amount, p.price))
            .filter(lambda p: p.amount * p.price <= p.balance))

    def _subscribe_for_completed_orders(self):
        return CompositeDisposable(
            (self._get_completed_orders_singly(self._events, self._options.pair)
                .subscribe(lambda order: logger.info('[%s] %s completed', self._options.pair, order))),
            (self._get_new_sell_orders(self._events, self._options.pair, self._options.min_amount)
                .subscribe(lambda p: self._create_sell_order(p.amount, p.price, self.REASON_ORDER_COMPLETED))),
            (self._get_new_buy_orders(self._events, self._options.pair, self._options.min_amount)
                .subscribe(lambda p: self._create_buy_order(p.amount, p.price, self.REASON_ORDER_COMPLETED)))
        )

    def _subscribe_for_jumping_price(self):
        return CompositeDisposable(
            (Observable
                .combine_latest(
                    self._get_jumping_price().map(partial(self._get_new_price, Order.TYPE_SELL)),
                    self._get_first_currency_balance().map(lambda p: p.balance),
                    d('price', 'balance')
                )
                .distinct_until_changed(lambda p: p.price)
                .filter(lambda p: self._options.deal_amount <= p.balance)
                .subscribe(lambda p: self._create_sell_order(self._options.deal_amount, p.price, self.REASON_PRICE_JUMP))),
            (Observable
                .combine_latest(
                    self._get_jumping_price().map(partial(self._get_new_price, Order.TYPE_BUY)),
                    self._get_second_currency_balance().map(lambda p: p.balance),
                    d('price', 'balance')
                )
                .distinct_until_changed(lambda p: p.price)
                .filter(lambda p: self._options.deal_amount * p.price <= p.balance)
                .subscribe(lambda p: self._create_buy_order(self._options.deal_amount, p.price, self.REASON_PRICE_JUMP))),
        )

    def _get_type_and_amount_and_price_for_new_order(self, order):
        if order.type == Order.TYPE_SELL:
            return Order.TYPE_BUY, order.amount, self._get_new_price(Order.TYPE_BUY, order.price)
        if order.type == Order.TYPE_BUY:
            return Order.TYPE_SELL, order.amount, self._get_new_price(Order.TYPE_SELL, order.price)
        raise Exception('unknown order type %s' % order.type)

    def _get_new_price(self, order_type, price):
        margin = self._options.margin + self._get_random_margin_jitter(self._options.margin_jitter)
        if order_type == Order.TYPE_SELL:
            return normalize_value(price + price * margin, self._options.pair.second.places)
        if order_type == Order.TYPE_BUY:
            return normalize_value(price - price * margin, self._options.pair.second.places)
        raise Exception('unknown order type %s' % order_type)

    def _create_sell_order(self, amount, price, reason):
        logger.info('[%s] Create sell order: %s for %s, reason is %s', self._options.pair, amount, price, reason)
        self._commands.on_next(commands.CreateSellOrderCommand(self._options.pair, amount, price))

    def _create_buy_order(self, amount, price, reason):
        logger.info('[%s] Create buy order: %s for %s, reason is %s', self._options.pair, amount, price, reason)
        self._commands.on_next(commands.CreateBuyOrderCommand(self._options.pair, amount, price))

    def _get_random_margin_jitter(self, jitter):
        return normalize_value(Decimal(uniform(-float(jitter), float(jitter))), 4)

    def _cancel_order(self, order):
        logger.info('[%s] Cancel outdated order %s created %s (%s ago)', self._options.pair, order, order.created,
                    datetime.utcnow() - order.created)
        self._commands.on_next(commands.CancelOrderCommand(order.id))

    def deinit(self):
        logger.info('Stopping %s', self)
        if self._subscription is not None:
            self._subscription.dispose()
        def subscribe(observer):
            nothing = lambda _: None
            group = CompositeDisposable()
            r = RefCountDisposable(group)
            left_map = OrderedDict()
            right_map = OrderedDict()
            left_id = 0
            right_id = 0

            def on_next_left(value):
                s = Subject()
                _id = left_id
                left_id += 1
                left_map.add(_id, s)
                
                try:
                    result = result_selector(value, add_ref(s, r))
                except Exception as e:
                    log.error("*** Exception: %s" % e)
                    left_values = left_map.getValues()
                    for left_value in left_values:
                        left_value.on_error(e)
                    
                    observer.on_error(e)
                    return
                
                observer.on_next(result)

                right_values = right_map.getValues()
                for right_value in right_values:
                    s.on_next(right_value)
                
                md = SingleAssignmentDisposable()
                group.add(md)

                def expire():
                    if left_map.remove(_id):
                        s.on_completed()
                    
                    group.remove(md)
                
                try:
                    duration = left_duration_selector(value)
                except Exception as e:
                    left_values = left_map.getValues()
                    for left_value in left_values:
                        left_value.on_error(e)
                    
                    observer.on_error(e)
                    return
                
                def on_error(e):
                    left_values = left_map.getValues()
                    for left_value in left_values:
                        left_value.on_error(e)
                    
                    observer.on_error(e)
                    
                md.disposable = duration.take(1).subscribe(
                    nothing,
                    on_error,
                    expire)
            
            def on_error_left(e):
                left_values = left_map.values()
                for left_value in left_values:
                    left_value.on_error(e)
                
                observer.on_error(e)
                
            group.add(left.subscribe(on_next_left, on_error_left, observer.on_completed))

            def on_next_right(value):
                left_values, i, len
                _id = right_id
                right_id += 1
                right_map.add(id, value)

                md = SingleAssignmentDisposable()
                group.add(md)

                def expire():
                    right_map.remove(id)
                    group.remove(md)
                
                try:
                    duration = right_duration_selector(value)
                except Exception as e:
                    left_values = left_map.getValues()
                    for left_value in left_values:
                        left_value.on_error(e)
                    
                    observer.on_error(e)
                    return
                
                def on_error(e):
                    left_values = left_map.getValues()
                    for left_value in left_map:
                        left_value.on_error(e)
                    
                    observer.on_error(e)
                    
                md.disposable = duration.take(1).subscribe(
                    nothing,
                    on_error,
                    expire)

                left_values = left_map.getValues()
                for left_value in left_values:
                    left_values.on_next(value)
            
            def on_error_right(e):
                left_values = left_map.values()
                for left_value in left_values:
                    left_value.on_error(e)
                
                observer.on_error(e)
            
            group.add(right.subscribe(on_next_right, on_error_right))
            return r
        def subscribe(observer):
            group = CompositeDisposable()
            left_done = False
            left_map = OrderedDict()
            left_id = 0
            right_done = False
            right_map = OrderedDict()
            right_id = 0
            
            def on_next_left(value):
                nonlocal left_id

                duration = None
                current_id = left_id
                left_id += 1
                md = SingleAssignmentDisposable()
                
                #log.debug("**** left_map[%s] = %s" % (current_id, value))
                #log.debug(value.get_hash_code())
                
                left_map[current_id] = value
                group.add(md)

                def expire():
                    if current_id in left_map:
                        del left_map[current_id]
                    if not len(left_map) and left_done:
                        observer.on_completed()
                
                    return group.remove(md)
                
                try:
                    duration = left_duration_selector(value)
                except Exception as exception:
                    log.error("*** Exception: %s" % exception)
                    observer.on_error(exception)
                    return
                
                md.disposable = duration.take(1).subscribe(noop, observer.on_error, lambda: expire())
                values = right_map.values()
                for val in values:
                    try:
                        result = result_selector(value, val)
                    except Exception as exception:
                        log.error("*** Exception: %s" % exception)
                        observer.on_error(exception)
                        return
                    
                    observer.on_next(result)
                
            def on_completed_left():
                nonlocal left_done

                left_done = True
                if right_done or not len(left_map):
                    observer.on_completed()
                
            group.add(left.subscribe(on_next_left, observer.on_error, on_completed_left))

            def on_next_right(value):
                nonlocal right_id

                duration = None
                current_id = right_id
                right_id += 1
                md = SingleAssignmentDisposable()
                right_map[current_id] = value
                group.add(md)

                def expire():
                    if current_id in right_map:
                        del right_map[current_id]
                    if not len(right_map) and right_done:
                        observer.on_completed()
                        
                    return group.remove(md)
                
                try:
                    duration = right_duration_selector(value)
                except Exception as exception:
                    log.error("*** Exception: %s" % exception)
                    observer.on_error(exception)
                    return
                
                md.disposable = duration.take(1).subscribe(noop, observer.on_error, lambda: expire())
                values = left_map.values()
                for val in values:
                    try:
                        result = result_selector(val, value)
                    except Exception as exception:
                        log.error("*** Exception: %s" % exception)
                        observer.on_error(exception)
                        return
                    
                    observer.on_next(result)
                
            def on_completed_right():
                nonlocal right_done

                right_done = True
                if left_done or not len(right_map):
                    observer.on_completed()
                
            group.add(right.subscribe(on_next_right, observer.on_error, on_completed_right))
            return group