quantitative 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -39,13 +39,13 @@
39
39
  # pp series
40
40
  #
41
41
  module Quant
42
- # +Quant::Interval+ abstracts away the concept of ticks (candles, bars, etc.) and their duration and offers some basic utilities for
43
- # working with multiple timeframes. Intervals are used in +Tick+ and +Series+ classes to define the duration of the ticks.
42
+ # {Quant::Interval} abstracts away the concept of ticks (candles, bars, etc.) and their duration and offers some basic utilities for
43
+ # working with multiple timeframes. Intervals are used in {Quant::Ticks::Tick} and {Quant::Series} classes to define the duration of the ticks.
44
44
  #
45
- # When the +Interval+ is unknown, it is set to +'na'+ (not available) and the duration is set to 0. The shorthand for this is
45
+ # When the {Quant::Interval} is unknown, it is set to +'na'+ (not available) and the duration is set to 0. The shorthand for this is
46
46
  # +Interval.na+. and +Interval[:na]+. and +Interval[nil]+.
47
47
  #
48
- # +Interval+ are instantiated in multple ways to support a wide variety of use-cases. Here's an example:
48
+ # {Quant::Interval} are instantiated in multple ways to support a wide variety of use-cases. Here's an example:
49
49
  # Quant::Interval.new("1d") # => #<Quant::Interval @interval="1d"> (daily interval)
50
50
  # Quant::Interval.new(:daily) # => #<Quant::Interval @interval="1d">
51
51
  # Quant::Interval[:daily] # => #<Quant::Interval @interval="1d">
@@ -122,7 +122,7 @@ module Quant
122
122
 
123
123
  # Instantiates an Interval from a resolution. For example, TradingView uses resolutions
124
124
  # like "1", "3", "5", "15", "30", "60", "240", "D", "1D" to represent the duration of a
125
- # candlestick. +from_resolution+ translates resolutions to the appropriate +Interval+.
125
+ # candlestick. +from_resolution+ translates resolutions to the appropriate {Quant::Interval}.
126
126
  def self.from_resolution(resolution)
127
127
  ensure_valid_resolution!(resolution)
128
128
 
@@ -130,7 +130,7 @@ module Quant
130
130
  end
131
131
 
132
132
  # Instantiates an Interval from a string or symbol. If the value is already
133
- # an +Interval+, it is returned as-is.
133
+ # an {Quant::Interval}, it is returned as-is.
134
134
  def self.[](value)
135
135
  return value if value.is_a? Interval
136
136
 
@@ -153,19 +153,28 @@ module Quant
153
153
  @interval = (interval || "na").to_s
154
154
  end
155
155
 
156
+ # Returns true when the duration of the interval is zero, such as for the `na` interval.
156
157
  def nil?
157
- interval == "na"
158
+ duration.zero?
158
159
  end
159
160
 
160
161
  def to_s
161
162
  interval
162
163
  end
163
164
 
165
+ # Returns the total span of seconds or duration for the interval.
166
+ # @example
167
+ # Quant::Interval.new("1d").duration => 86400
168
+ # Quant::Interval.new("1h").duration => 3600
169
+ # Quant::Interval.new("1m").duration => 60
170
+ # Quant::Interval.new("1s").duration => 1
171
+ # Quant::Interval.new("na").duration => 0
164
172
  def duration
165
173
  INTERVAL_DISTANCE[interval]
166
174
  end
167
175
  alias seconds duration
168
176
 
177
+ # Compares the interval to another interval, string, or symbol and returns true if they are equal.
169
178
  def ==(other)
170
179
  if other.is_a? String
171
180
  interval.to_s == other
@@ -176,13 +185,22 @@ module Quant
176
185
  end
177
186
  end
178
187
 
188
+ # Returns the number of ticks this interval represents per minute.
189
+ # @example
190
+ # Quant::Interval.new("1d").ticks_per_minute => 0.0006944444444444445
191
+ # Quant::Interval.new("1h").ticks_per_minute => 0.016666666666666666
192
+ # Quant::Interval.new("1m").ticks_per_minute => 1.0
193
+ # Quant::Interval.new("1s").ticks_per_minute => 60.0
179
194
  def ticks_per_minute
180
195
  60.0 / seconds
181
196
  end
182
197
 
198
+ # Returns the half-life of the interval in seconds.
199
+ # @example
200
+ # Quant::Interval.new("1d").half_life => 43200.0
201
+ # Quant::Interval.new("1h").half_life => 1800.0
202
+ # Quant::Interval.new("1m").half_life => 30.0
183
203
  def half_life
184
- raise "bad interval #{interval}" if duration.nil?
185
-
186
204
  duration / 2.0
187
205
  end
188
206
 
@@ -193,11 +211,16 @@ module Quant
193
211
  Interval.new intervals[intervals.index(interval) + 1] || intervals[-1]
194
212
  end
195
213
 
214
+ # Returns the full list of valid interval Strings that can be used to instantiate an {Quant::Interval}.
196
215
  def self.valid_intervals
197
216
  INTERVAL_DISTANCE.keys
198
217
  end
199
218
 
200
- # NOTE: if timestamp doesn't cover a full interval, it will be rounded up to 1
219
+ # Computes the number of ticks from present to given timestamp.
220
+ # If timestamp doesn't cover a full interval, it will be rounded up to 1
221
+ # @example
222
+ # interval = Quant::Interval.new("1d")
223
+ # interval.ticks_to(Time.now + 5.days) # => 5 NOTE: `5.days` is an ActiveSupport method
201
224
  def ticks_to(timestamp)
202
225
  ((timestamp - Quant.current_time) / duration).round(2).ceil
203
226
  end
@@ -209,7 +232,8 @@ module Quant
209
232
  def self.ensure_valid_resolution!(resolution)
210
233
  return if RESOLUTIONS.keys.include? resolution
211
234
 
212
- raise InvalidResolution, "resolution (#{resolution}) not a valid resolution. Should be one of: (#{RESOLUTIONS.keys.join(", ")})"
235
+ should_be_one_of = "Should be one of: (#{RESOLUTIONS.keys.join(", ")})"
236
+ raise Errors::InvalidResolution, "resolution (#{resolution}) not a valid resolution. #{should_be_one_of}"
213
237
  end
214
238
 
215
239
  private
@@ -221,7 +245,8 @@ module Quant
221
245
  def ensure_valid_interval!(interval)
222
246
  return if interval.nil? || valid_intervals.include?(interval.to_s)
223
247
 
224
- raise InvalidInterval, "interval (#{interval.inspect}) not a valid interval. Should be one of: (#{valid_intervals.join(", ")})"
248
+ should_be_one_of = "Should be one of: (#{valid_intervals.join(", ")})"
249
+ raise Errors::InvalidInterval, "interval (#{interval.inspect}) not a valid interval. #{should_be_one_of}"
225
250
  end
226
251
 
227
252
  def ensure_valid_resolution!(resolution)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "trig"
4
+
3
5
  module Quant
4
6
  module Mixins
5
7
  # 1. All the common filters useful for traders have a transfer response that can be written
@@ -1,6 +1,6 @@
1
1
  module Quant
2
2
  module Refinements
3
- # Refinements for the standard Ruby +Array+ class.
3
+ # Refinements for the standard Ruby {Quant::Array} class.
4
4
  # These refinements add statistical methods to the Array class as well as some optimizations that greatly
5
5
  # speed up some of the computations performed by the various indicators.
6
6
  #
@@ -67,7 +67,7 @@ module Quant
67
67
  end
68
68
 
69
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.
70
+ # That is, if the {Quant::Array} has 5 elements, prev(10) would return the first element in the array.
71
71
  #
72
72
  # @example
73
73
  # series = [1, 2, 3, 4]
@@ -89,11 +89,12 @@ module Quant
89
89
  # +max_size+, the first element is removed from the array.
90
90
  # This setting modifies :<< and :push methods.
91
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
92
+ # These guards are maybe unnecessary, but they are here until a use-case is found.
93
+ # Some indicators are built specifically against the +max_size+ of a given array.
94
+ # Adjusting the +max_size+ after the fact could lead to unexpected, unintended behavior.
95
+ raise Errors::ArrayMaxSizeError, "Cannot set max_size to nil." unless max_size
96
+ raise Errors::ArrayMaxSizeError, "The max_size can only be set once." if @max_size
97
+ raise Errors::ArrayMaxSizeError, "The size of Array #{size} exceeds max_size #{max_size}." if size > max_size
97
98
 
98
99
  @max_size = max_size
99
100
  self
data/lib/quant/series.rb CHANGED
@@ -3,34 +3,47 @@
3
3
  module Quant
4
4
  # Ticks belong to the first series they're associated with always.
5
5
  # There are no provisions for series merging their ticks to one series!
6
- # Indicators will be computed against the parent series of a list of ticks, so we
6
+ # {Indicators} will be computed against the parent series of a list of ticks, so we
7
7
  # can safely work with subsets of a series and indicators will compute just once.
8
8
  class Series
9
9
  include Enumerable
10
10
  extend Forwardable
11
11
 
12
- def self.from_file(filename:, symbol:, interval:, folder: nil)
13
- symbol = symbol.to_s.upcase
14
- interval = Interval[interval]
15
-
16
- filename = Rails.root.join("historical", folder, "#{symbol.upcase}.txt") if filename.nil?
12
+ # Loads a series of ticks when each line is a parsible JSON string that represents a tick.
13
+ # A {Quant::Ticks::TickSerializer} may be passed to convert the parsed JSON to {Quant::Ticks::Tick} object.
14
+ # @param filename [String] The filename to load the ticks from.
15
+ # @param symbol [String] The symbol of the series.
16
+ # @param interval [String] The interval of the series.
17
+ # @param serializer_class [Class] {Quant::Ticks::TickSerializer} class to use for the conversion.
18
+ def self.from_file(filename:, symbol:, interval:, serializer_class: nil)
17
19
  raise "File #{filename} does not exist" unless File.exist?(filename)
18
20
 
19
- lines = File.read(filename).split("\n")
20
- ticks = lines.map{ |line| Quant::Ticks::OHLC.from_json(line) }
21
-
22
- from_ticks(symbol: symbol, interval: interval, ticks: ticks)
21
+ ticks = File.read(filename).split("\n").map{ |line| Oj.load(line) }
22
+ from_hash symbol: symbol, interval: interval, hash: ticks, serializer_class: serializer_class
23
23
  end
24
24
 
25
- def self.from_json(symbol:, interval:, json:)
26
- from_hash symbol: symbol, interval: interval, hash: Oj.load(json)
25
+ # Loads a series of ticks when the JSON string represents an array of ticks.
26
+ # A {Quant::Ticks::TickSerializer} may be passed to convert the parsed JSON to {Quant::Ticks::Tick} object.
27
+ # @param symbol [String] The symbol of the series.
28
+ # @param interval [String] The interval of the series.
29
+ # @param json [String] The JSON string to parse into ticks.
30
+ # @param serializer_class [Class] {Quant::Ticks::TickSerializer} class to use for the conversion.
31
+ def self.from_json(symbol:, interval:, json:, serializer_class: nil)
32
+ ticks = Oj.load(json)
33
+ from_hash symbol: symbol, interval: interval, hash: ticks, serializer_class: serializer_class
27
34
  end
28
35
 
29
- def self.from_hash(symbol:, interval:, hash:)
30
- ticks = hash.map { |tick_hash| Quant::Ticks::OHLC.from(tick_hash) }
31
- from_ticks(symbol: symbol, interval: interval, ticks: ticks)
36
+ # Loads a series of ticks where the hash must be cast to an array of {Quant::Ticks::Tick} objects.
37
+ # @param symbol [String] The symbol of the series.
38
+ # @param interval [String] The interval of the series.
39
+ # @param hash [Array<Hash>] The array of hashes to convert to {Quant::Ticks::Tick} objects.
40
+ # @param serializer_class [Class] {Quant::Ticks::TickSerializer} class to use for the conversion.
41
+ def self.from_hash(symbol:, interval:, hash:, serializer_class: nil)
42
+ ticks = hash.map { |tick_hash| Quant::Ticks::OHLC.from(tick_hash, serializer_class: serializer_class) }
43
+ from_ticks symbol: symbol, interval: interval, ticks: ticks
32
44
  end
33
45
 
46
+ # Loads a series of ticks where the array represents an array of {Quant::Ticks::Tick} objects.
34
47
  def self.from_ticks(symbol:, interval:, ticks:)
35
48
  ticks = ticks.sort_by(&:close_timestamp)
36
49
 
@@ -43,7 +56,7 @@ module Quant
43
56
 
44
57
  def initialize(symbol:, interval:)
45
58
  @symbol = symbol
46
- @interval = interval
59
+ @interval = Interval[interval]
47
60
  @ticks = []
48
61
  end
49
62
 
@@ -64,11 +77,8 @@ module Quant
64
77
  def_delegator :@ticks, :[]
65
78
  def_delegator :@ticks, :size
66
79
  def_delegator :@ticks, :each
67
- def_delegator :@ticks, :select
68
80
  def_delegator :@ticks, :select!
69
- def_delegator :@ticks, :reject
70
81
  def_delegator :@ticks, :reject!
71
- def_delegator :@ticks, :first
72
82
  def_delegator :@ticks, :last
73
83
 
74
84
  def highest
@@ -92,7 +102,14 @@ module Quant
92
102
  end
93
103
 
94
104
  def <<(tick)
105
+ tick = Ticks::Spot.new(price: tick) if tick.is_a?(Numeric)
106
+ indicators << tick unless tick.series?
95
107
  @ticks << tick.assign_series(self)
108
+ self
109
+ end
110
+
111
+ def indicators
112
+ @indicators ||= IndicatorsSources.new(series: self)
96
113
  end
97
114
 
98
115
  def to_h
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quant
4
+ module Settings
5
+ # Indicator settings provide a way to configure the default settings for indicators.
6
+ # Many of the indicators are built in adaptive measuring of the dominant cycle and these settings
7
+ # provide a way to configure your choices for the indicators. The default values come from various
8
+ # papers and books on the subject of technical analysis by John Ehlers where he variously suggests
9
+ # a minimum period of 8 or 10 and a max period of 48.
10
+ #
11
+ # The half period is the average of the max_period and min_period.
12
+ # The micro period comes from Ehler's writings on Swami charts and auto-correlation computations, which
13
+ # is a period of 3 bars. It is useful enough in various indicators to be its own setting.
14
+ #
15
+ # The dominant cycle kind is the kind of dominant cycle to use in the indicator. The default is +:settings+
16
+ # which means the dominant cycle is whatever the +max_period+ is set to. It is not adaptive when configured
17
+ # this way. The other kinds are adaptive and are computed from the series data. The choices are:
18
+ # * +:settings+ - the max_period is the dominant cycle and is not adaptive
19
+ # * +:band_pass+ - The zero crossings of the band pass filter are used to compute the dominant cycle
20
+ # * +:auto_correlation_reversal+ - The dominant cycle is computed from the auto-correlation of the series.
21
+ # * +:homodyne+ - The dominant cycle is computed from the homodyne discriminator.
22
+ # * +:differential+ - The dominant cycle is computed from the differential discriminator.
23
+ # * +:phase_accumulator+ - The dominant cycle is computed from the phase accumulator.
24
+ #
25
+ # All of the above are adaptive and are computed from the series data and are described in John Ehlers' books
26
+ # and published papers.
27
+ #
28
+ # Pivot kinds are started as the classic pivot points and then expanded to include other kinds of bands that
29
+ # follow along with price action such as Donchian channels, Fibonacci bands, Bollinger bands, Keltner bands,
30
+ # etc. The choices are as follows:
31
+ # * +:pivot+ - Classic pivot points
32
+ # * +:donchian+ - Donchian channels
33
+ # * +:fibbonacci+ - Fibonacci bands
34
+ # * +:woodie+ - Woodie's pivot points
35
+ # * +:classic+ - Classic pivot points
36
+ # * +:camarilla+ - Camarilla pivot points
37
+ # * +:demark+ - Demark pivot points
38
+ # * +:murrey+ - Murrey math pivot points
39
+ # * +:keltner+ - Keltner bands
40
+ # * +:bollinger+ - Bollinger bands
41
+ # * +:guppy+ - Guppy bands
42
+ # * +:atr+ - ATR bands
43
+ #
44
+ class Indicators
45
+ # Returns an instance of the settings for indicators configured with defaults derived from
46
+ # defined constants in the +Quant::Settings+ module.
47
+ def self.defaults
48
+ new
49
+ end
50
+
51
+ attr_accessor :max_period, :min_period, :half_period, :micro_period
52
+ attr_accessor :dominant_cycle_kind, :pivot_kind
53
+
54
+ def initialize(**settings)
55
+ @max_period = settings[:max_period] || Settings::MAX_PERIOD
56
+ @min_period = settings[:min_period] || Settings::MIN_PERIOD
57
+ @half_period = settings[:half_period] || compute_half_period
58
+ @micro_period = settings[:micro_period] || Settings::MICRO_PERIOD
59
+
60
+ @dominant_cycle_kind = settings[:dominant_cycle_kind] || Settings::DOMINANT_CYCLE_KINDS.first
61
+ @pivot_kind = settings[:pivot_kind] || Settings::PIVOT_KINDS.first
62
+ end
63
+
64
+ def apply_settings(**settings)
65
+ @max_period = settings.fetch(:max_period, @max_period)
66
+ @min_period = settings.fetch(:min_period, @min_period)
67
+ @half_period = settings.fetch(:half_period, @half_period || compute_half_period)
68
+ @micro_period = settings.fetch(:micro_period, @micro_period)
69
+ @dominant_cycle_kind = settings.fetch(:dominant_cycle_kind, @dominant_cycle_kind)
70
+ @pivot_kind = settings.fetch(:pivot_kind, @pivot_kind)
71
+ end
72
+
73
+ def compute_half_period
74
+ (max_period + min_period) / 2
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,48 @@
1
+ module Quant
2
+ module Settings
3
+ MAX_PERIOD = 48
4
+ MIN_PERIOD = 10
5
+ HALF_PERIOD = (MAX_PERIOD + MIN_PERIOD) / 2
6
+ MICRO_PERIOD = 3
7
+
8
+ PIVOT_KINDS = %i(
9
+ pivot
10
+ donchian
11
+ fibbonacci
12
+ woodie
13
+ classic
14
+ camarilla
15
+ demark
16
+ murrey
17
+ keltner
18
+ bollinger
19
+ guppy
20
+ atr
21
+ ).freeze
22
+
23
+ DOMINANT_CYCLE_KINDS = %i(
24
+ settings
25
+ band_pass
26
+ auto_correlation_reversal
27
+ homodyne
28
+ differential
29
+ phase_accumulator
30
+ ).freeze
31
+
32
+ # ---- Risk Management Ratio Settings ----
33
+ # Risk Reward Breakeven Win Rate %
34
+ # 50 1 98%
35
+ # 10 1 91%
36
+ # 5 1 83%
37
+ # 3 1 75%
38
+ # 2 1 67%
39
+ # 1 1 50%
40
+ # 1 2 33%
41
+ # 1 3 25%
42
+ # 1 5 17%
43
+ # 1 10 9%
44
+ # 1 50 2%
45
+ PROFIT_TARGET_PCT = 0.03
46
+ STOP_LOSS_PCT = 0.01
47
+ end
48
+ end
@@ -4,14 +4,15 @@ require_relative "tick"
4
4
 
5
5
  module Quant
6
6
  module Ticks
7
- # An +OHLC+ is a bar or candle for a point in time that has an open, high, low, and close price.
8
- # It is the most common form of a +Tick+ and is usually used to representa time period such as a
9
- # minute, hour, day, week, or month. The +OHLC+ is used to represent the price action of an asset
10
- # The interval of the +OHLC+ is the time period that the +OHLC+ represents, such has hourly, daily, weekly, etc.
7
+ # An {Quant::Ticks::OHLC} is a bar or candle for a point in time that has an open, high, low, and close price.
8
+ # It is the most common form of a {Quant::Ticks::Tick} and is usually used to representa time period such as a
9
+ # minute, hour, day, week, or month. The {Quant::Ticks::OHLC} is used to represent the price action of an asset
10
+ # The interval of the {Quant::Ticks::OHLC} is the time period that the {Quant::Ticks::OHLC} represents,
11
+ # such has hourly, daily, weekly, etc.
11
12
  class OHLC < Tick
12
13
  include TimeMethods
13
14
 
14
- attr_reader :interval, :series
15
+ attr_reader :series
15
16
  attr_reader :close_timestamp, :open_timestamp
16
17
  attr_reader :open_price, :high_price, :low_price, :close_price
17
18
  attr_reader :base_volume, :target_volume, :trades
@@ -26,8 +27,6 @@ module Quant
26
27
  low_price:,
27
28
  close_price:,
28
29
 
29
- interval: nil,
30
-
31
30
  volume: nil,
32
31
  base_volume: nil,
33
32
  target_volume: nil,
@@ -44,8 +43,6 @@ module Quant
44
43
  @low_price = low_price.to_f
45
44
  @close_price = close_price.to_f
46
45
 
47
- @interval = Interval[interval]
48
-
49
46
  @base_volume = (volume || base_volume).to_i
50
47
  @target_volume = (target_volume || @base_volume).to_i
51
48
  @trades = trades.to_i
@@ -131,7 +128,7 @@ module Quant
131
128
  end
132
129
 
133
130
  def inspect
134
- "#<#{self.class.name} #{interval} ct=#{close_timestamp.iso8601} o=#{open_price} h=#{high_price} l=#{low_price} c=#{close_price} v=#{volume}>"
131
+ "#<#{self.class.name} ct=#{close_timestamp.iso8601} o=#{open_price} h=#{high_price} l=#{low_price} c=#{close_price} v=#{volume}>"
135
132
  end
136
133
  end
137
134
  end
@@ -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