-
Notifications
You must be signed in to change notification settings - Fork 0
/
randomwalker.go
74 lines (64 loc) · 2.39 KB
/
randomwalker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/*
Package randomwalker provides a parametric random walk generator. You can
instentiate a random walker that will start at a specific point, that will stay
between an upper and lower boundary, that will vary on each step by a specified
maximum magnetude.
Also, if you expect to use this walker for financial models, this walker is
going to give you something close to a Gaussian random walk, and not a Brownian
random walk.
*/
package randomwalker
import (
"math/rand"
"time"
)
// randomer abstracts the random number generator. We would not need this if
// it was not for the tests, which need a "replayable" random source, so we
// use this inteface to be able to stub the random generator in the tests.
type randomer interface {
Float32() float32
}
// RandomWalker represents one instance of a random walk generator.
type RandomWalker struct {
current float32
min float32
max float32
maxDynPcnt float32
random randomer
}
// NewRandomWalker returns a random walk generator. Once created, you can call
// Step() on this object to get a random walk value.
func NewRandomWalker(origin, min, max, maxDynPcnt float32) *RandomWalker {
source := rand.NewSource(time.Now().UnixNano())
return NewRandomWalkerWithRandSource(origin, min, max, maxDynPcnt, &source)
}
// NewRandomWalkerWithRandSource returns a random walk generator that will use
// the provided random source. Specifying the random can help making sure that
// parallel processes will not produce similar random walks, or if you want to
// skew the random walk to better fit a certain natural phenomenon you are
// trying to simulate. Once created, you can call Step() on this object to get
// a random walk value.
func NewRandomWalkerWithRandSource(origin, min, max, maxDynPcnt float32, source *rand.Source) *RandomWalker {
rw := RandomWalker{
current: origin,
min: min,
max: max,
maxDynPcnt: maxDynPcnt,
random: rand.New(*source),
}
return &rw
}
// Step does one step of a random walk and returns the resulting value.
func (rw *RandomWalker) Step() float32 {
maxDynamic := rw.current * rw.maxDynPcnt
// BUG(cdemers): The pseudorandom generator seems to be skewed on the
// low side by about 3%, which is extremely strange.
rw.current += (rw.random.Float32()*2 - 1) * maxDynamic
if rw.current < rw.min {
rw.current = rw.min
}
if rw.current > rw.max {
rw.current = rw.max
}
return rw.current
}