Module: Cotcube::Helpers
- Defined in:
- lib/cotcube-helpers/input.rb,
lib/cotcube-helpers.rb,
lib/cotcube-helpers/init.rb,
lib/cotcube-helpers/output.rb,
lib/cotcube-helpers/reduce.rb,
lib/cotcube-helpers/symbols.rb,
lib/cotcube-helpers/constants.rb,
lib/cotcube-helpers/expiration.rb,
lib/cotcube-helpers/get_id_set.rb,
lib/cotcube-helpers/subpattern.rb,
lib/cotcube-helpers/data_client.rb,
lib/cotcube-helpers/orderclient.rb,
lib/cotcube-helpers/parallelize.rb,
lib/cotcube-helpers/recognition.rb,
lib/cotcube-helpers/cache_client.rb,
lib/cotcube-helpers/ib_contracts.rb,
lib/cotcube-helpers/josch_client.rb,
lib/cotcube-helpers/order_client.rb,
lib/cotcube-helpers/simple_output.rb,
lib/cotcube-helpers/simple_series_stats.rb,
lib/cotcube-helpers/deep_decode_datetime.rb
Overview
Missing top level documentation
Defined Under Namespace
Modules: Candlestick_Recognition Classes: CacheClient, DataClient, ExpirationMonth, JoSchClient, OrderClient, SimpleOutput
Constant Summary collapse
- SYMBOL_HEADERS =
%i[ id symbol ib_symbol internal exchange currency ticksize power months type bcf reports format name ]
- SYMBOL_EXAMPLES =
[ { id: '13874U', symbol: 'ES', ib_symbol: 'ES', internal: 'ES', exchange: 'GLOBEX', currency: 'USD', ticksize: 0.25, power: 12.5, months: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'S&P 500 MICRO' }, { id: '209747', symbol: 'NQ', ib_symbol: 'NQ', internal: 'NQ', exchange: 'GLOBEx', currency: 'USD', ticksize: 0.25, power: 5.0, monhts: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'NASDAQ 100 MICRO' } ].freeze
- MICRO_EXAMPLES =
[ { id: '13874U', symbol: 'ET', ib_symbol: 'MES', internal: 'MES', exchange: 'GLOBEX', currency: 'USD', ticksize: 0.25, power: 1.25, months: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'MICRO S&P 500 MICRO' }, { id: '209747', symbol: 'NM', ib_symbol: 'MNQ', internal: 'MNQ', exchange: 'GLOBEX', currency: 'USD', ticksize: 0.25, power: 0.5, monhts: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'MICRO NASDAQ 100 MICRO' } ].freeze
- COLORS =
%i[light_red light_yellow light_green red yellow green cyan magenta blue].freeze
- MONTH_COLOURS =
{ 'F' => :cyan, 'G' => :green, 'H' => :light_green, 'J' => :blue, 'K' => :yellow, 'M' => :light_yellow, 'N' => :cyan, 'Q' => :magenta, 'U' => :light_magenta, 'V' => :blue, 'X' => :red, 'Z' => :light_red }.freeze
- MONTHS =
{ 'F' => 1, 'G' => 2, 'H' => 3, 'J' => 4, 'K' => 5, 'M' => 6, 'N' => 7, 'Q' => 8, 'U' => 9, 'V' => 10, 'X' => 11, 'Z' => 12, 1 => 'F', 2 => 'G', 3 => 'H', 4 => 'J', 5 => 'K', 6 => 'M', 7 => 'N', 8 => 'Q', 9 => 'U', 10 => 'V', 11 => 'X', 12 => 'Z' }.freeze
- CHICAGO =
Time.find_zone('America/Chicago')
- NEW_YORK =
Time.find_zone('America/New_York')
- BERLIN =
Time.find_zone('Europe/Berlin')
- DATE_FMT =
'%Y-%m-%d'
- LETTERS =
Simple mapper to get from MONTH to LETTER
{ "JAN"=> "F", "FEB"=> "G", "MAR"=> "H", "APR"=> "J", "MAY"=> "K", "JUN"=> "M", "JUL"=> "N", "AUG"=> "Q", "SEP"=> "U", "OCT"=> "V", "NOV"=> "X", "DEC"=> "Z" }
- CR =
Candlestick_Recognition
- JoschClient =
JoSchClient
- VALID_DATETIME_STRING =
lambda {|str| str.is_a?(String) and [10,25,29].include?(str.length) and str.count("^0-9:TZ+-= ").zero? }
Class Method Summary collapse
- .config_path ⇒ Object
- .config_prefix ⇒ Object
- .deep_decode_datetime(data, zone: DateTime) ⇒ Object
- .get_ib_contract ⇒ Object
- .get_id_set ⇒ Object
- .init ⇒ Object
- .instance_inspect(obj, keylength: 20, &block) ⇒ Object
- .keystroke ⇒ Object
- .micros ⇒ Object
- .parallelize ⇒ Object
- .reduce ⇒ Object
- .simple_series_stats ⇒ Object
- .sub ⇒ Object
- .symbols ⇒ Object
- .translate_ib_contract ⇒ Object
- .update_ib_contracts ⇒ Object
Instance Method Summary collapse
- #config_path ⇒ Object
- #config_prefix ⇒ Object
- #get_ib_contract(contract) ⇒ Object
- #get_id_set(symbol: nil, id: nil, contract: nil, config: init, mini: false, micro: false) ⇒ Object
- #init(config_file_name: nil, gem_name: nil, debug: false) ⇒ Object
- #keystroke(quit: false) ⇒ Object
- #micros(config: init, **args) ⇒ Object
- #parallelize(ary, processes: 1, threads: 1, progress: '', &block) ⇒ Object
- #reduce(bars:, to: nil, date_alike: :datetime, &block) ⇒ Object
-
#simple_series_stats(base:, ind: nil, dim: 0, format: '% 6.2f', prefix: '', print: true, &block) ⇒ Object
if given a block, :ind of base is set by block.call() dim reduces the sample size by top n% and least n%, so dim of 0.5 would remove 100% of the sample.
-
#sub(minimum: 1) ⇒ Object
sub (should be ‘subpattern’, but too long) is for use in case / when statements it returns a lambda, that checks the case’d expression for matching subpattern based on the the giving minimum.
- #symbols(config: init, **args) ⇒ Object
- #translate_ib_contract(contract) ⇒ Object
- #update_ib_contracts(symbol: nil) ⇒ Object
Class Method Details
.config_path ⇒ Object
.config_prefix ⇒ Object
.deep_decode_datetime(data, zone: DateTime) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/cotcube-helpers/deep_decode_datetime.rb', line 5 def deep_decode_datetime(data, zone: DateTime) case data when nil; nil when VALID_DATETIME_STRING res = nil begin res = zone.parse(data) rescue ArgumentError data end [ DateTime, ActiveSupport::TimeWithZone ].include?(res.class) ? res : data when Array; data.map! { |d| deep_decode_datetime(d, zone: zone) } when Hash; data.transform_values! { |v| deep_decode_datetime(v, zone: zone) } else; data end end |
.get_ib_contract ⇒ Object
.get_id_set ⇒ Object
.init ⇒ Object
.instance_inspect(obj, keylength: 20, &block) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 |
# File 'lib/cotcube-helpers/output.rb', line 4 def instance_inspect(obj, keylength: 20, &block) obj.instance_variables.map do |var| if block_given? block.call(var, obj.instance_variable_get(var)) else puts "#{format "%-#{keylength}s", var }: #{obj.instance_variable_get(var).inspect.scan(/.{1,120}/).join( "\n" + ' '*(keylength+2)) }" end end end |
.keystroke ⇒ Object
.micros ⇒ Object
.parallelize ⇒ Object
.reduce ⇒ Object
.simple_series_stats ⇒ Object
.sub ⇒ Object
.symbols ⇒ Object
.translate_ib_contract ⇒ Object
.update_ib_contracts ⇒ Object
Instance Method Details
#config_path ⇒ Object
18 19 20 |
# File 'lib/cotcube-helpers/init.rb', line 18 def config_path config_prefix + '/etc/cotcube' end |
#config_prefix ⇒ Object
6 7 8 9 10 11 12 13 14 15 16 |
# File 'lib/cotcube-helpers/init.rb', line 6 def config_prefix os = Gem::Platform.local.os case os when 'linux' '' when 'freebsd' '/usr/local' else raise RuntimeError, "Unsupported architecture: #{os}" end end |
#get_ib_contract(contract) ⇒ Object
3 4 5 6 7 8 9 10 |
# File 'lib/cotcube-helpers/ib_contracts.rb', line 3 def get_ib_contract(contract) symbol = contract[..1] # TODO consider file location to be found in configfile filepath = '/etc/cotcube/ibsymbols/' result = YAML.load(File.read( "#{filepath}/#{symbol}.yml"))[contract].transform_keys(&:to_sym) rescue nil result.nil? ? update_ib_contracts(symbol: contract[..1]) : (return result) YAML.load(File.read( "#{filepath}/#{symbol}.yml"))[contract].transform_keys(&:to_sym) rescue nil end |
#get_id_set(symbol: nil, id: nil, contract: nil, config: init, mini: false, micro: false) ⇒ Object
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 |
# File 'lib/cotcube-helpers/get_id_set.rb', line 6 def get_id_set(symbol: nil, id: nil, contract: nil, config: init, mini: false, micro: false) micro = mini || micro contract = contract.to_s.upcase if contract.is_a? Symbol id = id.to_s.upcase if id.is_a? Symbol symbol = symbol.to_s.upcase if symbol.is_a? Symbol if contract.is_a?(String) && ([2,3,4,5].include? contract.length) c_symbol = contract[0..1] if (not symbol.nil?) && (symbol != c_symbol) raise ArgumentError, "Mismatch between given symbol #{symbol} and contract #{contract}" end symbol = c_symbol end unless symbol.nil? sym = symbols(symbol: symbol).presence || micros(symbol: symbol) if sym.nil? || sym[:id].nil? raise ArgumentError, "Could not find match in #{config[:symbols_file]} or #{config[:micros_file]} for given symbol #{symbol}" end raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if (not id.nil?) && (sym[:id] != id) return micro ? micros(id: sym[:id]) : sym end unless id.nil? sym = symbols(id: id) if sym.nil? || sym[:id].nil? raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given id #{id}" end return micro ? micros(id: sym[:id]) : sym end raise ArgumentError, 'Need :id, :symbol or valid :contract ' end |
#init(config_file_name: nil, gem_name: nil, debug: false) ⇒ Object
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 |
# File 'lib/cotcube-helpers/init.rb', line 22 def init(config_file_name: nil, gem_name: nil, debug: false) gem_name ||= self.ancestors.first.to_s name = gem_name.split('::').last.downcase config_file_name = "#{name}.yml" config_file = config_path + "/#{config_file_name}" if File.exist?(config_file) require 'yaml' config = YAML.load(File.read config_file).transform_keys(&:to_sym) else config = {} end defaults = { data_path: '/var/cotcube/' + name, pid_file: "/var/run/cotcube/#{name}.pid" } config = defaults.merge(config) puts "CONFIG is '#{config}'" if debug config end |
#keystroke(quit: false) ⇒ Object
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 |
# File 'lib/cotcube-helpers/input.rb', line 7 def keystroke(quit: false) begin # save previous state of stty old_state = `stty -g` # disable echoing and enable raw (not having to press enter) system 'stty raw -echo' c = $stdin.getc.chr rescue '_' # rubocop:disable Style/RescueModifier # gather next two characters of special keys if c == "\e" extra_thread = Thread.new do c += $stdin.getc.chr c += $stdin.getc.chr end # wait just long enough for special keys to get swallowed extra_thread.join(0.00001) # kill thread so not-so-long special keys don't wait on getc extra_thread.kill end rescue StandardError => e puts "#{e.class}: #{e.}" puts e.backtrace ensure # restore previous state of stty system "stty #{old_state}" end c.each_byte do |x| # rubocop:disable Lint/UnreachableLoop case x when 3 puts 'Strg-C captured, exiting...' quit ? exit : (return true) when 13 return '_return_' when 27 puts 'ESCAPE gathered' return '_esc_' else return c end end end |
#micros(config: init, **args) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/cotcube-helpers/symbols.rb', line 38 def micros(config: init, **args) if config[:micros_file].nil? MICRO_EXAMPLES else CSV .read(config[:micros_file], headers: SYMBOL_HEADERS) .map{|row| row.to_h } .map{|row| [ :ticksize, :power, :bcf ].each {|z| row[z] = row[z].to_f } row[:format] = "%#{row[:format]}f" row[:currency] ||= 'USD' row[:multiplier] = (row[:power] / row[:ticksize]).round(8) row } .reject{|row| row[:id].nil? } .tap{ |all| args.keys.each { |header| unless SYMBOL_HEADERS.include? header puts "WARNING in Cotcube::Helpers.micros: '#{header}' is not a valid symbol header. Skipping..." next end all.select!{|x| x[header] == args[header]} unless args[header].nil? return all.first if all.size == 1 } return all } end end |
#parallelize(ary, processes: 1, threads: 1, progress: '', &block) ⇒ Object
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/cotcube-helpers/parallelize.rb', line 7 def parallelize(ary, processes: 1, threads: 1, progress: '', &block) chunks = [] if [0, 1].include? processes result = Parallel.map(ary, in_threads: threads, &block) elsif [0, 1].include? threads result = Parallel.map(ary, in_processes: processes, &block) else ary.each_slice(threads) { |chunk| chunks << chunk } result = if progress == '' Parallel.map(chunks, in_processes: processes) do |chunk| Parallel.map(chunk, in_threads: threads, &block) end else Parallel.map(chunks, progress: progress, in_processes: processes) do |chunk| Parallel.map(chunk, in_threads: threads, &block) end end end result end |
#reduce(bars:, to: nil, date_alike: :datetime, &block) ⇒ Object
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 |
# File 'lib/cotcube-helpers/reduce.rb', line 6 def reduce(bars:, to: nil, date_alike: :datetime, &block) case to when :days terminators = %i[last daily beginning_of_day] block = proc { |c, b| c[:day] == b[:day] } unless block_given? when :hours terminators = %i[first hours beginning_of_hour] block = proc { |c, b| c[:day] == b[:day] and c[:datetime].hour == b[:datetime].hour } unless block_given? when :weeks terminators = %i[first weeks beginning_of_week] block = proc { |a, b| a[:datetime].to_datetime.cweek == b[:datetime].to_datetime.cweek } unless block_given? when :months terminators = %i[first months beginning_of_month] block = proc { |a, b| a[:datetime].to_datetime.month == b[:datetime].to_datetime.month } unless block_given? else raise ArgumentError, 'Currently supported are reductions to :hours, :days, :weeks, :months ' end determine_date_alike = ->(ary) { ary.send(terminators.first)[date_alike].send(terminators.last) } = lambda do |ary, _date = nil| result = { contract: ary.first[:contract], symbol: ary.first[:symbol], datetime: determine_date_alike.call(ary), day: ary.first[:day], open: ary.first[:open], high: ary.map { |x| x[:high] }.max, low: ary.map { |x| x[:low] }.min, close: ary.last[:close], volume: ary.map { |x| x[:volume] }.reduce(:+), type: terminators[1] } result.map { |k, v| result.delete(k) if v.nil? } result end collector = [] final = [] .each do || if collector.empty? || block.call(collector.last, ) collector << else = .call(collector) final << collector = [] end end = .call(collector) final << final end |
#simple_series_stats(base:, ind: nil, dim: 0, format: '% 6.2f', prefix: '', print: true, &block) ⇒ Object
if given a block, :ind of base is set by block.call() dim reduces the sample size by top n% and least n%, so dim of 0.5 would remove 100% of the sample
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 |
# File 'lib/cotcube-helpers/simple_series_stats.rb', line 8 def simple_series_stats(base:, ind: nil, dim: 0, format: '% 6.2f', prefix: '', print: true, &block) raise ArgumentError, 'Need :ind of type integer' if base.first.is_a?(Array) and not ind.is_a?(Integer) raise ArgumentError, 'Need :ind to evaluate base' if base.first.is_a?(Hash) and ind.nil? dim = dim.to_f if dim.is_a? Numeric dim = 0 if dim==false raise ArgumentError, 'Expecting 0 <= :dim < 0.5' unless dim.is_a?(Float) and dim >= 0 and dim < 0.5 raise ArgumentError, 'Expecting arity of one if block given' if block_given? and not block.arity==1 precision = format[-1] == 'f' ? format[..-2].split('.').last.to_i : 0 worker = base. tap {|b| b.map{|x| x[ind] = block.call(x) } if block.is_a? Proc }. map {|x| ind.nil? ? x : x[ind] }. compact. sort unless dim.zero? reductor = (base.size * dim).round worker = worker[reductor..base.size - reductor] end result = {} result[:size] = worker.size result[:min] = worker.first result[:avg] = (worker.reduce(:+) / result[:size]).round(precision+1) result[:lower] = worker[ (result[:size] * 1 / 4).round ] result[:median] = worker[ (result[:size] * 2 / 4).round ] result[:upper] = worker[ (result[:size] * 3 / 4).round ] result[:max] = worker.last output = result. reject{|k,_| k == :size}. map{|k,v| { type: k, value: v, output: "#{k}: #{format(format, v)}".colorize(k==:avg ? :light_yellow : :white) } }. sort_by{|x| x[:value]}. map{|x| x[:output]} result[:output] = "#{format '%20s',(prefix.empty? ? '' : (prefix + ': '))}" + "[" + " size: #{format '%6d', result[:size]} | ".light_white + output.join(' | ') + " ]" puts result[:output] if print result end |
#sub(minimum: 1) ⇒ Object
sub (should be ‘subpattern’, but too long) is for use in case / when statements
it returns a lambda, that checks the case'd expression for matching subpattern
based on the the giving minimum. E.g. 'a', 'ab' .. 'abcd' will match sub(1){'abcd'}
but only 'abc' and 'abcd' will match sub(3){'abcd'}.:
The recommended use within evaluating user input, where abbreviation of incoming commands
is desirable (h for hoover and hyper, what will translate to sub(2){'hoover'} and sub(2){hyper})
To extend functionality even more, it is possible to send a group of patterns to, like
sub(2){[:hyper,:mega]}, what will respond truthy to "hy" and "meg" but not to "m" or "hypo"
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 |
# File 'lib/cotcube-helpers/subpattern.rb', line 17 def sub(minimum: 1) pattern = yield case pattern when String, Symbol, NilClass pattern = pattern.to_s lambda do |x| return false if x.nil? || (x.size < minimum) return ((pattern =~ /^#{Regexp.escape x}/i).nil? ? false : true) end when Array pattern.map do |x| unless [String, Symbol, NilClass].include? x.class raise TypeError, "Unsupported class '#{x.class}' for '#{x}' in pattern '#{pattern}'." end end lambda do |x| pattern.each do |sub| sub = sub.to_s return false if x.size < minimum result = ((sub =~ /^#{Regexp.escape x}/i).nil? ? false : true) return true if result end return false end else raise TypeError, "Unsupported class #{pattern.class} in Cotcube::Helpers::sub" end end |
#symbols(config: init, **args) ⇒ Object
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 |
# File 'lib/cotcube-helpers/symbols.rb', line 9 def symbols(config: init, **args) if config[:symbols_file].nil? SYMBOL_EXAMPLES else CSV .read(config[:symbols_file], headers: SYMBOL_HEADERS) .map{|row| row.to_h } .map{|row| [ :ticksize, :power, :bcf ].each {|z| row[z] = row[z].to_f} row[:format] = "%#{row[:format]}f" row[:currency] ||= 'USD' row[:multiplier] = (row[:power] / row[:ticksize]).round(8) row } .reject{|row| row[:id].nil? } .tap{ |all| args.keys.each { |header| unless SYMBOL_HEADERS.include? header puts "WARNING in Cotcube::Helpers.symbols: '#{header}' is not a valid symbol header. Skipping..." next end all.select!{|x| x[header] == args[header]} unless args[header].nil? return all.first if all.size == 1 } return all } end end |
#translate_ib_contract(contract) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/cotcube-helpers/ib_contracts.rb', line 54 def translate_ib_contract(contract) short = contract.split(" ").size == 1 sym_a = contract.split(short ? '' : ' ') year = sym_a.pop.to_i + (short ? 20 : 0) if short and sym_a[-1].to_i > 0 year = year - 20 + sym_a.pop.to_i * 10 end month = short ? sym_a.pop : LETTERS[sym_a.pop] sym = Cotcube::Helpers.symbols(internal: sym_a.join)[:symbol] rescue nil sym ||= Cotcube::Helpers.micros(internal: sym_a.join)[:symbol] rescue nil sym.nil? ? false : "#{sym}#{month}#{year}" end |
#update_ib_contracts(symbol: nil) ⇒ Object
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 |
# File 'lib/cotcube-helpers/ib_contracts.rb', line 12 def update_ib_contracts(symbol: nil) begin client = DataClient.new (Cotcube::Helpers.symbols + Cotcube::Helpers.micros).each do |sym| # TODO: consider file location to be located in config file = "/etc/cotcube/ibsymbols/#{sym[:symbol]}.yml" # TODO: VI publishes weekly options which dont match, the 3 others need multiplier enabled to work next if %w[ DY TM SI VI ].include? sym[:symbol] next if symbol and sym[:symbol] != symbol begin if File.exist? file next if Time.now - File.mtime(file) < 5.days data = nil data = YAML.load(File.read(file)) else data = {} end p file %w[ symbol sec_type exchange multiplier ticksize power internal ].each {|z| data.delete z} raw = client.get_contracts(symbol: sym[:symbol]) reply = JSON.parse(raw)['result'] reply.each do |set| contract = translate_ib_contract set['local_symbol'] data[contract] ||= set end keys = data.keys.sort_by{|z| z[2]}.sort_by{|z| z[-2..] }.select{|z| z[..1] == sym[:symbol] } data = data.slice(*keys) File.open(file, 'w'){|f| f.write(data.to_yaml) } rescue Exception => e puts e. p sym binding.irb end end ensure client.stop true end end |