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.
@@ -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], "iv" => "1m", "o" => 1.0, "h" => 2.0,
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, "iv" => "1d", "bv" => 0.0, "tv" => 0.0, "t" => 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 +Tick+ class serializes and deserializes +Tick+ objects to and from various formats.
7
- # These classes are wired into the Tick classes and are used to convert +Tick+ objects to and from
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 +Tick+ objects not shipped
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
@@ -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 +Tick+ and is usually used to represent
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 :interval, :series
22
+ attr_reader :series
23
23
  attr_reader :close_timestamp, :open_timestamp
24
- attr_reader :open_price, :high_price, :low_price, :close_price
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} #{interval} ct=#{close_timestamp} c=#{close_price.to_f} v=#{volume}>"
76
+ "#<#{self.class.name} ct=#{close_timestamp} c=#{close_price.to_f} v=#{volume}>"
77
77
  end
78
78
  end
79
79
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Quant
4
4
  module Ticks
5
- # +Tick+ is the abstract ancestor for all Ticks and holds the logic for interacting with series and indicators.
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 +Tick+ class is designed to be immutable and is intended to be used as a value object. This means that
10
- # once a +Tick+ is created, it cannot be changed. This is important for the integrity of the series and
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 +Tick+ from a Ruby +Hash+. The default serializer is used to generate the +Tick+.
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: default_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 +Tick+ from a JSON string. The default serializer is used to generate the +Tick+.
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) if @series.nil?
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 = new_series.interval if @interval.nil?
79
+ @interval ||= new_series.interval
67
80
  self
68
81
  end
69
82
 
data/lib/quant/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quant
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.6"
5
5
  end
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
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-21 00:00:00.000000000 Z
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
@@ -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