quantitative 0.1.4 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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