Rxjs RxSwift可观测创建,它获取流本身的最后一个值

Rxjs RxSwift可观测创建,它获取流本身的最后一个值,rxjs,rx-java,reactive-programming,rx-swift,Rxjs,Rx Java,Reactive Programming,Rx Swift,我试图使用RxSwift计算SMA(简单移动平均)和EMA(指数加权移动平均) 设置如下所示,这些方法将收盘价流作为输入可观察。因此,如果每次发出新的close价格,sma obervable将向流发出新的计算值 我完成了SMA版本,工作正常 func sma(_ source: Observable<Double>, _ length: Int) -> Observable<Double?> { let bag = DisposeBag() re

我试图使用RxSwift计算SMA(简单移动平均)和EMA(指数加权移动平均)

设置如下所示,这些方法将收盘价流作为输入
可观察
。因此,如果每次发出新的
close
价格,sma obervable将向流发出新的计算值

我完成了SMA版本,工作正常

func sma(_ source: Observable<Double>, _ length: Int) -> Observable<Double?> {
    let bag = DisposeBag()

    return Observable<Double?>.create { observer -> Disposable in
        source.scan([]) { Array($0 + [$1].suffix(length)) }.subscribe(onNext: { value in
                if value.count < length {
                    observer.onNext(nil)
                } else {
                    observer.onNext(value.reduce(0.0, { $0 + $1 / Double(length) }))
                }
        }).disposed(by: bag)

        return Disposables.create()
    }
} 
非常感谢任何帮助:祈祷

我找到了解决办法(不确定这是否是最干净的方法)

这是在函数声明内创建一个
行为继电器
,而在
可观察的外部创建一个
行为继电器。创建
块,该块保持计算出的最新EMA的本地副本

通过这种方式,它不需要函数的使用者注入可观察的依赖项,也不需要复杂的流转换

下面是使用RxSwift实现的EMA(指数加权移动平均值)

func ema(_ source: Observable<Double>, _ length: Int) -> Observable<Double?> {
    let bag = DisposeBag()
    let lastEMA: BehaviorRelay<Double?> = BehaviorRelay.init(value: nil)
    return Observable<Double?>.create { observer -> Disposable in
        source.scan([]) { Array($0 + [$1].suffix(length)) }
            .subscribe(onNext: { value in
                let alpha: Double = Double(2) / Double(length + 1)
                let src = value
                var sum: Double? = 0.0
                sum = na(lastEMA.value) ? sma(src, length) : alpha * src.last! + (1 - alpha) * nz(lastEMA.value)
                observer.onNext(sum)
                lastEMA.accept(sum)
        }).disposed(by: bag)
        return Disposables.create()
    }
}
func-ema(uu源:可观察,u长度:Int)->可观察{
let bag=DisposeBag()
设lastEMA:BehaviorRelay=BehaviorRelay.init(值:nil)
return Observable.create{observator->Disposable in
source.scan([]){Array($0+[$1]。后缀(长度))}
.subscribe(onNext:{中的值)
设alpha:Double=Double(2)/Double(长度+1)
设src=value
var总和:双精度?=0.0
总和=na(最后一次测量值)?sma(最后一次测量值,长度):alpha*src.last!+(1-alpha)*nz(最后一次测量值)
onNext观察员(总和)
最后接受金额(总和)
}).处置(由:袋)
返回一次性物品。创建()
}
}
备注:

na
nz
是从
TradingView
pinescript复制的方法


首先,让我们清理sma操作符。您正在函数内创建不合适的dispose bags。subscribe返回一个一次性的,create的闭包需要返回一个一次性的。只需返回订阅的一次性

func sma(_ source: Observable<Double>, _ length: Int) -> Observable<Double?> {
    Observable<Double?>.create { observer -> Disposable in
        source
            .scan([]) { Array($0 + [$1].suffix(length)) }
            .subscribe(onNext: { value in
                if value.count < length {
                    observer.onNext(nil)
                } else {
                    observer.onNext(value.reduce(0.0, { $0 + $1 / Double(length) }))
                }
            })
    }
}

当你只有一个可以观察到的输入时,考虑把它变成可观察类型的扩展,这样它就可以很容易地插入到一个链…

extension ObservableType where Element == Double {
    func sma(_ length: Int) -> Observable<Double?> {
        scan([]) { Array($0 + [$1].suffix(length)) }
            .map { $0.count < length ? nil : $0.reduce(0.0, { $0 + $1 / Double(length) }) }
    }
}
使用一些样本值,上述内容应易于测试。我会让你去做的。完成上述操作后,我们可以使用与
sma
相同的模式来创建操作符:

extension ObservableType where Element == Double {
    func ema(_ length: Int) -> Observable<Double?> {
        scan([]) { Array([$1] + $0).suffix(length) } // put the most recent price in front to correctly handle the formula
            .map { $0.count < length ? nil : getEMA(prices: $0) }
    }
}
扩展ObservableType,其中元素==Double{
func-ema(u-length:Int)->可观察{
扫描([]){Array([$1]+$0).suffix(length)}//将最新价格放在前面,以正确处理公式
.map{$0.count
我的
getEMA
公式的初始表达式是错误的。我用正确的表达更新了答案。非常感谢@Daniel T。SMA的修订版看起来更干净了。关于EMA,问题是,需要EMA-1来计算下一个EMA,这就像一个递归问题。在流创建阶段,我找不到rx操作员直接检索当前流的最后一个值。所以我想到了这个,我发布的代码是递归的,解决了这个问题。
extension ObservableType where Element == Double {
    func sma(_ length: Int) -> Observable<Double?> {
        scan([]) { Array($0 + [$1].suffix(length)) }
            .map { $0.count < length ? nil : $0.reduce(0.0, { $0 + $1 / Double(length) }) }
    }
}
func getEMA(prices: [Double], k: Double? = nil) -> Double {
    guard !prices.isEmpty else { return 0 }
    let k = k ?? Double(2 / (prices.count + 1))
    return prices[0] * k + getEMA(prices: prices.suffix(prices.count - 1), k: k) * (1 - k)
}
extension ObservableType where Element == Double {
    func ema(_ length: Int) -> Observable<Double?> {
        scan([]) { Array([$1] + $0).suffix(length) } // put the most recent price in front to correctly handle the formula
            .map { $0.count < length ? nil : getEMA(prices: $0) }
    }
}