sqa 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +90 -4
- data/checksums/sqa-0.0.3.gem.sha512 +1 -0
- data/checksums/sqa-0.0.4.gem.sha512 +1 -0
- data/checksums/sqa-0.0.5.gem.sha512 +1 -0
- data/lib/sqa/data_frame.rb +13 -0
- data/lib/sqa/errors.rb +1 -0
- data/lib/sqa/portfolio.rb +9 -0
- data/lib/sqa/strategy/README.md +54 -0
- data/lib/sqa/strategy/common.rb +38 -0
- data/lib/sqa/strategy/consensus.rb +43 -0
- data/lib/sqa/strategy/ema.rb +19 -0
- data/lib/sqa/strategy/mp.rb +19 -0
- data/lib/sqa/strategy/mr.rb +15 -0
- data/lib/sqa/strategy/random.md +5 -0
- data/lib/sqa/strategy/random.rb +18 -0
- data/lib/sqa/strategy/rsi.rb +19 -0
- data/lib/sqa/strategy/sma.rb +19 -0
- data/lib/sqa/strategy.rb +29 -37
- data/lib/sqa/trade.rb +24 -0
- data/lib/sqa/version.rb +1 -1
- data/lib/sqa.rb +18 -4
- metadata +186 -5
- data/.irbrc +0 -3
- data/lib/sqa/protfolio.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54f8ba7f993ae95bf01b2cdeeb32c76b17b02df6799dc046b85fde9b3d54f202
|
4
|
+
data.tar.gz: 91ea7ae14f99322618c3854f7ac16c4ee79c9a7d39609228d589528c381644df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f20d2ed8800c892905e1155006794832af992c7f1309121860e62cc010b26d23af4d04f6e0b03775b4db0a8dbcafca321ea4c0a39ffc7f6533d6f75bbb59976
|
7
|
+
data.tar.gz: 014345e1802899212fb443bec50783988b535d143f7f8fae50ebbacf4a0a9bf52144d6417b395f342fa0f68d94aceb7878179e9eac2bf429f980af9689dd9249
|
data/README.md
CHANGED
@@ -4,23 +4,109 @@ This is a very simplistic set of tools for running technical analysis on a stock
|
|
4
4
|
|
5
5
|
The BUY/SELL signals that it generates are part of a game. **DO NOT USE** when real money is at stake.
|
6
6
|
|
7
|
+
## This is a Work in Progress
|
8
|
+
|
9
|
+
I'm making use of lots of gems which may not be part of the gemspec at this time. I will be adding them as they make the final cut as to fitness for the intended function. Some gems are configurable. For example the default for the plotting library is `gruff`. There are several available that the `daru` gem can use.
|
10
|
+
|
7
11
|
## Installation
|
8
12
|
|
9
13
|
Install the gem and add to the application's Gemfile by executing:
|
10
14
|
|
11
|
-
|
15
|
+
bundle add sqa
|
12
16
|
|
13
17
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
18
|
|
15
|
-
|
19
|
+
gem install sqa
|
20
|
+
|
21
|
+
## ShoutOut To `daru`
|
22
|
+
|
23
|
+
**D**ata **A**nalysis in **RU**by
|
24
|
+
|
25
|
+
http://github.com/v0dro/daru
|
26
|
+
|
27
|
+
Its `DataFrame` class is a very interesting in memory data structure.
|
16
28
|
|
17
29
|
## Usage
|
18
30
|
|
19
|
-
Do not use
|
31
|
+
**Do not use!**
|
32
|
+
|
33
|
+
## Playing in IRB
|
34
|
+
|
35
|
+
You can play around in IRB with the SQA framework in two different areas. First is the stocks and indicators. The second is with trading strategies.
|
36
|
+
|
37
|
+
### With Stocks and Indicators
|
38
|
+
|
39
|
+
You will need some CSV files.
|
40
|
+
|
41
|
+
#### Get Historical Prices
|
42
|
+
|
43
|
+
Go to https::/finance.yahoo.com and down some historical price data for your favorite stocks. Put those CSV files in to the `sqa_data` directory in your HOME directory.
|
44
|
+
|
45
|
+
You may need to create a `portfolio.csv` file or you may not. TODO
|
46
|
+
|
47
|
+
The CSV files will be named by the stock's ticker symbol. For example: AAPL.csv
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
require 'sqa'
|
51
|
+
# TODO: See the documentation on configurable items
|
52
|
+
# Omit to use defaults
|
53
|
+
SQA::Config.from_file(path_to_config_file)
|
54
|
+
|
55
|
+
# initialize framework from configuration values
|
56
|
+
SQA.init
|
57
|
+
|
58
|
+
aapl = SQA::Stock.new('aapl')
|
59
|
+
```
|
60
|
+
|
61
|
+
`aapl.df` is the Daru::DataFrame
|
62
|
+
see the `daru` gem for how to manipulate the DataFrame
|
63
|
+
The SQA::Indicator class methods use Arrays not the DataFrame
|
64
|
+
Here is an example:
|
65
|
+
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
prices = aapl.df.adj_close_price.to_a
|
69
|
+
period = 14 # size of the window in prices to analyze
|
70
|
+
|
71
|
+
rsi = SQA::Indicator.rsi(prices, period)
|
72
|
+
```
|
73
|
+
|
74
|
+
### With Strategies
|
75
|
+
|
76
|
+
The strategies work off of an Object that contains the information required to make its recommendation. Build on the previous Ruby snippet ...
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require 'ostruct'
|
80
|
+
vector = OpenStruct.new
|
81
|
+
vector.rsi = rsi
|
82
|
+
|
83
|
+
# Load some trading strategies
|
84
|
+
ss = SWA::Strategy.new
|
85
|
+
ss.auto_load # loads everything in the lib/sqa/strategy folder
|
86
|
+
|
87
|
+
# Select some strategies to execute
|
88
|
+
ss.add SWA::Strategy::Random # 3-sided coin flip
|
89
|
+
ss.add SQA::Strategy::RSI
|
90
|
+
|
91
|
+
# This is an Array with each "trade" method
|
92
|
+
# that is defined in each strategy added
|
93
|
+
ss.strategies
|
94
|
+
|
95
|
+
# Execute those strategies
|
96
|
+
results = ss.execute(vector)
|
97
|
+
```
|
98
|
+
|
99
|
+
`results` is an Array with an entry for each strategy executed. The entries are either :buy, :sell or :hold.
|
100
|
+
|
101
|
+
Currently the strategies are executed sequentially so the results can easily be mapped back to which strategy produced which result. In the future that will change so that the strategies are executed concurrently. When that change is introduced the entries in the `results` object will change -- most likely to an Array of Hashes.
|
102
|
+
|
103
|
+
### See my **experiments** Repository in the **stocks** Directory
|
104
|
+
|
105
|
+
I have a program `analysis.rb` that I'm writing along with this `sqa` gem. Its intended as a practical example/test case for how the gem can be used to analyze a complete portfolio one stock at a time.
|
20
106
|
|
21
107
|
## Contributing
|
22
108
|
|
23
|
-
I can always use some help on this stuff. Got an idea for a new
|
109
|
+
I can always use some help on this stuff. Got an idea for a new indicator or strategy? Want to improve the math? Make the signals better? Let's collaborate!
|
24
110
|
|
25
111
|
Bug reports and pull requests are welcome on GitHub at https://github.com/MadBomber/sqa.
|
26
112
|
|
@@ -0,0 +1 @@
|
|
1
|
+
0bf41b69d8f760412a87aa119292b29f8d23c1a09daf059643314cb00fc251a5bd88a4843d9ed9e2493a9b177389a4bba2b7aff998d90bc7b50b81fb9fd81e47
|
@@ -0,0 +1 @@
|
|
1
|
+
e63dcb48ad13d1d8397b5445e25d15e58ede7191e6b9f5c5a106d701a01020036eb37cc1b45954d7fb7aa819527452df7b16b82678f439b138833c8ad0377dec
|
@@ -0,0 +1 @@
|
|
1
|
+
a307251c474edb1367cd0d0a7d2cc663acf98052d070273ff157a155370f87cb43fe10c33403fa53e7b07c9a649d00ef12013c7194fd19ee73c1955e0146b046
|
data/lib/sqa/data_frame.rb
CHANGED
@@ -13,4 +13,17 @@ class SQA::DataFrame < Daru::DataFrame
|
|
13
13
|
df[:ticker] = ticker
|
14
14
|
df
|
15
15
|
end
|
16
|
+
|
17
|
+
def self.load(filename)
|
18
|
+
source = path(filename)
|
19
|
+
type = source.extname.downcase
|
20
|
+
|
21
|
+
if ".csv" == type
|
22
|
+
@df = Daru::DataFrame.from_csv(source)
|
23
|
+
elsif ".json" == type
|
24
|
+
@df = Daru::DataFrame.from_json(source)
|
25
|
+
else
|
26
|
+
raise SQA::BadParamenterError, "supports csv or json only"
|
27
|
+
end
|
28
|
+
end
|
16
29
|
end
|
data/lib/sqa/errors.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Trading Strategies
|
2
|
+
|
3
|
+
A **strategy** is implemented as a class within the namespace of SQA::Strategy -- see the file lib/sqa/strategy/random.rb as an example. All strategy classes should have a class method **trade** which is the primary entry point into the strategy. The class can be extended using the SQA::Strategy::Common module which adds common class methods such as **trade_against** and **desc**
|
4
|
+
|
5
|
+
## The **trade** Class Method
|
6
|
+
|
7
|
+
The method accepts a single parameter called a vector. The vector parameter is an object that contains the information required by the strategy in order to make a recommendation. In the examples provided this object is an OpenStruct. You can either follow this pattern or use your own; however, at this time only one parameter is allowed for the trade method.
|
8
|
+
|
9
|
+
## The **trade_against** Class Method
|
10
|
+
|
11
|
+
If **trade** is consistently wrong many more times than it is right, don't throw it out. Just start using the **trade_against** class method instead. This method takes the recommendation of the **trade** class method and suggests the opposite.
|
12
|
+
|
13
|
+
## The **desc** Class Method
|
14
|
+
|
15
|
+
You can document your strategy in a markdown formatted file. The **desc** class method will look for the filename in the same directory as the strategy file. This is typically in the lib/sqa/strategy directory.
|
16
|
+
|
17
|
+
The **desc** class method will find the markdown file and return its contents as a String so that you can do with it as you please.
|
18
|
+
|
19
|
+
## Example Strategies
|
20
|
+
|
21
|
+
The follow examples are provided:
|
22
|
+
|
23
|
+
* ema.rb
|
24
|
+
* mp.rb
|
25
|
+
* mr.rb
|
26
|
+
* random.rb
|
27
|
+
* rsi.rb
|
28
|
+
* sma.rb
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
Tge SQA::Strategy class manages an Array of trading strategies. You can add to the Array multiple strategies like this:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require 'sqa'
|
36
|
+
ss = SQA::Strategy.new
|
37
|
+
ss.add SQA::Strategy::Random.method(:trade)
|
38
|
+
ss.add SQA::Strategy::SMA.method(:trade)
|
39
|
+
ss.add SQA::Strategy::EMA
|
40
|
+
```
|
41
|
+
|
42
|
+
Note that if your primary entry point to the trading strategy class is **trade** then the parameter to the **add** function does not have to include the ".method(:entry_point)" -- just use the class name by itself. If you do not use **trade** as as class method in you strategy class, then the **trade_against** method added by the SQA::Strategy::Common module will not work.
|
43
|
+
|
44
|
+
If you want to evaluate the **trade_against** class method in a strategy then you must the "ss.add SQA::Strategy::Random.method(:trade_against)" pattern.
|
45
|
+
|
46
|
+
Then for any specific stock create your vector object that contains all the information that the trading strategies need in order to make their recommendations. Once you have you vector you can then **execute** the Array of strategies.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
vector = ...
|
50
|
+
result = ss.execute(vector)
|
51
|
+
```
|
52
|
+
|
53
|
+
**result** will be an Array of recommendations from the different strategies on whether to :bur, :sell or :hold.
|
54
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# lib/sqa/strategry/common.rb
|
2
|
+
|
3
|
+
# This module needs to be extend'ed within
|
4
|
+
# a strategy class so that these common class
|
5
|
+
# methods are available in every trading strategy.
|
6
|
+
|
7
|
+
class SQA::Strategy
|
8
|
+
module Common
|
9
|
+
def trade_against(vector)
|
10
|
+
return :hold unless respond_to? :trade
|
11
|
+
|
12
|
+
recommendation = trade(vector)
|
13
|
+
|
14
|
+
if :sell == recommendation
|
15
|
+
:buy
|
16
|
+
elsif :buy == recommendation
|
17
|
+
:sell
|
18
|
+
else
|
19
|
+
:hold
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def desc
|
24
|
+
doc_filename = self.name.split('::').last.downcase + ".md"
|
25
|
+
doc_path = Pathname.new(__dir__) + doc_filename
|
26
|
+
|
27
|
+
debug_me{[ :doc_path ]}
|
28
|
+
|
29
|
+
if doc_path.exist?
|
30
|
+
doc = doc_path.read
|
31
|
+
else
|
32
|
+
doc = "A description of #{self.name} is not available"
|
33
|
+
end
|
34
|
+
|
35
|
+
puts doc
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# lib/sqa/strategry/consensus.rb
|
2
|
+
|
3
|
+
require_relative 'common'
|
4
|
+
|
5
|
+
class SQA::Strategy::Consensus
|
6
|
+
extend SQA::Strategy::Common
|
7
|
+
|
8
|
+
def self.trade(vector)
|
9
|
+
new(vector).my_fancy_trader
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(vector)
|
13
|
+
@vector = vector
|
14
|
+
@results = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def my_fancy_trader
|
18
|
+
strat_one
|
19
|
+
strat_two
|
20
|
+
strat_three
|
21
|
+
strat_four
|
22
|
+
strat_five
|
23
|
+
consensus
|
24
|
+
end
|
25
|
+
|
26
|
+
def consensus
|
27
|
+
count = @results.group_by(&:itself).transform_values(&:count)
|
28
|
+
|
29
|
+
if count[:buy] > count[:sell]
|
30
|
+
:buy
|
31
|
+
elsif count[:sell] > count[:buy]
|
32
|
+
:sell
|
33
|
+
else
|
34
|
+
:hold
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def strat_one = @results << (0==rand(2) ? :buy : :sell)
|
39
|
+
def strat_two = @results << (0==rand(2) ? :buy : :sell)
|
40
|
+
def strat_three = @results << (0==rand(2) ? :buy : :sell)
|
41
|
+
def strat_four = @results << (0==rand(2) ? :buy : :sell)
|
42
|
+
def strat_five = @results << :hold
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# lib/sqa/strategry/ema.rb
|
2
|
+
|
3
|
+
require_relative 'common'
|
4
|
+
|
5
|
+
class SQA::Strategy::EMA
|
6
|
+
extend SQA::Strategy::Common
|
7
|
+
|
8
|
+
def self.trade(vector)
|
9
|
+
ema_trend = vector.ema[:trend]
|
10
|
+
|
11
|
+
if :up == ema_trend
|
12
|
+
:buy
|
13
|
+
elsif :down == ema_trend
|
14
|
+
:sell
|
15
|
+
else
|
16
|
+
:hold
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# lib/sqa/strategry/mp.rb
|
2
|
+
|
3
|
+
require_relative 'common'
|
4
|
+
|
5
|
+
class SQA::Strategy::MP
|
6
|
+
extend SQA::Strategy::Common
|
7
|
+
|
8
|
+
def self.trade(vector)
|
9
|
+
mp = vector.market_profile=:mixed,
|
10
|
+
|
11
|
+
if :resistance == mp
|
12
|
+
:sell
|
13
|
+
elsif :support == mp
|
14
|
+
:buy
|
15
|
+
else
|
16
|
+
:hold
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# SQA::Strategy::Random
|
2
|
+
|
3
|
+
A random flip of a three headed coin.
|
4
|
+
|
5
|
+
Responds with a :buy, :sell or :hold recommendation based upon a random number. Out of 9 possible random numbers, the lower third, middle third and upper third of the number set are used to determine the recommendation.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# lib/sqa/strategry/random.rb
|
2
|
+
|
3
|
+
require_relative 'common'
|
4
|
+
|
5
|
+
class SQA::Strategy::Random
|
6
|
+
extend SQA::Strategy::Common
|
7
|
+
|
8
|
+
def self.trade(vector)
|
9
|
+
case rand(9)
|
10
|
+
when (0..2)
|
11
|
+
:buy
|
12
|
+
when (3..5)
|
13
|
+
:sell
|
14
|
+
else
|
15
|
+
:hold
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# lib/sqa/strategry/rsi.rb
|
2
|
+
|
3
|
+
require_relative 'common'
|
4
|
+
|
5
|
+
class SQA::Strategy::RSI
|
6
|
+
extend SQA::Strategy::Common
|
7
|
+
|
8
|
+
def self.trade(vector)
|
9
|
+
rsi_trend = vector.rsi[:trend]
|
10
|
+
|
11
|
+
if :over_bought == rsi_trend
|
12
|
+
:sell
|
13
|
+
elsif :over_sold == rsi_trend
|
14
|
+
:buy
|
15
|
+
else
|
16
|
+
:hold
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# lib/sqa/strategry/sma.rb
|
2
|
+
|
3
|
+
require_relative 'common'
|
4
|
+
|
5
|
+
class SQA::Strategy::SMA
|
6
|
+
extend SQA::Strategy::Common
|
7
|
+
|
8
|
+
def self.trade(vector)
|
9
|
+
sma_trend = vector.rsi[:trend]
|
10
|
+
|
11
|
+
if :up == sma_trend
|
12
|
+
:buy
|
13
|
+
elsif :down == sma_trend
|
14
|
+
:sell
|
15
|
+
else
|
16
|
+
:hold
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/sqa/strategy.rb
CHANGED
@@ -7,9 +7,16 @@ class SQA::Strategy
|
|
7
7
|
@strategies = []
|
8
8
|
end
|
9
9
|
|
10
|
-
def add(
|
11
|
-
|
12
|
-
|
10
|
+
def add(a_strategy)
|
11
|
+
raise SQA::BadParameterError unless [Class, Method].include? a_strategy.class
|
12
|
+
|
13
|
+
a_proc = if Class == a_strategy.class
|
14
|
+
a_strategy.method(:trade)
|
15
|
+
else
|
16
|
+
a_strategy
|
17
|
+
end
|
18
|
+
|
19
|
+
@strategies << a_proc
|
13
20
|
end
|
14
21
|
|
15
22
|
def execute(v)
|
@@ -18,48 +25,33 @@ class SQA::Strategy
|
|
18
25
|
@strategies.each { |signal| result << signal.call(v) }
|
19
26
|
result
|
20
27
|
end
|
21
|
-
end
|
22
28
|
|
23
|
-
|
29
|
+
def auto_load(except: [:common], only: [])
|
30
|
+
dir_path = Pathname.new(__dir__) + "strategy"
|
31
|
+
except = Array(except).map{|f| f.to_s.downcase}
|
32
|
+
only = Array(only).map{|f| f.to_s.downcase}
|
24
33
|
|
25
|
-
|
26
|
-
|
34
|
+
dir_path.children.each do |child|
|
35
|
+
next unless ".rb" == child.extname.downcase
|
27
36
|
|
28
|
-
|
37
|
+
basename = child.basename.to_s.split('.').first.downcase
|
29
38
|
|
30
|
-
|
31
|
-
|
32
|
-
when (8..)
|
33
|
-
:buy
|
34
|
-
when (..3)
|
35
|
-
:sell
|
36
|
-
else
|
37
|
-
:hold
|
38
|
-
end
|
39
|
-
end
|
39
|
+
next if except.include? basename
|
40
|
+
next if !only.empty? && !only.include?(basename)
|
40
41
|
|
42
|
+
print "loading #{basename} ... "
|
43
|
+
load child
|
44
|
+
puts "done"
|
45
|
+
end
|
41
46
|
|
42
|
-
|
43
|
-
case rand(10)
|
44
|
-
when (8..)
|
45
|
-
:sell
|
46
|
-
when (..3)
|
47
|
-
:buy
|
48
|
-
else
|
49
|
-
:keep
|
47
|
+
nil
|
50
48
|
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def magic(vector)
|
54
|
-
0 == rand(2) ? :spend : :save
|
55
|
-
end
|
56
49
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
vector.rsi[:rsi]
|
50
|
+
def available
|
51
|
+
ObjectSpace.each_object(Class).select { |klass|
|
52
|
+
klass.to_s.start_with?("SQA::Strategy::")
|
53
|
+
}
|
62
54
|
end
|
63
55
|
end
|
64
56
|
|
65
|
-
|
57
|
+
|
data/lib/sqa/trade.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# lib/sqa/trade.rb
|
2
|
+
|
3
|
+
|
4
|
+
class SQA::Trade
|
5
|
+
attr_accessor :df
|
6
|
+
|
7
|
+
def initialize(filename="trades.csv")
|
8
|
+
@df = SQA::DataFrame.load(filename)
|
9
|
+
end
|
10
|
+
|
11
|
+
def place(signal, ticker, shares, price=nil)
|
12
|
+
# TODO: insert row into @df
|
13
|
+
|
14
|
+
uuid = rand(100000) # FIXME: place holder
|
15
|
+
end
|
16
|
+
|
17
|
+
def confirm(uuid, shares, price)
|
18
|
+
# TODO: update the row in the data frame
|
19
|
+
end
|
20
|
+
|
21
|
+
def save
|
22
|
+
# TODO: save the data frame
|
23
|
+
end
|
24
|
+
end
|
data/lib/sqa/version.rb
CHANGED
data/lib/sqa.rb
CHANGED
@@ -4,10 +4,6 @@ require 'active_support'
|
|
4
4
|
require 'active_support/core_ext/string'
|
5
5
|
require 'daru'
|
6
6
|
require 'date'
|
7
|
-
|
8
|
-
require 'debug_me'
|
9
|
-
include DebugMe
|
10
|
-
|
11
7
|
require 'descriptive_statistics'
|
12
8
|
require 'mixlib/config'
|
13
9
|
require 'nenv'
|
@@ -20,6 +16,22 @@ end
|
|
20
16
|
|
21
17
|
|
22
18
|
module SQA
|
19
|
+
Signal = {
|
20
|
+
hold: 0,
|
21
|
+
buy: 1,
|
22
|
+
sell: 2
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
Trend = {
|
26
|
+
up: 0,
|
27
|
+
down: 1
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
Swing = {
|
31
|
+
valley: 0,
|
32
|
+
peak: 1,
|
33
|
+
}.freeze
|
34
|
+
|
23
35
|
module Config
|
24
36
|
extend Mixlib::Config
|
25
37
|
config_strict_mode true
|
@@ -40,8 +52,10 @@ end
|
|
40
52
|
require_relative "sqa/data_frame"
|
41
53
|
require_relative "sqa/errors"
|
42
54
|
require_relative "sqa/indicator"
|
55
|
+
require_relative "sqa/portfolio"
|
43
56
|
require_relative "sqa/strategy"
|
44
57
|
require_relative "sqa/stock"
|
58
|
+
require_relative "sqa/trade"
|
45
59
|
require_relative "sqa/version"
|
46
60
|
|
47
61
|
|
metadata
CHANGED
@@ -1,15 +1,183 @@
|
|
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.5
|
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-
|
12
|
-
dependencies:
|
11
|
+
date: 2023-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: active_support
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: daru
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: descriptive_statistics
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: gruff
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mixlib-config
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: nenv
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: version_gem
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: amazing_print
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: bundler
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: debug_me
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: minitest
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rake
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
13
181
|
description: Simplistic playpen (e.g. not for serious use) for doing technical analysis
|
14
182
|
of stock prices.
|
15
183
|
email:
|
@@ -20,7 +188,6 @@ extensions: []
|
|
20
188
|
extra_rdoc_files: []
|
21
189
|
files:
|
22
190
|
- ".envrc"
|
23
|
-
- ".irbrc"
|
24
191
|
- CHANGELOG.md
|
25
192
|
- LICENSE
|
26
193
|
- README.md
|
@@ -28,6 +195,9 @@ files:
|
|
28
195
|
- bin/sqa
|
29
196
|
- checksums/sqa-0.0.1.gem.sha512
|
30
197
|
- checksums/sqa-0.0.2.gem.sha512
|
198
|
+
- checksums/sqa-0.0.3.gem.sha512
|
199
|
+
- checksums/sqa-0.0.4.gem.sha512
|
200
|
+
- checksums/sqa-0.0.5.gem.sha512
|
31
201
|
- docs/README.md
|
32
202
|
- docs/average_true_range.md
|
33
203
|
- docs/bollinger_bands.md
|
@@ -77,9 +247,20 @@ files:
|
|
77
247
|
- lib/sqa/indicator/simple_moving_average_trend.rb
|
78
248
|
- lib/sqa/indicator/stochastic_oscillator.rb
|
79
249
|
- lib/sqa/indicator/true_range.rb
|
80
|
-
- lib/sqa/
|
250
|
+
- lib/sqa/portfolio.rb
|
81
251
|
- lib/sqa/stock.rb
|
82
252
|
- lib/sqa/strategy.rb
|
253
|
+
- lib/sqa/strategy/README.md
|
254
|
+
- lib/sqa/strategy/common.rb
|
255
|
+
- lib/sqa/strategy/consensus.rb
|
256
|
+
- lib/sqa/strategy/ema.rb
|
257
|
+
- lib/sqa/strategy/mp.rb
|
258
|
+
- lib/sqa/strategy/mr.rb
|
259
|
+
- lib/sqa/strategy/random.md
|
260
|
+
- lib/sqa/strategy/random.rb
|
261
|
+
- lib/sqa/strategy/rsi.rb
|
262
|
+
- lib/sqa/strategy/sma.rb
|
263
|
+
- lib/sqa/trade.rb
|
83
264
|
- lib/sqa/version.rb
|
84
265
|
homepage: https://github.com/MadBomber/sqa
|
85
266
|
licenses:
|
data/.irbrc
DELETED