cotcube-level 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7756ccc0318477cf223fc205720c986daaaad4304439108b960ca410a15acf79
4
- data.tar.gz: ba7a79e928da4c89b0e7103394899096ab42f5977986892c5e7396fe5d4abeff
3
+ metadata.gz: 81e95a5e617ba6dd8fdf5df13c0395455ff19a337e2e72e79f485a37489049c0
4
+ data.tar.gz: d592daecc1be2e4686fd3a6807d82c5f5429de6b451b96e4aee30430fe0fbe9d
5
5
  SHA512:
6
- metadata.gz: ceb38b4a6c827b0d8a7b09e0c0f86e9050e49dce54504cf5075d2d2da5fb9fe99186c8e9aa3403a14b61d5d6fa831e86a43886279dea8e20efd1c818e119d1f0
7
- data.tar.gz: f53b20c6848b4d7cc825a18819ace61ebade1238706dbd125e76318eeb2dd1e4abbc6dca2d9872accb0069df8ac9edb16762c45ea7bc9a8293b95452d36102a6
6
+ metadata.gz: 78459e2694a2c1f97357307d550df8c9920b18be785b1c28d712b8d61c074ba4326c867b107b62985030fac412805c548858ee086f13802dec13b01e75c10f9c
7
+ data.tar.gz: 0a7ccd8153cda7309f70fbffb2fc2fe7f2595194ddf4688bcc13d28c86418fbe0f23322f5c47a2fba7b4aca0832e95b877747158bd17ad1da33a634bbc3eb529
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## 0.3.2 (August 29, 2021)
2
+ - tritangulate: fixing 'finalize', as Integer zero won't comparte to Float zero
3
+ - cotcube-level.rb: added :check_exceedance
4
+ - helpers:member_to_human: added :daily param to distinguish stencils
5
+ - tritangulate: added 'min_ratio' as param with lambda
6
+ - eod_stencil: added #use to calculate current swap line value / dist;
7
+ - tritangulate: added interval to be saved with the swap information.
8
+ - helpers: moved puts_swaps to puts_swap, and added a :short switch for 1-liners per swap
9
+
10
+ ## 0.3.1.1 (August 25, 2021)
11
+ - trying to fix versioning mistake
12
+ - Bump version to 0.3.2.1.
13
+ - minor fixes correcting mistakes sneaked in during documentation rework
14
+ - minor fix
15
+
16
+ ## 0.3.2.1 (August 25, 2021)
17
+ - minor fixes correcting mistakes sneaked in during documentation rework
18
+ - minor fix
19
+
1
20
  ## 0.3.1 (August 24, 2021)
2
21
  - renaming triangulation to tritangulation
3
22
  - minor fixes in README and gemspec
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.3.2
@@ -43,7 +43,6 @@ module Cotcube
43
43
  range: nil, # used to shrink the stencil size, accepts String or Date
44
44
  interval:,
45
45
  swap_type:,
46
- contract: nil,
47
46
  date: nil,
48
47
  debug: false,
49
48
  version: nil, # when referring to a specicic version of the stencil
@@ -54,7 +53,6 @@ module Cotcube
54
53
  @debug = debug
55
54
  @interval = interval == :continuous ? :daily : interval
56
55
  @swap_type = swap_type
57
- @contract = contract
58
56
  @warnings = warnings
59
57
  step = case @interval
60
58
  when :hours, :hour; 1.hour
@@ -108,7 +106,6 @@ module Cotcube
108
106
  interval: @interval,
109
107
  swap_type: @swap_type,
110
108
  date: @date,
111
- contract: @contract,
112
109
  stencil: @base.map{|x| x.dup}
113
110
  )
114
111
  end
@@ -139,6 +136,22 @@ module Cotcube
139
136
  to.reject!{|x| x[:x].nil? }
140
137
  end
141
138
 
139
+ def use(with:, sym:, zero:, grace: -2)
140
+ # todo: validate with (check if vslid swap
141
+ # sym (check keys)
142
+ # zero (ohlc with x.zero?)
143
+ # side ( upper or lower)
144
+ swap = with.dup
145
+ high = swap[:side] == :upper
146
+ ohlc = high ? :high : :low
147
+ start = base.find{|x| swap[:datetime] == x[:datetime]}
148
+ swap[:current_change] = (swap[:tpi] * start[:x]).round(8)
149
+ swap[:current_value] = swap[:members].last[ ohlc ] + swap[:current_change] * sym[:ticksize]
150
+ swap[:current_diff] = (swap[:current_value] - zero[ohlc]) * (high ? 1 : -1 )
151
+ swap[:current_dist] = (swap[:current_diff] / sym[:ticksize]).to_i
152
+ swap[:exceeded] = zero[:datetime] if swap[:current_dist] < grace
153
+ swap
154
+ end
142
155
  end
143
156
 
144
157
  end
@@ -17,15 +17,15 @@ module Cotcube
17
17
  member[:yy] =
18
18
  member[:y] +
19
19
  (member[:dx].nil? ? member[:x] : member[:dx]) * tan
20
- bar
20
+ member
21
21
  }
22
22
  end
23
23
 
24
24
  # human readable output
25
25
  # please note the format must be given, that should be taken from :sym
26
- def member_to_human(member,side: ,format:)
26
+ def member_to_human(member,side: ,format:, daily: false)
27
27
  high = side == :upper
28
- "#{member[:datetime].strftime("%a, %Y-%m-%d %I:%M%p")
28
+ "#{member[:datetime].strftime("%a, %Y-%m-%d#{daily ? "" :" %I:%M%p"}")
29
29
  } x: #{format '%-4d', member[:x]
30
30
  } dx: #{format '%-8.3f', (member[:dx].nil? ? member[:x] : member[:dx].round(3))
31
31
  } #{high ? "high" : "low"
@@ -37,14 +37,32 @@ module Cotcube
37
37
  end
38
38
 
39
39
  # human readable output
40
- # list all swaps contained in an array (e.g. result of tritangulate)
41
- def puts_swaps(swaps, format: )
42
- swaps = [ swaps ] unless swaps.is_a? Array
43
- swaps.each do |swap|
40
+ # format: e.g. sym[:format]
41
+ # short: print one line / less verbose
42
+ # notice: add this to output as well
43
+ def puts_swap(swap, format: , short: false, notice: nil)
44
+ return if swap[:empty]
45
+ daily = %i[ continuous daily ].include?(swap[:interval])
46
+ datetime_format = daily ? '%Y-%m-%d' : '%Y-%m-%d %H:%M'
47
+ high = swap[:side] == :high
48
+ ohlc = high ? :high : :low
49
+ if short
50
+ puts "S: #{swap[:side]
51
+ } L: #{format '%4d', swap[:length]
52
+ } R: #{format '%4d', swap[:rating]
53
+ } D: #{format '%4d', swap[:depth]
54
+ } P: #{format '%10s', (format '%5.2f', swap[:ppi])
55
+ } F: #{format format, swap[:members].last[ ohlc ]
56
+ } S: #{swap[:members].first[:datetime].strftime(datetime_format)
57
+ } - #{swap[:members].last[:datetime].strftime(datetime_format)
58
+ }#{format('%20s', (swap[:exceeded] ? " XXX: #{swap[:exceeded].strftime(datetime_format)}" : ''))
59
+ }#{" NOTE: #{notice}" unless notice.nil?}".colorize(swap[:color] || :white )
60
+ else
44
61
  puts "side: #{swap[:side] }\tlen: #{swap[:length]} \trating: #{swap[:rating]}".colorize(swap[:color] || :white )
45
62
  puts "diff: #{swap[:ticks]}\tdif: #{swap[:diff].round(7)}\tdepth: #{swap[:depth]}".colorize(swap[:color] || :white )
46
63
  puts "tpi: #{swap[:tpi] }\tppi: #{swap[:ppi]}".colorize(swap[:color] || :white )
47
- swap[:members].each {|x| puts member_to_human(x, side: swap[:side], format: format) }
64
+ puts "NOTE: #{notice}".colorize(:light_white) unless notice.nil?
65
+ swap[:members].each {|x| puts member_to_human(x, side: swap[:side], format: format, daily: daily) }
48
66
  end
49
67
  end
50
68
 
@@ -66,42 +84,95 @@ module Cotcube
66
84
 
67
85
  # the name says it all.
68
86
  # just note the addition of a digest, that serves to check whether same swap has been yet saved
69
- # to the cache
70
- def save_swaps(swaps, interval:, swap_type:, contract:, sym: nil)
87
+ # to the cache.
88
+ #
89
+ # there are actually 3 types of information, that are saved here:
90
+ # 1. a swap
91
+ # 2. an 'empty' information, referring to an interval that has been processed but no swaps were found
92
+ # 3. an 'exceeded' information, referring to another swap, that has been exceeded
93
+ #
94
+ def save_swaps(swaps, interval:, swap_type:, contract:, sym: nil, quiet: false)
71
95
  file = get_jsonl_name(interval: interval, swap_type: swap_type, contract: contract, sym: sym)
96
+ swaps = [ swaps ] unless swaps.is_a? Array
72
97
  swaps.each do |swap|
73
- swap_json = swap.to_json
98
+ raise "Illegal swap info: Must contain keys :datetime, :side... #{swap}" unless (%i[ datetime side ] - swap.keys).empty?
99
+ %i[ interval swap_type ].map {|key| swap.delete(key) }
100
+ sorted_keys = [ :datetime, :side ] + ( swap.keys - [ :datetime, :side ])
101
+ swap_json = swap.slice(*sorted_keys).to_json
74
102
  digest = Digest::SHA256.hexdigest swap_json
75
- res = `cat #{file} | grep #{digest}`.strip
103
+ res = `cat #{file} | grep '"digest":"#{digest}"'`.strip
76
104
  unless res.empty?
77
- puts "Cannot save swap, it is already in #{file}:"
78
- p swap
105
+ puts "Cannot save swap, it is already in #{file}:".light_red unless quiet
106
+ p swap unless quiet
79
107
  else
80
108
  swap[:digest] = digest
81
- File.open(file, 'a+'){|f| f.write(swap.to_json + "\n") }
109
+ sorted_keys += %i[digest]
110
+ File.open(file, 'a+'){|f| f.write(swap.slice(*sorted_keys).to_json + "\n") }
82
111
  end
83
112
  end
84
113
  end
85
114
 
86
115
  # loading of swaps is also straight forward
87
116
  # it takes few more efforts to normalize the values to their expected format
88
- def load_swaps(interval:, swap_type:, contract:, sym: nil)
117
+ def load_swaps(interval:, swap_type:, contract:, sym: nil, datetime: nil)
89
118
  file = get_jsonl_name(interval: interval, swap_type: swap_type, contract: contract, sym: sym)
90
119
  jsonl = File.read(file)
91
- jsonl.
120
+ data = jsonl.
92
121
  each_line.
93
122
  map do |x|
94
123
  JSON.parse(x).
95
124
  deep_transform_keys(&:to_sym).
96
125
  tap do |sw|
97
126
  sw[:datetime] = DateTime.parse(sw[:datetime]) rescue nil
98
- sw[:side] = sw[:side].to_sym
99
- unless sw[:empty]
127
+ (sw[:exceeded] = DateTime.parse(sw[:exceeded]) rescue nil) if sw[:exceeded]
128
+ sw[:interval] = interval
129
+ sw[:swap_type] = swap_type
130
+ sw[:contract] = contract
131
+ %i[ side ].each {|key| sw[key] = sw[key].to_sym rescue false }
132
+ unless sw[:empty] or sw[:exceeded]
100
133
  sw[:color] = sw[:color].to_sym
101
134
  sw[:members].map{|mem| mem[:datetime] = DateTime.parse(mem[:datetime]) }
102
135
  end
103
136
  end
104
137
  end
138
+ # assign exceedance data to actual swaps
139
+ data.select{|swap| swap[:exceeded] }.each do |exc|
140
+ swap = data.find{|ref| ref[:digest] == exc[:ref]}
141
+ raise RuntimeError, "Consistent history for '#{exc}'. Origin not found." if swap.nil?
142
+ swap[:exceeded] = exc[:exceeded]
143
+ end
144
+ # do not return bare exceedance information
145
+ data.reject!{|swap| swap[:exceeded] and swap[:members].nil? }
146
+ # do not return swaps that are found 'later'
147
+ data.reject!{|swap| swap[:datetime] > datetime } unless datetime.nil?
148
+ # do not return exceeded swaps, that are exceeded in the past
149
+ data.reject!{|swap| swap[:exceeded] and swap[:exceeded] < datetime } unless datetime.nil?
150
+ # remove exceedance information that is found 'later'
151
+ data.map{|swap| swap.delete(:exceeded) if swap[:exceeded] and swap[:exceeded] > datetime}
152
+ data
153
+ end
154
+
155
+ # :swaps is an array of swaps
156
+ # :zero is the current interval (ohlc)
157
+ # :stencil is the according current stencil (eod or intraday)
158
+ def check_exceedance(swaps:, zero:, stencil:, contract:, sym:, debug: false)
159
+ swaps.map do |swap|
160
+ # swaps cannot exceed the day they are found (or if they are found in the future)
161
+ next if swap[:datetime] >= zero[:datetime] or swap[:empty]
162
+ update = stencil.use with: swap, sym: sym, zero: zero
163
+ if update[:exceeded]
164
+ to_save = {
165
+ datetime: zero[:datetime],
166
+ ref: swap[:digest],
167
+ side: swap[:side],
168
+ exceeded: update[:exceeded]
169
+ }
170
+ save_swaps to_save, interval: swap[:interval], swap_type: swap[:swap_type], contract: contract, sym: sym, quiet: (not debug)
171
+ swap[:exceeded] = update[:exceeded]
172
+ end
173
+ %i[ current_change current_value current_diff current_dist ].map{|key| swap[key] = update[key] }
174
+ swap
175
+ end.compact
105
176
  end
106
177
 
107
178
  end
@@ -9,15 +9,18 @@ module Cotcube
9
9
  range: (0..-1), # range is relative to base
10
10
  max: 90, # the range which to scan for swaps goes from deg 0 to max
11
11
  debug: false,
12
- min_rating: 3, # swaps having a lower rating are discarded
13
- min_length: 8, # shorter swaps are discared
12
+ min_rating: 3, # 1st criteria: swaps having a lower rating are discarded
13
+ min_length: 8, # 2nd criteria: shorter swaps are discared
14
+ min_ratio: # 3rd criteria: the ratio between rating and length (if true, swap is discarded)
15
+ lambda {|r,l| r < l / 4.0 },
14
16
  save: true, # allow saving of results
15
17
  cached: true, # allow loading of cached results
16
18
  interval: , # interval (currently) is one of %i[ daily continuous halfs ]
17
19
  swap_type: nil, # if not given, a warning is printed and swaps won't be saved or loaded
18
20
  with_flaws: 0, # the maximum amount of consecutive bars that would actually break the current swap
19
21
  # should be set to 0 for dailies and I suggest no more than 3 for intraday
20
- deviation: 2) # the maximum shift of :x-values of found members
22
+ deviation: 2 # the maximum shift of :x-values of found members
23
+ )
21
24
 
22
25
  raise ArgumentError, "'0 < max < 90, but got '#{max}'" unless max.is_a? Numeric and 0 < max and max <= 90
23
26
  raise ArgumentError, 'need :side either :upper or :lower for dots' unless [:upper, :lower].include? side
@@ -26,7 +29,7 @@ module Cotcube
26
29
  # init some helpers
27
30
  #
28
31
  high = side == :upper
29
- # DELETE first = base.to_a.find{|x| not x[:high].nil? }
32
+ first = base.to_a.find{|x| not x[:high].nil? }
30
33
  zero = base.select{|x| x[:x].zero? }
31
34
  raise ArgumentError, "Inappropriate base, it should contain ONE :x.zero, but contains #{zero.size}." unless zero.size==1
32
35
  zero = zero.first
@@ -34,15 +37,16 @@ module Cotcube
34
37
  contract ||= zero[:contract]
35
38
  sym ||= Cotcube::Helpers.get_id_set(contract: contract)
36
39
 
40
+
37
41
  if cached
38
42
  if interval.nil? or swap_type.nil?
39
43
  puts "Warning: Cannot use cache as both :interval and :swap_type must be given".light_yellow
40
44
  else
41
- cache = load_swaps(interval: interval, swap_type: swap_type, contract: contract, sym: sym)
42
- # if the current datetime has been yet processed but nothing has been found,
45
+ cache = load_swaps(interval: interval, swap_type: swap_type, contract: contract, sym: sym, datetime: zero[:datetime])
46
+ # if the current datetime was already processed but nothing has been found,
43
47
  # an 'empty' value is saved.
44
- # that means, if neither an array of swaps nor :empty is found, the datetime has not been processed yet
45
- selected = cache.select{|sw| sw[:datetime] == zero[:datetime] and sw[:side] == side}
48
+ # that means, if neither a swap (or more) nor :empty is found, the datetime has not been processed yet
49
+ selected = cache.select{|sw| sw[:datetime] == zero[:datetime] and sw[:side] == side }
46
50
  unless selected.empty?
47
51
  puts 'cache_hit'.light_white if debug
48
52
  return (selected.first[:empty] ? [] : selected )
@@ -100,13 +104,12 @@ module Cotcube
100
104
  finalize = lambda do |results|
101
105
  results.map do |result|
102
106
  result[:members].each do |member|
103
- next if member[:yy].nil? or member[:yy].zero?
107
+ next if member[:yy].nil? or member[:yy].round(PRECISION-5).zero?
104
108
 
105
109
  diff = (member[:x] - member[:dx]).abs / 2.0
106
110
  member[:dx] = member[:x] + diff
107
111
  # it employs another binary-search
108
- while member[:yy].round(PRECISION) != 0
109
- print '.' if debug
112
+ while member[:yy].round(PRECISION-5) != 0.0
110
113
  member[:yy] = shear_to_deg(deg: result[:deg], base: [ member ] ).first[:yy]
111
114
  diff /= 2.0
112
115
  if member[:yy] > 0
@@ -115,7 +118,7 @@ module Cotcube
115
118
  member[:dx] -= diff
116
119
  end
117
120
  end
118
- member[:yy] = member[:yy].abs.round(8)
121
+ member[:yy] = member[:yy].abs.round(PRECISION-5)
119
122
  end
120
123
 
121
124
  puts 'done!'.magenta if debug
@@ -174,7 +177,7 @@ module Cotcube
174
177
  current_slope[:members] << b[i] unless current_slope[:members].map{|x| x[:datetime]}.include? b[i][:datetime]
175
178
  current_slope[:members].sort_by!{|x| x[:datetime]}
176
179
  end
177
- current_slope
180
+ return current_slope
178
181
 
179
182
  end
180
183
  # all new members found in current iteration have now receive their new :x value, depending on their distance to
@@ -215,6 +218,7 @@ module Cotcube
215
218
  swap_base = shear_to_deg(base: swap_base, deg: swap[:deg])
216
219
  swap_base.map!{|x| x[:dev] = (x[:yy] / sym[:ticksize].to_f); x[:dev] = -( x[:dev] > 0 ? x[:dev].floor : x[:dev].ceil); x}
217
220
  invalids = swap_base.select{|x| x[:dev] < 0 }
221
+ with_flaws = 0 unless with_flaws # support legacy versions, where with_flaws was boolean
218
222
  if with_flaws > 0
219
223
  # TODO: this behaves only as expected when with_flaws == 2
220
224
  last_invalid = invalids[(invalids[-2][:i] + 1 == invalids[-1][:i] ) ? -3 : -2] rescue nil
@@ -243,6 +247,8 @@ module Cotcube
243
247
  swap[:avg_dev] = (swap_base.reject{|x| x[:dev].zero?}.map{|x| x[:dev].abs}.reduce(:+) / (swap_base.size - swap[:members].size).to_f).ceil rescue 0
244
248
  # depth: the maximum distance to the swap line
245
249
  swap[:depth] = swap_base.max_by{|x| x[:dev]}[:dev]
250
+ swap[:interval] = interval
251
+ swap[:swap_type] = swap_type
246
252
  swap[:raw] = swap[:members].map{|x| x[:x]}.reverse
247
253
  swap[:size] = swap[:members].size
248
254
  swap[:length] = swap[:raw][-1] - swap[:raw][0]
@@ -256,11 +262,11 @@ module Cotcube
256
262
  unless %i[ daily continuous ].include? interval
257
263
  swap[:color] = ((rat > 150) ? :light_blue : (rat > 80) ? :magenta : (rat > 30) ? :light_magenta : (rat > 15) ? :light_yellow : high ? :green : :red)
258
264
  end
259
- swap[:diff] = swap[:members].last[ high ? :high : :low ] - swap[:members].first[ high ? :high : :low ]
265
+ swap[:diff] = (swap[:members].last[ high ? :high : :low ] - swap[:members].first[ high ? :high : :low ]).round(8)
260
266
  swap[:ticks] = (swap[:diff] / sym[:ticksize]).to_i
261
267
  # tpi: ticks per interval, how many ticks are passed each :interval
262
268
  swap[:tpi] = (swap[:ticks].to_f / swap[:length]).round(3)
263
- # ppi: power per interval, how many dollar value is passed each :interval
269
+ # ppi: power per interval, how many $dollar value is passed each :interval
264
270
  swap[:ppi] = (swap[:tpi] * sym[:power]).round(3)
265
271
  end # swap
266
272
  end # lambda
@@ -303,7 +309,7 @@ module Cotcube
303
309
  binding.irb if debug
304
310
 
305
311
  # reject all results that do not suffice
306
- current_results.reject!{|swap| swap[:rating] < min_rating or swap[:length] < min_length or swap[:rating] < swap[:length] / 4.to_f}
312
+ current_results.reject!{|swap| swap[:rating] < min_rating or swap[:length] < min_length or min_ratio.call(swap[:rating],swap[:length])}
307
313
 
308
314
  #####################################################################################################################3
309
315
  # finally save results for caching and return them
@@ -312,7 +318,7 @@ module Cotcube
312
318
  puts "WARNING: Cannot save swaps, as both :interval and :swap_type must be given".colorize(:light_yellow)
313
319
  else
314
320
  current_results.map{|sw| mem = sw[:members]; sw[:slope] = (mem.last[:y] - mem.first[:y]) / (mem.last[mem.last[:dx].nil? ? :x : :dx] - mem.first[mem.first[:dx].nil? ? :x : :dx]).to_f }
315
- to_save = current_results.empty? ? [ { datetime: zero[:datetime], side: side, empty: true } ] : current_results
321
+ to_save = current_results.empty? ? [ { datetime: zero[:datetime], side: side, empty: true, interval: interval, swap_type: swap_type } ] : current_results
316
322
  save_swaps(to_save, interval: interval, swap_type: swap_type, contract: contract, sym: sym)
317
323
  end
318
324
  end
data/lib/cotcube-level.rb CHANGED
@@ -35,10 +35,11 @@ module Cotcube
35
35
  :shear_to_rad, # same all below
36
36
  :rad2deg,
37
37
  :deg2rad,
38
- :puts_swaps,
38
+ :puts_swap,
39
39
  :save_swaps,
40
40
  :get_jsonl_name,
41
41
  :load_swaps,
42
+ :check_exceedance,
42
43
  :member_to_human
43
44
 
44
45
  # please note that module_functions of sources provided in non-public files must slso be published within these
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cotcube-level
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin L. Tischendorf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-24 00:00:00.000000000 Z
11
+ date: 2021-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -116,7 +116,6 @@ executables: []
116
116
  extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
- - ".irbrc.rb"
120
119
  - CHANGELOG.md
121
120
  - Gemfile
122
121
  - README.md
data/.irbrc.rb DELETED
@@ -1,12 +0,0 @@
1
- def verbose_toggle
2
- irb_context.echo ? irb_context.echo = false : irb_context.echo = true
3
- end
4
-
5
- alias vt verbose_toggle
6
-
7
- $debug = true
8
- IRB.conf[:USE_MULTILINE] = false
9
- # require 'bundler'
10
- # Bundler.require
11
-
12
- require_relative 'lib/cotcube-level'