Class: Quant::Series

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Enumerable
Defined in:
lib/quant/series.rb

Overview

Ticks belong to the first series they’re associated with always. There are no provisions for series merging their ticks to one series! Indicators will be computed against the parent series of a list of ticks, so we can safely work with subsets of a series and indicators will compute just once.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(symbol:, interval:) ⇒ Series

Returns a new instance of Series.



57
58
59
60
61
# File 'lib/quant/series.rb', line 57

def initialize(symbol:, interval:)
  @symbol = symbol
  @interval = Interval[interval]
  @ticks = []
end

Instance Attribute Details

#intervalObject (readonly)

Returns the value of attribute interval.



55
56
57
# File 'lib/quant/series.rb', line 55

def interval
  @interval
end

#symbolObject (readonly)

Returns the value of attribute symbol.



55
56
57
# File 'lib/quant/series.rb', line 55

def symbol
  @symbol
end

#ticksObject (readonly)

Returns the value of attribute ticks.



55
56
57
# File 'lib/quant/series.rb', line 55

def ticks
  @ticks
end

Class Method Details

.from_file(filename:, symbol:, interval:, serializer_class: nil) ⇒ Object

Loads a series of ticks when each line is a parsible JSON string that represents a tick. A Ticks::TickSerializer may be passed to convert the parsed JSON to Ticks::Tick object.

Parameters:

  • filename (String)

    The filename to load the ticks from.

  • symbol (String)

    The symbol of the series.

  • interval (String)

    The interval of the series.

  • serializer_class (Class) (defaults to: nil)

    Ticks::TickSerializer class to use for the conversion.



18
19
20
21
22
23
# File 'lib/quant/series.rb', line 18

def self.from_file(filename:, symbol:, interval:, serializer_class: nil)
  raise "File #{filename} does not exist" unless File.exist?(filename)

  ticks = File.read(filename).split("\n").map{ |line| Oj.load(line) }
  from_hash symbol:, interval:, hash: ticks, serializer_class:
end

.from_hash(symbol:, interval:, hash:, serializer_class: nil) ⇒ Object

Loads a series of ticks where the hash must be cast to an array of Ticks::Tick objects.

Parameters:

  • symbol (String)

    The symbol of the series.

  • interval (String)

    The interval of the series.

  • hash (Array<Hash>)

    The array of hashes to convert to Ticks::Tick objects.

  • serializer_class (Class) (defaults to: nil)

    Ticks::TickSerializer class to use for the conversion.



41
42
43
44
# File 'lib/quant/series.rb', line 41

def self.from_hash(symbol:, interval:, hash:, serializer_class: nil)
  ticks = hash.map { |tick_hash| Quant::Ticks::OHLC.from(tick_hash, serializer_class:) }
  from_ticks symbol:, interval:, ticks:
end

.from_json(symbol:, interval:, json:, serializer_class: nil) ⇒ Object

Loads a series of ticks when the JSON string represents an array of ticks. A Ticks::TickSerializer may be passed to convert the parsed JSON to Ticks::Tick object.

Parameters:

  • symbol (String)

    The symbol of the series.

  • interval (String)

    The interval of the series.

  • json (String)

    The JSON string to parse into ticks.

  • serializer_class (Class) (defaults to: nil)

    Ticks::TickSerializer class to use for the conversion.



31
32
33
34
# File 'lib/quant/series.rb', line 31

def self.from_json(symbol:, interval:, json:, serializer_class: nil)
  hash = Oj.load(json)
  from_hash symbol:, interval:, hash:, serializer_class:
end

.from_ticks(symbol:, interval:, ticks:) ⇒ Object

Loads a series of ticks where the array represents an array of Ticks::Tick objects.



47
48
49
50
51
52
53
# File 'lib/quant/series.rb', line 47

def self.from_ticks(symbol:, interval:, ticks:)
  ticks = ticks.sort_by(&:close_timestamp)

  new(symbol:, interval:).tap do |series|
    ticks.each { |tick| series << tick }
  end
end

Instance Method Details

#<<(tick) ⇒ Object



118
119
120
121
122
123
# File 'lib/quant/series.rb', line 118

def <<(tick)
  tick = Ticks::Spot.new(price: tick) if tick.is_a?(Numeric)
  indicators << tick unless tick.series?
  @ticks << tick.assign_series(self)
  self
end

#==(other) ⇒ Object



92
93
94
# File 'lib/quant/series.rb', line 92

def ==(other)
  [symbol, interval, ticks] == [other.symbol, other.interval, other.ticks]
end

#dupObject



96
97
98
# File 'lib/quant/series.rb', line 96

def dup
  self.class.from_ticks(symbol:, interval:, ticks:)
end

#highestObject



84
85
86
# File 'lib/quant/series.rb', line 84

def highest
  ticks.max_by(&:high_price)
end

#indicatorsObject



125
126
127
# File 'lib/quant/series.rb', line 125

def indicators
  @indicators ||= IndicatorsSources.new(series: self)
end

#inspectObject



100
101
102
# File 'lib/quant/series.rb', line 100

def inspect
  "#<#{self.class.name} symbol=#{symbol} interval=#{interval} ticks=#{ticks.size}>"
end

#limit(period) ⇒ Object



70
71
72
73
74
75
# File 'lib/quant/series.rb', line 70

def limit(period)
  selected_ticks = ticks.select{ |tick| period.cover?(tick.close_timestamp) }
  return self if selected_ticks.size == ticks.size

  self.class.from_ticks(symbol:, interval:, ticks: selected_ticks)
end

#limit_iterations(start_iteration, stop_iteration) ⇒ Object



63
64
65
66
67
68
# File 'lib/quant/series.rb', line 63

def limit_iterations(start_iteration, stop_iteration)
  selected_ticks = ticks[start_iteration..stop_iteration]
  return self if selected_ticks.size == ticks.size

  self.class.from_ticks(symbol:, interval:, ticks: selected_ticks)
end

#lowestObject



88
89
90
# File 'lib/quant/series.rb', line 88

def lowest
  ticks.min_by(&:low_price)
end

#new_indicator(indicator) ⇒ Object

When the first indicator is instantiated, it will also lead to instantiating the dominant cycle indicator. The ‘new_indicator_lock` prevents reentrant calls to the `new_indicator` method with infinite recursion.



107
108
109
110
111
112
113
114
115
116
# File 'lib/quant/series.rb', line 107

def new_indicator(indicator)
  return if @new_indicator_lock

  begin
    @new_indicator_lock = true
    indicators.new_indicator(indicator)
  ensure
    @new_indicator_lock = false
  end
end

#to_hObject



129
130
131
132
133
# File 'lib/quant/series.rb', line 129

def to_h
  { "symbol" => symbol,
    "interval" => interval.to_s,
    "ticks" => ticks.map(&:to_h) }
end

#to_json(*args) ⇒ Object



135
136
137
# File 'lib/quant/series.rb', line 135

def to_json(*args)
  Oj.dump(to_h, *args)
end