cotcube-indicators 0.1.9

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bf34a07745a24a8973103aad577a9a50ff0c51d8024110b1919a1cc8761a2ab2
4
+ data.tar.gz: 28e0479f8ee5d3b307b1734b7b1dc2daab0e63fcfaf83b683c2db860b8749bde
5
+ SHA512:
6
+ metadata.gz: 6e8ac4bfd2c0c3b1c4a148ce8af941b3641ce3d607ceec42f717dcbad154808436fe6f455f52cd54c0d3dc65f1d847522b216969ded24d9edfb990d6ec76001b
7
+ data.tar.gz: 75313bd5ad890dc65b6733b4a71e391a1f6ad1e29fd4021fed5f5d990a60c3c47a798a853025d389d692a8ebc69b916f9184a314eca15c7d4235757a54eb451d
@@ -0,0 +1,15 @@
1
+ Gemfile.lock
2
+ vendors/
3
+ .idea/
4
+ .bundle/
5
+ Passengerfile.json
6
+ passenger/
7
+ bin/
8
+ public/upload/
9
+ public/css/
10
+ public/full.raw
11
+ public/full.raw.html
12
+ compass/.sass-cache/
13
+ stash/
14
+ .rspec_status
15
+ .yardoc
@@ -0,0 +1,9 @@
1
+ ## 0.1.9 (December 02, 2020)
2
+ - worked through rubocops
3
+ - wrote according README.md
4
+ - added rspec dependency to gemspec
5
+ - added all indicators needed for current COTsignal calculations
6
+
7
+ ## 0.1.1 (December 01, 2020)
8
+
9
+
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in tritangent.gemspec
4
+ gemspec
5
+
@@ -0,0 +1,103 @@
1
+ # Cotcube::Indicators
2
+
3
+ This gem containing the module 'Indicators' is part of the Cotcube suite. Its purpose
4
+ is the application on indicators on a given time series. An example should help to
5
+ understand how it works:
6
+
7
+ ```ruby
8
+ series = 10.times.map {|i| Hash[x: i, y: ((i**2 + 2.0)/(4*i + 1)).round(2)] }
9
+ indicators = {
10
+ first: Cotcube::Indicators.change(key: :y),
11
+ second: Cotcube::Indicators.change(key: :first, lookback: 2)
12
+ }
13
+ series.each {|dataset| indicators.map{|key, lambada| dataset[key] = lambada.call(dataset).round(3) } }
14
+ ```
15
+
16
+ This results in the following series (note that the applied function is arbitrary):
17
+
18
+ | x | y | first | second |
19
+ | --- | --- | --- | --- |
20
+ | 0 | 2.0 | 2.0 | 0.0 |
21
+ | 1 | 0.6 | -1.4 | -1.4 |
22
+ | 2 | 0.67 | 0.07 | -1.93 |
23
+ | 3 | 0.85 | 0.18 | 1.58 |
24
+ | 4 | 1.06 | 0.21 | 0.14 |
25
+ | 5 | 1.29 | 0.23 | 0.05 |
26
+ | 6 | 1.52 | 0.23 | 0.02 |
27
+ | 7 | 1.76 | 0.24 | 0.01 |
28
+ | 8 | 2.0 | 0.24 | 0.01 |
29
+ | 9 | 2.24 | 0.24 | 0.0 |
30
+
31
+ where first displays the change of current `y` to its predecessor and second displays the change of `first` to its pre-predescessor.
32
+
33
+ ## What is this 'Carrier' thingy
34
+
35
+ It's just a super tiny helper class. Most indicators rely on a backward aggregation of data. For this purpose the Carrier
36
+ class maintains an array of given length for its indicator, FIFOing out-aged data.
37
+
38
+ ## Currently available indicators
39
+
40
+ ### calc
41
+
42
+ This indicators is basically a helper. It allows arithmetical combination of 2 parameters (resp. calculation on one by omitting the other). The operation is passed as a block.
43
+
44
+ ### change
45
+
46
+ Returns the difference (i.e. change) between `carrier.get.last` and `carrier.get.first`, where in most occasions `carrier.size` would be 2--hence comparing a current value with it's predecessor.
47
+
48
+ When passed a block, the indicator returns a value only when the block evaluates truthy, otherwise it returns 0'
49
+
50
+ ### index
51
+
52
+ Returns an index-position of the current value within the carrier (i.e. 1.0 for being the highest, 0.0 for being the lowest value).
53
+
54
+ - When passed a block, the indicator returns a value only when the block evaluates truthy, otherwise it returns 0'
55
+ - When passed `abs: true`, the absolute value is processed.
56
+ - When passed `reverse: true`, the result will be inverted by `1.0 - result`.
57
+
58
+ ### score
59
+
60
+ Returns a score position if the current value within the carrier (i.e. 1 for being the highest, __carrier.size__ for being the lowest value).
61
+
62
+ - When passed `abs: true`, the absolute value is processed.
63
+ - When passed `index: true`, the score is converted to an index, so score 1 matches 1.0 and __carrier.size__ matches 0.0.
64
+
65
+ ### true\_range
66
+
67
+ Returns the true range of the current value and it predescessor. Only works when the series contains [:high, :low. :close] or effective alternatives are promoted as parameters. __to get ATR combine this one with sma.__
68
+
69
+ ### sma, ema, rsi
70
+
71
+ Returns the according classical indicator based on the provided `:key` and `:length`.
72
+
73
+
74
+ ## Installation
75
+
76
+ Add this line to your application's Gemfile:
77
+
78
+ ```ruby
79
+ gem 'cotcube-indicators'
80
+ ```
81
+
82
+ And then execute:
83
+
84
+ $ bundle install
85
+
86
+ Or install it yourself as:
87
+
88
+ $ gem install cotcube-indicators
89
+
90
+ ## Development
91
+
92
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
93
+
94
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
95
+
96
+ ## Contributing
97
+
98
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/cotcube-indicators.
99
+
100
+
101
+ ## License
102
+
103
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.9
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'cotcube-indicators'
5
+ spec.version = File.read("#{__dir__}/VERSION")
6
+ spec.authors = ['Benjamin L. Tischendorf']
7
+ spec.email = ['donkeybridge@jtown.eu']
8
+
9
+ spec.summary = 'Lambda based indicators, decoupled from legacy cotcube'
10
+ spec.description = 'Lambda based indicators, decoupled from legacy cotcube'
11
+
12
+ spec.homepage = 'https://github.com/donkeybridge/cotcube-indicators'
13
+ spec.license = 'BSD-4-Clause'
14
+ spec.required_ruby_version = Gem::Requirement.new('~> 2.7')
15
+
16
+ # spec.metadata['allowed_push_host'] = 'http://100.100.0.14:5001'
17
+
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/donkeybridge/cotcube-indicators'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/donkeybridege/cotcube-indicators/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = 'bin'
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_development_dependency 'rake'
32
+ spec.add_development_dependency 'rspec', '~>3.6'
33
+ spec.add_development_dependency 'yard', '~>0.9'
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems/package'
4
+ require_relative 'cotcube-indicators/__carrier'
5
+ require_relative 'cotcube-indicators/calc'
6
+ require_relative 'cotcube-indicators/change'
7
+ require_relative 'cotcube-indicators/index'
8
+ require_relative 'cotcube-indicators/score'
9
+ require_relative 'cotcube-indicators/ema'
10
+ require_relative 'cotcube-indicators/sma'
11
+ require_relative 'cotcube-indicators/rsi'
12
+ require_relative 'cotcube-indicators/true_range'
13
+
14
+ module Cotcube
15
+ module Indicators
16
+ module_function :calc, :change, :ema, :sma, :rsi, :true_range, :index, :score
17
+ end
18
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ class Carrier
6
+ def initialize(length:)
7
+ raise ArgumentError, 'Need a length to be an integer' unless length.is_a? Integer
8
+
9
+ @length = length
10
+ @content = []
11
+ end
12
+
13
+ def <<(obj)
14
+ @content << obj
15
+ @content.shift if @content.length > @length
16
+ obj
17
+ end
18
+
19
+ def shift
20
+ @content.shift
21
+ # sending explicit return to avoid returning a value here
22
+ nil
23
+ end
24
+
25
+ def clear
26
+ @content = []
27
+ end
28
+
29
+ def get
30
+ @content
31
+ end
32
+
33
+ def size
34
+ @content.size
35
+ end
36
+ alias length size
37
+
38
+ def empty?
39
+ @content.empty?
40
+ end
41
+
42
+ def [](key)
43
+ @content.map { |x| x[key] }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ def calc(a:, b:, &block) # rubocop:disable Naming/MethodParameterName
6
+ lambda do |x|
7
+ block.call(x[a.to_sym], x[b.to_sym]).to_f
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ # returns the difference between the current value and the first available value in carrier.
6
+ # if block given, the block is evaluated as conditional, using current and carrier.get.last.
7
+ # if evaluation is false, carrier remains untouched and 0 is returned
8
+ def change(key:, lookback: 1, debug: false)
9
+ unless lookback.is_a?(Integer) && (lookback >= 1)
10
+ raise ArgumentError, 'invalid lookback period, need integer >= 1'
11
+ end
12
+
13
+ carrier = Cotcube::Indicators::Carrier.new(length: lookback + 1)
14
+
15
+ lambda do |x|
16
+ current = x[key.to_sym]
17
+ current = 0 unless current.is_a?(Numeric)
18
+ carrier << 0 if carrier.empty?
19
+ if debug
20
+ puts "comparing #{current} from #{key.to_sym} ... "\
21
+ "#{lookback} ... #{carrier.inspect} ... yield"\
22
+ " #{block_given? ? yield(carrier.get.last, current) : ''}"
23
+ end
24
+ if (not block_given?) || yield(carrier.get.last, current) # rubocop:disable Style/GuardClause
25
+ carrier << current
26
+ ret = carrier.size < lookback + 1 ? 0 : (carrier.get.last - carrier.get.first)
27
+ return ret.to_f
28
+ else
29
+ return 0
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ # the classic exponential moving average
6
+ def ema(key:, length:, smoothing:)
7
+ raise 'Missing parameter, need :length' if length.nil? || (not length.is_a? Integer)
8
+ raise 'Missing parameter, need :key' if key.nil?
9
+
10
+ smoothing ||= (2 / (length - 1).to_f.round(2))
11
+ carrier = Carrier.new(length)
12
+ lambda do |x|
13
+ current = x[key.to_sym]
14
+ carrier << if carrier.empty?
15
+ current * (smoothing / (1 + length))
16
+ else
17
+ current * (smoothing / (1 + length)) + carrier.get[-1] * (1 - (smoothing / (1 + length)))
18
+ end
19
+ carrier.size < length ? -1.0 : carrier.get.last
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ def index(key:, length:, debug: false, reverse: false, abs: false)
6
+ carrier = Carrier.new(length: length)
7
+
8
+ lambda do |x|
9
+ current = x[key.to_sym]
10
+ if debug
11
+ puts "comparing #{current} from #{key} ... "\
12
+ "#{length.nil? ? 0 : length} with yield #{block_given? ? yield(current) : ''}"
13
+ end
14
+ if (not block_given?) || yield(current)
15
+ current = current.abs if abs
16
+ puts "CURRENT: #{current}" if debug
17
+ carrier << current
18
+ puts "CARRIER: #{carrier.inspect}" if debug
19
+ divisor = carrier.get.max - carrier.get.min
20
+ puts "#{divisor} = #{carrier.get.max} - #{carrier.get.min}" if debug
21
+ return -1.0 if divisor.zero?
22
+
23
+ res = ((current - carrier.get.min) / divisor.to_f)
24
+ puts "RESULT: #{res}" if debug
25
+ return reverse ? (1 - res.to_f).round(3).to_f : res.round(3).to_f
26
+ else
27
+ carrier.shift
28
+ return 0
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ def rsi(length:, key:, debug: false)
6
+ raise 'Missing parameter, need :key and :length options' if length.nil? || key.nil?
7
+
8
+ carrier = Carrier.new(length: length)
9
+
10
+ lambda do |x|
11
+ current = x[key.to_sym]
12
+ carrier << current
13
+ puts ". #{carrier.get} ." if debug
14
+ return 50.0 if carrier.length < length
15
+
16
+ u = carrier.get.select(&:positive?).map(&:abs).reduce(:+)
17
+ d = carrier.get.select(&:negative?).map(&:abs).reduce(:+)
18
+ d = d.nil? ? 10**-12 : d / length.to_f
19
+ u = u.nil? ? 10**-12 : u / length.to_f
20
+ return (100 - (100 / (1 + u / d))).to_f
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ def score(key:, length:, abs: false, index: false)
6
+ # returns the position of current within carrier after sorting, i.e. if current has the highest rank it becomes
7
+ # 1, the lowest becomes :length to create comparable results for changing lengths, the score is put onto an
8
+ # index with top rank of 1 (1 - 0/:length)
9
+ carrier = Carrier.new(length: length)
10
+ lambda do |x|
11
+ current = x[key.to_sym]
12
+ current = current.abs if abs
13
+ if current.zero?
14
+ carrier.shift
15
+ return 0
16
+
17
+ end
18
+ carrier << current
19
+ score = carrier.get.sort.reverse.find_index(current)
20
+ score_index = (1 - score / length.to_f).round(3)
21
+ index ? score_index : (score + 1)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ def sma(length:, key:)
6
+ raise 'Missing parameter, need :length and :value options' if opts[:length].nil? || opts[:value].nil?
7
+
8
+ carrier = Cotcube::Indicators::Carrier.new(length: length)
9
+ lambda do |x|
10
+ current = x[key.to_sym]
11
+ carrier << current
12
+ carrier.size < length ? 0.0 : (carrier.get.reduce(:+) / carrier.length).to_f
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cotcube
4
+ module Indicators
5
+ def true_range(high: :high, low: :low, close: :close)
6
+ carrier = Carrier.new(length: 1)
7
+ lambda do |current|
8
+ raise "Missing keys of #{high}, #{low}, #{close} in '#{current}'" unless [high, low, close] - current.keys == []
9
+
10
+ if carrier.empty?
11
+ carrier << current
12
+ return (current[high] - current[low]).to_f
13
+ end
14
+ last = carrier.get.first
15
+ carrier << current
16
+ ([current[high], last[close]].max - [current[low], last[close]].min).to_f
17
+ end
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cotcube-indicators
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.9
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin L. Tischendorf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ description: Lambda based indicators, decoupled from legacy cotcube
56
+ email:
57
+ - donkeybridge@jtown.eu
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - CHANGELOG.md
64
+ - Gemfile
65
+ - README.md
66
+ - Rakefile
67
+ - VERSION
68
+ - cotcube-indicators.gemspec
69
+ - lib/cotcube-indicators.rb
70
+ - lib/cotcube-indicators/__carrier.rb
71
+ - lib/cotcube-indicators/calc.rb
72
+ - lib/cotcube-indicators/change.rb
73
+ - lib/cotcube-indicators/ema.rb
74
+ - lib/cotcube-indicators/index.rb
75
+ - lib/cotcube-indicators/rsi.rb
76
+ - lib/cotcube-indicators/score.rb
77
+ - lib/cotcube-indicators/sma.rb
78
+ - lib/cotcube-indicators/true_range.rb
79
+ homepage: https://github.com/donkeybridge/cotcube-indicators
80
+ licenses:
81
+ - BSD-4-Clause
82
+ metadata:
83
+ homepage_uri: https://github.com/donkeybridge/cotcube-indicators
84
+ source_code_uri: https://github.com/donkeybridge/cotcube-indicators
85
+ changelog_uri: https://github.com/donkeybridege/cotcube-indicators/CHANGELOG.md
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '2.7'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubygems_version: 3.1.2
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Lambda based indicators, decoupled from legacy cotcube
105
+ test_files: []