cotcube-level 0.2.0 → 0.3.4

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.
@@ -1,238 +0,0 @@
1
- module Cotcube
2
- module Level
3
- def triangulate(
4
- contract: nil, # contract actually isnt needed to triangulation, but allows much more convenient output
5
- side:, # :upper or :lower
6
- base:, # the base of a readily injected stencil
7
- range: (0..-1), # range is relative to base
8
- max: 90, # the range which to scan for swaps goes from deg 0 to max
9
- debug: false,
10
- format: '% 5.2f',
11
- min_members: 3, # this param should not be changed manually, it is used for the guess operation
12
- min_rating: 3, # swaps having a lower rating are discarded
13
- allow_sub: true, # this param determines whether guess can be called or not
14
- save: true, # allow saving of results
15
- cached: true, # allow loading of yet cached intervals
16
- interval: nil, # interval and swap_type are only needed if saving / caching of swaps is desired
17
- swap_type: nil, # if not given, a warning is printed and swaps are not saved
18
- deviation: 2)
19
-
20
- raise ArgumentError, "'0 < max < 90, but got '#{max}'" unless max.is_a? Numeric and 0 < max and max <= 90
21
- raise ArgumentError, 'need :side either :upper or :lower for dots' unless [:upper, :lower].include? side
22
-
23
- ###########################################################################################################################
24
- # init some helpers
25
- #
26
- high = side == :upper
27
- first = base.to_a.find{|x| not x[:high].nil? }
28
- zero = base.select{|x| x[:x].zero? }
29
- raise ArgumentError, "Inappropriate base, it should contain ONE :x.zero, but contains #{zero.size}." unless zero.size==1
30
- zero = zero.first
31
- contract ||= zero.contract
32
- sym = Cotcube::Helpers.get_id_set(contract: contract)
33
-
34
- if cached
35
- if interval.nil? or swap_type.nil?
36
- puts "Warning: Cannot use cache as both :interval and :swap_type must be given".light_yellow
37
- else
38
- cache = load_swaps(interval: interval, swap_type: swap_type, contract: contract, sym: sym)
39
- selected = cache.select{|sw| sw[:datetime] == zero[:datetime] and sw[:side] == side}
40
- unless selected.empty?
41
- puts 'cache_hit'.light_white if debug
42
- return (selected.first[:empty] ? [] : selected )
43
- end
44
- end
45
- end
46
- ticksize = sym[:ticksize] / sym[:bcf] # need to adjust, as we are working on barchart data, not on exchange data !!
47
-
48
-
49
- ###########################################################################################################################
50
- # prepare base (i.e. dupe the original, create proper :y, and reject unneeded items)
51
- #
52
- base = base.
53
- map { |x|
54
- y = x.dup
55
- y[:y] = (high ?
56
- (y[:high] - zero[:high]).round(8) :
57
- (zero[:low] - y[:low]).round(8)
58
- ) unless y[:high].nil?
59
- y
60
- }.
61
- reject{|b| b.nil? or b[:datetime] < first[:datetime] or b[:x] < 0 or b[:y].nil?}[range]
62
- abs_peak = base.send(high ? :max_by : :min_by){|x| x[high ? :high : :low] }[:datetime]
63
-
64
- base.reject!{|x| x[:datetime] < abs_peak}
65
-
66
- ###########################################################################################################################z
67
- # only if (and only if) the range portion above change the underlying base
68
- # the offset has to be fixed for :x and :y
69
-
70
- unless range == (0..-1)
71
- puts "adjusting range to '#{range}'".light_yellow if debug
72
- offset_x = base.last[:x]
73
- offset_y = base.last[:y]
74
- base.map!{|b| b[:x] -= offset_x; b[:y] -= offset_y ; b}
75
- end
76
- base.each_index.map{|i| base[i][:i] = -base.size + i }
77
-
78
-
79
- ###########################################################################################################################
80
- # LAMBDA no1: simplifying DEBUG output
81
- #
82
- present = lambda {|z| swap_to_human(z) }
83
-
84
-
85
-
86
- ###########################################################################################################################
87
- # LAMBDA no2: all members except the pivot itself now most probably are too far to the left
88
- # finalizing tries to get the proper dx value for them
89
- #
90
- finalize = lambda do |res|
91
- res.map do |r|
92
- r[:members].each do |m|
93
- next if m[:yy].nil? or m[:yy].zero?
94
-
95
- diff = (m[:x] - m[:dx]).abs / 2.0
96
- m[:dx] = m[:x] + diff
97
- # it employs another binary-search
98
- while m[:yy].round(PRECISION) != 0 # or m[:yy].round(PRECISION) < -ticksize
99
- print '.' if debug
100
- m[:yy] = shear_to_deg(deg: r[:deg], base: [ m ] ).first[:yy]
101
- diff /= 2.0
102
- if m[:yy] > 0
103
- m[:dx] += diff
104
- else
105
- m[:dx] -= diff
106
- end
107
- end
108
- m[:yy] = m[:yy].abs.round(8)
109
- end # r.members
110
- puts 'done!'.light_yellow if debug
111
-
112
- r[:members].each {|x| puts "finalizing #{x}".magenta } if debug
113
- ## The transforming part
114
-
115
- r
116
- end # res
117
- end # lambda
118
-
119
- ###########################################################################################################################
120
- # LAMDBA no3: the actual 'function' to retrieve the slope
121
- #
122
- get_slope = lambda do |b|
123
- if debug
124
- puts "in get_slope ... SETTING BASE: ".light_green
125
- puts "Last:\t#{present.call b.last}"
126
- puts "First:\t#{present.call b.first}"
127
- end
128
- members = [ b.last[:i] ]
129
- loop do
130
- current_slope = detect_slope(base: b, ticksize: ticksize, format: sym[:format], debug: debug)
131
- current_members = current_slope[:members]
132
- .map{|dot| dot[:i]}
133
- new_members = current_members - members
134
- puts "New members: #{new_members} as of #{current_members} - #{members}" if debug
135
- # the return condition is if no new members are found in slope
136
- # except lowest members are neighbours, what causes a re-run
137
- if new_members.empty?
138
- mem_sorted=members.sort
139
- if mem_sorted[1] == mem_sorted[0] + 1
140
- b2 = b[mem_sorted[1]..mem_sorted[-1]].map{|x| x.dup; x[:dx] = nil; x}
141
- puts 'starting rerun' if debug
142
- alternative_slope = get_slope.call(b2)
143
- alternative = alternative_slope[:members].map{|bar| bar[:i]}
144
- if (mem_sorted[1..-1] - alternative).empty?
145
- current_slope = alternative_slope
146
- members = alternative
147
- end
148
- end
149
- if min_members >= 3 and members.size >= 3
150
- current_slope[:raw] = members.map{|x| x.abs }.sort
151
- current_slope[:length] = current_slope[:raw][-1] - current_slope[:raw][0]
152
- current_slope[:rating] = current_slope[:raw][1..-2].map{|dot| [ dot - current_slope[:raw][0], current_slope[:raw][-1] - dot].min }.max
153
- end
154
- members.sort_by{|i| -i}.each do |x|
155
- puts "#{range}\t#{present.call(b[x])}" if debug
156
- current_slope[:members] << b[x] unless current_slope[:members].map{|x| x[:datetime]}.include? b[x][:datetime]
157
- current_slope[:members].sort_by!{|x| x[:datetime]}
158
- end
159
- return current_slope
160
-
161
- end
162
- new_members.each do |mem|
163
- current_deviation = (0.1 * b[mem][:x])
164
- current_deviation = 1 if current_deviation < 1
165
- current_deviation = deviation if current_deviation > deviation
166
- b[mem][:dx] = b[mem][:x] + current_deviation
167
- end
168
- members += new_members
169
- end
170
- end # of lambda
171
-
172
- analyze = lambda do |swaps|
173
- swaps.each do |swap|
174
- swap[:datetime] = swap[:members].last[:datetime]
175
- swap[:side] = side
176
- rat = swap[:rating]
177
- swap[:color ] = (rat > 75) ? :light_blue : (rat > 30) ? :magenta : (rat > 15) ? :light_magenta : (rat > 7) ? (high ? :light_green : :light_red) : high ? :green : :red
178
- swap[:diff] = swap[:members].last[ high ? :high : :low ] - swap[:members].first[ high ? :high : :low ]
179
- swap[:ticks] = (swap[:diff] / sym[:ticksize]).to_i
180
- swap[:tpi] = (swap[:ticks].to_f / swap[:length]).round(3)
181
- swap[:ppi] = (swap[:tpi] * sym[:power]).round(3)
182
- swap_base = shear_to_deg(base: base[swap[:members].first[:i]..], deg: swap[:deg]).map{|x| x[:dev] = (x[:yy] / sym[:ticksize]).abs.floor; x}
183
- swap[:depth] = swap_base.max_by{|x| x[:dev]}[:dev]
184
- swap[:avg_dev]= (swap_base.reject{|x| x[:dev].zero?}.map{|x| x[:dev]}.reduce(:+) / (swap_base.size - swap[:members].size).to_f).ceil rescue 0
185
- # a miss is considered a point that is less than 10% of the average deviation away of the slope
186
- unless swap[:avg_dev].zero?
187
- misses = swap_base.select{|x| x[:dev] <= swap[:avg_dev] / 10.to_f and x[:dev] > 0}.map{|x| x[:miss] = x[:dev]; x}
188
- # misses are sorted among members, but stay marked
189
- swap[:members]= (swap[:members] + misses).sort_by{|x| x[:datetime] }
190
- end
191
- end # swap
192
- end # of lambda
193
-
194
- ###########################################################################################################################
195
- # after declaring lambdas, the rest is quite few code
196
- #
197
- current_range = (0..-1) # RANGE set
198
- current_slope = { members: [] } # SLOPE reset
199
- current_base = base[current_range] # BASE set
200
- current_results = [ ] # RESULTS reset
201
- while current_base.size >= 5 # LOOP
202
-
203
- puts '-------------------------------------------------------------------------------------' if debug
204
-
205
- while current_base.size >= 5 and current_slope[:members].size < min_members
206
- puts "---- #{current_base.size} #{current_range.to_s.light_yellow} ------" if debug
207
- current_slope = get_slope.call(current_base) # SLOPE call
208
- next_i = current_slope[:members][-2]
209
- current_range = ((next_i.nil? ? -2 : next_i[:i])+1..-1) # RANGE adjust
210
- current_base = base[current_range] # BASE adjust
211
- if debug
212
- print 'Hit <enter> to continue...'
213
- STDIN.gets
214
- end
215
- end
216
- puts "Current slope: ".light_yellow + "#{current_slope}" if debug
217
- current_results << current_slope if current_slope # RESULTS add
218
- current_slope = { members: [] } # SLOPE reset
219
- end
220
- current_results.select!{|x| x[:members].size >= min_members }
221
-
222
- # Adjust all members (except pivot) to fit the actual dx-value
223
- finalize.call(current_results)
224
- analyze.call(current_results)
225
- current_results.reject!{|swap| swap[:rating] < min_rating}
226
- if save
227
- if interval.nil? or swap_type.nil?
228
- puts "WARNING: Cannot save swaps, as both :interval and :swap_type must be given".colorize(:light_yellow)
229
- else
230
- to_save = current_results.empty? ? [ { datetime: zero[:datetime], side: side, empty: true } ] : current_results
231
- save_swaps(to_save, interval: interval, swap_type: swap_type, contract: contract, sym: sym)
232
- end
233
- end
234
- current_results
235
- end
236
- end
237
- end
238
-