1
+ import { SeriesSmoothing , SeriesSmoothingOptions } from "./smoothing_base"
2
+ import { Indicator , PointValue } from "../../../models/run"
3
+ import { mapRange } from "../utils" ;
4
+
5
+ export class MovingAverage extends SeriesSmoothing {
6
+ private readonly trimSmoothEnds : boolean
7
+ private readonly smoothWindow : number [ ]
8
+
9
+ constructor ( opt : SeriesSmoothingOptions , trimSmoothEnds : boolean ) {
10
+ super ( opt )
11
+ this . trimSmoothEnds = trimSmoothEnds
12
+ this . smoothWindow = this . getSmoothWindow ( opt . indicators , opt . smoothValue )
13
+ }
14
+
15
+ protected smooth ( ) : void {
16
+ for ( let i = 0 ; i < this . indicators . length ; i ++ ) {
17
+ let ind = this . indicators [ i ]
18
+ let windowSize = this . smoothWindow [ i ]
19
+
20
+ let result : PointValue [ ] = [ ]
21
+ windowSize = ~ ~ windowSize
22
+ let extraWindow = windowSize / 2
23
+ extraWindow = ~ ~ extraWindow
24
+
25
+ let count = 0
26
+ let total = 0
27
+
28
+ for ( let i = 0 ; i < ind . series . length + extraWindow ; i ++ ) {
29
+ let j = i - extraWindow
30
+ if ( i < ind . series . length ) {
31
+ total += ind . series [ i ] . value
32
+ count ++
33
+ }
34
+ if ( j - extraWindow - 1 >= 0 ) {
35
+ total -= ind . series [ j - extraWindow - 1 ] . value
36
+ count --
37
+ }
38
+ if ( j >= 0 ) {
39
+ result . push ( { step : ind . series [ j ] . step , value : ind . series [ j ] . value , smoothed : total / count ,
40
+ lastStep : ind . series [ j ] . lastStep } )
41
+ }
42
+ }
43
+ ind . series = result
44
+ }
45
+ }
46
+
47
+ private getSmoothWindow ( indicators : Indicator [ ] , smoothValue : number ) : number [ ] {
48
+ let maxRange : number = Number . MIN_SAFE_INTEGER
49
+ for ( let ind of indicators ) {
50
+ if ( ind . series . length > 1 && ! ind . is_summary ) {
51
+ maxRange = Math . max ( maxRange , ind . series [ ind . series . length - 1 ] . step - ind . series [ 0 ] . step )
52
+ }
53
+ }
54
+ if ( maxRange == Number . MIN_SAFE_INTEGER ) { // all single points. -> can't smooth
55
+ let stepRange = [ ]
56
+ for ( let _ of indicators ) {
57
+ stepRange . push ( 1 )
58
+ }
59
+ return stepRange
60
+ }
61
+
62
+ let smoothRange = mapRange ( smoothValue , 1 , 100 , 1 , 2 * maxRange )
63
+
64
+ let stepRange = [ ]
65
+
66
+ for ( let ind of indicators ) {
67
+ if ( ind . series . length >= 2 && ! ind . is_summary ) {
68
+ let stepGap = ind . series [ 1 ] . step - ind . series [ 0 ] . step
69
+ let numSteps = Math . max ( 1 , Math . ceil ( smoothRange / stepGap ) )
70
+ stepRange . push ( numSteps )
71
+ } else { // can't smooth - just a single point
72
+ stepRange . push ( 1 )
73
+ }
74
+ }
75
+
76
+ return stepRange
77
+ }
78
+
79
+ override trim ( ) : void {
80
+ this . indicators . forEach ( ( ind , i ) => {
81
+ let localSmoothWindow = Math . floor ( this . smoothWindow [ i ] / 2 ) // remove half from each end
82
+
83
+ if ( ind . series . length <= 1 ) {
84
+ localSmoothWindow = 0
85
+ } else if ( this . smoothWindow [ i ] >= ind . series . length ) {
86
+ localSmoothWindow = Math . floor ( ind . series . length / 2 )
87
+ }
88
+
89
+ let localMin = this . min
90
+ let localMax = this . max
91
+
92
+ if ( localMin == - 1 ) {
93
+ localMin = ind . series [ 0 ] . step
94
+ }
95
+ if ( this . trimSmoothEnds ) {
96
+ localMin = Math . max ( localMin , ind . series [ localSmoothWindow ] . step )
97
+ }
98
+
99
+ if ( localMax == - 1 ) {
100
+ localMax = ind . series [ ind . series . length - 1 ] . step
101
+ }
102
+ if ( this . trimSmoothEnds ) {
103
+ localMax = Math . min ( localMax , ind . series [ ind . series . length - 1 - localSmoothWindow +
104
+ ( ind . series . length % 2 == 0 && localSmoothWindow != 0 ? 1 : 0 ) ] . step ) // get the mid value for even length series
105
+ }
106
+
107
+ localMin = Math . floor ( localMin ) - 0.5
108
+ localMax = Math . ceil ( localMax ) + 0.5
109
+
110
+ let minIndex = ind . series . length - 1
111
+ let maxIndex = 0
112
+
113
+ for ( let i = 0 ; i < ind . series . length ; i ++ ) {
114
+ let p = ind . series [ i ]
115
+ if ( p . step >= localMin && p . step <= localMax ) {
116
+ minIndex = Math . min ( i , minIndex )
117
+ maxIndex = Math . max ( i , maxIndex )
118
+ }
119
+ }
120
+
121
+ ind . lowTrimIndex = minIndex
122
+ ind . highTrimIndex = maxIndex
123
+ } )
124
+ }
125
+ }
0 commit comments