quantitative 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|