cotcube-indicators 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []