sqa 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efdd7d63be5b0fd195ed3633a47759c701727e9dafdfd4f457ba6fec91f91426
4
- data.tar.gz: e0acdaf0f99e81412313e6b1e5bc7de9d884e36e39f0be01c4942aa601e3c916
3
+ metadata.gz: 9ed5a0bfe672b2a11993e5afd8ed8cbb103355f033c95e791c8c2ef57dbd8464
4
+ data.tar.gz: 308d76cd23b50c057816ed7cde0815a51b4bb5e196ea14e231fd68ad06fe76b5
5
5
  SHA512:
6
- metadata.gz: 819b3afd73a08905b5d7047e16604be254821a68f48443740277be3cb2ad0950c59e311d7f41ec2e72a7c0feac698902fec8c3784fdb954a900c55b6dce52523
7
- data.tar.gz: aeb60130dbfef691da2e561c40d5deacb61e90e0865502a74e3648be279797d3064a383b114f90c898f4327e5881fc7a40f70d70667ee40542a9eca5f5b25b0f
6
+ metadata.gz: 1e8297f1d895d4ae7509a24486dc5ae3f542e1f0aeead4308a37da0a9a240f9b8e7bc2858914d928277044be78aff8eb6880c3b86329457e1cc329903d2faf23
7
+ data.tar.gz: d58c62150ae5ed500222704cf550f7f34d7740fe4a5e7761354962ce3fbd79c21353d9d5b6a017f6b3c2e636316c745e05b261507883bace63901769b1959049
@@ -0,0 +1 @@
1
+ be57e2bf9d7c00b65b63cb41c672ad99f0b27bac91d7f75d9fd237d39de91cb9417530dd0ff77387356de211516f116bb40fb8301438e4eb0d9d7adeb275b676
@@ -0,0 +1 @@
1
+ 796e5ebed8e42b3486d01b1501823e79ed60fe08c0057db0110d5cc4688715317f63ba3f6dc3be130326e03992a99199597e9d09d12ff9ecad8fb5ca14c179ad
data/docs/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.html
@@ -0,0 +1,15 @@
1
+ # Predict Next Value
2
+
3
+ As a stock quantitative analyst, having a predict next value method on a timeseries array of closing day stock price data would be extremely helpful. This method would enable us to forecast the future values of stock prices based on historical data patterns.
4
+
5
+ - **Forecasting**: The predict next value method would allow us to predict the future movement of stock prices. By analyzing trends and patterns in the historical data, we can estimate the potential direction and magnitude of future price movements. This forecast can help in making informed investment decisions and developing trading strategies.
6
+
7
+ - **Risk Management**: Predicting the next value in a timeseries array of stock prices can assist in assessing and managing risks. By having an idea of the potential future price movements, we can identify potential pitfalls and take appropriate measures to mitigate losses. This method would enable us to set stop loss orders or implement hedging strategies to protect our investments.
8
+
9
+ - **Trading Strategies**: A predict next value method would be invaluable in developing trading strategies. By accurately forecasting future stock price movements, we can identify profitable trading opportunities. For example, if the model predicts an uptrend, we may consider buying stocks, or if it predicts a downtrend, we may consider selling or shorting stocks. This method can help optimize entry and exit points, resulting in improved trading performance.
10
+
11
+ - **Quantitative Analysis**: As a quantitative analyst, this predictive method provides a quantitative approach to analyzing stock prices. By utilizing mathematical models and statistical techniques, we can determine the probability of various price scenarios. This adds rigor and objectivity to the analysis process, giving us a deeper understanding of the underlying data.
12
+
13
+ - **Automation and Efficiency**: Automating the predict next value method allows for efficient analysis of large datasets. Instead of manually analyzing each data point, the algorithm can quickly process the time series array of prices and generate predictions. This saves significant time and effort, allowing us to focus on interpreting and using the predictions for decision-making purposes.
14
+
15
+ In summary, having a predict next value method on a timeseries array of closing day stock price data would be an invaluable tool for a stock quantitative analyst. It would aid in forecasting, risk management, trading strategy development, quantitative analysis, and automation, ultimately enhancing the accuracy and efficiency of our analysis and decision-making processes.
@@ -0,0 +1,19 @@
1
+ # lib/patches/daru/category.rb
2
+
3
+ module Daru
4
+ module Category
5
+
6
+ def plotting_lig lib
7
+ if :svg_graph = lib
8
+ @plotting_library = lib
9
+ if Daru.send("has_#{lib}?".to_sym)
10
+ extend Module.const_get(
11
+ "Daru::Plotting::Category::#{lib.to_s.capitalize}Library"
12
+ )
13
+ end
14
+ else
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # lib/patches/daru/data_frame.rb
2
+
3
+ module Daru
4
+ module DataFrame
5
+
6
+ def plotting_lig lib
7
+ if :svg_graph = lib
8
+ @plotting_library = lib
9
+ if Daru.send("has_#{lib}?".to_sym)
10
+ extend Module.const_get(
11
+ "Daru::Plotting::DataFrame::#{lib.to_s.capitalize}Library"
12
+ )
13
+ end
14
+ else
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ # lib/patches/daru/plotting/svg-graph/category.rb
2
+
3
+ # NOTE: Code originally from Gruff
4
+ # TODO: Tailor the code to SvgGraph
5
+
6
+ module Daru
7
+ module Plotting
8
+ module Category
9
+ module SvgGraphLibrary
10
+ def plot opts={}
11
+ type = opts[:type] || :bar
12
+ size = opts[:size] || 500
13
+ case type
14
+ when :bar, :pie, :sidebar
15
+ plot = send("category_#{type}_plot".to_sym, size, opts[:method])
16
+ else
17
+ raise ArgumentError, 'This type of plot is not supported.'
18
+ end
19
+ yield plot if block_given?
20
+ plot
21
+ end
22
+
23
+ private
24
+
25
+ def category_bar_plot size, method
26
+ plot = SvgGraph::Bar.new size
27
+ method ||= :count
28
+ dv = frequencies(method)
29
+ plot.labels = size.times.to_a.zip(dv.index.to_a).to_h
30
+ plot.data name || :vector, dv.to_a
31
+ plot
32
+ end
33
+
34
+ def category_pie_plot size, method
35
+ plot = SvgGraph::Pie.new size
36
+ method ||= :count
37
+ frequencies(method).each_with_index do |data, index|
38
+ plot.data index, data
39
+ end
40
+ plot
41
+ end
42
+
43
+ def category_sidebar_plot size, method
44
+ plot = SvgGraph::SideBar.new size
45
+ plot.labels = {0 => (name.to_s || 'vector')}
46
+ method ||= :count
47
+ frequencies(method).each_with_index do |data, index|
48
+ plot.data index, data
49
+ end
50
+ plot
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,105 @@
1
+ # lib/patches/daru/plotting/svg-graph/dataframe.rb
2
+
3
+ # NOTE: Code originally from Gruff
4
+ # TODO: Tailor the code to SvgGraph
5
+
6
+ module Daru
7
+ module Plotting
8
+ module DataFrame
9
+ module SvgGraphLibrary
10
+ def plot opts={}
11
+ opts[:type] ||= :line
12
+ opts[:size] ||= 500
13
+
14
+ x = extract_x_vector opts[:x]
15
+ y = extract_y_vectors opts[:y]
16
+
17
+ opts[:type] = process_type opts[:type], opts[:categorized]
18
+
19
+ type = opts[:type]
20
+
21
+ if %o[line bar scatter].include? type
22
+ graph = send("#{type}_plot", size, x, y)
23
+
24
+ elsif :scatter_categorized == type
25
+ graph = scatter_with_category(size, x, y, opts[:categorized])
26
+
27
+ else
28
+ raise ArgumentError, 'This type of plot is not supported.'
29
+ end
30
+
31
+ yield graph if block_given?
32
+ graph
33
+ end
34
+
35
+ private
36
+
37
+ def process_type type, categorized
38
+ type == :scatter && categorized ? :scatter_categorized : type
39
+ end
40
+
41
+ ##########################################################
42
+ def line_plot size, x, y
43
+ plot = SvgGraph::Line.new size
44
+ plot.labels = size.times.to_a.zip(x).to_h
45
+ y.each do |vec|
46
+ plot.data vec.name || :vector, vec.to_a
47
+ end
48
+ plot
49
+ end
50
+
51
+ ##########################################################
52
+ def bar_plot size, x, y
53
+ plot = SvgGraph::Bar.new size
54
+ plot.labels = size.times.to_a.zip(x).to_h
55
+ y.each do |vec|
56
+ plot.data vec.name || :vector, vec.to_a
57
+ end
58
+ plot
59
+ end
60
+
61
+ ##########################################################
62
+ def scatter_plot size, x, y
63
+ plot = SvgGraph::Scatter.new size
64
+ y.each do |vec|
65
+ plot.data vec.name || :vector, x, vec.to_a
66
+ end
67
+ plot
68
+ end
69
+
70
+ ##########################################################
71
+ def scatter_with_category size, x, y, opts
72
+ x = Daru::Vector.new x
73
+ y = y.first
74
+ plot = SvgGraph::Scatter.new size
75
+ cat_dv = self[opts[:by]]
76
+
77
+ cat_dv.categories.each do |cat|
78
+ bools = cat_dv.eq cat
79
+ plot.data cat, x.where(bools).to_a, y.where(bools).to_a
80
+ end
81
+
82
+ plot
83
+ end
84
+
85
+ def extract_x_vector x_name
86
+ x_name && self[x_name].to_a || index.to_a
87
+ end
88
+
89
+ def extract_y_vectors y_names
90
+ y_names =
91
+ case y_names
92
+ when nil
93
+ vectors.to_a
94
+ when Array
95
+ y_names
96
+ else
97
+ [y_names]
98
+ end
99
+
100
+ y_names.map { |y| self[y] }.select(&:numeric?)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,102 @@
1
+ # lib/patches/daru/plotting/svg-graph/vector.rb
2
+
3
+ # NOTE: Code originally from Gruff
4
+ # TODO: Tailor the code to SvgGraph
5
+
6
+ module Daru
7
+ module Plotting
8
+ module Vector
9
+ module SvgGraphLibrary
10
+ def plot opts={}
11
+ opts[:type] ||= :line
12
+ opts[:size] ||= 500 # SMELL: What is this?
13
+ opts[:height] ||= 720
14
+ opts[:width] ||= 720
15
+ opts[:title] ||= name || :vector
16
+
17
+ debug_me{[
18
+ :opts,
19
+ :self
20
+ ]}
21
+
22
+ if %i[line bar pie scatter sidebar].include? type
23
+ graph = send("#{type}_plot", opts)
24
+ else
25
+ raise ArgumentError, 'This type of plot is not supported.'
26
+ end
27
+
28
+ yield graph if block_given?
29
+
30
+ graph
31
+ end
32
+
33
+ private
34
+
35
+ ####################################################
36
+ def line_plot opts={}
37
+ graph = SVG::Graph::Line.new opts
38
+
39
+ graph.add_data(
40
+ data: to_a,
41
+ title: opts[:title]
42
+ )
43
+
44
+ graph
45
+ end
46
+
47
+
48
+ ####################################################
49
+ def bar_plot opts
50
+ graph = SVG::Graph::Bar.new opts
51
+
52
+ graph.add_data(
53
+ data: to_a,
54
+ title: opts[:title]
55
+ )
56
+
57
+ graph
58
+ end
59
+
60
+
61
+ ####################################################
62
+ def pie_plot opts
63
+ graph = SVG::Graph::Pie.new opts
64
+
65
+ graph.add_data(
66
+ data: to_a,
67
+ title: opts[:title]
68
+ )
69
+
70
+ graph
71
+ end
72
+
73
+
74
+ ####################################################
75
+ def scatter_plot size
76
+ graph = SVG::Graph::Plot.new opts
77
+
78
+
79
+ graph.add_data(
80
+ data: to_a.zip(index.to_a)
81
+ title: opts[:title]
82
+ )
83
+
84
+ graph
85
+ end
86
+
87
+
88
+ ####################################################
89
+ def sidebar_plot size
90
+ graph = SVG::Graph::BarHorizontal.new opts
91
+
92
+ graph.add_data(
93
+ data: to_a,
94
+ title: opts[:title]
95
+ )
96
+
97
+ graph
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,7 @@
1
+ # lib/patches/daru/plotting/svg-graph.rb
2
+
3
+ require 'svg-graph'
4
+
5
+ require_relative 'SvgGraph/category.rb'
6
+ require_relative 'SvgGraph/vector.rb'
7
+ require_relative 'SvgGraph/dataframe.rb'
@@ -0,0 +1,19 @@
1
+ # lib/patches/daru/vector.rb
2
+
3
+ module Daru
4
+ module Vector
5
+
6
+ def plotting_lig lib
7
+ if :svg_graph = lib
8
+ @plotting_library = lib
9
+ if Daru.send("has_#{lib}?".to_sym)
10
+ extend Module.const_get(
11
+ "Daru::Plotting::Vector::#{lib.to_s.capitalize}Library"
12
+ )
13
+ end
14
+ else
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # lib/patches/daru.rb
2
+
3
+ require_relative 'daru/category'
4
+ require_relative 'daru/data_frame'
5
+ require_relative 'daru/vector'
6
+
7
+ module Daru
8
+ create_has_library :svg_graph
9
+
10
+ class << self
11
+ def plotting_library lib
12
+ if :svg_graph = lib
13
+ @plotting_library = lib
14
+ else
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,306 @@
1
+ # lib/sqa/command/analysis.rb
2
+
3
+ module SQA
4
+ class Analysis < CLI
5
+ include TTY::Option
6
+
7
+ command "Analysis"
8
+
9
+ desc "Provide an Analysis of a Portfolio"
10
+
11
+
12
+ def initialize
13
+ # TODO: something
14
+ end
15
+ end
16
+ end
17
+
18
+ __END__
19
+
20
+
21
+
22
+ ###################################################
23
+ ## This is the old thing that got me started ...
24
+
25
+ #!/usr/bin/env ruby
26
+ # experiments/stocks/analysis.rb
27
+ #
28
+ # Some technical indicators from FinTech gem
29
+ #
30
+ # optional date CLI option in format YYYY-mm-dd
31
+ # if not present uses Date.today
32
+ #
33
+
34
+
35
+ INVEST = 1000.00
36
+
37
+ require 'pathname'
38
+
39
+ require_relative 'stock'
40
+ require_relative 'datastore'
41
+
42
+
43
+ STOCKS = Pathname.pwd + "stocks.txt"
44
+ TRADES = Pathname.pwd + "trades.csv"
45
+
46
+ TRADES_FILE = File.open(TRADES, 'a')
47
+
48
+ unless STOCKS.exist?
49
+ puts
50
+ puts "ERROR: The 'stocks.txt' file does not exist."
51
+ puts
52
+ exot(-1)
53
+ end
54
+
55
+ require 'debug_me'
56
+ include DebugMe
57
+
58
+ require 'csv'
59
+ require 'date'
60
+ require 'tty-table'
61
+
62
+ require 'fin_tech'
63
+ require 'previous_dow'
64
+
65
+ class NilClass
66
+ def blank?() = true
67
+ end
68
+
69
+ class String
70
+ def blank?() = strip().empty?
71
+ end
72
+
73
+ class Array
74
+ def blank?() = empty?
75
+ end
76
+
77
+
78
+ def tickers
79
+ return @tickers unless @tickers.blank?
80
+
81
+ @tickers = []
82
+
83
+ STOCKS.readlines.each do |a_line|
84
+ ticker_symbol = a_line.chomp.strip.split()&.first&.downcase
85
+ next if ticker_symbol.blank? || '#' == ticker_symbol
86
+ @tickers << ticker_symbol unless @tickers.include?(ticker_symbol)
87
+ end
88
+
89
+ @tickers.sort!
90
+ end
91
+
92
+ given_date = ARGV.first ? Date.parse(ARGV.first) : Date.today
93
+
94
+ start_date = Date.new(2019, 1, 1)
95
+ end_date = previous_dow(:friday, given_date)
96
+
97
+ ASOF = end_date.to_s.tr('-','')
98
+
99
+
100
+ #######################################################################
101
+ # download a CSV file from https://query1.finance.yahoo.com
102
+ # given a stock ticker symbol as a String
103
+ # start and end dates
104
+ #
105
+ # For ticker "aapl" the downloaded file will be named "aapl.csv"
106
+ # That filename will be renamed to "aapl_YYYYmmdd.csv" where the
107
+ # date suffix is the end_date of the historical data.
108
+ #
109
+ def download_historical_prices(ticker, start_date, end_date)
110
+ data_path = Pathname.pwd + "#{ticker}_#{ASOF}.csv"
111
+ return if data_path.exist?
112
+
113
+ mew_path = Pathname.pwd + "#{ticker}.csv"
114
+
115
+ start_timestamp = start_date.to_time.to_i
116
+ end_timestamp = end_date.to_time.to_i
117
+ ticker_upcase = ticker.upcase
118
+ filename = "#{ticker.downcase}.csv"
119
+
120
+ `curl -o #{filename} "https://query1.finance.yahoo.com/v7/finance/download/#{ticker_upcase}?period1=#{start_timestamp}&period2=#{end_timestamp}&interval=1d&events=history&includeAdjustedClose=true"`
121
+
122
+ mew_path.rename data_path
123
+ end
124
+
125
+
126
+ #######################################################################
127
+ # Read the CSV file associated with the give ticker symbol
128
+ # and the ASOF date.
129
+ #
130
+ def read_csv(ticker)
131
+ filename = "#{ticker.downcase}_#{ASOF}.csv"
132
+ data = []
133
+
134
+ CSV.foreach(filename, headers: true) do |row|
135
+ data << row.to_h
136
+ end
137
+
138
+ data
139
+ end
140
+
141
+ ##########################
142
+ # record a recommend trade
143
+
144
+ def trade(ticker, signal, shares, price)
145
+ TRADES_FILE.puts "#{ticker},#{ASOF},#{signal},#{shares},#{price}"
146
+ end
147
+
148
+ #######################################################################
149
+ ###
150
+ ## Main
151
+ #
152
+
153
+
154
+ tickers.each do |ticker|
155
+ download_historical_prices(ticker, start_date, end_date)
156
+ end
157
+
158
+ result = {}
159
+
160
+ mwfd = 14 # moving_window_forcast_days
161
+
162
+ headers = %w[ Ticker AdjClose Trend Slope M'tum RSI Analysis MACD Target Signal $]
163
+ values = []
164
+
165
+ tickers.each do |ticker|
166
+
167
+ data = read_csv ticker
168
+ prices = data.map{|r| r["Adj Close"].to_f}
169
+ volumes = data.map{|r| r["volume"].to_f}
170
+
171
+ if data.blank?
172
+ puts
173
+ puts "ERROR: cannot get data for #{ticker}"
174
+ puts
175
+ next
176
+ end
177
+
178
+
179
+ result[ticker] = {
180
+ date: data.last["Date"],
181
+ adj_close: data.last["Adj Close"].to_f
182
+ }
183
+
184
+ result[ticker][:market] = FinTech.classify_market_profile(
185
+ volumes.last(mwfd),
186
+ prices.last(mwfd),
187
+ prices.last(mwfd).first,
188
+ prices.last
189
+ )
190
+
191
+ fr = FinTech.fibonacci_retracement( prices.last(mwfd).first,
192
+ prices.last).map{|x| x.round(3)}
193
+
194
+
195
+ puts "\n#{result[ticker][:market]} .. #{ticker}\t#{fr}"
196
+ print "\t"
197
+ print FinTech.head_and_shoulders_pattern?(prices.last(mwfd))
198
+ print "\t"
199
+ print FinTech.double_top_bottom_pattern?(prices.last(mwfd))
200
+ print "\t"
201
+ mr = FinTech.mean_reversion?(prices, mwfd, 0.5)
202
+ print mr
203
+
204
+ if mr
205
+ print "\t"
206
+ print FinTech.mr_mean(prices, mwfd).round(3)
207
+ end
208
+
209
+ print "\t"
210
+ print FinTech.identify_wave_condition?(prices, 2*mwfd, 1.0)
211
+ puts
212
+
213
+ print "\t"
214
+ print FinTech.ema_analysis(prices, mwfd).except(:ema_values)
215
+
216
+ puts
217
+
218
+ row = [ ticker ]
219
+
220
+ # result[ticker][:moving_averages] = FinTech.sma(data, mwfd)
221
+ result[ticker][:trend] = FinTech.sma_trend(data, mwfd)
222
+ result[ticker][:momentum] = FinTech.momentum(prices, mwfd)
223
+ result[ticker][:rsi] = FinTech.rsi(data, mwfd)
224
+ result[ticker][:bollinger_bands] = FinTech.bollinger_bands(data, mwfd, 2)
225
+ result[ticker][:macd] = FinTech.macd(data, mwfd, 2*mwfd, mwfd/2)
226
+
227
+ price = result[ticker][:adj_close].round(3)
228
+
229
+ row << price
230
+ row << result[ticker][:trend][:trend]
231
+ row << result[ticker][:trend][:angle].round(3)
232
+ row << result[ticker][:momentum].round(3)
233
+ row << result[ticker][:rsi][:rsi].round(3)
234
+ row << result[ticker][:rsi][:meaning]
235
+ row << result[ticker][:macd].first.round(3)
236
+ row << result[ticker][:macd].last.round(3)
237
+
238
+ analysis = result[ticker][:rsi][:meaning]
239
+
240
+ signal = ""
241
+ macd_diff = result[ticker][:macd].first
242
+ target = result[ticker][:macd].last
243
+ current = result[ticker][:adj_close]
244
+
245
+ trend_down = "down" == result[ticker][:trend][:trend]
246
+
247
+ if current < target
248
+ signal = "buy" unless "Over Bought" == analysis
249
+ elsif (current > target) && trend_down
250
+ signal = "sell" unless "Over Sold" == analysis
251
+ end
252
+
253
+ if "buy" == signal
254
+ pps = target - price
255
+ shares = INVEST.to_i / price.to_i
256
+ upside = (shares * pps).round(2)
257
+ trade(ticker, signal, shares, price)
258
+ elsif "sell" == signal
259
+ pps = target - price
260
+ shares = INVEST.to_i / price.to_i
261
+ upside = (shares * pps).round(2)
262
+ trade(ticker, signal, shares, price)
263
+ else
264
+ upside = ""
265
+ end
266
+
267
+ row << signal
268
+ row << upside
269
+
270
+ values << row
271
+ end
272
+
273
+ # debug_me{[
274
+ # :result
275
+ # ]}
276
+
277
+
278
+ the_table = TTY::Table.new(headers, values)
279
+
280
+ puts
281
+ puts "Analysis as of Friday Close: #{end_date}"
282
+
283
+ puts the_table.render(
284
+ :unicode,
285
+ {
286
+ padding: [0, 0, 0, 0],
287
+ alignments: [
288
+ :left, # ticker
289
+ :right, # adj close
290
+ :center, # trend
291
+ :right, # slope
292
+ :right, # momentum
293
+ :right, # rsi
294
+ :center, # meaning / analysis
295
+ :right, # macd
296
+ :right, # target
297
+ :center, # signal
298
+ :right # upside
299
+ ],
300
+ }
301
+ )
302
+ puts
303
+
304
+ TRADES_FILE.close
305
+
306
+