Class: SQA::Stock

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/sqa/stock.rb

Overview

SMELL: SQA::Stock is now pretty coupled to the Alpha Vantage

API service.  Should that stuff be extracted into a
separate class and injected by the requiring program?

Constant Summary collapse

CONNECTION =
Faraday.new(url: "https://www.alphavantage.co")
@@top =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ticker:, source: :alpha_vantage) ⇒ Stock

Returns a new instance of Stock.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/sqa/stock.rb', line 24

def initialize(
      ticker:,
      source: :alpha_vantage
    )

  @ticker     = ticker.downcase
  @source     = source

  raise "Invalid Ticker #{ticker}" unless SQA::Ticker.valid?(ticker)

  @data_path  = SQA.data_dir + "#{@ticker}.json"
  @df_path    = SQA.data_dir + "#{@ticker}.csv"

  @klass         = "SQA::DataFrame::#{@source.to_s.camelize}".constantize
  @transformers  = "SQA::DataFrame::#{@source.to_s.camelize}::TRANSFORMERS".constantize

  if @data_path.exist?
    load
  else
    create
    update
    save
  end

  update_the_dataframe
end

Instance Attribute Details

#dataObject

General Info – SQA::DataFrame::Data



13
14
15
# File 'lib/sqa/stock.rb', line 13

def data
  @data
end

#dfObject

Historical Prices – SQA::DataFrame::Data



14
15
16
# File 'lib/sqa/stock.rb', line 14

def df
  @df
end

#klassObject

class of historical and current prices



16
17
18
# File 'lib/sqa/stock.rb', line 16

def klass
  @klass
end

#strategyObject

Holds the SQA::Strategy class name which seems to work the best for this stock.



21
22
23
# File 'lib/sqa/stock.rb', line 21

def strategy
  @strategy
end

#transformersObject

procs for changing column values from String to Numeric



17
18
19
# File 'lib/sqa/stock.rb', line 17

def transformers
  @transformers
end

Class Method Details

.topObject

Top Gainers, Losers and Most Active for most recent closed trading day.



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/sqa/stock.rb', line 187

def top
  return @@top unless @@top.nil?

  a_hash  = JSON.parse(
              CONNECTION.get(
                "/query?function=TOP_GAINERS_LOSERS&apikey=#{SQA.av.key}"
              ).to_hash[:body]
            )

  mash = Hashie::Mash.new(a_hash)

  keys = mash.top_gainers.first.keys

  %w[top_gainers top_losers most_actively_traded].each do |collection|
    mash.send(collection).each do |e|
      keys.each do |k|
        case k
        when 'ticker'
          # Leave it as a String
        when 'volume'
          e[k] = e[k].to_i
        else
          e[k] = e[k].to_f
        end
      end
    end
  end

  @@top = mash
end

Instance Method Details

#associate_best_strategy(strategies) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/sqa/stock.rb', line 154

def associate_best_strategy(strategies)
  best_strategy = nil
  best_accuracy = 0

  strategies.each do |strategy|
    accuracy = evaluate_strategy(strategy)

    if accuracy > best_accuracy
      best_strategy = strategy
      best_accuracy = accuracy
    end
  end

  self.strategy = best_strategy
end

#createObject



59
60
61
62
63
64
65
66
67
68
# File 'lib/sqa/stock.rb', line 59

def create
  @data =
    SQA::DataFrame::Data.new(
      {
        ticker:       @ticker,
        source:       @source,
        indicators:   { xyzzy: "Magic" },
      }
    )
end

#evaluate_strategy(strategy) ⇒ Object



171
172
173
174
# File 'lib/sqa/stock.rb', line 171

def evaluate_strategy(strategy)
  # TODO: Implement this method to evaluate the accuracy of the strategy
  #       on the historical data of this stock.
end

#loadObject



52
53
54
55
56
# File 'lib/sqa/stock.rb', line 52

def load
  @data = SQA::DataFrame::Data.new(
            JSON.parse(@data_path.read)
          )
end

#merge_overviewObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/sqa/stock.rb', line 123

def merge_overview
  temp = JSON.parse(
    CONNECTION.get("/query?function=OVERVIEW&symbol=#{ticker.upcase}&apikey=#{SQA.av.key}")
      .to_hash[:body]
  )

  if temp.has_key? "Information"
    ApiError.raise(temp["Information"])
  end

  # TODO: CamelCase hash keys look common in Alpha Vantage
  #       JSON; look at making a special Hashie-based class
  #       to convert the keys to normal Ruby standards.

  temp2 = {}

  string_values = %w[ address asset_type cik country currency
                      description dividend_date ex_dividend_date
                      exchange fiscal_year_end industry latest_quarter
                      name sector symbol
                    ]

  temp.keys.each do |k|
    new_k         = k.underscore
    temp2[new_k]  = string_values.include?(new_k) ? temp[k] : temp[k].to_f
  end

  @data.overview = temp2
end

#saveObject



76
77
78
# File 'lib/sqa/stock.rb', line 76

def save
  @data_path.write @data.to_json
end

#to_sObject Also known as: inspect



117
118
119
# File 'lib/sqa/stock.rb', line 117

def to_s
  "#{ticker} with #{@df.size} data points from #{@df.timestamp.first} to #{@df.timestamp.last}"
end

#updateObject



71
72
73
# File 'lib/sqa/stock.rb', line 71

def update
  merge_overview
end

#update_the_dataframeObject



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/sqa/stock.rb', line 91

def update_the_dataframe
  if @df_path.exist?
    @df     = SQA::DataFrame.load(
      source:       @df_path,
      transformers: @transformers
    )
  else
    @df     = klass.recent(@ticker, full: true)
    @df.to_csv(@df_path)
    return
  end

  from_date = Date.parse(@df.timestamp.last) + 1
  df2       = klass.recent(@ticker, from_date: from_date)

  return if df2.nil?  # CSV file is up to date.

  df_nrows  = @df.nrows
  @df.append!(df2)

  if @df.nrows > df_nrows
    @df.to_csv(@df_path)
  end
end