简体   繁体   English

Rx.NET“独特”获得最新价值?

[英]Rx.NET 'Distinct' to get the lastest value?

I'm new to Rx and I'm trying to make a GUI to display stock market data. 我是Rx的新手,我正在尝试制作一个GUI以显示股市数据。 The concept is a bit like ReactiveTrader , but I'll need to display the whole "depth", ie, all prices and their buy/sell quantities in the market instead of only the "top level" of the market buy/sells, sorting by price. 这个概念有点像ReactiveTrader ,但是我需要显示整个“深度”,即所有价格及其在市场中的买/卖数量,而不是仅是市场买/卖的“最高级别”,进行排序按价格。

The data structure for each "price level" is like this: 每个“价格水平”的数据结构如下:

public class MarketDepthLevel
{
    public int MarketBidQuantity { get; set; }
    public decimal Price { get; set; }
    public int MarketAskQuantity { get; set; }
}

And underneath the GUI, a socket listens to network updates and return them as an Observable: 在GUI的下面,套接字监听网络更新并将其作为Observable返回:

IObservable<MarketDepthLevel> MarketPriceLevelStream;

Which after transformed into a ReactiveList, eventually bound to a DataGrid. 将其转换为ReactiveList之后,最终绑定到DataGrid。

The transformation would basically choose the latest updates of each price level, and sort them by price. 转换基本上将选择每个价格水平的最新更新,然后按价格对其进行排序。 So I come up with something like this: 所以我想出了这样的东西:

    public IReactiveDerivedList<MarketDepthLevel> MarketDepthStream
    {
        get
        {
            return MarketDepthLevelStream
                .Distinct(x => x.Price)
                .CreateCollection()
                .CreateDerivedCollection(x => x, orderer: (x, y) => y.Price.CompareTo(x.Price));
        }
    }

But there are problems: 但是有问题:

  1. When 'Distinct' sees a same price as appeared before, it discards the new one , but I need the new one to replace the old ones (as they contain the lasted MarketBidQuantity/MarketAskQuantity) 当'Distinct'看到与以前相同的价格时,它会丢弃新的价格,但是我需要新的价格来替换旧的价格 (因为它们包含持久的MarketBidQuantity / MarketAskQuantity)

  2. It seems a bit clumsy to CreateCollection/CreateDerivedColleciton CreateCollection / CreateDerivedColleciton似乎有点笨拙

Any thoughts on solving these (especially the 1st problem)? 关于解决这些问题(尤其是第一个问题)有什么想法吗? Thanks 谢谢

Just group the items and then project each group to be the last item in the group: 只需对项目进行分组,然后将每个组投影为该组中的最后一个项目:

return MarketDepthLevelStream
    .GroupBy(x => x.Price, (key, items) => items.Last());

If I understand you correctly, you want to project a stream of MarketDepthLevel updates in a list of the latest bid/ask quantities for each level (in finance parlance, this is a type of ladder ). 如果我对您的理解正确,则希望在每个级别的最新出价/要价数量列表中MarketDepthLevel更新流(按财务说法,这是一种阶梯 )。 The ladder is held as a ReactiveList bound the UI. 梯子作为绑定到UI的ReactiveList保存。 ( ObserveOn may be required, although ReactiveList handles this in most cases I believe) (虽然我相信大多数情况下ReactiveList处理,但可能需要ObserveOn

Here's an example ladder snapped from http://ratesetter.com , where the "Price" is expressed as a percentage (Rate), and the bid/ask sizes by the amount lenders want and borrowers need at each price level: 以下是从http://ratesetter.com捕捉的示例梯形图,其中“价格”表示为百分比(比率),而买入/卖出价大小则由贷方在每个价格水平上想要和需要的数量决定:

RateSetter梯子

At this point, I begin to get slightly lost. 在这一点上,我开始迷路了。 I'm confused as to why you need any further Rx operators, since you could simply Subscribe to the update stream as is and have the handler update a fixed list data-bound to a UI. 我对为什么需要任何其他Rx运算符感到困惑,因为您可以直接Subscribe更新流,并让处理程序更新绑定到UI的固定列表数据。 Doesn't each new event simply need to be added to the ReactiveList if it's a new price, or replace an existing entry with a matching price? 如果不是每个新事件都是新价格,是否只需将其添加到ReactiveList ,还是用匹配的价格替换现有条目? Imperative code to do this is fine if it's the last step in the chain to the UI. 如果这是UI链中的最后一步,那么执行此命令式代码就可以了。

There is only value in doing this in the Rx stream itself if you need to convert the MarketDepthLevelStream into a stream of ladders. 如果您需要将MarketDepthLevelStream转换为阶梯流,则在Rx流本身中执行此操作仅具有价值。 That could be useful, but you don't mention this need. 这可能很有用,但是您没有提及此需求。

Such a need could be driven by either the desire to multicast the stream to many subscribers, and/or because you have further transformations or projections you need to make on the ladders. 可能需要将流多播到许多订户,和/或因为您需要在梯子上进行进一步的转换或投影,因此可能导致这种需求。

Bear in mind, if the ladder is large, then working with whole ladder updates in the UI might give you performance issues - in many cases, individual updates into a mutable structure like a ReactiveList are a practical way to go. 请记住,如果阶梯很大,那么在UI中使用整个阶梯更新可能会给您带来性能问题-在许多情况下,将单个更新变为可变结构(如ReactiveList是一种可行的方法。

If working with a stream of ladders is a requirement, then look to Observable.Scan . 如果梯子流的工作必需的,然后看向Observable.Scan This is the work-horse operator in Rx that maintains local state. 这是Rx中的工作马运算符,用于维护本地状态。 It is used for any form of running aggregate - such as a running total, average etc., or in this case, a ladder. 它用于任何形式的运行汇总(例如运行总计,平均值等,在这种情况下为阶梯)。

Now if all you need is the static list described above, I am potentially off on a massive detour here, but it's a useful discussion so... 现在,如果您需要的只是上述静态列表,那么我可能会绕道而行,但这是一个有用的讨论,所以...

You'll want to think carefully about the type used for the ladder aggregate - you need to be concious of how down-stream events would be consumed. 您需要仔细考虑用于阶梯聚合的类型-您需要意识到下游事件的消耗方式。 It's likely to need an immutable collection type so that things don't get weird for subscribers (each event should be in effect a static snapshot of the ladder). 它可能需要一个不可变的集合类型,以使订阅者不会感到奇怪(每个事件实际上应该是梯形图的静态快照)。 With immutable collections , it may be important to think about memory efficiency. 对于不可变的集合 ,考虑内存效率可能很重要。

Here's a simple example of how a ladder stream might work, using an immutable collection from nuget pre-release package System.Collections.Immutable : 这是一个简单的示例,说明如何使用nuget预发行包System.Collections.Immutable的不可变集合来梯形图流如何工作:

public static class ObservableExtensions
{
    public static IObservable<ImmutableSortedDictionary<decimal, MarketDepthLevel>>
        ToLadder(this IObservable<MarketDepthLevel> source)
    {
        return source.Scan(ImmutableSortedDictionary<decimal, MarketDepthLevel>.Empty,
            (lastLadder, depthLevel) =>
                lastLadder.SetItem(depthLevel.Price, depthLevel));
    }
}

The ToLadder extension method creates an empty immutable ladder as the seed aggregate, and each successive MarketDepthLevel event produces a new update ladder. ToLadder扩展方法将创建一个空的不可变阶梯作为种子聚合,并且每个后续的MarketDepthLevel事件都会产生一个新的更新阶梯。 You may want to see if ImmutableSortedSet is sufficient. 您可能想看看ImmutableSortedSet是否足够。

You would probably want to wrap/project this into your own type, but hopefully you get the idea. 您可能希望将其包装/投影到您自己的类型中,但希望您能理解。

Ultimately, this still leaves you with the challenge of updating a UI - and as mentioned before now you are stuck with the whole ladder, meaning you have to bind a whole ladder every time, or convert it back to a stream of individual updates - and it's getting way to off topic to tackle that here...! 最终,这仍然给您带来了更新UI的挑战-并且如前所述,您陷于整个梯子中,这意味着您每次必须绑定整个梯子,或将其转换回单个更新流中-它正在成为解决这个问题的方法...!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM