quantitative 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/.yardopts +7 -0
- data/Gemfile +7 -1
- data/Gemfile.lock +6 -2
- data/lib/quant/errors.rb +1 -0
- data/lib/quant/refinements/array.rb +195 -0
- data/lib/quant/ticks/ohlc.rb +1 -1
- data/lib/quant/version.rb +1 -1
- data/lib/quantitative.rb +1 -2
- metadata +8 -61
- /data/{DISCLAIMER.txt → DISCLAIMER.md} +0 -0
- /data/{LICENSE.txt → LICENSE} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b99f939e67992ff9a3659ef69ef8ff4bd5897baba1d269c491a7db7d1717c85
|
4
|
+
data.tar.gz: e9f456c34e16ed0c44b4b23f6e274ed44cce4ca6ad7fe654602dbd73afc281bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fca7722561f54543b92a7b50700eed4cb706b9bfca65405a8a101908b6ae49b547bcdd2cd13c4656c94a30ad817225118c22c905e47076c88f8feff85947bc4
|
7
|
+
data.tar.gz: 78b256560d7aec2ee19833618bd19e89f6c87ae718ad966b1d18caa18e08727b37d1232b169bce1fbc0b4ca4bac951e956fd4efefaa27450d533769f68d8ac23
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.3.0
|
data/.yardopts
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
quantitative (0.1.
|
4
|
+
quantitative (0.1.1)
|
5
5
|
oj (~> 3.10)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
ast (2.4.2)
|
11
|
+
benchmark-ips (2.13.0)
|
12
|
+
bigdecimal (3.1.6)
|
11
13
|
coderay (1.1.3)
|
12
14
|
debug (1.8.0)
|
13
15
|
irb (>= 1.5.0)
|
@@ -44,7 +46,8 @@ GEM
|
|
44
46
|
notiffany (0.1.3)
|
45
47
|
nenv (~> 0.1)
|
46
48
|
shellany (~> 0.0)
|
47
|
-
oj (3.16.
|
49
|
+
oj (3.16.3)
|
50
|
+
bigdecimal (>= 3.0)
|
48
51
|
parallel (1.24.0)
|
49
52
|
parser (3.3.0.4)
|
50
53
|
ast (~> 2.4.1)
|
@@ -112,6 +115,7 @@ PLATFORMS
|
|
112
115
|
arm64-darwin-22
|
113
116
|
|
114
117
|
DEPENDENCIES
|
118
|
+
benchmark-ips (~> 2.9)
|
115
119
|
debug
|
116
120
|
guard-rspec (~> 4.7)
|
117
121
|
quantitative!
|
data/lib/quant/errors.rb
CHANGED
@@ -0,0 +1,195 @@
|
|
1
|
+
module Quant
|
2
|
+
module Refinements
|
3
|
+
# Refinements for the standard Ruby +Array+ class.
|
4
|
+
# These refinements add statistical methods to the Array class as well as some optimizations that greatly
|
5
|
+
# speed up some of the computations performed by the various indicators.
|
6
|
+
#
|
7
|
+
# In addtion to the statistical methods, the refinements also add a +max_size!+ method to the Array class, which
|
8
|
+
# allows us to bound the array to a maximum number of elements, which is useful for indicators that are computing
|
9
|
+
# averages or sums over a fixed number of lookback ticks.
|
10
|
+
#
|
11
|
+
# There are some various performance benchmarks in the spec/performance directory that show the performance
|
12
|
+
# improvements of using these refinements.
|
13
|
+
#
|
14
|
+
# Keep in mind that within this library, we're generally concerned with adding to the tail of the arrays and
|
15
|
+
# rarely with removing or popping, so there's few optimizations or protections for those operations in
|
16
|
+
# conjunction with the +max_size+ setting. The +max_size+ has also been designed to be set only once, to avoid
|
17
|
+
# adding additional complexity to the code that is unnecessary until a use-case presents itself.
|
18
|
+
#
|
19
|
+
# Usage: Call +using Quant+ in the file or scope where you want to use these refinements. It does not matter
|
20
|
+
# if the arrays were instantiated outside the scope of the refinement, the refinements will still be applied.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# using Quant
|
24
|
+
#
|
25
|
+
# array = [1, 2, 3, 4, 5]
|
26
|
+
# array.mean => 3.0
|
27
|
+
# another_array.max_size!(3).push(1, 2, 3, 4, 5, 6) => [4, 5, 6]
|
28
|
+
#
|
29
|
+
# @note The behavior of out of bound indexes into the Array deviates from standard Ruby and always returns an element.
|
30
|
+
# If an array only has three elements and 4 or more are requested for +n+, the method constrains itself to
|
31
|
+
# the size of the array. This is an intentional design decision, but it may be a gotcha if you're not expecting it.
|
32
|
+
# The refined behavior generally only exists within the library's scope, but if you call `using Quant` in your
|
33
|
+
# own code, you may encounter the changed behavior unexpectedly.
|
34
|
+
module Array
|
35
|
+
|
36
|
+
# Overrides the standard +<<+ method to track the +maximum+ and +minimum+ values
|
37
|
+
# while also respecting the +max_size+ setting.
|
38
|
+
def <<(value)
|
39
|
+
push(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Overrides the standard +push+ method to track the +maximum+ and +minimum+ values
|
43
|
+
# while also respecting the +max_size+ setting.
|
44
|
+
def push(*objects)
|
45
|
+
Array(objects).each do |object|
|
46
|
+
super(object)
|
47
|
+
if @max_size && size > @max_size
|
48
|
+
voted_off = shift
|
49
|
+
@minimum = min if voted_off == @minimum
|
50
|
+
@maximum = max if voted_off == @maximum
|
51
|
+
else
|
52
|
+
@maximum = object if @maximum.nil? || object > @maximum
|
53
|
+
@minimum = object if @minimum.nil? || object < @minimum
|
54
|
+
end
|
55
|
+
end
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the maximum element value in the array. It is an optimized version of the standard +max+ method.
|
60
|
+
def maximum
|
61
|
+
@maximum || max
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the minimum element value in the array. It is an optimized version of the standard +min+ method.
|
65
|
+
def minimum
|
66
|
+
@minimum || min
|
67
|
+
end
|
68
|
+
|
69
|
+
# Treats the tail of the array as starting at zero and counting up. Does not overflow the head of the array.
|
70
|
+
# That is, if the +Array+ has 5 elements, prev(10) would return the first element in the array.
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# series = [1, 2, 3, 4]
|
74
|
+
# series.prev(0) # => series[-1] => series[3] => 4
|
75
|
+
# series.prev(1) # => series[-2] => series[3] => 3
|
76
|
+
# series.prev(2) # => series[-3] => series[2] => 2
|
77
|
+
# series.prev(3) # => series[-4] => series[1] => 1
|
78
|
+
# series.prev(4) # => series[-4] => series[0] => 1 (no out of bounds!)
|
79
|
+
#
|
80
|
+
# Useful for when translating TradingView or MQ4 indicators to Ruby where those programs' indexing starts at 0
|
81
|
+
# for most recent bar and counts up to the oldest bar.
|
82
|
+
def prev(index)
|
83
|
+
raise ArgumentError, "index must be positive" if index < 0
|
84
|
+
|
85
|
+
self[[size - (index + 1), 0].max]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Sets the maximum size of the array. When the size of the array exceeds the
|
89
|
+
# +max_size+, the first element is removed from the array.
|
90
|
+
# This setting modifies :<< and :push methods.
|
91
|
+
def max_size!(max_size)
|
92
|
+
# These guards are maybe not necessary, but they are here until a use-case is found.
|
93
|
+
# My concern lies with indicators that are built specifically against the +max_size+ of a given array.
|
94
|
+
raise Quant::ArrayMaxSizeError, 'cannot set max_size to nil.' unless max_size
|
95
|
+
raise Quant::ArrayMaxSizeError, 'can only max_size! once.' if @max_size
|
96
|
+
raise Quant::ArrayMaxSizeError, "size of Array #{size} exceeds max_size #{max_size}." if size > max_size
|
97
|
+
|
98
|
+
@max_size = max_size
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
# Computes the mean of the array. When +n+ is specified, the mean is computed over
|
103
|
+
# the last +n+ elements.
|
104
|
+
#
|
105
|
+
# @param n [Integer] the number of elements to compute the mean over
|
106
|
+
# @return [Float]
|
107
|
+
def mean(n: size)
|
108
|
+
subset = last(n)
|
109
|
+
return 0.0 if subset.empty?
|
110
|
+
|
111
|
+
sum = subset.sum / subset.size.to_f
|
112
|
+
end
|
113
|
+
|
114
|
+
# Computes the Exponential Moving Average (EMA) of the array. When +n+ is specified,
|
115
|
+
# the EMA is computed over the last +n+ elements.
|
116
|
+
# An Array of EMA's is returned, with the first entry always the first value in the subset.
|
117
|
+
#
|
118
|
+
# @params n [Integer] the number of elements to compute the EMA over.
|
119
|
+
# @return [Array<Float>]
|
120
|
+
def ema(n: size)
|
121
|
+
subset = last(n)
|
122
|
+
return [] if subset.empty?
|
123
|
+
|
124
|
+
alpha = 2.0 / (subset.size + 1)
|
125
|
+
naught_alpha = (1.0 - alpha)
|
126
|
+
pvalue = subset[0]
|
127
|
+
subset.map do |value|
|
128
|
+
pvalue = (alpha * value) + (naught_alpha * pvalue)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Computes the Simple Moving Average (SMA) of the array. When +n+ is specified,
|
133
|
+
# the SMA is computed over the last +n+ elements.
|
134
|
+
# An Array of SMA's is returned, with the first entry always the first value in the subset.
|
135
|
+
#
|
136
|
+
# @param n [Integer] the number of elements to compute the SMA over
|
137
|
+
# @return [Array<Float>]
|
138
|
+
def sma(n: size)
|
139
|
+
subset = last(n)
|
140
|
+
return [] if subset.empty?
|
141
|
+
|
142
|
+
pvalue = subset[0]
|
143
|
+
subset.map do |value|
|
144
|
+
pvalue = (pvalue + value) / 2.0
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Computes the Weighted Moving Average (WMA) of the array. When +n+ is specified,
|
149
|
+
# the WMA is computed over the last +n+ elements.
|
150
|
+
# An Array of WMA's is returned, with the first entry always the first value in the subset.
|
151
|
+
#
|
152
|
+
# @param n [Integer] the number of elements to compute the WMA over
|
153
|
+
# @return [Array<Float>]
|
154
|
+
def wma(n: size)
|
155
|
+
subset = last(n)
|
156
|
+
return [] if subset.empty?
|
157
|
+
|
158
|
+
# ensures we return not more than number of elements in full array,
|
159
|
+
# yet have enough values to compute each iteration
|
160
|
+
max_size = [size, n].min
|
161
|
+
while subset.size <= max_size + 2
|
162
|
+
subset.unshift(subset[0])
|
163
|
+
end
|
164
|
+
|
165
|
+
subset.each_cons(4).map do |v1, v2, v3, v4|
|
166
|
+
(4.0 * v4 + 3.0 * v3 + 2.0 * v2 + v1) / 10.0
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Computes the Standard Deviation of the array. When +n+ is specified,
|
171
|
+
# the Standard Deviation is computed over the last +n+ elements.
|
172
|
+
#
|
173
|
+
# @param n [Integer] the number of elements to compute the Standard Deviation over.
|
174
|
+
# @return [Float]
|
175
|
+
def stddev(reference_value, n: size)
|
176
|
+
variance(reference_value, n: n) ** 0.5
|
177
|
+
end
|
178
|
+
|
179
|
+
def variance(reference_value, n: size)
|
180
|
+
subset = last(n)
|
181
|
+
return 0.0 if subset.empty?
|
182
|
+
|
183
|
+
subset.empty? ? 0.0 : subset.map{ |v| (v - reference_value)**2 }.mean
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
refine Array do
|
189
|
+
import_methods Quant::Refinements::Array
|
190
|
+
|
191
|
+
alias std_dev stddev
|
192
|
+
alias standard_deviation stddev
|
193
|
+
alias var variance
|
194
|
+
end
|
195
|
+
end
|
data/lib/quant/ticks/ohlc.rb
CHANGED
@@ -59,7 +59,7 @@ module Quant
|
|
59
59
|
green: false,
|
60
60
|
doji: nil)
|
61
61
|
|
62
|
-
super(price: close_price, timestamp: close_timestamp, interval
|
62
|
+
super(price: close_price, timestamp: close_timestamp, interval: interval, trades: trades)
|
63
63
|
@open_timestamp = extract_time(open_timestamp)
|
64
64
|
@open_price = open_price.to_f
|
65
65
|
@high_price = high_price.to_f
|
data/lib/quant/version.rb
CHANGED
data/lib/quantitative.rb
CHANGED
@@ -11,7 +11,6 @@ quant_folder = File.join(lib_folder, "quant")
|
|
11
11
|
Dir.glob(File.join(quant_folder, "*.rb")).each { |fn| require fn }
|
12
12
|
|
13
13
|
# require sub-folders and their sub-folders
|
14
|
-
%w(ticks).each do |sub_folder|
|
15
|
-
Dir.glob(File.join(quant_folder, sub_folder, "*.rb")).each { |fn| require fn }
|
14
|
+
%w(refinements ticks).each do |sub_folder|
|
16
15
|
Dir.glob(File.join(quant_folder, sub_folder, "**/*.rb")).each { |fn| require fn }
|
17
16
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quantitative
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Lang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -24,62 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.10'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: debug
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: guard-rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '4.7'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '4.7'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: rspec
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '3.2'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '3.2'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: yard
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0.9'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0.9'
|
83
27
|
description: Quantitative and statistical tools written for Ruby 3.x for trading and
|
84
28
|
finance.
|
85
29
|
email:
|
@@ -90,12 +34,14 @@ extra_rdoc_files: []
|
|
90
34
|
files:
|
91
35
|
- ".rspec"
|
92
36
|
- ".rubocop.yml"
|
37
|
+
- ".ruby-version"
|
38
|
+
- ".yardopts"
|
93
39
|
- CODE_OF_CONDUCT.md
|
94
|
-
- DISCLAIMER.
|
40
|
+
- DISCLAIMER.md
|
95
41
|
- Gemfile
|
96
42
|
- Gemfile.lock
|
97
43
|
- Guardfile
|
98
|
-
- LICENSE
|
44
|
+
- LICENSE
|
99
45
|
- README.md
|
100
46
|
- Rakefile
|
101
47
|
- lib/quant/errors.rb
|
@@ -109,6 +55,7 @@ files:
|
|
109
55
|
- lib/quant/mixins/super_smoother.rb
|
110
56
|
- lib/quant/mixins/trig.rb
|
111
57
|
- lib/quant/mixins/weighted_average.rb
|
58
|
+
- lib/quant/refinements/array.rb
|
112
59
|
- lib/quant/ticks/core.rb
|
113
60
|
- lib/quant/ticks/indicator_points.rb
|
114
61
|
- lib/quant/ticks/ohlc.rb
|
@@ -142,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
89
|
- !ruby/object:Gem::Version
|
143
90
|
version: '0'
|
144
91
|
requirements: []
|
145
|
-
rubygems_version: 3.
|
92
|
+
rubygems_version: 3.5.5
|
146
93
|
signing_key:
|
147
94
|
specification_version: 4
|
148
95
|
summary: Quantitative and statistical tools written for Ruby 3.x for trading and finance.
|
File without changes
|
/data/{LICENSE.txt → LICENSE}
RENAMED
File without changes
|