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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -0
- data/README.md +14 -6
- data/VERSION +1 -1
- data/cotcube-level.gemspec +1 -1
- data/lib/cotcube-level/detect_slope.rb +79 -98
- data/lib/cotcube-level/{stencil.rb → eod_stencil.rb} +26 -16
- data/lib/cotcube-level/helpers.rb +198 -74
- data/lib/cotcube-level/intraday_stencil.rb +213 -0
- data/lib/cotcube-level/tritangulate.rb +332 -0
- data/lib/cotcube-level.rb +17 -14
- metadata +6 -6
- data/.irbrc.rb +0 -12
- data/lib/cotcube-level/triangulate.rb +0 -238
@@ -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
|
-
|