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 +4 -4
- data/checksums/sqa-0.0.7.gem.sha512 +1 -0
- data/checksums/sqa-0.0.8.gem.sha512 +1 -0
- data/docs/.gitignore +1 -0
- data/docs/predict_next_value.md +15 -0
- data/lib/patches/daru/category.rb +19 -0
- data/lib/patches/daru/data_frame.rb +19 -0
- data/lib/patches/daru/plotting/svg-graph/category.rb +55 -0
- data/lib/patches/daru/plotting/svg-graph/dataframe.rb +105 -0
- data/lib/patches/daru/plotting/svg-graph/vector.rb +102 -0
- data/lib/patches/daru/plotting/svg-graph.rb +7 -0
- data/lib/patches/daru/vector.rb +19 -0
- data/lib/patches/daru.rb +19 -0
- data/lib/sqa/analysis.rb +306 -0
- data/lib/sqa/cli.rb +145 -296
- data/lib/sqa/config.rb +169 -0
- data/lib/sqa/constants.rb +23 -0
- data/lib/sqa/indicator/predict_next_value.rb +63 -0
- data/lib/sqa/version.rb +9 -2
- data/lib/sqa/web.rb +159 -0
- data/lib/sqa.rb +43 -49
- metadata +107 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ed5a0bfe672b2a11993e5afd8ed8cbb103355f033c95e791c8c2ef57dbd8464
|
4
|
+
data.tar.gz: 308d76cd23b50c057816ed7cde0815a51b4bb5e196ea14e231fd68ad06fe76b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,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
|
data/lib/patches/daru.rb
ADDED
@@ -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
|
data/lib/sqa/analysis.rb
ADDED
@@ -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
|
+
|