sqa 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/average_true_range.md +9 -0
- data/docs/data_frame.md +164 -0
- data/docs/fibonacci_retracement.md +30 -0
- data/docs/identify_wave_condition.md +14 -0
- data/docs/peaks_and_valleys.md +11 -0
- data/docs/stochastic_oscillator.md +4 -0
- data/lib/sqa/cli.rb +1 -1
- data/lib/sqa/data_frame/yahoo_finance.rb +24 -0
- data/lib/sqa/data_frame.rb +11 -0
- data/lib/sqa/indicator/average_true_range.rb +22 -9
- data/lib/sqa/indicator/bollinger_bands.rb +2 -2
- data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +1 -1
- data/lib/sqa/indicator/donchian_channel.rb +1 -1
- data/lib/sqa/indicator/double_top_bottom_pattern.rb +1 -1
- data/lib/sqa/indicator/elliott_wave_theory.rb +57 -0
- data/lib/sqa/indicator/exponential_moving_average.rb +25 -0
- data/lib/sqa/indicator/exponential_moving_average_trend.rb +36 -0
- data/lib/sqa/indicator/fibonacci_retracement.rb +5 -7
- data/lib/sqa/indicator/head_and_shoulders_pattern.rb +1 -1
- data/lib/sqa/indicator/{classify_market_profile.rb → market_profile.rb} +7 -8
- data/lib/sqa/indicator/mean_reversion.rb +1 -1
- data/lib/sqa/indicator/momentum.rb +9 -7
- data/lib/sqa/indicator/moving_average_convergence_divergence.rb +1 -1
- data/lib/sqa/indicator/peaks_and_valleys.rb +29 -0
- data/lib/sqa/indicator/{relative_strength_index.md.rb → relative_strength_index.rb} +2 -2
- data/lib/sqa/indicator/simple_moving_average.rb +6 -3
- data/lib/sqa/indicator/simple_moving_average_trend.rb +15 -14
- data/lib/sqa/indicator/stochastic_oscillator.rb +32 -3
- data/lib/sqa/indicator/true_range.rb +14 -12
- data/lib/sqa/indicator.rb +4 -4
- data/lib/sqa/stock.rb +6 -13
- data/lib/sqa/version.rb +1 -1
- data/lib/sqa.rb +27 -6
- metadata +30 -29
- data/lib/sqa/datastore/active_record.rb +0 -89
- data/lib/sqa/datastore/csv/yahoo_finance.rb +0 -51
- data/lib/sqa/datastore/csv.rb +0 -93
- data/lib/sqa/datastore/sqlite.rb +0 -7
- data/lib/sqa/datastore.rb +0 -6
- data/lib/sqa/indicator/average_true_range.md +0 -9
- data/lib/sqa/indicator/ema_analysis.rb +0 -70
- data/lib/sqa/indicator/fibonacci_retracement.md +0 -3
- data/lib/sqa/indicator/identify_wave_condition.md +0 -6
- data/lib/sqa/indicator/identify_wave_condition.rb +0 -40
- data/lib/sqa/indicator/stochastic_oscillator.md +0 -5
- /data/{lib/sqa/indicator → docs}/README.md +0 -0
- /data/{lib/sqa/indicator → docs}/bollinger_bands.md +0 -0
- /data/{lib/sqa/indicator → docs}/candlestick_pattern_recognizer.md +0 -0
- /data/{lib/sqa/indicator → docs}/donchian_channel.md +0 -0
- /data/{lib/sqa/indicator → docs}/double_top_bottom_pattern.md +0 -0
- /data/{lib/sqa/indicator/ema_analysis.md → docs/exponential_moving_average.md} +0 -0
- /data/{lib/sqa/indicator → docs}/head_and_shoulders_pattern.md +0 -0
- /data/{lib/sqa/indicator/classify_market_profile.md → docs/market_profile.md} +0 -0
- /data/{lib/sqa/indicator → docs}/mean_reversion.md +0 -0
- /data/{lib/sqa/indicator → docs}/momentum.md +0 -0
- /data/{lib/sqa/indicator → docs}/moving_average_convergence_divergence.md +0 -0
- /data/{lib/sqa/indicator → docs}/relative_strength_index.md +0 -0
- /data/{lib/sqa/indicator → docs}/simple_moving_average.md +0 -0
- /data/{lib/sqa/indicator → docs}/true_range.md +0 -0
@@ -1,12 +1,12 @@
|
|
1
1
|
# lib/sqa/indicator/relative_strength_index.rb
|
2
2
|
|
3
|
-
|
3
|
+
class SQA::Indicator; class << self
|
4
4
|
|
5
5
|
def relative_strength_index(
|
6
6
|
prices, # Array of prices
|
7
7
|
period, # Integer how many to consider at a time
|
8
8
|
over_sold = 30.0, # Float break over point in trend
|
9
|
-
|
9
|
+
over_bought = 70.0 # Float break over point in trend
|
10
10
|
)
|
11
11
|
gains = []
|
12
12
|
losses = []
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# lib/sqa/indicator/simple_moving_average.rb
|
2
2
|
|
3
|
-
|
3
|
+
class SQA::Indicator; class << self
|
4
4
|
|
5
5
|
def simple_moving_average(
|
6
6
|
prices, # Array of prices
|
@@ -8,9 +8,12 @@ module SQA::Indicator; class << self
|
|
8
8
|
)
|
9
9
|
moving_averages = []
|
10
10
|
|
11
|
+
(0..period-2).to_a.each do |x|
|
12
|
+
moving_averages << prices[0..x].mean
|
13
|
+
end
|
14
|
+
|
11
15
|
prices.each_cons(period) do |window|
|
12
|
-
|
13
|
-
moving_averages << moving_average
|
16
|
+
moving_averages << window.mean
|
14
17
|
end
|
15
18
|
|
16
19
|
moving_averages # Array
|
@@ -1,28 +1,29 @@
|
|
1
1
|
# lib/sqa/indicator/simple_moving_average_trend.rb
|
2
2
|
|
3
|
-
|
3
|
+
class SQA::Indicator; class << self
|
4
4
|
|
5
5
|
def simple_moving_average_trend(
|
6
|
-
|
6
|
+
prices, # Array of prices
|
7
7
|
period, # Integer number of entries to consider
|
8
|
-
delta = 0
|
8
|
+
delta = 1.0 # Float defines the angle range(+/-) for :neutral trend
|
9
9
|
)
|
10
|
-
sma = simple_moving_average(
|
10
|
+
sma = simple_moving_average(prices, period)
|
11
11
|
last_sma = sma.last
|
12
|
-
prev_sma = sma
|
12
|
+
prev_sma = sma.last(period).first
|
13
13
|
angle = Math.atan((last_sma - prev_sma) / period) * (180 / Math::PI)
|
14
14
|
|
15
|
-
if angle >
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
trend = if angle > delta
|
16
|
+
:up
|
17
|
+
elsif angle < -delta
|
18
|
+
:down
|
19
|
+
else
|
20
|
+
:neutral
|
21
|
+
end
|
22
22
|
|
23
23
|
{
|
24
|
-
|
25
|
-
|
24
|
+
sma: sma, # Array
|
25
|
+
trend: trend, # Symbol :up, :down, :neutral
|
26
|
+
angle: angle # Float how step the trend
|
26
27
|
}
|
27
28
|
end
|
28
29
|
alias_method :sma_trend, :simple_moving_average_trend
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# lib/sqa/indicator/stochastic_oscillator.rb
|
2
2
|
|
3
|
-
|
3
|
+
class SQA::Indicator; class << self
|
4
4
|
|
5
5
|
# @param high_prices [Array]
|
6
6
|
# @param low_prices [Array]
|
@@ -20,10 +20,10 @@ module SQA::Indicator; class << self
|
|
20
20
|
k_values = []
|
21
21
|
d_values = []
|
22
22
|
|
23
|
-
closing_prices.each_cons(period) do |
|
23
|
+
closing_prices.each_cons(period) do |window|
|
24
24
|
highest_high = high_prices.max(period)
|
25
25
|
lowest_low = low_prices.min(period)
|
26
|
-
current_close =
|
26
|
+
current_close = window.last
|
27
27
|
k_values << (current_close - lowest_low) / (highest_high - lowest_low) * 100 # Calculate the k_value
|
28
28
|
end
|
29
29
|
|
@@ -35,5 +35,34 @@ module SQA::Indicator; class << self
|
|
35
35
|
end
|
36
36
|
alias_method :so, :stochastic_oscillator
|
37
37
|
|
38
|
+
|
39
|
+
def stochastic_oscillator2(
|
40
|
+
prices, # Array of prices
|
41
|
+
period # Integer number of events to consider
|
42
|
+
)
|
43
|
+
k_values = []
|
44
|
+
d_values = []
|
45
|
+
|
46
|
+
prices.each_cons(period) do |window|
|
47
|
+
low = window.min # Lowest price in the period
|
48
|
+
high = window.max # Highest price in the period
|
49
|
+
current_price = window.last # Current closing price
|
50
|
+
|
51
|
+
k_values << (current_price - low) * 100 / (high - low)
|
52
|
+
end
|
53
|
+
|
54
|
+
k_values.each_cons(period) do |window|
|
55
|
+
d_values << window.mean
|
56
|
+
end
|
57
|
+
|
58
|
+
{
|
59
|
+
k: k_values,
|
60
|
+
d: d_values
|
61
|
+
}
|
62
|
+
end
|
63
|
+
alias_method :so2, :stochastic_oscillator2
|
64
|
+
|
65
|
+
|
66
|
+
|
38
67
|
end; end
|
39
68
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# See Also: average_true_range
|
4
4
|
|
5
|
-
|
5
|
+
class SQA::Indicator; class << self
|
6
6
|
|
7
7
|
# @param high_prices [Array]
|
8
8
|
# @param low_prices [Array]
|
@@ -13,21 +13,23 @@ module SQA::Indicator; class << self
|
|
13
13
|
def true_range(
|
14
14
|
high_prices, # Array of high prices
|
15
15
|
low_prices, # Array of low prices
|
16
|
-
|
16
|
+
closing_prices # Array of closing prices
|
17
17
|
)
|
18
18
|
true_ranges = []
|
19
19
|
|
20
20
|
high_prices.each_with_index do |high, index|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
if index > 0
|
22
|
+
low = low_prices[index]
|
23
|
+
previous_close = closing_prices[index - 1]
|
24
|
+
|
25
|
+
true_range = [
|
26
|
+
high - low,
|
27
|
+
(high - previous_close).abs,
|
28
|
+
(low - previous_close).abs
|
29
|
+
].max
|
30
|
+
|
31
|
+
true_ranges << true_range
|
32
|
+
end
|
31
33
|
end
|
32
34
|
|
33
35
|
true_ranges # Array of True Range values
|
data/lib/sqa/indicator.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
# lib/sqa/
|
1
|
+
# lib/sqa/indicator.rb
|
2
2
|
|
3
|
-
|
3
|
+
class SQA::Indicator
|
4
4
|
end
|
5
5
|
|
6
6
|
# setup a shortcut for the namespace
|
7
7
|
SQAI = SQA::Indicator
|
8
8
|
|
9
|
-
Dir["indicator/*.rb"].each do |file|
|
10
|
-
|
9
|
+
Dir[__dir__ + "/indicator/*.rb"].each do |file|
|
10
|
+
load file
|
11
11
|
end
|
data/lib/sqa/stock.rb
CHANGED
@@ -1,26 +1,19 @@
|
|
1
1
|
# lib/sqa/stock.rb
|
2
2
|
|
3
|
-
|
4
|
-
require_relative 'datastore'
|
5
|
-
|
6
|
-
class SQA::Stock < ActiveRecord::Base
|
7
|
-
include SQA::Indicators
|
8
|
-
|
9
|
-
# has_many :activities using ticker as the foreign key
|
10
|
-
# primary id is ticker it is unique
|
11
|
-
|
3
|
+
class SQA::Stock
|
12
4
|
attr_accessor :company_name
|
13
|
-
attr_accessor :
|
5
|
+
attr_accessor :df # The DataFrane
|
14
6
|
attr_accessor :ticker
|
15
7
|
|
16
|
-
def initialize(ticker,
|
8
|
+
def initialize(ticker:, source: :yahoo_finance, type: :csv)
|
17
9
|
@ticker = ticker
|
18
10
|
@company_name = "Company Name"
|
19
|
-
|
11
|
+
klass = "SQA::DataFrame::#{source.to_s.camelize}".constantize
|
12
|
+
@df = klass.send("from_#{type.downcase}", ticker)
|
20
13
|
end
|
21
14
|
|
22
15
|
def to_s
|
23
|
-
"#{ticker} with #{@
|
16
|
+
"#{ticker} with #{@df.size} data points from #{@df.timestamp.first} to #{@df.timestamp.last}"
|
24
17
|
end
|
25
18
|
end
|
26
19
|
|
data/lib/sqa/version.rb
CHANGED
data/lib/sqa.rb
CHANGED
@@ -1,12 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require 'csv'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/string'
|
5
|
+
require 'daru'
|
7
6
|
require 'date'
|
8
|
-
|
7
|
+
|
8
|
+
require 'debug_me'
|
9
|
+
include DebugMe
|
10
|
+
|
11
|
+
require 'descriptive_statistics'
|
12
|
+
require 'mixlib/config'
|
13
|
+
require 'nenv'
|
9
14
|
require 'pathname'
|
10
15
|
|
11
|
-
|
16
|
+
unless defined?(HOME)
|
17
|
+
HOME = Pathname.new(Nenv.home)
|
18
|
+
end
|
19
|
+
|
20
|
+
module SQA
|
21
|
+
module Config
|
22
|
+
extend Mixlib::Config
|
23
|
+
config_strict_mode true
|
24
|
+
|
25
|
+
default :data_dir, HOME + "sqa_data"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require_relative "sqa/data_frame"
|
12
30
|
require_relative "sqa/errors"
|
31
|
+
require_relative "sqa/indicator"
|
32
|
+
require_relative "sqa/stock"
|
33
|
+
require_relative "sqa/version"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dewayne VanHoozer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Simplistic playpen (e.g. not for serious use) for doing technical analysis
|
14
14
|
of stock prices.
|
@@ -26,52 +26,53 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- bin/sqa
|
28
28
|
- checksums/sqa-0.0.1.gem.sha512
|
29
|
+
- docs/README.md
|
30
|
+
- docs/average_true_range.md
|
31
|
+
- docs/bollinger_bands.md
|
32
|
+
- docs/candlestick_pattern_recognizer.md
|
33
|
+
- docs/data_frame.md
|
34
|
+
- docs/donchian_channel.md
|
35
|
+
- docs/double_top_bottom_pattern.md
|
36
|
+
- docs/exponential_moving_average.md
|
37
|
+
- docs/fibonacci_retracement.md
|
38
|
+
- docs/head_and_shoulders_pattern.md
|
39
|
+
- docs/identify_wave_condition.md
|
40
|
+
- docs/market_profile.md
|
41
|
+
- docs/mean_reversion.md
|
42
|
+
- docs/momentum.md
|
43
|
+
- docs/moving_average_convergence_divergence.md
|
44
|
+
- docs/peaks_and_valleys.md
|
45
|
+
- docs/relative_strength_index.md
|
29
46
|
- docs/requirements.md
|
47
|
+
- docs/simple_moving_average.md
|
48
|
+
- docs/stochastic_oscillator.md
|
49
|
+
- docs/true_range.md
|
30
50
|
- lib/sqa.rb
|
31
51
|
- lib/sqa/activity.rb
|
32
52
|
- lib/sqa/cli.rb
|
33
|
-
- lib/sqa/
|
34
|
-
- lib/sqa/
|
35
|
-
- lib/sqa/datastore/csv.rb
|
36
|
-
- lib/sqa/datastore/csv/yahoo_finance.rb
|
37
|
-
- lib/sqa/datastore/sqlite.rb
|
53
|
+
- lib/sqa/data_frame.rb
|
54
|
+
- lib/sqa/data_frame/yahoo_finance.rb
|
38
55
|
- lib/sqa/errors.rb
|
39
56
|
- lib/sqa/indicator.rb
|
40
|
-
- lib/sqa/indicator/README.md
|
41
|
-
- lib/sqa/indicator/average_true_range.md
|
42
57
|
- lib/sqa/indicator/average_true_range.rb
|
43
|
-
- lib/sqa/indicator/bollinger_bands.md
|
44
58
|
- lib/sqa/indicator/bollinger_bands.rb
|
45
|
-
- lib/sqa/indicator/candlestick_pattern_recognizer.md
|
46
59
|
- lib/sqa/indicator/candlestick_pattern_recognizer.rb
|
47
|
-
- lib/sqa/indicator/classify_market_profile.md
|
48
|
-
- lib/sqa/indicator/classify_market_profile.rb
|
49
|
-
- lib/sqa/indicator/donchian_channel.md
|
50
60
|
- lib/sqa/indicator/donchian_channel.rb
|
51
|
-
- lib/sqa/indicator/double_top_bottom_pattern.md
|
52
61
|
- lib/sqa/indicator/double_top_bottom_pattern.rb
|
53
|
-
- lib/sqa/indicator/
|
54
|
-
- lib/sqa/indicator/
|
55
|
-
- lib/sqa/indicator/
|
62
|
+
- lib/sqa/indicator/elliott_wave_theory.rb
|
63
|
+
- lib/sqa/indicator/exponential_moving_average.rb
|
64
|
+
- lib/sqa/indicator/exponential_moving_average_trend.rb
|
56
65
|
- lib/sqa/indicator/fibonacci_retracement.rb
|
57
|
-
- lib/sqa/indicator/head_and_shoulders_pattern.md
|
58
66
|
- lib/sqa/indicator/head_and_shoulders_pattern.rb
|
59
|
-
- lib/sqa/indicator/
|
60
|
-
- lib/sqa/indicator/identify_wave_condition.rb
|
61
|
-
- lib/sqa/indicator/mean_reversion.md
|
67
|
+
- lib/sqa/indicator/market_profile.rb
|
62
68
|
- lib/sqa/indicator/mean_reversion.rb
|
63
|
-
- lib/sqa/indicator/momentum.md
|
64
69
|
- lib/sqa/indicator/momentum.rb
|
65
|
-
- lib/sqa/indicator/moving_average_convergence_divergence.md
|
66
70
|
- lib/sqa/indicator/moving_average_convergence_divergence.rb
|
67
|
-
- lib/sqa/indicator/
|
68
|
-
- lib/sqa/indicator/relative_strength_index.
|
69
|
-
- lib/sqa/indicator/simple_moving_average.md
|
71
|
+
- lib/sqa/indicator/peaks_and_valleys.rb
|
72
|
+
- lib/sqa/indicator/relative_strength_index.rb
|
70
73
|
- lib/sqa/indicator/simple_moving_average.rb
|
71
74
|
- lib/sqa/indicator/simple_moving_average_trend.rb
|
72
|
-
- lib/sqa/indicator/stochastic_oscillator.md
|
73
75
|
- lib/sqa/indicator/stochastic_oscillator.rb
|
74
|
-
- lib/sqa/indicator/true_range.md
|
75
76
|
- lib/sqa/indicator/true_range.rb
|
76
77
|
- lib/sqa/protfolio.rb
|
77
78
|
- lib/sqa/stock.rb
|
@@ -1,89 +0,0 @@
|
|
1
|
-
# lib/sqa/datastore/active_record.rb
|
2
|
-
|
3
|
-
|
4
|
-
require 'active_record'
|
5
|
-
require 'sqlite3'
|
6
|
-
|
7
|
-
|
8
|
-
module SQA::Datastore
|
9
|
-
class ActiveRecord
|
10
|
-
def initialize(ticker); end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
__END__
|
16
|
-
|
17
|
-
# An example of how to use active record with sqlite3 ...
|
18
|
-
|
19
|
-
#!/usr/bin/env ruby
|
20
|
-
# See: https://gist.github.com/unnitallman/944011
|
21
|
-
|
22
|
-
require 'active_record'
|
23
|
-
require 'sqlite3'
|
24
|
-
|
25
|
-
ActiveRecord::Base.logger = Logger.new(STDERR)
|
26
|
-
# TDV ActiveRecord::Base.colorize_logging = false
|
27
|
-
|
28
|
-
ActiveRecord::Base.establish_connection(
|
29
|
-
adapter: "sqlite3",
|
30
|
-
database: './database.db'
|
31
|
-
)
|
32
|
-
|
33
|
-
ActiveRecord::Schema.define do
|
34
|
-
create_table :albums do |table|
|
35
|
-
table.column :title, :string
|
36
|
-
table.column :performer, :string
|
37
|
-
end
|
38
|
-
|
39
|
-
create_table :tracks do |table|
|
40
|
-
table.column :album_id, :integer
|
41
|
-
table.column :track_number, :integer
|
42
|
-
table.column :title, :string
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class Album < ActiveRecord::Base
|
47
|
-
has_many :tracks
|
48
|
-
end
|
49
|
-
|
50
|
-
class Track < ActiveRecord::Base
|
51
|
-
belongs_to :album
|
52
|
-
end
|
53
|
-
|
54
|
-
album = Album.create(:title => 'Black and Blue',
|
55
|
-
:performer => 'The Rolling Stones')
|
56
|
-
album.tracks.create(:track_number => 1, :title => 'Hot Stuff')
|
57
|
-
album.tracks.create(:track_number => 2, :title => 'Hand Of Fate')
|
58
|
-
album.tracks.create(:track_number => 3, :title => 'Cherry Oh Baby ')
|
59
|
-
album.tracks.create(:track_number => 4, :title => 'Memory Motel ')
|
60
|
-
album.tracks.create(:track_number => 5, :title => 'Hey Negrita')
|
61
|
-
album.tracks.create(:track_number => 6, :title => 'Fool To Cry')
|
62
|
-
album.tracks.create(:track_number => 7, :title => 'Crazy Mama')
|
63
|
-
album.tracks.create(:track_number => 8,
|
64
|
-
:title => 'Melody (Inspiration By Billy Preston)')
|
65
|
-
|
66
|
-
album = Album.create(:title => 'Sticky Fingers',
|
67
|
-
:performer => 'The Rolling Stones')
|
68
|
-
album.tracks.create(:track_number => 1, :title => 'Brown Sugar')
|
69
|
-
album.tracks.create(:track_number => 2, :title => 'Sway')
|
70
|
-
album.tracks.create(:track_number => 3, :title => 'Wild Horses')
|
71
|
-
album.tracks.create(:track_number => 4,
|
72
|
-
:title => 'Can\'t You Hear Me Knocking')
|
73
|
-
album.tracks.create(:track_number => 5, :title => 'You Gotta Move')
|
74
|
-
album.tracks.create(:track_number => 6, :title => 'Bitch')
|
75
|
-
album.tracks.create(:track_number => 7, :title => 'I Got The Blues')
|
76
|
-
album.tracks.create(:track_number => 8, :title => 'Sister Morphine')
|
77
|
-
album.tracks.create(:track_number => 9, :title => 'Dead Flowers')
|
78
|
-
album.tracks.create(:track_number => 10, :title => 'Moonlight Mile')
|
79
|
-
|
80
|
-
puts Album.find(1).tracks.length
|
81
|
-
puts Album.find(2).tracks.length
|
82
|
-
|
83
|
-
puts Album.find_by_title('Sticky Fingers').title
|
84
|
-
puts Track.find_by_title('Fool To Cry').album_id
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# lib/sqa/datastore/csv/yahoo_finance.rb
|
2
|
-
|
3
|
-
# processes a CSV file downloaded from finance.yahoo.com
|
4
|
-
# Date,Open,High,Low,Close,Adj Close,Volume
|
5
|
-
|
6
|
-
require 'csv-importer'
|
7
|
-
|
8
|
-
class SQA::Datastore::CSV::YahooFinance
|
9
|
-
include CSVImporter
|
10
|
-
|
11
|
-
model ::SQA::Activity # an active record like model
|
12
|
-
|
13
|
-
column :date, to: ->(x) { Date.parse(x)}, required: true
|
14
|
-
column :open, to: ->(x) {x.to_f}, required: true
|
15
|
-
column :high, to: ->(x) {x.to_f}, required: true
|
16
|
-
column :low, to: ->(x) {x.to_f}, required: true
|
17
|
-
column :close, to: ->(x) {x.to_f}, required: true
|
18
|
-
column :adj_close, to: ->(x) {x.to_f}, required: true
|
19
|
-
column :volumn, to: ->(x) {x.to_i}, required: true
|
20
|
-
|
21
|
-
# TODO: make the identifier compound [ticker, date]
|
22
|
-
# so we can put all the data into a single table.
|
23
|
-
|
24
|
-
identifier :date
|
25
|
-
|
26
|
-
when_invalid :skip # or :abort
|
27
|
-
|
28
|
-
|
29
|
-
column :email, to: ->(email) { email.downcase }, required: true
|
30
|
-
column :first_name, as: [ /first.?name/i, /pr(é|e)nom/i ]
|
31
|
-
column :last_name, as: [ /last.?name/i, "nom" ]
|
32
|
-
column :published, to: ->(published, user) { user.published_at = published ? Time.now : nil }
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def self.load(ticker)
|
38
|
-
import = new(file: "#{ticker.upcase}.csv")
|
39
|
-
|
40
|
-
import.valid_header? # => false
|
41
|
-
import.report.message # => "The following columns are required: email"
|
42
|
-
|
43
|
-
# Assuming the header was valid, let's run the import!
|
44
|
-
|
45
|
-
import.run!
|
46
|
-
import.report.success? # => true
|
47
|
-
import.report.message # => "Import completed. 4 created, 2 updated, 1 failed to update" end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
data/lib/sqa/datastore/csv.rb
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
# lib/sqa/datastore/csv.rb
|
2
|
-
|
3
|
-
require 'csv'
|
4
|
-
require 'forwardable'
|
5
|
-
|
6
|
-
module SQA::Datastore
|
7
|
-
class CSV
|
8
|
-
extend Forwardable
|
9
|
-
def_delegators :@data, :first, :last, :size, :empty?, :[], :map, :select, :reject
|
10
|
-
|
11
|
-
SOURCE_DOMAIN = "https://query1.finance.yahoo.com/v7/finance/download/"
|
12
|
-
# curl -o AAPL.csv -L --url "https://query1.finance.yahoo.com/v7/finance/download/AAPL?period1=345427200&period2=1691712000&interval=1d&events=history&includeAdjustedClose=true"
|
13
|
-
|
14
|
-
|
15
|
-
attr_accessor :ticker
|
16
|
-
attr_accessor :data
|
17
|
-
|
18
|
-
def initialize(ticker, adapter = YahooFinance)
|
19
|
-
@ticker = ticker
|
20
|
-
@data_path = Pathname.pwd + "#{ticker.downcase}.csv"
|
21
|
-
@adapter = adapter
|
22
|
-
@data = adapter.load(ticker)
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
#######################################################################
|
27
|
-
# Read the CSV file associated with the give ticker symbol
|
28
|
-
#
|
29
|
-
# def read_csv_data
|
30
|
-
# download_historical_prices unless @data_path.exist?
|
31
|
-
|
32
|
-
# csv_data = []
|
33
|
-
|
34
|
-
# ::CSV.foreach(@data_path, headers: true) do |row|
|
35
|
-
# csv_data << row.to_h
|
36
|
-
# end
|
37
|
-
|
38
|
-
# csv_data
|
39
|
-
# end
|
40
|
-
|
41
|
-
#######################################################################
|
42
|
-
# download a CSV file from https://query1.finance.yahoo.com
|
43
|
-
# given a stock ticker symbol as a String
|
44
|
-
# start and end dates
|
45
|
-
#
|
46
|
-
# For ticker "aapl" the downloaded file will be named "aapl.csv"
|
47
|
-
# That filename will be renamed to "aapl_YYYYmmdd.csv" where the
|
48
|
-
# date suffix is the end_date of the historical data.
|
49
|
-
#
|
50
|
-
# def download_historical_prices(
|
51
|
-
# start_date: Date.new(2019, 1, 1),
|
52
|
-
# end_date: previous_dow(:friday, Date.today)
|
53
|
-
# )
|
54
|
-
|
55
|
-
# start_timestamp = start_date.to_time.to_i # Convert to unix timestamp
|
56
|
-
# end_timestamp = end_date.to_time.to_i
|
57
|
-
|
58
|
-
# user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"
|
59
|
-
|
60
|
-
# # TODO: replace curl with Faraday
|
61
|
-
|
62
|
-
# `curl -A "#{user_agent}" --cookie-jar cookies.txt -o #{@data_path} -L --url "#{SOURCE_DOMAIN}/#{ticker.upcase}?period1=#{start_timestamp}&period2=#{end_timestamp}&interval=1d&events=history&includeAdjustedClose=true"`
|
63
|
-
|
64
|
-
# check_csv_file
|
65
|
-
# end
|
66
|
-
|
67
|
-
|
68
|
-
# def check_csv_file
|
69
|
-
# f = File.open(@data_path, 'r')
|
70
|
-
# c1 = f.read(1)
|
71
|
-
|
72
|
-
# if '{' == c1
|
73
|
-
# error_msg = JSON.parse("#{c1}#{f.read}")
|
74
|
-
# raise "Not OK: #{error_msg}"
|
75
|
-
# end
|
76
|
-
# end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
__END__
|
81
|
-
|
82
|
-
{
|
83
|
-
"finance": {
|
84
|
-
"error": {
|
85
|
-
"code": "Unauthorized",
|
86
|
-
"description": "Invalid cookie"
|
87
|
-
}
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
data/lib/sqa/datastore/sqlite.rb
DELETED
data/lib/sqa/datastore.rb
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
# Average True Range
|
2
|
-
|
3
|
-
Calculates the Average True Range (ATR) for a given set of price data.
|
4
|
-
|
5
|
-
The Average True Range is an indicator that calculates the average of the True Range values over a specified period. It provides a measure of the average volatility of a security over that period.
|
6
|
-
|
7
|
-
The ATR is commonly used to assess the volatility of a security, identify potential trend reversals, and determine appropriate stop-loss levels. Higher ATR values indicate higher volatility, while lower ATR values indicate lower volatility.
|
8
|
-
|
9
|
-
For example, a 14-day Average True Range would calculate the average of the True Range values over the past 14 trading days. Traders and analysts may use this indicator to set stop-loss levels based on the average volatility of the security.
|