exp*_*ite 5 monads recurrence haskell state-monad moving-average
我正在http://www.thalesians.com/archive/public/academic/finance/papers/Zumbach_2000.pdf中对运营商进行新的实施: 编辑:此处的解释更清楚:https : //www.olseninvest.com/客户/pdf/paper/001207-emaOfEma.pdf
简而言之,它是一堆基于指数移动平均值的递归关系的很酷的时间序列运算符,其中ema()运算符的每个应用都采用新值和ema的先前结果。我似乎无法在此堆栈交换上进行乳胶操作,但是无论如何我现在的问题是软件问题。
我在Scala中通过在创建EMA函数的thunk中隐藏了一个变量来实现了这一点。所有这些都可以,但是非常棘手,因为再次调用ema(5)然后再次调用ema(5)自然会导致不同的结果。我想使用State Monads重做所有这些操作,但是我很快就迷失了自己。
例如,我在Haskell中具有以下简化的EMA State monad:
import Control.Monad.State
type EMAState = Double
type Tau = Double
ema :: Tau -> Double -> State EMAState Double
ema tau x = state $ \y ->
let alpha = 1 / tau
mu = exp(-alpha)
mu' = 1 - mu
y' = (mu * y) + (mu' * x)
in (y', y')
Run Code Online (Sandbox Code Playgroud)
我可以在GHCI中轻松测试:
*Main Control.Monad.State> runState (ema 5 10) 0
(1.8126924692201818,1.8126924692201818)
Run Code Online (Sandbox Code Playgroud)
将输入10应用于初始化为0的5周期EMA。这很好,使用forM可以应用多个输入值,等等。现在,下一步是实现“迭代EMA”,即应用的EMA自己N次。
iEMA[n](x) = EMA(iEMA[n-1](x))
Run Code Online (Sandbox Code Playgroud)
这些中间EMA均需要具有自己的状态(即先前的结果),才能正确计算迭代EMA的向量。所以,我正在寻找的是这样的东西(我认为):
iema :: Int -> Tau -> Double -> State [EMAState] [Double]
Run Code Online (Sandbox Code Playgroud)
本质上是EMA的菊花链:
iEMA[3](x) = EMA(EMA(EMA(x,s1),s2),s3) = (x, [s1,s2,s3]) -> ([y1,y2,y3], [s1',s2',s3'])
Run Code Online (Sandbox Code Playgroud)
如果我只关心第三个迭代EMA ...
... -> (y3, [s1', s2', s3'])
Run Code Online (Sandbox Code Playgroud)
本文从此继续发展,在迭代的EMA及其平均数等基础上创建越来越复杂的运算符,因此我希望能够在功能上和纯粹上构成这些有状态运算符,从而构建更加复杂的状态,但输入和输出仍然非常简单。
我真的感觉这就是函数式编程擅长的领域,但是我还没有专门知识来了解如何以正确的方式组合这些State monad。有人可以通过这些迭代的递归运算符为我指明正确的方向吗?
编辑:
几个有用的人建议对输入数据重复使用相同的ema运算符,但这还不够。每个ema运算符都需要保持自己的先前值。这是一个例子:
tau 5
mu 0.818730753
muprime 0.181269247
ema1 ema2 ema3
x 0 0 0 <- States_0
1 0.1812 0.03285 0.00595 <- States_1
5 1.0547 0.21809 0.04441 <- States_2
Run Code Online (Sandbox Code Playgroud)
x列是原始输入,ema1使用其左侧作为输入,使用up1作为重复/状态。ema2的左边用于输入(不是x!),它用于状态。这是ema(ema(x))。同上ema3 = ema(ema(ema(ema(x))))。我想做的事,我认为必须是可行的,给它一个ema状态monad,组成ema3状态monad,甚至更好的是[ema]状态monad,每个随后的ema都对前一个输出进行操作。
更新了答案...
定义:
combine :: [ a -> State s a ] -> a -> State [s] a
combine fs a = state $ \ys ->
let zs = zipWith (\f y a -> runState (f a) y) fs ys
pairs = chain a zs
as' = map fst pairs
a' = last as' -- we are only returning one result in this case
ys' = map snd pairs
in (a', ys')
chain :: a -> [ a -> (a,s) ] -> [ (a,s) ]
chain a [] = []
chain a (f:fs) = let (a',s) = f a
in (a',s) : chain a' fs
ema3 t = combine $ replicate 3 (ema t)
ghci> runState (ema3 5 1) [0,0,0]
(5.956242778945897e-3,[0.18126924692201818,3.2858539879675595e-2,5.956242778945897e-3])
ghci> runState (do ema3 5 1; ema3 5 5) [0,0,0]
(4.441089130249448e-2,[1.0547569416524334,0.21809729359983737,4.441089130249448e-2])
Run Code Online (Sandbox Code Playgroud)
可以combine轻松修改以返回所有结果 - 只需 returnas'而不是a'。
原答案:
combine :: (a -> State s b) -> (b -> State t c) -> (a -> State (s,t) c)
combine f g a = state $ \(s,t) ->
let (b,s') = runState (f a) s
(c,t') = runState (g b) t
in (c,(s',t'))
Run Code Online (Sandbox Code Playgroud)
然后:
ema3 tau = ema tau `combine` ema tau `combine` ema tau
Run Code Online (Sandbox Code Playgroud)
并且em3有类型:
ema3 :: Tau -> Double -> State ((EMAState, EMAState), EMAState) Double
Run Code Online (Sandbox Code Playgroud)
例如:
ghci> runState (ema3 5 1) ((0,0),0)
(5.956242778945897e-3,((0.18126924692201818,3.2858539879675595e-2),5.956242778945897e-3))
Run Code Online (Sandbox Code Playgroud)
请注意, 的状态类型ema3不是((Double,Double),Double)三元组或列表。
在您的示例中,您(ema3 5)首先使用输入运行x = 1,然后使用x = 5
具有初始状态的输入运行((0,0),0):
ghci> runState (do ema3 5 1; ema3 5 5) ((0,0),0)
(4.441089130249448e-2,((1.0547569416524334,0.21809729359983737),4.441089130249448e-2))
Run Code Online (Sandbox Code Playgroud)
这就是表中的第二行。