cotcube-level 0.2.0 → 0.3.4

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