quantitative 0.1.4 → 0.1.6
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/.rubocop.yml +2 -0
- data/Gemfile.lock +1 -1
- data/lib/quant/asset.rb +88 -0
- data/lib/quant/{security_class.rb → asset_class.rb} +14 -14
- data/lib/quant/attributes.rb +218 -0
- data/lib/quant/errors.rb +30 -5
- data/lib/quant/indicators/indicator.rb +94 -53
- data/lib/quant/indicators/indicator_point.rb +9 -21
- data/lib/quant/indicators/ma.rb +14 -20
- data/lib/quant/indicators/ping.rb +22 -0
- data/lib/quant/indicators.rb +1 -23
- data/lib/quant/indicators_proxy.rb +61 -0
- data/lib/quant/indicators_sources.rb +18 -0
- data/lib/quant/interval.rb +37 -12
- data/lib/quant/mixins/filters.rb +2 -0
- data/lib/quant/mixins/moving_averages.rb +86 -0
- data/lib/quant/mixins/super_smoother.rb +1 -2
- data/lib/quant/refinements/array.rb +12 -10
- data/lib/quant/series.rb +36 -19
- data/lib/quant/settings.rb +2 -0
- data/lib/quant/ticks/ohlc.rb +8 -11
- data/lib/quant/ticks/serializers/ohlc.rb +33 -22
- data/lib/quant/ticks/serializers/spot.rb +1 -4
- data/lib/quant/ticks/serializers/tick.rb +3 -3
- data/lib/quant/ticks/spot.rb +7 -7
- data/lib/quant/ticks/tick.rb +22 -9
- data/lib/quant/version.rb +1 -1
- data/lib/quantitative.rb +2 -2
- metadata +9 -5
- data/lib/quant/mixins/weighted_average.rb +0 -26
- data/lib/quant/security.rb +0 -80
@@ -16,13 +16,44 @@ module Quant
|
|
16
16
|
from(hash, tick_class: tick_class)
|
17
17
|
end
|
18
18
|
|
19
|
+
# Instantiates a tick from a +Hash+. The hash keys are expected to be the same as the serialized keys.
|
20
|
+
#
|
21
|
+
# Serialized Keys:
|
22
|
+
# - ot: open timestamp
|
23
|
+
# - ct: close timestamp
|
24
|
+
# - o: open price
|
25
|
+
# - h: high price
|
26
|
+
# - l: low price
|
27
|
+
# - c: close price
|
28
|
+
# - bv: base volume
|
29
|
+
# - tv: target volume
|
30
|
+
# - t: trades
|
31
|
+
# - g: green
|
32
|
+
# - j: doji
|
33
|
+
def self.from(hash, tick_class:)
|
34
|
+
tick_class.new \
|
35
|
+
open_timestamp: hash["ot"],
|
36
|
+
close_timestamp: hash["ct"],
|
37
|
+
|
38
|
+
open_price: hash["o"],
|
39
|
+
high_price: hash["h"],
|
40
|
+
low_price: hash["l"],
|
41
|
+
close_price: hash["c"],
|
42
|
+
|
43
|
+
base_volume: hash["bv"],
|
44
|
+
target_volume: hash["tv"],
|
45
|
+
|
46
|
+
trades: hash["t"],
|
47
|
+
green: hash["g"],
|
48
|
+
doji: hash["j"]
|
49
|
+
end
|
50
|
+
|
19
51
|
# Returns a +Hash+ of the Spot tick's key properties
|
20
52
|
#
|
21
53
|
# Serialized Keys:
|
22
54
|
#
|
23
55
|
# - ot: open timestamp
|
24
56
|
# - ct: close timestamp
|
25
|
-
# - iv: interval
|
26
57
|
# - o: open price
|
27
58
|
# - h: high price
|
28
59
|
# - l: low price
|
@@ -37,12 +68,11 @@ module Quant
|
|
37
68
|
# @return [Hash]
|
38
69
|
# @example
|
39
70
|
# Quant::Ticks::Serializers::Tick.to_h(tick)
|
40
|
-
# # => { "ot" => [Time], "ct" => [Time], "
|
71
|
+
# # => { "ot" => [Time], "ct" => [Time], "o" => 1.0, "h" => 2.0,
|
41
72
|
# # "l" => 0.5, "c" => 1.5, "bv" => 6.0, "tv" => 5.0, "t" => 1, "g" => true, "j" => true }
|
42
73
|
def self.to_h(tick)
|
43
74
|
{ "ot" => tick.open_timestamp,
|
44
75
|
"ct" => tick.close_timestamp,
|
45
|
-
"iv" => tick.interval.to_s,
|
46
76
|
|
47
77
|
"o" => tick.open_price,
|
48
78
|
"h" => tick.high_price,
|
@@ -56,25 +86,6 @@ module Quant
|
|
56
86
|
"g" => tick.green,
|
57
87
|
"j" => tick.doji }
|
58
88
|
end
|
59
|
-
|
60
|
-
def self.from(hash, tick_class:)
|
61
|
-
tick_class.new \
|
62
|
-
open_timestamp: hash["ot"],
|
63
|
-
close_timestamp: hash["ct"],
|
64
|
-
interval: hash["iv"],
|
65
|
-
|
66
|
-
open_price: hash["o"],
|
67
|
-
high_price: hash["h"],
|
68
|
-
low_price: hash["l"],
|
69
|
-
close_price: hash["c"],
|
70
|
-
|
71
|
-
base_volume: hash["bv"],
|
72
|
-
target_volume: hash["tv"],
|
73
|
-
|
74
|
-
trades: hash["t"],
|
75
|
-
green: hash["g"],
|
76
|
-
doji: hash["j"]
|
77
|
-
end
|
78
89
|
end
|
79
90
|
end
|
80
91
|
end
|
@@ -21,7 +21,6 @@ module Quant
|
|
21
21
|
# Serialized Keys:
|
22
22
|
#
|
23
23
|
# - ct: close timestamp
|
24
|
-
# - iv: interval
|
25
24
|
# - cp: close price
|
26
25
|
# - bv: base volume
|
27
26
|
# - tv: target volume
|
@@ -31,11 +30,10 @@ module Quant
|
|
31
30
|
# @return [Hash]
|
32
31
|
# @example
|
33
32
|
# Quant::Ticks::Serializers::Tick.to_h(tick)
|
34
|
-
# # => { "ct" => "2024-02-13 03:12:23 UTC", "cp" => 5.0, "
|
33
|
+
# # => { "ct" => "2024-02-13 03:12:23 UTC", "cp" => 5.0, "bv" => 0.0, "tv" => 0.0, "t" => 0 }
|
35
34
|
def self.to_h(tick)
|
36
35
|
{ "ct" => tick.close_timestamp,
|
37
36
|
"cp" => tick.close_price,
|
38
|
-
"iv" => tick.interval.to_s,
|
39
37
|
"bv" => tick.base_volume,
|
40
38
|
"tv" => tick.target_volume,
|
41
39
|
"t" => tick.trades }
|
@@ -45,7 +43,6 @@ module Quant
|
|
45
43
|
tick_class.new(
|
46
44
|
close_timestamp: hash["ct"],
|
47
45
|
close_price: hash["cp"],
|
48
|
-
interval: hash["iv"],
|
49
46
|
base_volume: hash["bv"],
|
50
47
|
target_volume: hash["tv"],
|
51
48
|
trades: hash["t"]
|
@@ -3,10 +3,10 @@
|
|
3
3
|
module Quant
|
4
4
|
module Ticks
|
5
5
|
module Serializers
|
6
|
-
# The
|
7
|
-
# These classes are wired into the Tick classes and are used to convert
|
6
|
+
# The {Quant::Ticks::Tick} class serializes and deserializes {Quant::Ticks::Tick} objects to and from various formats.
|
7
|
+
# These classes are wired into the Tick classes and are used to convert {Quant::Ticks::Tick} objects to and from
|
8
8
|
# Ruby hashes, JSON strings, and CSV strings. They're not typically used directly, but are extracted
|
9
|
-
# to make it easier to provide custom serialization and deserialization for
|
9
|
+
# to make it easier to provide custom serialization and deserialization for {Quant::Ticks::Tick} objects not shipped
|
10
10
|
# with the library.
|
11
11
|
# @abstract
|
12
12
|
class Tick
|
data/lib/quant/ticks/spot.rb
CHANGED
@@ -4,7 +4,7 @@ require_relative "tick"
|
|
4
4
|
|
5
5
|
module Quant
|
6
6
|
module Ticks
|
7
|
-
# A +Spot+ is a single price point in time. It is the most basic form of a
|
7
|
+
# A +Spot+ is a single price point in time. It is the most basic form of a {Quant::Ticks::Tick} and is usually used to represent
|
8
8
|
# a continuously streaming tick that just has a single price point at a given point in time.
|
9
9
|
# @example
|
10
10
|
# spot = Quant::Ticks::Spot.new(price: 100.0, timestamp: Time.now)
|
@@ -19,9 +19,9 @@ module Quant
|
|
19
19
|
class Spot < Tick
|
20
20
|
include TimeMethods
|
21
21
|
|
22
|
-
attr_reader :
|
22
|
+
attr_reader :series
|
23
23
|
attr_reader :close_timestamp, :open_timestamp
|
24
|
-
attr_reader :
|
24
|
+
attr_reader :close_price
|
25
25
|
attr_reader :base_volume, :target_volume, :trades
|
26
26
|
|
27
27
|
def initialize(
|
@@ -30,7 +30,6 @@ module Quant
|
|
30
30
|
close_price: nil,
|
31
31
|
close_timestamp: nil,
|
32
32
|
volume: nil,
|
33
|
-
interval: nil,
|
34
33
|
base_volume: nil,
|
35
34
|
target_volume: nil,
|
36
35
|
trades: nil
|
@@ -39,8 +38,6 @@ module Quant
|
|
39
38
|
|
40
39
|
@close_price = (close_price || price).to_f
|
41
40
|
|
42
|
-
@interval = Interval[interval]
|
43
|
-
|
44
41
|
@close_timestamp = extract_time(timestamp || close_timestamp || Quant.current_time)
|
45
42
|
@open_timestamp = @close_timestamp
|
46
43
|
|
@@ -53,6 +50,9 @@ module Quant
|
|
53
50
|
|
54
51
|
alias timestamp close_timestamp
|
55
52
|
alias price close_price
|
53
|
+
alias high_price close_price
|
54
|
+
alias low_price close_price
|
55
|
+
alias open_price close_price
|
56
56
|
alias oc2 close_price
|
57
57
|
alias hl2 close_price
|
58
58
|
alias hlc3 close_price
|
@@ -73,7 +73,7 @@ module Quant
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def inspect
|
76
|
-
"#<#{self.class.name}
|
76
|
+
"#<#{self.class.name} ct=#{close_timestamp} c=#{close_price.to_f} v=#{volume}>"
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
data/lib/quant/ticks/tick.rb
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
module Quant
|
4
4
|
module Ticks
|
5
|
-
#
|
5
|
+
# {Quant::Ticks::Tick} is the abstract ancestor for all Ticks and holds the logic for interacting with series and indicators.
|
6
6
|
# The public interface is devoid of properties around price, volume, and timestamp, etc. Descendant classes
|
7
7
|
# are responsible for defining the properties and how they are represented.
|
8
8
|
#
|
9
|
-
# The
|
10
|
-
# once a
|
9
|
+
# The {Quant::Ticks::Tick} class is designed to be immutable and is intended to be used as a value object. This means that
|
10
|
+
# once a {Quant::Ticks::Tick} is created, it cannot be changed. This is important for the integrity of the series and
|
11
11
|
# indicators that depend on the ticks within the series.
|
12
12
|
#
|
13
13
|
# When a tick is added to a series, it is locked into the series and ownership cannot be changed. This is important
|
@@ -17,7 +17,7 @@ module Quant
|
|
17
17
|
#
|
18
18
|
# Ticks can be serialized to and from Ruby Hash, JSON strings, and CSV strings.
|
19
19
|
class Tick
|
20
|
-
# Returns a
|
20
|
+
# Returns a {Quant::Ticks::Tick} from a Ruby +Hash+. The default serializer is used to generate the {Quant::Ticks::Tick}.
|
21
21
|
# @param hash [Hash]
|
22
22
|
# @param serializer_class [Class] The serializer class to use for the conversion.
|
23
23
|
# @return [Quant::Ticks::Tick]
|
@@ -25,11 +25,12 @@ module Quant
|
|
25
25
|
# hash = { "timestamp" => "2018-01-01 12:00:00 UTC", "price" => 100.0, "volume" => 1000 }
|
26
26
|
# Quant::Ticks::Tick.from(hash)
|
27
27
|
# # => #<Quant::Ticks::Spot:0x00007f9e3b8b3e08 @timestamp=2018-01-01 12:00:00 UTC, @price=100.0, @volume=1000>
|
28
|
-
def self.from(hash, serializer_class:
|
28
|
+
def self.from(hash, serializer_class: nil)
|
29
|
+
serializer_class ||= default_serializer_class
|
29
30
|
serializer_class.from(hash, tick_class: self)
|
30
31
|
end
|
31
32
|
|
32
|
-
# Returns a
|
33
|
+
# Returns a {Quant::Ticks::Tick} from a JSON string. The default serializer is used to generate the {Quant::Ticks::Tick}.
|
33
34
|
# @param json [String]
|
34
35
|
# @param serializer_class [Class] The serializer class to use for the conversion.
|
35
36
|
# @return [Quant::Ticks::Tick]
|
@@ -41,21 +42,33 @@ module Quant
|
|
41
42
|
serializer_class.from_json(json, tick_class: self)
|
42
43
|
end
|
43
44
|
|
44
|
-
attr_reader :series
|
45
|
+
attr_reader :series, :indicators
|
45
46
|
|
46
47
|
def initialize
|
47
48
|
# Set the series by appending to the series or calling #assign_series method
|
48
49
|
@series = nil
|
50
|
+
@interval = nil
|
51
|
+
@indicators = {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def interval
|
55
|
+
@series&.interval || Interval[nil]
|
49
56
|
end
|
50
57
|
|
51
58
|
# Ticks always belong to the first series they're assigned so we can easily spin off
|
52
59
|
# sub-sets or new series with the same ticks while allowing each series to have
|
53
60
|
# its own state and full control over the ticks within its series
|
54
61
|
def assign_series(new_series)
|
55
|
-
assign_series!(new_series)
|
62
|
+
assign_series!(new_series) unless series?
|
56
63
|
self
|
57
64
|
end
|
58
65
|
|
66
|
+
# Returns true if the tick is assigned to a series. The first series a tick is assigned
|
67
|
+
# to is the series against which the indicators compute.
|
68
|
+
def series?
|
69
|
+
!!@series
|
70
|
+
end
|
71
|
+
|
59
72
|
# Ticks always belong to the first series they're assigned so we can easily spin off
|
60
73
|
# sub-sets or new series with the same ticks. However, if you need to reassign the
|
61
74
|
# series, you can use this method to force the change of series ownership.
|
@@ -63,7 +76,7 @@ module Quant
|
|
63
76
|
# The series interval is also assigned to the tick if it is not already set.
|
64
77
|
def assign_series!(new_series)
|
65
78
|
@series = new_series
|
66
|
-
@interval
|
79
|
+
@interval ||= new_series.interval
|
67
80
|
self
|
68
81
|
end
|
69
82
|
|
data/lib/quant/version.rb
CHANGED
data/lib/quantitative.rb
CHANGED
@@ -12,6 +12,6 @@ quant_folder = File.join(lib_folder, "quant")
|
|
12
12
|
Dir.glob(File.join(quant_folder, "*.rb")).each { |fn| require fn }
|
13
13
|
|
14
14
|
# require sub-folders and their sub-folders
|
15
|
-
%w(refinements settings ticks indicators).each do |sub_folder|
|
15
|
+
%w(refinements mixins settings ticks indicators).each do |sub_folder|
|
16
16
|
Dir.glob(File.join(quant_folder, sub_folder, "**/*.rb")).each { |fn| require fn }
|
17
|
-
end
|
17
|
+
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.6
|
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-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oj
|
@@ -44,25 +44,29 @@ files:
|
|
44
44
|
- LICENSE
|
45
45
|
- README.md
|
46
46
|
- Rakefile
|
47
|
+
- lib/quant/asset.rb
|
48
|
+
- lib/quant/asset_class.rb
|
49
|
+
- lib/quant/attributes.rb
|
47
50
|
- lib/quant/config.rb
|
48
51
|
- lib/quant/errors.rb
|
49
52
|
- lib/quant/indicators.rb
|
50
53
|
- lib/quant/indicators/indicator.rb
|
51
54
|
- lib/quant/indicators/indicator_point.rb
|
52
55
|
- lib/quant/indicators/ma.rb
|
56
|
+
- lib/quant/indicators/ping.rb
|
57
|
+
- lib/quant/indicators_proxy.rb
|
58
|
+
- lib/quant/indicators_sources.rb
|
53
59
|
- lib/quant/interval.rb
|
54
60
|
- lib/quant/mixins/direction.rb
|
55
61
|
- lib/quant/mixins/filters.rb
|
56
62
|
- lib/quant/mixins/fisher_transform.rb
|
57
63
|
- lib/quant/mixins/high_pass_filter.rb
|
58
64
|
- lib/quant/mixins/hilbert_transform.rb
|
65
|
+
- lib/quant/mixins/moving_averages.rb
|
59
66
|
- lib/quant/mixins/stochastic.rb
|
60
67
|
- lib/quant/mixins/super_smoother.rb
|
61
68
|
- lib/quant/mixins/trig.rb
|
62
|
-
- lib/quant/mixins/weighted_average.rb
|
63
69
|
- lib/quant/refinements/array.rb
|
64
|
-
- lib/quant/security.rb
|
65
|
-
- lib/quant/security_class.rb
|
66
70
|
- lib/quant/series.rb
|
67
71
|
- lib/quant/settings.rb
|
68
72
|
- lib/quant/settings/indicators.rb
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Quant
|
4
|
-
module Mixins
|
5
|
-
module WeightedAverage
|
6
|
-
def weighted_average(source)
|
7
|
-
value = source.is_a?(Symbol) ? p0.send(source) : source
|
8
|
-
[4.0 * value,
|
9
|
-
3.0 * p1.send(source),
|
10
|
-
2.0 * p2.send(source),
|
11
|
-
p3.send(source),].sum / 10.0
|
12
|
-
end
|
13
|
-
|
14
|
-
def extended_weighted_average(source)
|
15
|
-
value = source.is_a?(Symbol) ? p0.send(source) : source
|
16
|
-
[7.0 * value,
|
17
|
-
6.0 * p1.send(source),
|
18
|
-
5.0 * p2.send(source),
|
19
|
-
4.0 * p3.send(source),
|
20
|
-
3.0 * prev(4).send(source),
|
21
|
-
2.0 * prev(5).send(source),
|
22
|
-
prev(6).send(source),].sum / 28.0
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/quant/security.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "security_class"
|
4
|
-
|
5
|
-
module Quant
|
6
|
-
# A +Security+ is a representation of a financial instrument such as a stock, option, future, or currency.
|
7
|
-
# It is used to represent the instrument that is being traded, analyzed, or managed.
|
8
|
-
# @example
|
9
|
-
# security = Quant::Security.new(symbol: "AAPL", name: "Apple Inc.", security_class: :stock, exchange: "NASDAQ")
|
10
|
-
# security.symbol # => "AAPL"
|
11
|
-
# security.name # => "Apple Inc."
|
12
|
-
# security.stock? # => true
|
13
|
-
# security.option? # => false
|
14
|
-
# security.future? # => false
|
15
|
-
# security.currency? # => false
|
16
|
-
# security.exchange # => "NASDAQ"
|
17
|
-
# security.to_h # => { "s" => "AAPL", "n" => "Apple Inc.", "sc" => "stock", "x" => "NASDAQ" }
|
18
|
-
class Security
|
19
|
-
attr_reader :symbol, :name, :security_class, :id, :exchange, :source, :meta, :created_at, :updated_at
|
20
|
-
|
21
|
-
def initialize(
|
22
|
-
symbol:,
|
23
|
-
name: nil,
|
24
|
-
id: nil,
|
25
|
-
active: true,
|
26
|
-
tradeable: true,
|
27
|
-
exchange: nil,
|
28
|
-
source: nil,
|
29
|
-
security_class: nil,
|
30
|
-
created_at: Quant.current_time,
|
31
|
-
updated_at: Quant.current_time,
|
32
|
-
meta: {}
|
33
|
-
)
|
34
|
-
raise ArgumentError, "symbol is required" unless symbol
|
35
|
-
|
36
|
-
@symbol = symbol.to_s.upcase
|
37
|
-
@name = name
|
38
|
-
@id = id
|
39
|
-
@tradeable = tradeable
|
40
|
-
@active = active
|
41
|
-
@exchange = exchange
|
42
|
-
@source = source
|
43
|
-
@security_class = SecurityClass.new(security_class)
|
44
|
-
@created_at = created_at
|
45
|
-
@updated_at = updated_at
|
46
|
-
@meta = meta
|
47
|
-
end
|
48
|
-
|
49
|
-
def active?
|
50
|
-
!!@active
|
51
|
-
end
|
52
|
-
|
53
|
-
def tradeable?
|
54
|
-
!!@tradeable
|
55
|
-
end
|
56
|
-
|
57
|
-
SecurityClass::CLASSES.each do |class_name|
|
58
|
-
define_method("#{class_name}?") do
|
59
|
-
security_class == class_name
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def to_h(full: false)
|
64
|
-
return { "s" => symbol } unless full
|
65
|
-
|
66
|
-
{ "s" => symbol,
|
67
|
-
"n" => name,
|
68
|
-
"id" => id,
|
69
|
-
"t" => tradeable?,
|
70
|
-
"a" => active?,
|
71
|
-
"x" => exchange,
|
72
|
-
"sc" => security_class.to_s,
|
73
|
-
"src" => source.to_s }
|
74
|
-
end
|
75
|
-
|
76
|
-
def to_json(*args, full: false)
|
77
|
-
Oj.dump(to_h(full: full), *args)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|