-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
deviation_checker.go
80 lines (67 loc) · 2.58 KB
/
deviation_checker.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
75
76
77
78
79
80
package fluxmonitorv2
import (
"github.com/shopspring/decimal"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)
// DeviationThresholds carries parameters used by the threshold-trigger logic
type DeviationThresholds struct {
Rel float64 // Relative change required, i.e. |new-old|/|old| >= Rel
Abs float64 // Absolute change required, i.e. |new-old| >= Abs
}
// DeviationChecker checks the deviation of the next answer against the current
// answer.
type DeviationChecker struct {
Thresholds DeviationThresholds
lggr logger.Logger
}
// NewDeviationChecker constructs a new deviation checker with thresholds.
func NewDeviationChecker(rel, abs float64, lggr logger.Logger) *DeviationChecker {
return &DeviationChecker{
Thresholds: DeviationThresholds{
Rel: rel,
Abs: abs,
},
lggr: lggr.Named("DeviationChecker").With("threshold", rel, "absoluteThreshold", abs),
}
}
// NewZeroDeviationChecker constructs a new deviation checker with 0 as thresholds.
func NewZeroDeviationChecker(lggr logger.Logger) *DeviationChecker {
return NewDeviationChecker(0, 0, lggr)
}
// OutsideDeviation checks whether the next price is outside the threshold.
// If both thresholds are zero (default value), always returns true.
func (c *DeviationChecker) OutsideDeviation(curAnswer, nextAnswer decimal.Decimal) bool {
loggerFields := []interface{}{
"currentAnswer", curAnswer,
"nextAnswer", nextAnswer,
}
if c.Thresholds.Rel == 0 && c.Thresholds.Abs == 0 {
c.lggr.Debugw(
"Deviation thresholds both zero; short-circuiting deviation checker to "+
"true, regardless of feed values", loggerFields...)
return true
}
diff := curAnswer.Sub(nextAnswer).Abs()
loggerFields = append(loggerFields, "absoluteDeviation", diff)
if !diff.GreaterThan(decimal.NewFromFloat(c.Thresholds.Abs)) {
c.lggr.Debugw("Absolute deviation threshold not met", loggerFields...)
return false
}
if curAnswer.IsZero() {
if nextAnswer.IsZero() {
c.lggr.Debugw("Relative deviation is undefined; can't satisfy threshold", loggerFields...)
return false
}
c.lggr.Infow("Threshold met: relative deviation is ∞", loggerFields...)
return true
}
// 100*|new-old|/|old|: Deviation (relative to curAnswer) as a percentage
percentage := diff.Div(curAnswer.Abs()).Mul(decimal.NewFromInt(100))
loggerFields = append(loggerFields, "percentage", percentage)
if percentage.LessThan(decimal.NewFromFloat(c.Thresholds.Rel)) {
c.lggr.Debugw("Relative deviation threshold not met", loggerFields...)
return false
}
c.lggr.Infow("Relative and absolute deviation thresholds both met", loggerFields...)
return true
}