0

I'm trying to create a tool for analysing stock prices.

I've got a stream of price data for different stocks, and I want to have an observable to emit events whenever it receives a new, distinct and complete set of prices.

My plan: grouping the stream into different sub-streams for different stocks, and recombining their latest values.

Let's say I've got a stream of events like this:

from rx import Observable

stock_events = [
    {'stock': 'A', 'price': 15},
    {'stock': 'A', 'price': 16},
    {'stock': 'B', 'price': 24},
    {'stock': 'C', 'price': 37},
    {'stock': 'A', 'price': 18},
    {'stock': 'D', 'price': 42},
    {'stock': 'B', 'price': 27},
    {'stock': 'B', 'price': 27},
    {'stock': 'C', 'price': 31},
    {'stock': 'D', 'price': 44}
]

price_source = Observable.from_list(stock_events)

Here is my first (naive) approach:

a_source = price_source.filter(lambda x: x['stock'] == 'A').distinct_until_changed()
b_source = price_source.filter(lambda x: x['stock'] == 'B').distinct_until_changed()
c_source = price_source.filter(lambda x: x['stock'] == 'C').distinct_until_changed()
d_source = price_source.filter(lambda x: x['stock'] == 'D').distinct_until_changed()

(Observable
    .combine_latest(a_source, b_source, c_source, d_source, lambda *x: x)
    .subscribe(print))

This correctly gives me:

({'stock': 'A', 'price': 18}, {'stock': 'B', 'price': 24}, {'stock': 'C', 'price': 37}, {'stock': 'D', 'price': 42})
({'stock': 'A', 'price': 18}, {'stock': 'B', 'price': 27}, {'stock': 'C', 'price': 37}, {'stock': 'D', 'price': 42})
({'stock': 'A', 'price': 18}, {'stock': 'B', 'price': 27}, {'stock': 'C', 'price': 31}, {'stock': 'D', 'price': 42})
({'stock': 'A', 'price': 18}, {'stock': 'B', 'price': 27}, {'stock': 'C', 'price': 31}, {'stock': 'D', 'price': 44})

Yet, I feel that this should be better handled by group_by, instead of several filterings, so here's a re-write:

(price_source
 .group_by(lambda e: e['stock'])
 .map(lambda obs: obs.distinct_until_changed())
 .combine_latest(lambda *x: x)
 .subscribe(print))

But this time, I get:

(<rx.core.anonymousobservable.AnonymousObservable object at 0x000000000105EA20>,)
(<rx.core.anonymousobservable.AnonymousObservable object at 0x000000000776AB00>,)
(<rx.core.anonymousobservable.AnonymousObservable object at 0x000000000776A438>,)
(<rx.core.anonymousobservable.AnonymousObservable object at 0x000000000775E7F0>,)

What have I missed here? How do I "unwrap" the nested observables?

  • I'm not sure that GroupBy is the correct way to go in your circumstances. Since you don't want to output an element until all stocks have emitted it implies you know what the stock tickers are. I think Combine latest is perfect for your use case. You might want to share the underlying sub though with Publish. – user630190 May 08 '17 at 15:24
  • did you ever solve this. and if so could you post an answer – Rubber Duck Mar 08 '20 at 14:18

1 Answers1

0

If you did want to use groupby it would be something like below in C#. This doesn't meet your requirement of a "complete" set though. As per comments, suspect CombineLatest would be better here.

price_source.GroupBy(x => x.Stock)
            .Select(gp => gp.DistinctUntilChanged(x => x.Price))
            .SelectMany(x => x)
            .Subscribe(s => Console.WriteLine($"{s.Stock} : {s.Price}"));
user630190
  • 1,142
  • 2
  • 11
  • 26