cotcube-helpers 0.1.10 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/VERSION +1 -1
- data/lib/cotcube-helpers/constants.rb +4 -4
- data/lib/cotcube-helpers/output.rb +20 -0
- data/lib/cotcube-helpers/recognition.rb +509 -0
- data/lib/cotcube-helpers/symbols.rb +48 -18
- data/lib/cotcube-helpers.rb +2 -0
- data/scripts/symbols +13 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38df15256f996b15dfeb1755f7464e932a98541e62aa43efda0a264be58bafb2
|
4
|
+
data.tar.gz: cd336b6a91442a6d3bbace66ec8a25049ae979ab78eef03d1c8b44a548da8023
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcfc562ab118939e7c23dc37b89fee881871552b6b7b17ec7066cb4a2028fa2661e50cd5512725da28332dd1b983d6817d416d31ba05059dd15d824a0e101f7c
|
7
|
+
data.tar.gz: 9ca3305120eb630283e303206b764cf9d8dd533ceaf0a2aa044f702fa2f2a01055ff70a4331ec865d854580b1dcf367f365c5addbb988605537c0ee9b9b6fad9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.2.0 (November 07, 2021)
|
2
|
+
- added module Candlestick_Recognition
|
3
|
+
- added instance_inspect method to 'scan' objects for contents of instance variables
|
4
|
+
- symbols: made selection of symbols more versatile by key
|
5
|
+
- added headers (:ib_symbol, :internal, :exchange, :currency) to symbol headers as well as symbol examples
|
6
|
+
- added scripts/symbols to list (and filter) symbols from command line (put to PATH!)
|
7
|
+
|
1
8
|
## 0.1.10 (October 28, 2021)
|
2
9
|
- added script cron_ruby_wrapper.sh (linkable as /usr/local/bin/cruw.sh)
|
3
10
|
- added numeric ext .with_delimiter to support printing like 123_456_789.00121
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -3,13 +3,13 @@
|
|
3
3
|
module Cotcube
|
4
4
|
module Helpers
|
5
5
|
SYMBOL_EXAMPLES = [
|
6
|
-
{ id: '13874U', symbol: 'ES', ticksize: 0.25, power: 12.5, months: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'S&P 500 MICRO' },
|
7
|
-
{ id: '209747', symbol: 'NQ', ticksize: 0.25, power: 5.0, monhts: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'NASDAQ 100 MICRO' }
|
6
|
+
{ 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' },
|
7
|
+
{ 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' }
|
8
8
|
].freeze
|
9
9
|
|
10
10
|
MICRO_EXAMPLES = [
|
11
|
-
{ id: '13874U', symbol: 'ET', ticksize: 0.25, power: 1.25, months: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'MICRO S&P 500 MICRO' },
|
12
|
-
{ id: '209747', symbol: 'NM', ticksize: 0.25, power: 0.5, monhts: 'HMUZ', bcf: 1.0, reports: 'LF', format: '8.2f', name: 'MICRO NASDAQ 100 MICRO' }
|
11
|
+
{ 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' },
|
12
|
+
{ 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' }
|
13
13
|
].freeze
|
14
14
|
|
15
15
|
COLORS = %i[light_red light_yellow light_green red yellow green cyan magenta blue].freeze
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Cotcube
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def instance_inspect(obj, keylength: 20, &block)
|
5
|
+
obj.instance_variables.map do |var|
|
6
|
+
if block_given?
|
7
|
+
block.call(var, obj.instance_variable_get(var))
|
8
|
+
else
|
9
|
+
puts "#{format "%-#{keylength}s", var
|
10
|
+
}: #{obj.instance_variable_get(var).inspect.scan(/.{1,120}/).join( "\n" + ' '*(keylength+2))
|
11
|
+
}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module_function :instance_inspect
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,509 @@
|
|
1
|
+
module Cotcube
|
2
|
+
module Helpers
|
3
|
+
module Candlestick_Recognition
|
4
|
+
|
5
|
+
SUPERFLUOUS = %i[wap datetime prev_slope upper lower bar_size body_size lower_wick upper_wick ranges vranges vavg trades type bullish bearish doji contract]
|
6
|
+
COMMON = [:symbol, :timestamp, :size, :rth, :time_end,
|
7
|
+
:open, :high, :vhigh, :vlow, :low, :close,
|
8
|
+
:interval, :offset, :appeared, :datetime, :volume, :dist, :vperd, :vol_i, :contract, :date,
|
9
|
+
:bar_size, :upper_wick, :lower_wick, :body_size, :upper, :lower, :slope, :rel, :tr, :atr, :ranges, :vranges, :vavg]
|
10
|
+
|
11
|
+
# recognize serves as interface
|
12
|
+
def recognize(contract:, interval: :quarters, short: true, base:, return_as_string: false, sym: )
|
13
|
+
s = bas
|
14
|
+
CR::candles s, ticksize: sym[:ticksize]
|
15
|
+
CR::patterns s, contract: contract, ticksize: sym[:ticksize]
|
16
|
+
s.map{|x| x[:datetime] += 7.hours } if s and %w[ GG DY ].include?(contract[..1]) and interval == :quarters
|
17
|
+
s.map{|x| x[:datetime] += 1.hour } if s and %w[ GC PA PL SI HG NG HO RB CL ].include?(contract[..1]) and interval == :quarters
|
18
|
+
make_string = lambda {|c| "#{contract
|
19
|
+
}\t#{c[:datetime].strftime( interval == :quarters ? '%Y-%m-%d %H:%M' : '%Y-%m-%d' )}".colorize(:light_white) +
|
20
|
+
"\t#{print_bar(bar: c, format: sym[:format], power: sym[:power], short: short)
|
21
|
+
} #{"\n\n" if c[:datetime].wday == 5 and interval == :days}" }
|
22
|
+
return_as_string ? s[-count..].map{|candle| make_string.call(candle) }.join("\n") : s
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def print_bar(bar:, format:, power:, short: false)
|
27
|
+
x = bar.dup
|
28
|
+
dir = x[:bullish] ? :bullish : x[:bearish] ? :bearish : x[:doji] ? :doji : :none
|
29
|
+
contract = bar[:contract]
|
30
|
+
SUPERFLUOUS.map{|s| x.delete(s)}
|
31
|
+
%i[ UPPER_PIVOT LOWER_PIVOT UPPER_ISOPIVOT LOWER_ISOPIVOT ].map{|s| x.delete(s)}
|
32
|
+
|
33
|
+
|
34
|
+
vol = x.keys.select{|x| x.to_s =~ /_volume/}[0]
|
35
|
+
x.delete vol
|
36
|
+
|
37
|
+
vol = vol.to_s.split("_")[0]
|
38
|
+
col = case dir
|
39
|
+
when :bullish
|
40
|
+
:light_green
|
41
|
+
when :bearish
|
42
|
+
:light_red
|
43
|
+
when :doji
|
44
|
+
:light_blue
|
45
|
+
else:light_black
|
46
|
+
end
|
47
|
+
|
48
|
+
special = lambda do |s|
|
49
|
+
case s
|
50
|
+
when *%i[ THRUSTING_LINE SHOOTING_STAR HANGING_MAN EVENING_STAR EVENING_DOJI_STAR UPSIDEGAP_TWO_CROWS UMKEHRSTAB_BEARISH ]
|
51
|
+
s.to_s.colorize(:light_red)
|
52
|
+
when *%i[ PIERCING_LINE INVERTED_HAMMER HAMMER MORNING_STAR MORNING_DOJI_STAR DOWNGAP_TWO_RIVERS UMKEHRSTAB_BULLISH ]
|
53
|
+
s.to_s.colorize(:light_green)
|
54
|
+
else
|
55
|
+
s.to_s.colorize(:light_cyan)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
"#{ format '%12s', (format % x[:open ]) }" +
|
62
|
+
" #{format '%12s', (format % x[:high ])}".colorize( (x.keys.include?(:UPPER_PIVOT) or x.keys.include?(:UPPER_ISOPIVOT)) ? :light_blue : :white ) +
|
63
|
+
" #{format '%12s', (format % x[:low ])}".colorize( (x.keys.include?(:LOWER_PIVOT) or x.keys.include?(:LOWER_ISOPIVOT)) ? :light_blue : :white ) +
|
64
|
+
" #{format '%12s', ( format % x[:close ])}" +
|
65
|
+
(short ? "" : " #{"%10s" % ("[ % -4.1f ]" % x[:slope])}".colorize( if x[:slope].abs < 3; :yellow; elsif x[:slope] >= 3; :green; else; :red; end)) +
|
66
|
+
(short ? "" : " #{"% 8d" % x[:volume]}") +
|
67
|
+
" #{vol}".colorize( case vol; when *["BREAKN", "FAINTN"]; :light_blue; when *["RISING","FALLIN"]; :cyan; else; :white; end) +
|
68
|
+
(short ? "" : " D:#{"%5d" % x[:dist]}" ) +
|
69
|
+
(short ? "" : " P:#{"%10.2f" % (x[:dist] * power)} ".cyan ) +
|
70
|
+
format('%10s', dir.to_s).colorize(col) +
|
71
|
+
(short ? "" : format('%-22s', " >>> #{x.keys.map{|v| (COMMON.include?(v) or v.upcase == v)? nil : v}.compact.join(" ")}").colorize( col )) +
|
72
|
+
"\t#{x.keys.map{|v| (COMMON.include?(v) or v.upcase != v)? nil : special.call(v)}.compact.join(" ")}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def candles(candles, debug: false, sym: )
|
76
|
+
ticksize = sym[:ticksize]
|
77
|
+
|
78
|
+
candles.each_with_index do |bar, i|
|
79
|
+
# rel simply sets a grace limit based on the full height of the bar, so we won't need to use the hard limit of zero
|
80
|
+
begin
|
81
|
+
rel = ((bar[:high] - bar[:low]) * 0.05).round(8)
|
82
|
+
rel = 2 * ticksize if rel < 2 * ticksize
|
83
|
+
rescue
|
84
|
+
puts "Warning, found inappropriate bar".light_white + " #{bar}"
|
85
|
+
raise
|
86
|
+
end
|
87
|
+
bar[:rel] = rel
|
88
|
+
bar[:dist] ||= ((bar[:high] - bar[:low])/ticksize).round(8)
|
89
|
+
|
90
|
+
bar[:upper] = [bar[:open], bar[:close]].max
|
91
|
+
bar[:lower] = [bar[:open], bar[:close]].min
|
92
|
+
bar[:bar_size] = (bar[:high] - bar[:low])
|
93
|
+
bar[:body_size] = (bar[:open] - bar[:close]).abs
|
94
|
+
bar[:lower_wick] = (bar[:lower] - bar[:low])
|
95
|
+
bar[:upper_wick] = (bar[:high] - bar[:upper])
|
96
|
+
bar.each{|k,v| bar[k] = v.round(8) if v.is_a? Float}
|
97
|
+
|
98
|
+
# a doji's open and close are same (or only differ by rel)
|
99
|
+
bar[:doji] = true if bar[:body_size] <= rel and bar[:dist] >= 3
|
100
|
+
bar[:tiny] = true if bar[:dist] <= 5
|
101
|
+
|
102
|
+
next if bar[:tiny]
|
103
|
+
|
104
|
+
bar[:bullish] = true if not bar[:doji] and bar[:close] > bar[:open]
|
105
|
+
bar[:bearish] = true if not bar[:doji] and bar[:close] < bar[:open]
|
106
|
+
|
107
|
+
bar[:spinning_top] = true if bar[:body_size] <= bar[:bar_size] / 4 and
|
108
|
+
bar[:lower_wick] >= bar[:bar_size] / 4 and
|
109
|
+
bar[:upper_wick] >= bar[:bar_size] / 4
|
110
|
+
|
111
|
+
# a marubozu open at high or low and closes at low or high
|
112
|
+
bar[:marubozu] = true if bar[:upper_wick] < rel and bar[:lower_wick] < rel
|
113
|
+
|
114
|
+
# a bar is considered bearish if it has at least a dist of 5 ticks and it's close it near high (low resp)
|
115
|
+
bar[:bullish_close] = true if (bar[:high] - bar[:close]) <= rel and not bar[:marubozu]
|
116
|
+
bar[:bearish_close] = true if (bar[:close] - bar[:low]) <= rel and not bar[:marubozu]
|
117
|
+
|
118
|
+
# the distribution of main volume is shown in 5 segments, like [0|0|0|4|5] shows that most volume concentrated at the bottom, [0|0|3|0|0] is heavily centered
|
119
|
+
# TODO
|
120
|
+
|
121
|
+
end
|
122
|
+
candles
|
123
|
+
end
|
124
|
+
|
125
|
+
def comparebars(prev, curr)
|
126
|
+
bullishscore = 0
|
127
|
+
bearishscore = 0
|
128
|
+
bullishscore += 1 if prev[:high] <= curr[:high]
|
129
|
+
bullishscore += 1 if prev[:low] <= curr[:low]
|
130
|
+
bullishscore += 1 if prev[:close] <= curr[:close]
|
131
|
+
bearishscore += 1 if prev[:close] >= curr[:close]
|
132
|
+
bearishscore += 1 if prev[:low] >= curr[:low]
|
133
|
+
bearishscore += 1 if prev[:high] >= curr[:high]
|
134
|
+
r = {}
|
135
|
+
r[:bullish] = true if bullishscore >= 2
|
136
|
+
r[:bearish] = true if bearishscore >= 2
|
137
|
+
return r
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def patterns(candles, debug: false, size: 5, contract:, ticksize: nil )
|
142
|
+
candles.each_with_index do |bar, i|
|
143
|
+
preceeding = candles.select{|x| x[:datetime] <= bar[:datetime] }
|
144
|
+
if i.zero?
|
145
|
+
bar[:slope] = 0
|
146
|
+
next
|
147
|
+
end
|
148
|
+
ppprev= candles[i-3]
|
149
|
+
pprev= candles[i-2]
|
150
|
+
prev = candles[i-1]
|
151
|
+
succ = candles[i+1]
|
152
|
+
|
153
|
+
bar[:huge] = true if bar[:true_range] >= bar[:atr] * 1.5
|
154
|
+
bar[:small] = true if bar[:true_range] <= bar[:atr5] * 0.6666
|
155
|
+
|
156
|
+
bar[:vavg] = (bar[:vranges].reduce(:+) / bar[:vranges].size.to_f).round if bar[:vranges] and bar[:vranges].compact.size > 0
|
157
|
+
bar[:vranges] = prev[:vranges].nil? ? [ bar[:volume] ] : prev[:vranges] + [ bar[:volume] ]
|
158
|
+
bar[:vranges].shift while bar[:vranges].size > size
|
159
|
+
bar[:vavg] ||= (bar[:vranges].reduce(:+) / bar[:vranges].size.to_f).round if bar[:vranges] and bar[:vranges].compact.size > 0
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
# VOLUME
|
164
|
+
if bar[:volume] > bar[:vavg] * 1.3
|
165
|
+
bar[:BREAKN_volume] = true
|
166
|
+
elsif bar[:volume] >= bar[:vavg] * 1.1
|
167
|
+
bar[:RISING_volume] = true
|
168
|
+
elsif bar[:volume] < bar[:vavg] * 0.7
|
169
|
+
bar[:FAINTN_volume] = true
|
170
|
+
elsif bar[:volume] <= bar[:vavg] * 0.9
|
171
|
+
bar[:FALLIN_volume] = true
|
172
|
+
else
|
173
|
+
bar[:STABLE_volume] = true
|
174
|
+
end
|
175
|
+
|
176
|
+
# GAPS
|
177
|
+
bar[:bodygap] = true if bar[:lower] > prev[:upper] or bar[:upper] < prev[:lower]
|
178
|
+
bar[:gap] = true if bar[:low] > prev[:high] or bar[:high] < prev[:low]
|
179
|
+
|
180
|
+
|
181
|
+
bar[:slope] = slopescore(pprev, prev, bar, debug)
|
182
|
+
bar[:prev_slope] = prev[:slope]
|
183
|
+
|
184
|
+
# UPPER_PIVOTs define by having higher highs and higher lows than their neighor
|
185
|
+
bar[:UPPER_ISOPIVOT] = true if succ and prev[:high] < bar[:high] and prev[:low] <= bar[:low] and succ[:high] < bar[:high] and succ[:low] <= bar[:low] and bar[:lower] >= [prev[:upper], succ[:upper]].max
|
186
|
+
bar[:LOWER_ISOPIVOT] = true if succ and prev[:high] >= bar[:high] and prev[:low] > bar[:low] and succ[:high] >= bar[:high] and succ[:low] > bar[:low] and bar[:upper] <= [prev[:lower], succ[:lower]].min
|
187
|
+
bar[:UPPER_PIVOT] = true if succ and prev[:high] < bar[:high] and prev[:low] <= bar[:low] and succ[:high] < bar[:high] and succ[:low] <= bar[:low] and not bar[:UPPER_ISOPIVOT]
|
188
|
+
bar[:LOWER_PIVOT] = true if succ and prev[:high] >= bar[:high] and prev[:low] > bar[:low] and succ[:high] >= bar[:high] and succ[:low] > bar[:low] and not bar[:LOWER_ISOPIVOT]
|
189
|
+
|
190
|
+
# stopping volume is defined as high volume candle during downtrend then closes above mid candle (i.e. lower_wick > body_size)
|
191
|
+
bar[:stopping_volume] = true if (bar[:BREAKN_volume] or bar[:RISING_volume]) and prev[:slope] < -5 and bar[:lower_wick] >= bar[:body_size]
|
192
|
+
bar[:stopping_volume] = true if (bar[:BREAKN_volume] or bar[:RISING_volume]) and prev[:slope] > 5 and bar[:upper_wick] >= bar[:body_size]
|
193
|
+
bar[:volume_lower_wick] = true if bar[:vhigh] and (bar[:vol_i].nil? or bar[:vol_i] >= 2) and bar[:vhigh] <= bar[:lower] and not bar[:FAINTN_volume] and not bar[:FALLIN_volume]
|
194
|
+
bar[:volume_upper_wick] = true if bar[:vlow] and (bar[:vol_i].nil? or bar[:vol_i] >= 2) and bar[:vlow] >= bar[:upper] and not bar[:FAINTN_volume] and not bar[:FALLIN_volume]
|
195
|
+
|
196
|
+
|
197
|
+
###################################
|
198
|
+
# SINGLE CANDLE PATTERNS
|
199
|
+
###################################
|
200
|
+
|
201
|
+
# a hammer is a bar, whose open or close is at the high and whose body is lte 1/3 of the size, found on falling slope, preferrably gapping away
|
202
|
+
bar[:HAMMER] = true if bar[:upper_wick] <= bar[:rel] and bar[:body_size] <= bar[:bar_size] / 3 and bar[ :slope] <= -6
|
203
|
+
# same shape, but found at a raising slope without the need to gap away
|
204
|
+
bar[:HANGING_MAN] = true if bar[:upper_wick] <= bar[:rel] and bar[:body_size] <= bar[:bar_size] / 3 and prev[:slope] >= 6
|
205
|
+
|
206
|
+
# a shooting star is the inverse of the hammer, while the inverted hammer is the inverse of the hanging man
|
207
|
+
bar[:SHOOTING_STAR] = true if bar[:lower_wick] <= bar[:rel] and bar[:body_size] <= bar[:bar_size] / 2.5 and bar[ :slope] >= 6
|
208
|
+
bar[:INVERTED_HAMMER] = true if bar[:lower_wick] <= bar[:rel] and bar[:body_size] <= bar[:bar_size] / 3 and prev[:slope] <= -6
|
209
|
+
|
210
|
+
# a star is simply gapping away the preceding slope
|
211
|
+
if ((bar[:lower] > prev[:upper] and bar[:slope] >= 6 and bar[:high] >= prev[:high]) or
|
212
|
+
(bar[:upper] < prev[:lower] and bar[:slope] <= -6) and bar[:low] <= prev[:low])
|
213
|
+
bar[:doji] ? bar[:DOJI_STAR] = true : bar[:STAR] = true
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# a belthold is has a gap in the open, but reverses strong
|
218
|
+
bar[:BULLISH_BELTHOLD] = true if bar[:lower_wick] <= bar[:rel] and bar[:body_size] >= bar[:bar_size] / 2 and
|
219
|
+
prev[:slope] <= -4 and bar[:lower] <= prev[:low ] and bar[:bullish] and not prev[:bullish] and bar[:bar_size] >= prev[:bar_size]
|
220
|
+
bar[:BEARISH_BELTHOLD] = true if bar[:upper_wick] <= bar[:rel] and bar[:body_size] >= bar[:bar_size] / 2 and
|
221
|
+
prev[:slope] >= -4 and bar[:upper] <= prev[:high] and bar[:bearish] and not prev[:bearish] and bar[:bar_size] >= prev[:bar_size]
|
222
|
+
|
223
|
+
|
224
|
+
###################################
|
225
|
+
# DUAL CANDLE PATTERNS
|
226
|
+
###################################
|
227
|
+
|
228
|
+
|
229
|
+
# ENGULFINGS
|
230
|
+
bar[:BULLISH_ENGULFING] = true if bar[:bullish] and prev[:bearish] and bar[:lower] <= prev[:lower] and bar[:upper] > prev[:upper] and prev[:slope] <= -6
|
231
|
+
bar[:BEARISH_ENGULFING] = true if bar[:bearish] and prev[:bullish] and bar[:lower] < prev[:lower] and bar[:upper] >= prev[:upper] and prev[:slope] >= 6
|
232
|
+
|
233
|
+
|
234
|
+
# DARK-CLOUD-COVER / PIERCING-LINE (on-neck / in-neck / thrusting / piercing / PDF pg 63)
|
235
|
+
bar[:DARK_CLOUD_COVER] = true if bar[:slope] > 5 and prev[:bullish] and bar[:open] > prev[:high] and bar[:close] < prev[:upper] - prev[:body_size] * 0.5 and
|
236
|
+
not bar[:BEARISH_ENGULFING]
|
237
|
+
bar[:PIERCING_LINE] = true if bar[:slope] < -5 and prev[:bearish] and bar[:open] < prev[:low ] and bar[:close] > prev[:lower] + prev[:body_size] * 0.5 and
|
238
|
+
not bar[:BULLISH_ENGULFING]
|
239
|
+
bar[:SMALL_CLOUD_COVER] = true if bar[:slope] > 5 and prev[:bullish] and bar[:open] > prev[:high] and bar[:close] < prev[:upper] - prev[:body_size] * 0.25 and
|
240
|
+
not bar[:BEARISH_ENGULFING] and not bar[:DARK_CLOUD_COVER]
|
241
|
+
bar[:THRUSTING_LINE] = true if bar[:slope] < -5 and prev[:bearish] and bar[:open] < prev[:low ] and bar[:close] > prev[:lower] + prev[:body_size] * 0.25 and
|
242
|
+
not bar[:BULLISH_ENGULFING] and not bar[:PIERCING_LINE]
|
243
|
+
|
244
|
+
|
245
|
+
# COUNTER ATTACKS are like piercings / cloud covers, but insist on a large reverse while only reaching the preceding close
|
246
|
+
bar[:BULLISH_COUNTERATTACK] = true if bar[:slope] < 6 and prev[:bearish] and bar[:bar_size] > bar[:atr] * 0.66 and (bar[:close] - prev[:close]).abs < 2 * bar[:rel] and
|
247
|
+
bar[:body_size] >= bar[:bar_size] * 0.5 and bar[:bullish]
|
248
|
+
bar[:BEARISH_COUNTERATTACK] = true if bar[:slope] > 6 and prev[:bullish] and bar[:bar_size] > bar[:atr] * 0.66 and (bar[:close] - prev[:close]).abs < 2 * bar[:rel] and
|
249
|
+
bar[:body_size] >= bar[:bar_size] * 0.5 and bar[:bearish]
|
250
|
+
|
251
|
+
|
252
|
+
# HARAMIs are an unusual long body embedding the following small body
|
253
|
+
bar[:HARAMI] = true if bar[:body_size] < prev[:body_size] / 2.5 and prev[:bar_size] >= bar[:atr] and
|
254
|
+
prev[:upper] > bar[:upper] and prev[:lower] < bar[:lower] and not bar[:doji]
|
255
|
+
bar[:HARAMI_CROSS] = true if bar[:body_size] < prev[:body_size] / 2.5 and prev[:bar_size] >= bar[:atr] and
|
256
|
+
prev[:upper] > bar[:upper] and prev[:lower] < bar[:lower] and bar[:doji]
|
257
|
+
if bar[:HARAMI] or bar[:HARAMI_CROSS]
|
258
|
+
puts [ :date, :open, :high, :low, :close, :upper, :lower ].map{|x| prev[x]}.join("\t") if debug
|
259
|
+
puts [ :date, :open, :high, :low, :close, :upper, :lower ].map{|x| bar[ x]}.join("\t") if debug
|
260
|
+
puts "" if debug
|
261
|
+
end
|
262
|
+
|
263
|
+
# TODO TWEEZER_TOP and TWEEZER_BOTTOM
|
264
|
+
# actually being a double top / bottom, this dual candle pattern has to be unfolded. It is valid on daily or weekly charts,
|
265
|
+
# and valid if
|
266
|
+
# 1 it has an according
|
267
|
+
|
268
|
+
|
269
|
+
###################################
|
270
|
+
# TRIPLE CANDLE PATTERNS
|
271
|
+
###################################
|
272
|
+
|
273
|
+
# morning star, morning doji star
|
274
|
+
next unless prev and pprev
|
275
|
+
bar[:MORNING_STAR] = true if prev[:STAR] and bar[:bullish] and bar[:close] >= pprev[:lower] and prev[:slope] < -6
|
276
|
+
bar[:MORNING_DOJI_STAR] = true if prev[:DOJI_STAR] and bar[:bullish] and bar[:close] >= pprev[:lower] and prev[:slope] < -6
|
277
|
+
bar[:EVENING_STAR] = true if prev[:STAR] and bar[:bearish] and bar[:close] <= pprev[:upper] and prev[:slope] > 6
|
278
|
+
bar[:EVENING_DOJI_STAR] = true if prev[:DOJI_STAR] and bar[:bearish] and bar[:close] <= pprev[:upper] and prev[:slope] > 6
|
279
|
+
|
280
|
+
# the abandoned baby escalates above stars by gapping the inner star candle to both framing it
|
281
|
+
bar[:ABANDONED_BABY] = true if (bar[:MORNING_STAR] or bar[:MORNING_DOJI_STAR]) and prev[:high] <= [ pprev[:low ], bar[:low ] ].min
|
282
|
+
bar[:ABANDONED_BABY] = true if (bar[:EVENING_STAR] or bar[:EVENING_DOJI_STAR]) and prev[:low ] >= [ pprev[:high], bar[:high] ].max
|
283
|
+
|
284
|
+
# UPSIDEGAP_TWO_CROWS
|
285
|
+
bar[:UPSIDEGAP_TWO_CROWS] = true if (prev[:STAR] or prev[:DOJI_STAR]) and prev[:slope] > 4 and bar[:bearish] and prev[:bearish] and bar[:close] > pprev[:close]
|
286
|
+
bar[:DOWNGAP_TWO_RIVERS] = true if (prev[:STAR] or prev[:DOJI_STAR]) and prev[:slope] < 4 and bar[:bullish] and prev[:bullish] and bar[:close] < pprev[:close]
|
287
|
+
|
288
|
+
# THREE BLACK CROWS / THREE WHITE SOLDIERS
|
289
|
+
bar[:THREE_BLACK_CROWS] = true if [ bar, prev, pprev ].map{|x| x[:bearish] and x[:bar_size] > 0.5 * bar[:atr] }.reduce(:&) and
|
290
|
+
pprev[:close] - prev[ :close] > bar[:atr] * 0.2 and
|
291
|
+
prev[ :close] - bar[ :close] > bar[:atr] * 0.2
|
292
|
+
bar[:THREE_WHITE_SOLDIERS] = true if [ bar, prev, pprev ].map{|x| x[:bullish] and x[:bar_size] > 0.5 * bar[:atr] }.reduce(:&) and
|
293
|
+
prev[:close] - pprev[:close] > bar[:atr] * 0.2 and
|
294
|
+
bar[ :close] - prev[ :close] > bar[:atr] * 0.2
|
295
|
+
|
296
|
+
#### MARKTTECHNIK ####
|
297
|
+
|
298
|
+
# Umkehrstäbe
|
299
|
+
# Ein Umkehrstab bullish ist ein Candle, der zumindest einen Downtrend aus 3 Kerzen beenden könnte.
|
300
|
+
# dazu muss der stab selber ein niedrigers tief haben als seine beiden vorgänger,
|
301
|
+
# der TR muss above average sein und der vorgänger muss einen slopescore von < -5 haben
|
302
|
+
#
|
303
|
+
if pprev[:low] > prev[:low] and prev[:low] > bar[:low] and
|
304
|
+
( bar[:tr] > bar[:atr] * 1.25 or bar[:BREAKN_volume] ) and
|
305
|
+
prev[:slope] <= -5 and
|
306
|
+
bar[:close] - bar[:low] >= (bar[:bar_size]) * 0.7
|
307
|
+
#bar[:close] >= (bar[:high] + bar[:low]) / 2.0
|
308
|
+
bar[:UMKEHRSTAB_BULLISH] = true
|
309
|
+
bar[:CLASSIC] = true
|
310
|
+
end
|
311
|
+
|
312
|
+
if pprev[:high] < prev[:high] and prev[:high] < bar[:high] and
|
313
|
+
( bar[:tr] > bar[:atr] * 1.25 or bar[:BREAKN_volume] ) and
|
314
|
+
prev[:slope] >= 5 and
|
315
|
+
bar[:high] - bar[:close] >= (bar[:bar_size]) * 0.7
|
316
|
+
#bar[:close] <= (bar[:high] + bar[:low]) / 2.0
|
317
|
+
bar[:UMKEHRSTAB_BEARISH] = true
|
318
|
+
bar[:CLASSIC] = true
|
319
|
+
end
|
320
|
+
|
321
|
+
# TINY REVERSALS only work for short periods of time (i.e. lte 5 min)
|
322
|
+
|
323
|
+
if bar[:datetime] and bar[:datetime].respond_to?(:-) and bar[:datetime] - prev[:datetime] <= 5*60
|
324
|
+
if pprev[:low] > prev[:low] and prev[:low] > bar[:low] and
|
325
|
+
((not bar[:tiny] and bar[:bar_size] >= 0.75 * bar[:atr]) or bar[:BREAKN_volume]) and
|
326
|
+
bar[:high] - bar[:close] < bar[:bar_size] / 2.5
|
327
|
+
bar[:UMKEHRSTAB_BULLISH] = true
|
328
|
+
bar[:TINY] = true
|
329
|
+
end
|
330
|
+
if pprev[:high] < prev[:high] and prev[:high] < bar[:high] and
|
331
|
+
((not bar[:tiny] and bar[:bar_size] >= 0.75 * bar[:atr]) or bar[:BREAKN_volume]) and
|
332
|
+
bar[:close] - bar[:low] < bar[:bar_size] / 2.5
|
333
|
+
bar[:UMKEHRSTAB_BEARISH] = true
|
334
|
+
bar[:TINY] = true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# GEM reversals are just another set of criteria
|
339
|
+
if prev[:low] > bar[:low] and prev[:high] > bar[:high] and
|
340
|
+
(bar[:bar_size] >= bar[:atr] or bar[:BREAKN_volume]) and
|
341
|
+
bar[:high] - bar[:close] < bar[:bar_size] / 4.0
|
342
|
+
bar[:UMKEHRSTAB_BULLISH] = true
|
343
|
+
bar[:GEM] = true
|
344
|
+
end
|
345
|
+
if prev[:low] < bar[:low] and prev[:high] < bar[:high] and
|
346
|
+
(bar[:bar_size] >= bar[:atr] or bar[:BREAKN_volume]) and
|
347
|
+
bar[:close] - bar[:low] < bar[:bar_size] / 4.0
|
348
|
+
bar[:UMKEHRSTAB_BEARISH] = true
|
349
|
+
bar[:GEM] = true
|
350
|
+
end
|
351
|
+
|
352
|
+
# DOUBLEBAR reversals are reversals, that span over 2 bars instead of one
|
353
|
+
unless ppprev.nil? or pprev[:slope].nil?
|
354
|
+
pot = {
|
355
|
+
open: prev[:open],
|
356
|
+
high: [prev[:high], bar[:high]].max,
|
357
|
+
low: [prev[:low], bar[:low]].min,
|
358
|
+
close: bar[:close]
|
359
|
+
}
|
360
|
+
pot[:size] = (pot[:high] - pot[:low]).round(8)
|
361
|
+
if (pprev[:slope] >=8 or (prev[:low] > pprev[:low] and pprev[:low] > ppprev[:low])) and pot[:high] > pprev[:high] and
|
362
|
+
pot[:high] - pot[:close] >= pot[:size] * 0.7
|
363
|
+
bar[:UMKEHRSTAB_BEARISH] = true
|
364
|
+
bar[:DOUBLE] = true
|
365
|
+
end
|
366
|
+
if (pprev[:slope] <=-8 or (prev[:high] < pprev[:high] and pprev[:high] < ppprev[:high])) and
|
367
|
+
pot[:low] < pprev[:low] and
|
368
|
+
pot[:close] - pot[:low] >= pot[:size] * 0.7
|
369
|
+
bar[:UMKEHRSTAB_BULLISH] = true
|
370
|
+
bar[:DOUBLE] = true
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# BULLISH_MOVE und BEARISH_MOVE
|
375
|
+
|
376
|
+
unless pprev[:atr].nil?
|
377
|
+
if pprev[:high] > prev[:high] and prev[:high] > bar[:high] and
|
378
|
+
pprev[:low] > prev[:low] and prev[:low] > bar[:low] and
|
379
|
+
bar[:bar_size] >= pprev[:atr] and pprev[:bar_size] >= pprev[:atr] and prev[:bar_size] >= pprev[:atr] and
|
380
|
+
bar[:close] - bar[:low] < bar[:bar_size] / 3
|
381
|
+
bar[:BEARISH_MOVE] = prev[:BEARISH_MOVE].nil? ? (pprev[:BEARISH_MOVE].nil? ? 1 : pprev[:BEARISH_MOVE] + 2) : prev[:BEARISH_MOVE] + 1
|
382
|
+
end
|
383
|
+
if pprev[:high] < prev[:high] and prev[:high] < bar[:high] and
|
384
|
+
pprev[:low] < prev[:low] and prev[:low] < bar[:low] and
|
385
|
+
bar[:bar_size] >= pprev[:atr] and pprev[:bar_size] >= pprev[:atr] and prev[:bar_size] >= pprev[:atr] and
|
386
|
+
bar[:high] - bar[:close] < bar[:bar_size] / 3
|
387
|
+
bar[:BULLISH_MOVE] = prev[:BULLISH_MOVE].nil? ? (pprev[:BULLISH_MOVE].nil? ? 1 : pprev[:BULLISH_MOVE] + 2) : prev[:BULLISH_MOVE] + 1
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# support and resistance
|
392
|
+
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
# SLOPE SCORE
|
398
|
+
def slopescore(pprev, prev, bar, debug = false)
|
399
|
+
# the slope between to bars is considered bullish, if 2 of three points match
|
400
|
+
# - higher high
|
401
|
+
# - higher close
|
402
|
+
# - higher low
|
403
|
+
# the opposite counts for bearish
|
404
|
+
#
|
405
|
+
# this comparison is done between the current bar and previous bar
|
406
|
+
# - if it confirms the score of the previous bar, the new slope score is prev + curr
|
407
|
+
# - otherwise the is compared to score of the pprevious bar
|
408
|
+
# - if it confirms there, the new slope score is pprev + curr
|
409
|
+
# - otherwise the trend is destroyed and tne new score is solely curr
|
410
|
+
|
411
|
+
if bar[:bullish]
|
412
|
+
curr = 1
|
413
|
+
curr += 1 if bar[:bullish_close]
|
414
|
+
elsif bar[:bearish]
|
415
|
+
curr = -1
|
416
|
+
curr -= 1 if bar[:bearish_close]
|
417
|
+
else
|
418
|
+
curr = 0
|
419
|
+
end
|
420
|
+
puts "curr set to #{curr} @ #{bar[:date]}".yellow if debug
|
421
|
+
if prev.nil?
|
422
|
+
puts "no prev found, score == curr: #{curr}" if debug
|
423
|
+
score = curr
|
424
|
+
else
|
425
|
+
comp = comparebars(prev, bar)
|
426
|
+
|
427
|
+
puts prev.select{|k,v| [:high,:low,:close,:score].include?(k)} if debug
|
428
|
+
puts bar if debug
|
429
|
+
puts "COMPARISON 1: #{comp}" if debug
|
430
|
+
|
431
|
+
if prev[:slope] >= 0 and comp[:bullish] # bullish slope confirmed
|
432
|
+
score = prev[:slope]
|
433
|
+
score += curr if curr > 0
|
434
|
+
[ :gap, :bodygap ] .each {|x| score += 0.5 if bar[x] }
|
435
|
+
score += 1 if bar[:RISING_volume]
|
436
|
+
score += 2 if bar[:BREAKN_volume]
|
437
|
+
puts "found bullish slope confirmed, new score #{score}" if debug
|
438
|
+
elsif prev[:slope] <= 0 and comp[:bearish] # bearish slope confirmed
|
439
|
+
score = prev[:slope]
|
440
|
+
score += curr if curr < 0
|
441
|
+
[ :gap, :bodygap ] .each {|x| score -= 0.5 if bar[x] }
|
442
|
+
score -= 1 if bar[:RISING_volume]
|
443
|
+
score -= 2 if bar[:BREAKN_volume]
|
444
|
+
puts "found bearish slope confirmed, new score #{score} (including #{curr} and #{bar[:bodygap]} and #{bar[:gap]}" if debug
|
445
|
+
else #if prev[:slope] > 0 # slopes failed
|
446
|
+
puts "confirmation failed: " if debug
|
447
|
+
if pprev.nil?
|
448
|
+
score = curr
|
449
|
+
else
|
450
|
+
comp2 = comparebars(pprev, bar)
|
451
|
+
puts "\t\tCOMPARISON 2: #{comp2}" if debug
|
452
|
+
if pprev[:slope] >= 0 and comp2[:bullish] # bullish slope confirmed on pprev
|
453
|
+
score = pprev[:slope]
|
454
|
+
score += curr if curr > 0
|
455
|
+
[ :gap, :bodygap ] .each {|x| score += 0.5 if bar[x] }
|
456
|
+
puts "\t\tfound bullish slope confirmed, new score #{score}" if debug
|
457
|
+
score += 1 if bar[:RISING_volume]
|
458
|
+
score += 2 if bar[:BREAKN_volume]
|
459
|
+
elsif pprev[:slope] <= 0 and comp2[:bearish] # bearish slope confirmed
|
460
|
+
score = pprev[:slope]
|
461
|
+
score += curr if curr < 0
|
462
|
+
[ :gap, :bodygap ] .each {|x| score -= 0.5 if bar[x] }
|
463
|
+
score -= 1 if bar[:RISING_volume]
|
464
|
+
score -= 2 if bar[:BREAKN_volume]
|
465
|
+
puts "\t\tfound bearish slope confirmed, new score #{score}" if debug
|
466
|
+
else #slope confirmation finally failed
|
467
|
+
comp3 = comparebars(pprev, prev)
|
468
|
+
if prev[:slope] > 0 # was bullish, turning bearish now
|
469
|
+
score = curr
|
470
|
+
score -= 1 if comp3[:bearish]
|
471
|
+
score -= 1 if comp[:bearish]
|
472
|
+
score -= 1 if prev[:bearish]
|
473
|
+
score -= 1 if prev[:RISING_volume] and comp3[:bearish]
|
474
|
+
score -= 2 if prev[:BREAKN_volume] and comp3[:bearish]
|
475
|
+
score -= 1 if bar[:RISING_volume] and comp[:bearish]
|
476
|
+
score -= 2 if bar[:BREAKN_volume] and comp[:bearish]
|
477
|
+
score -= 1 if bar[:RISING_volume] and comp[:bearish]
|
478
|
+
score -= 2 if bar[:BREAKN_volume] and comp[:bearish]
|
479
|
+
[ :gap, :bodygap ] .each {|x| score += 0.5 if bar[x] }
|
480
|
+
puts "\t\tfinally gave up, turning bearish now, new score #{score}" if debug
|
481
|
+
elsif prev[:slope] < 0
|
482
|
+
score = curr
|
483
|
+
score += 1 if comp3[:bullish]
|
484
|
+
score += 1 if comp[:bullish]
|
485
|
+
score += 1 if prev[:bullish]
|
486
|
+
score += 1 if prev[:RISING_volume] and comp3[:bullish]
|
487
|
+
score += 2 if prev[:BREAKN_volume] and comp3[:bullish]
|
488
|
+
score += 1 if bar[:RISING_volume] and comp[:bullish]
|
489
|
+
score += 2 if bar[:BREAKN_volume] and comp[:bullish]
|
490
|
+
score += 1 if bar[:RISING_volume] and comp[:bullish]
|
491
|
+
score += 2 if bar[:BREAKN_volume] and comp[:bullish]
|
492
|
+
[ :gap, :bodygap ] .each {|x| score -= 0.5 if bar[x] } if curr < 0
|
493
|
+
puts "\t\tfinally gave up, turning bullish now, new score #{score}" if debug
|
494
|
+
else
|
495
|
+
score = 0
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|
501
|
+
puts "" if debug
|
502
|
+
score
|
503
|
+
end
|
504
|
+
|
505
|
+
end
|
506
|
+
|
507
|
+
CR = Candlestick_Recognition
|
508
|
+
end
|
509
|
+
end
|
@@ -4,34 +4,64 @@ module Cotcube
|
|
4
4
|
# Missing top level documentation
|
5
5
|
module Helpers
|
6
6
|
|
7
|
-
|
7
|
+
SYMBOL_HEADERS = %i[ id symbol ib_symbol internal exchange currency ticksize power months type bcf reports format name ]
|
8
|
+
|
9
|
+
def symbols(config: init, **args)
|
8
10
|
if config[:symbols_file].nil?
|
9
11
|
SYMBOL_EXAMPLES
|
10
12
|
else
|
11
13
|
CSV
|
12
|
-
.read(config[:symbols_file], headers:
|
14
|
+
.read(config[:symbols_file], headers: SYMBOL_HEADERS)
|
13
15
|
.map{|row| row.to_h }
|
14
|
-
.map{|row|
|
16
|
+
.map{|row|
|
17
|
+
[ :ticksize, :power, :bcf ].each {|z| row[z] = row[z].to_f}
|
18
|
+
row[:format] = "%#{row[:format]}f"
|
19
|
+
row[:currency] ||= 'USD'
|
20
|
+
row[:multiplier] = (row[:ticksize] / row[:power]).round(8)
|
21
|
+
row
|
22
|
+
}
|
15
23
|
.reject{|row| row[:id].nil? }
|
16
|
-
.tap
|
17
|
-
|
18
|
-
|
24
|
+
.tap{ |all|
|
25
|
+
args.keys.each { |header|
|
26
|
+
unless SYMBOL_HEADERS.include? header
|
27
|
+
puts "WARNING in Cotcube::Helpers.symbols: '#{header}' is not a valid symbol header. Skipping..."
|
28
|
+
next
|
29
|
+
end
|
30
|
+
all.select!{|x| x[header] == args[header]} unless args[header].nil?
|
31
|
+
return all.first if all.size == 1
|
32
|
+
}
|
33
|
+
return all
|
34
|
+
}
|
19
35
|
end
|
20
36
|
end
|
21
37
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
38
|
+
def micros(config: init, symbol: nil, id: nil)
|
39
|
+
if config[:micros_file].nil?
|
40
|
+
MICRO_EXAMPLES
|
41
|
+
else
|
42
|
+
CSV
|
43
|
+
.read(config[:micros_file], headers: SYMBOL_HEADERS)
|
44
|
+
.map{|row| row.to_h }
|
45
|
+
.map{|row|
|
46
|
+
[ :ticksize, :power, :bcf ].each {|z| row[z] = row[z].to_f }
|
47
|
+
row[:format] = "%#{row[:format]}f"
|
48
|
+
row[:currency] ||= 'USD'
|
49
|
+
row
|
50
|
+
}
|
51
|
+
.reject{|row| row[:id].nil? }
|
52
|
+
.tap{ |all|
|
53
|
+
args.keys.each { |header|
|
54
|
+
unless SYMBOL_HEADERS.include? header
|
55
|
+
puts "WARNING in Cotcube::Helpers.symbols: '#{header}' is not a valid symbol header. Skipping..."
|
56
|
+
next
|
57
|
+
end
|
58
|
+
all.select!{|x| x[header] == args[header]} unless args[header].nil?
|
59
|
+
return all.first if all.size == 1
|
60
|
+
}
|
61
|
+
return all
|
62
|
+
}
|
63
|
+
end
|
33
64
|
end
|
34
65
|
end
|
35
|
-
end
|
36
66
|
|
37
67
|
end
|
data/lib/cotcube-helpers.rb
CHANGED
@@ -21,10 +21,12 @@ require_relative 'cotcube-helpers/simple_output'
|
|
21
21
|
require_relative 'cotcube-helpers/simple_series_stats'
|
22
22
|
require_relative 'cotcube-helpers/constants'
|
23
23
|
require_relative 'cotcube-helpers/input'
|
24
|
+
require_relative 'cotcube-helpers/output'
|
24
25
|
require_relative 'cotcube-helpers/reduce'
|
25
26
|
require_relative 'cotcube-helpers/symbols'
|
26
27
|
require_relative 'cotcube-helpers/init'
|
27
28
|
require_relative 'cotcube-helpers/get_id_set'
|
29
|
+
require_relative 'cotcube-helpers/recognition'
|
28
30
|
|
29
31
|
module Cotcube
|
30
32
|
module Helpers
|
data/scripts/symbols
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
option=$1
|
4
|
+
headers='ID,Symbol,ticksize,power,months,type,factor,reports,format,name'
|
5
|
+
|
6
|
+
if [ -z "${option}" ]
|
7
|
+
then
|
8
|
+
(echo ${headers} && cat /etc/cotcube/symbols.csv /etc/cotcube/symbols_micros.csv) | sed 's/,/ , /g' | column -s, -t
|
9
|
+
else
|
10
|
+
(echo ${headers} && cat /etc/cotcube/symbols.csv /etc/cotcube/symbols_micros.csv) | grep -i "$option\|reports,format" | sed 's/,/ , /g' | column -s, -t
|
11
|
+
fi
|
12
|
+
|
13
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cotcube-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
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-
|
11
|
+
date: 2021-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -104,8 +104,10 @@ files:
|
|
104
104
|
- lib/cotcube-helpers/init.rb
|
105
105
|
- lib/cotcube-helpers/input.rb
|
106
106
|
- lib/cotcube-helpers/numeric_ext.rb
|
107
|
+
- lib/cotcube-helpers/output.rb
|
107
108
|
- lib/cotcube-helpers/parallelize.rb
|
108
109
|
- lib/cotcube-helpers/range_ext.rb
|
110
|
+
- lib/cotcube-helpers/recognition.rb
|
109
111
|
- lib/cotcube-helpers/reduce.rb
|
110
112
|
- lib/cotcube-helpers/simple_output.rb
|
111
113
|
- lib/cotcube-helpers/simple_series_stats.rb
|
@@ -116,6 +118,7 @@ files:
|
|
116
118
|
- lib/cotcube-helpers/swig/recognition.rb
|
117
119
|
- lib/cotcube-helpers/symbols.rb
|
118
120
|
- scripts/cron_ruby_wrapper.sh
|
121
|
+
- scripts/symbols
|
119
122
|
homepage: https://github.com/donkeybridge/cotcube-helpers
|
120
123
|
licenses:
|
121
124
|
- BSD-4-Clause
|
@@ -138,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
141
|
- !ruby/object:Gem::Version
|
139
142
|
version: '0'
|
140
143
|
requirements: []
|
141
|
-
rubygems_version: 3.1.
|
144
|
+
rubygems_version: 3.1.6
|
142
145
|
signing_key:
|
143
146
|
specification_version: 4
|
144
147
|
summary: Some helpers and core extensions as part of the Cotcube Suite.
|