Module: Cotcube::Indicators
- Defined in:
- lib/cotcube-indicators.rb,
lib/cotcube-indicators/ema.rb,
lib/cotcube-indicators/rsi.rb,
lib/cotcube-indicators/sma.rb,
lib/cotcube-indicators/calc.rb,
lib/cotcube-indicators/index.rb,
lib/cotcube-indicators/score.rb,
lib/cotcube-indicators/change.rb,
lib/cotcube-indicators/__carrier.rb,
lib/cotcube-indicators/threshold.rb,
lib/cotcube-indicators/true_range.rb
Defined Under Namespace
Classes: Carrier
Class Method Summary collapse
- .calc ⇒ Object
- .change ⇒ Object
- .ema ⇒ Object
- .index ⇒ Object
- .rsi ⇒ Object
- .score ⇒ Object
- .sma ⇒ Object
- .threshold ⇒ Object
- .true_range ⇒ Object
Instance Method Summary collapse
-
#calc(a:, b:, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil, finalize: :to_f, &block) ⇒ Object
rubocop:disable Naming/MethodParameterName.
-
#change(key:, lookback: 1, debug: false) ⇒ Object
returns the difference between the current value and the first available value in carrier.
-
#ema(key:, length:, smoothing: nil) ⇒ Object
the classic exponential moving average.
- #index(key:, length:, debug: false, reverse: false, abs: false) ⇒ Object
- #rsi(length:, key:, debug: false) ⇒ Object
- #score(key:, length:, abs: false, index: false) ⇒ Object
- #sma(length:, key:) ⇒ Object
-
#threshold(key:, upper: nil, lower: nil, repeat: false, abs: false, debug: false) ⇒ Object
threshold returns.
- #true_range(high: :high, low: :low, close: :close) ⇒ Object
Class Method Details
.calc ⇒ Object
.change ⇒ Object
.ema ⇒ Object
.index ⇒ Object
.rsi ⇒ Object
.score ⇒ Object
.sma ⇒ Object
.threshold ⇒ Object
.true_range ⇒ Object
Instance Method Details
#calc(a:, b:, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil, finalize: :to_f, &block) ⇒ Object
rubocop:disable Naming/MethodParameterName
5 6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/cotcube-indicators/calc.rb', line 5 def calc(a:, b:, c:nil, d:nil, e:nil, f:nil, g:nil, h:nil, finalize: :to_f, &block) # rubocop:disable Naming/MethodParameterName lambda do |x| block.call( x[a.to_sym], (b.nil? ? nil : x[b.to_sym]), (c.nil? ? nil : x[c.to_sym]), (d.nil? ? nil : x[d.to_sym]), (e.nil? ? nil : x[e.to_sym]), (f.nil? ? nil : x[f.to_sym]), (g.nil? ? nil : x[g.to_sym]), ).send(finalize.to_sym) end end |
#change(key:, lookback: 1, debug: false) ⇒ Object
returns the difference between the current value and the first available value in carrier.
if block given, the block is evaluated as conditional, using current and carrier.get.last.
if evaluation is false, carrier remains untouched and 0 is returned
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/cotcube-indicators/change.rb', line 8 def change(key:, lookback: 1, debug: false) unless lookback.is_a?(Integer) && (lookback >= 1) raise ArgumentError, 'invalid lookback period, need integer >= 1' end carrier = Cotcube::Indicators::Carrier.new(length: lookback + 1) lambda do |x| current = x[key.to_sym] current = 0 unless current.is_a?(Numeric) carrier << 0 if carrier.empty? if debug puts "comparing #{current} from #{key.to_sym} ... "\ "#{lookback} ... #{carrier.inspect} ... yield"\ " #{block_given? ? yield(carrier.get.last, current) : ''}" end if (not block_given?) || yield(carrier.get.last, current) # rubocop:disable Style/GuardClause carrier << current ret = carrier.size < lookback + 1 ? 0 : (carrier.get.last - carrier.get.first) return ret.to_f else return 0 end end end |
#ema(key:, length:, smoothing: nil) ⇒ Object
the classic exponential moving average
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/cotcube-indicators/ema.rb', line 6 def ema(key:, length:, smoothing: nil) raise ArgumentError, 'Improper :length parameter, should be positive Integer' unless length.is_a? Integer and length.positive? smoothing ||= (2 / (length - 1).to_f.round(2)) raise ArgumentError, 'Improper smoothing, should be Numeric' unless smoothing.is_a? Numeric carrier = Cotcube::Indicators::Carrier.new(length: length) lambda do |x| current = x[key.to_sym] carrier << if carrier.empty? current * (smoothing / (1.0 + length)) else current * (smoothing / (1.0 + length)) + carrier.get[-1] * (1.0 - (smoothing / (1.0 + length))) end carrier.size < length ? -1.0 : carrier.get.last end end |
#index(key:, length:, debug: false, reverse: false, abs: false) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/cotcube-indicators/index.rb', line 5 def index(key:, length:, debug: false, reverse: false, abs: false) carrier = Carrier.new(length: length) lambda do |x| current = x[key.to_sym] if debug puts "comparing #{current} from #{key} ... "\ "#{length.nil? ? 0 : length} with yield #{block_given? ? yield(current) : ''}" end if (not block_given?) || yield(current) current = current.abs if abs puts "CURRENT: #{current}" if debug carrier << current puts "CARRIER: #{carrier.inspect}" if debug divisor = carrier.get.max - carrier.get.min puts "#{divisor} = #{carrier.get.max} - #{carrier.get.min}" if debug return -1.0 if divisor.zero? res = ((current - carrier.get.min) / divisor.to_f) puts "RESULT: #{res}" if debug return reverse ? (1 - res.to_f).round(3).to_f : res.round(3).to_f else carrier << 0 return 0 end end end |
#rsi(length:, key:, debug: false) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/cotcube-indicators/rsi.rb', line 5 def rsi(length:, key:, debug: false) carrier = Carrier.new(length: length) lambda do |x| current = x[key.to_sym] carrier << current puts ". #{carrier.get} ." if debug return 50.0 if carrier.length < length u = carrier.get.select(&:positive?).map(&:abs).reduce(:+) d = carrier.get.select(&:negative?).map(&:abs).reduce(:+) d = d.nil? ? 10**-12 : d / length.to_f u = u.nil? ? 10**-12 : u / length.to_f return (100 - (100 / (1 + u / d))).to_f end end |
#score(key:, length:, abs: false, index: false) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/cotcube-indicators/score.rb', line 5 def score(key:, length:, abs: false, index: false) # returns the position of current within carrier after sorting, i.e. if current has the highest rank it becomes # 1, the lowest becomes :length to create comparable results for changing lengths, the score is put onto an # index with top rank of 1 (1 - 0/:length) carrier = Carrier.new(length: length) lambda do |x| current = x[key.to_sym] current = current.abs if abs if current.zero? carrier << 0 return 0 end carrier << current score = carrier.get.sort.reverse.find_index(current) score_index = (1 - score / length.to_f).round(3) index ? score_index : (score + 1) end end |
#sma(length:, key:) ⇒ Object
5 6 7 8 9 10 11 12 13 |
# File 'lib/cotcube-indicators/sma.rb', line 5 def sma(length:, key:) carrier = Cotcube::Indicators::Carrier.new(length: length) lambda do |x| current = x[key.to_sym] carrier << current carrier.size < length ? 0.0 : (carrier.get.reduce(:+) / carrier.length.to_f).to_f end end |
#threshold(key:, upper: nil, lower: nil, repeat: false, abs: false, debug: false) ⇒ Object
threshold returns
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/cotcube-indicators/threshold.rb', line 6 def threshold(key:, upper: nil, lower: nil, repeat: false, abs: false, debug: false) raise ArgumentError, "Need to set :upper: (set to Float::INFINITY to omit)" if upper.nil? raise ArgumentError, "Need to set :lower unless :abs is set" if lower.nil? and not abs raise ArgumentError, ":lower, upper must be numeric" if not upper.is_a?(Numeric) and ( abs ? true : lower.is_a?(Numeric) ) raise ArgumentError, "Found bogus, :lower #{lower} must be lower than :upper #{upper}" if not abs and lower > upper # For this indicator, we just need to remembers -1, 0 or 1. That does not need a helper class. carrier = 0 lambda do |current| current = current[key.to_sym] current = current.abs if abs puts "UPPER: #{upper}\tLOWER: #{lower}\tCARRIER was: #{carrier}\tCURRENT: #{current}" if debug # facing options # 1. carrier is zero if carrier.zero? # 1.a. and threshold if current >= upper carrier = 1 return 1 elsif not abs and current <= lower carrier = -1 return -1 # 1.b. and no threshold else return 0 end # 2. carrier is non-zero else # and threshold # if threshold matches carrier, return 0 and keep carrier or return carrier if :repeat # if not abs, allow reversing on appropriate threshold if carrier.positive? and current >= upper return repeat ? 1 : 0 elsif not abs and carrier.negative? and current <= lower return repeat ? -1 : 0 elsif not abs and carrier.positive? and current <= lower carrier = -1 return -1 elsif not abs and carrier.negative? and current >= upper carrier = 1 return 1 # and no threshold # silently unset carrier, return 0 else carrier = 0 return 0 end end end end |
#true_range(high: :high, low: :low, close: :close) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# File 'lib/cotcube-indicators/true_range.rb', line 5 def true_range(high: :high, low: :low, close: :close) carrier = Carrier.new(length: 1) lambda do |current| raise "Missing keys of #{high}, #{low}, #{close} in '#{current}'" unless [high, low, close] - current.keys == [] if carrier.empty? carrier << current return (current[high] - current[low]).to_f end last = carrier.get.first carrier << current ([current[high], last[close]].max - [current[low], last[close]].min).to_f end end |