cotcube-bardata 0.1.12 → 0.1.15.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 +32 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/cotcube-bardata.gemspec +1 -1
- data/lib/cotcube-bardata/cached.rb +7 -3
- data/lib/cotcube-bardata/daily.rb +144 -43
- data/lib/cotcube-bardata/helpers.rb +4 -0
- data/lib/cotcube-bardata/provide.rb +9 -2
- data/lib/cotcube-bardata/quarters.rb +1 -0
- data/lib/cotcube-bardata/range_matrix.rb +0 -1
- data/lib/cotcube-bardata/suggest.rb +28 -0
- data/lib/cotcube-bardata/trade_dates.rb +38 -12
- data/lib/cotcube-bardata/trading_hours.rb +10 -4
- data/lib/cotcube-bardata.rb +8 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48c3d1d43865ad1b7bba959025aa38ecf0ea574a8a946740f8226aa06741e600
|
4
|
+
data.tar.gz: 4da3026ca588bd848648242c8f21d482e86bb8be72f4a6f0e45b543fca461e05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1235c3ba86c1aa51cd0925912e8f60b07c898498070d86f00d45747aa3041b69039974f04524c4fec187f924bff495596ac0ec32d812d444f8cb5d2b8d223afe
|
7
|
+
data.tar.gz: a9c4753b1cb05598d18c9d0f62d4ecb652b4430ec84ee8ad9900d2cdf0d9bc5330e5b5a8b8b0fa6ff2dda82a72b1087b5f8c28d257b9ce3c6763dd22080c1681
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
## 0.1.15.4 (October 26, 2021)
|
2
|
+
- daily: including the sourcing factor (:bcf) into provide_daily and continuous
|
3
|
+
|
4
|
+
## 0.1.15.3 (August 04, 2021)
|
5
|
+
- daily.rb: Hotfixing
|
6
|
+
|
7
|
+
## 0.1.15.2 (August 04, 2021)
|
8
|
+
- fixed leftover debug setting
|
9
|
+
|
10
|
+
## 0.1.15.1 (August 04, 2021)
|
11
|
+
- fixed license mismatch
|
12
|
+
|
13
|
+
## 0.1.15 (August 04, 2021)
|
14
|
+
- daily.rb: added support to add eod data on incomplete dailies
|
15
|
+
- adding dep: cotcube-indicators
|
16
|
+
- provide: new method :determine_significant_volume
|
17
|
+
- suggest: adding silence
|
18
|
+
- cached: Adding :dist
|
19
|
+
- daily: new method :determine_significant_volume
|
20
|
+
- added :dist generically to quarters
|
21
|
+
- trading_hours: added param to return headers only
|
22
|
+
- last_trade_date: Enabled caching instead of fetching each time
|
23
|
+
|
24
|
+
## 0.1.14 (May 07, 2021)
|
25
|
+
- few changes in provide / cached / daily for a more straigtforward forcing of cache renewal
|
26
|
+
- suggest: new method to suggest a contract for given symbol and date
|
27
|
+
|
28
|
+
## 0.1.13 (April 07, 2021)
|
29
|
+
- daily: fixed const_caching in continuous
|
30
|
+
- trade_dates: FIXING call with HTTParty must send Agent Header
|
31
|
+
- helpers/get_id_set: added support for params given as Symbols (:NG instead of 'NG')
|
32
|
+
|
1
33
|
## 0.1.12 (March 13, 2021)
|
2
34
|
- range_matrix: adapted to accept block for range provision; added params :days_only and :last_n
|
3
35
|
- minor fix on previous patch
|
data/README.md
CHANGED
@@ -90,5 +90,5 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/donkey
|
|
90
90
|
|
91
91
|
## License
|
92
92
|
|
93
|
-
The gem is available as open source under the terms of the [
|
93
|
+
The gem is available as open source under the terms of the [BSD-3-Clause-License](https://opensource.org/licenses/BSD-3-Clause).
|
94
94
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.15.4
|
data/cotcube-bardata.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.description = 'Functions to provide bardata; and some simple time series aggregations '
|
11
11
|
|
12
12
|
spec.homepage = "https://github.com/donkeybridge/#{spec.name}"
|
13
|
-
spec.license = 'BSD-
|
13
|
+
spec.license = 'BSD-3-Clause'
|
14
14
|
spec.required_ruby_version = Gem::Requirement.new('~> 2.7')
|
15
15
|
|
16
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
@@ -12,7 +12,7 @@ module Cotcube
|
|
12
12
|
timezone: Time.find_zone('America/Chicago'),
|
13
13
|
filter: :full, # most probably either :full or :rth
|
14
14
|
force_update: false, # force reloading via provide_quarters
|
15
|
-
force_recent: false) #
|
15
|
+
force_recent: false) # provide base.last even if dayswitch hasn't happen yet
|
16
16
|
|
17
17
|
unless range.nil? ||
|
18
18
|
range.is_a?(Range) &&
|
@@ -39,17 +39,20 @@ module Cotcube
|
|
39
39
|
file = "#{dir}/#{contract}.csv"
|
40
40
|
quarters_file = "#{config[:data_path]}/quarters/#{sym[:id]}/#{contract[-3..]}.csv"
|
41
41
|
if File.exist?(file) && (not force_update)
|
42
|
+
puts "Working with existing #{file}, no update was forced" if debug
|
43
|
+
puts " Using quarters from #{quarters_file}" if debug
|
42
44
|
base = CSV.read(file, headers: headers).map do |x|
|
43
45
|
x = x.to_h
|
44
46
|
x[:datetime] = timezone.parse(x[:datetime])
|
45
47
|
%i[open high low close].each { |z| x[z] = x[z].to_f.round(9) }
|
46
48
|
x[:volume] = x[:volume].to_i
|
49
|
+
x[:dist] = ((x[:high] - x[:low]) / sym[:ticksize] ).to_i
|
47
50
|
x[:type] = "#{filter.to_s.downcase}_day".to_sym
|
48
51
|
x
|
49
52
|
end
|
50
53
|
if base.last[:high].zero?
|
51
54
|
# contract exists but is closed (has the CLOSED marker)
|
52
|
-
base.pop
|
55
|
+
base.pop
|
53
56
|
# rubocop:disable Metrics/BlockNesting
|
54
57
|
result = if range.nil?
|
55
58
|
base
|
@@ -96,8 +99,9 @@ module Cotcube
|
|
96
99
|
end
|
97
100
|
|
98
101
|
base = Cotcube::Helpers.reduce(bars: data, to: :days)
|
102
|
+
puts "Reduced base ends at #{bast.last[:datetime].strftime('%Y-%m-%d')}" if debug
|
99
103
|
|
100
|
-
# remove last day of result
|
104
|
+
# remove last day of result if suspecting incomplete last base
|
101
105
|
base.pop if base.last[:datetime].to_date == timezone.now.to_date and not force_recent
|
102
106
|
|
103
107
|
base.map do |x|
|
@@ -8,8 +8,12 @@ module Cotcube
|
|
8
8
|
symbol: nil, id: nil,
|
9
9
|
range: nil,
|
10
10
|
timezone: Time.find_zone('America/Chicago'),
|
11
|
+
keep_last: false,
|
12
|
+
add_eods: true,
|
13
|
+
indicators: {},
|
11
14
|
config: init)
|
12
15
|
contract = contract.to_s.upcase
|
16
|
+
rounding = 8
|
13
17
|
unless contract.is_a?(String) && [3, 5].include?(contract.size)
|
14
18
|
raise ArgumentError, "Contract '#{contract}' is bogus, should be like 'M21' or 'ESM21'"
|
15
19
|
end
|
@@ -41,14 +45,45 @@ module Cotcube
|
|
41
45
|
data = CSV.read(data_file, headers: %i[contract date open high low close volume oi]).map do |row|
|
42
46
|
row = row.to_h
|
43
47
|
row.each do |k, _|
|
44
|
-
|
48
|
+
if %i[open high low close].include? k
|
49
|
+
row[k] = row[k].to_f
|
50
|
+
row[k] = (row[k] * sym[:bcf]).round(8) unless sym[:bcf] == 1.0
|
51
|
+
end
|
45
52
|
row[k] = row[k].to_i if %i[volume oi].include? k
|
46
53
|
end
|
47
54
|
row[:datetime] = timezone.parse(row[:date])
|
55
|
+
row[:dist] = ((row[:high] - row[:low]) / sym[:ticksize] ).to_i
|
48
56
|
row[:type] = :daily
|
49
57
|
row
|
50
58
|
end
|
51
|
-
|
59
|
+
contract_expired = data.last[:high].zero?
|
60
|
+
data.pop if contract_expired and not keep_last
|
61
|
+
if not contract_expired and add_eods
|
62
|
+
today = Date.today
|
63
|
+
eods = [ ]
|
64
|
+
while today.strftime('%Y-%m-%d') > data.last[:date]
|
65
|
+
eods << provide_eods(symbol: sym[:symbol], dates: today, contracts_only: false, quiet: true)
|
66
|
+
today -= 1
|
67
|
+
end
|
68
|
+
eods.flatten!.map!{|x| x.tap {|y| %i[ volume_part oi_part ].map{|z| y.delete(z)} } }
|
69
|
+
eods.select!{|x| x[:contract] == "#{sym[:symbol]}#{contract}" }
|
70
|
+
eods.map!{|x| x.tap{|y|
|
71
|
+
if sym[:bcf] != 1.0
|
72
|
+
%i[open high low close].map{|k|
|
73
|
+
y[k] = (y[k] * sym[:bcf]).round(8)
|
74
|
+
}
|
75
|
+
end
|
76
|
+
y[:datetime] = timezone.parse(y[:date])
|
77
|
+
y[:dist] = ((y[:high] - y[:low]) / sym[:ticksize] ).to_i
|
78
|
+
y[:type] = :eod
|
79
|
+
} }
|
80
|
+
data += eods.reverse
|
81
|
+
end
|
82
|
+
data.map do |bar|
|
83
|
+
indicators.each do |k,v|
|
84
|
+
bar[k] = v.call(bar).round(rounding)
|
85
|
+
end
|
86
|
+
end unless indicators.empty?
|
52
87
|
if range.nil?
|
53
88
|
data
|
54
89
|
else
|
@@ -61,51 +96,104 @@ module Cotcube
|
|
61
96
|
|
62
97
|
# reads all files in bardata/daily/<id> and aggregates by date
|
63
98
|
# (what is a pre-stage of a continuous based on daily bars)
|
64
|
-
def continuous(symbol: nil, id: nil, config: init, date: nil, measure: nil, force_rewrite: false)
|
99
|
+
def continuous(symbol: nil, id: nil, config: init, date: nil, measure: nil, force_rewrite: false, selector: nil, debug: false, add_eods: true, indicators: nil)
|
65
100
|
raise ArgumentError, ':measure, if given, must be a Time object (e.g. Time.now)' unless [NilClass, Time].include? measure.class
|
66
101
|
measuring = lambda {|c| puts "[continuous] Time measured until '#{c}': #{(Time.now.to_f - measure.to_f).round(2)}sec" unless measure.nil? }
|
67
102
|
|
68
103
|
measuring.call("Starting")
|
69
104
|
sym = get_id_set(symbol: symbol, id: id)
|
70
105
|
id = sym[:id]
|
106
|
+
symbol = sym[:symbol]
|
107
|
+
ticksize = sym[:ticksize]
|
108
|
+
effective_selector = selector || :volume
|
109
|
+
raise ArgumentError, 'selector must be in %i[ nil :volume ;oi].' unless [ nil, :volume, :oi ].include? selector
|
71
110
|
id_path = "#{config[:data_path]}/daily/#{id}"
|
72
111
|
c_file = "#{id_path}/continuous.csv"
|
112
|
+
puts "Using file #{c_file}" if debug
|
73
113
|
|
74
114
|
# instead of using the provide_daily methods above, for this bulk operation a 'continuous.csv' is created
|
75
115
|
# this boosts from 4.5sec to 0.3sec
|
76
|
-
rewriting = force_rewrite or not(File.exist?(c_file)) or (Time.now - File.mtime(c_file) > 8.days)
|
116
|
+
rewriting = (force_rewrite or not(File.exist?(c_file)) or (Time.now - File.mtime(c_file) > 8.days))
|
77
117
|
if rewriting
|
78
118
|
puts "In daily+continuous: Rewriting #{c_file} #{force_rewrite ? "forcibly" : "due to fileage"}.".light_yellow
|
79
|
-
`rm #{c_file}; find #{id_path} | xargs cat 2>/dev/null | grep -v '
|
119
|
+
`rm #{c_file}; find #{id_path} | xargs cat 2>/dev/null | grep -v ',0,' | grep -v ',0$'| sort -t, -k2 | cut -d, -f1-8 | grep ',.*,' | uniq > #{c_file}`
|
80
120
|
end
|
81
121
|
loading = lambda do
|
82
122
|
data = CSV.read(c_file).map do |row|
|
83
123
|
r = { contract: row[0],
|
84
124
|
date: row[1],
|
85
|
-
|
86
|
-
|
125
|
+
open: row[2],
|
126
|
+
high: row[3],
|
127
|
+
low: row[4],
|
128
|
+
close: row[5],
|
129
|
+
volume: row[6].to_i,
|
130
|
+
oi: row[7].to_i,
|
131
|
+
type: :cont
|
87
132
|
}
|
88
133
|
end
|
134
|
+
if add_eods
|
135
|
+
today = Date.today
|
136
|
+
eods = [ ]
|
137
|
+
while today.strftime('%Y-%m-%d') > data.last[:date]
|
138
|
+
eods << provide_eods(symbol: symbol, dates: today, contracts_only: false, quiet: true)
|
139
|
+
today -= 1
|
140
|
+
end
|
141
|
+
eods.flatten!.map!{|x| x.tap {|y| %i[ volume_part oi_part ].map{|z| y.delete(z)} } }
|
142
|
+
eods.delete_if { |elem| elem.flatten.empty? }
|
143
|
+
data += eods.reverse
|
144
|
+
end
|
89
145
|
|
90
146
|
measuring.call("Finished retrieving dailies.")
|
91
147
|
result = []
|
148
|
+
rounding = 8 # sym[:format].split('.').last.to_i rescue 6
|
149
|
+
indicators ||= {
|
150
|
+
typical: Cotcube::Indicators.calc(a: :high, b: :low, c: :close) {|high, low, close| (high + low + close) / 3 },
|
151
|
+
sma250_high: Cotcube::Indicators.sma(key: :high, length: 250),
|
152
|
+
sma250_low: Cotcube::Indicators.sma(key: :low, length: 250),
|
153
|
+
sma250_typ: Cotcube::Indicators.sma(key: :typical, length: 250),
|
154
|
+
dist: Cotcube::Indicators.calc(a: :high, b: :low, finalize: :to_i) {|high, low| ((high-low) / ticksize) },
|
155
|
+
}
|
156
|
+
|
157
|
+
|
158
|
+
|
92
159
|
data.group_by { |x| x[:date] }.map do |k, v|
|
93
160
|
v.map { |x| x.delete(:date) }
|
94
|
-
|
161
|
+
avg_bar = {
|
95
162
|
date: k,
|
96
|
-
|
97
|
-
|
163
|
+
contract: v.max_by{|x| x[:oi] }[:contract],
|
164
|
+
open: nil, high: nil, low: nil, close: nil,
|
165
|
+
volume: v.map { |x| x[:volume] }.reduce(:+),
|
166
|
+
oi: v.map { |x| x[:oi] }.reduce(:+),
|
167
|
+
type: :cont_eod
|
168
|
+
}
|
169
|
+
|
170
|
+
%i[ open high low close ].each do |ohlc|
|
171
|
+
avg_bar[ohlc] = (v.map{|x| x[ohlc].to_f * x[effective_selector] }.reduce(:+) / avg_bar[effective_selector]).round(rounding)
|
172
|
+
avg_bar[ohlc] = (avg_bar[ohlc] * sym[:bcf]).round(8) unless sym[:bcf] == 1.0
|
173
|
+
|
174
|
+
end
|
175
|
+
p avg_bar if debug
|
176
|
+
indicators.each do |k,v|
|
177
|
+
print format('%12s: ', k.to_s) if debug
|
178
|
+
avg_bar[k] = v.call(avg_bar).round(rounding)
|
179
|
+
puts avg_bar[k] if debug
|
180
|
+
end
|
181
|
+
%i[tr atr5].each { |ind|
|
182
|
+
avg_bar[ind] = (avg_bar[ind] / sym[:ticksize]).round.to_i unless avg_bar[ind].nil?
|
98
183
|
}
|
184
|
+
result << avg_bar
|
99
185
|
result.last[:contracts] = v
|
100
186
|
end
|
101
187
|
result
|
102
188
|
end
|
103
|
-
constname = "CONTINUOUS_#{symbol}".to_sym
|
189
|
+
constname = "CONTINUOUS_#{symbol}#{selector.nil? ? '' : ('_' + selector.to_s)}".to_sym
|
104
190
|
if rewriting or not Cotcube::Bardata.const_defined?( constname)
|
191
|
+
old = $VERBOSE; $VERBOSE = nil
|
105
192
|
Cotcube::Bardata.const_set constname, loading.call
|
193
|
+
$VERBOSE = old
|
106
194
|
end
|
107
195
|
measuring.call("Finished processing")
|
108
|
-
date.nil? ? Cotcube::Bardata.const_get(constname) : Cotcube::Bardata.const_get(constname).find { |x| x[:date] == date }
|
196
|
+
date.nil? ? Cotcube::Bardata.const_get(constname).map{|z| z.dup } : Cotcube::Bardata.const_get(constname).find { |x| x[:date] == date }
|
109
197
|
end
|
110
198
|
|
111
199
|
def continuous_ml(symbol: nil, id: nil, base: nil)
|
@@ -158,7 +246,7 @@ module Cotcube
|
|
158
246
|
oi: x[:contracts].sort_by { |z| - z[:oi] }[0..4].compact.reject { |z| z[:oi].zero? }
|
159
247
|
}
|
160
248
|
end
|
161
|
-
measuring.call("Retrieved continuous")
|
249
|
+
measuring.call("Retrieved continuous for #{sym[:symbol]}")
|
162
250
|
data.reject! { |x| x[selector].empty? }
|
163
251
|
result = data.group_by { |x| x[selector].first[:contract] }
|
164
252
|
result.each_key do |key|
|
@@ -194,6 +282,8 @@ module Cotcube
|
|
194
282
|
selector: :volume,
|
195
283
|
filter: nil,
|
196
284
|
date: Date.today,
|
285
|
+
short: true,
|
286
|
+
silent: false,
|
197
287
|
measure: nil,
|
198
288
|
debuglevel: 1,
|
199
289
|
debug: false)
|
@@ -201,6 +291,7 @@ module Cotcube
|
|
201
291
|
debuglevel = debug
|
202
292
|
debug = debuglevel > 0 ? true : false
|
203
293
|
end
|
294
|
+
silent = false if debug
|
204
295
|
|
205
296
|
raise ArgumentError, ':measure, if given, must be a Time object (e.g. Time.now)' unless [NilClass, Time].include? measure.class
|
206
297
|
measuring = lambda {|c| puts "[continuous_table] Time measured until '#{c}': #{(Time.now.to_f - measure.to_f).round(2)}sec" unless measure.nil? }
|
@@ -225,74 +316,84 @@ module Cotcube
|
|
225
316
|
|
226
317
|
ytoday = date.yday
|
227
318
|
data = continuous_overview(id: id, selector: selector, filter: filter, human: false, config: init, measure: measure)
|
228
|
-
.reject { |k, _| k[-2..].to_i
|
319
|
+
.reject { |k, _| k[-2..].to_i >= date.year % 2000 }
|
229
320
|
.group_by { |k, _| k[2] }
|
230
321
|
measuring.call("Retrieved continous_overview")
|
231
322
|
output_sent = []
|
232
323
|
early_year=nil
|
324
|
+
long_output = []
|
233
325
|
data.keys.sort.each do |month|
|
234
326
|
puts "Processing #{sym[:symbol]}#{month}" if debuglevel > 1
|
235
327
|
v0 = data[month]
|
236
|
-
|
328
|
+
# ldays is the list of 'last days'
|
329
|
+
ldays = v0.map { |_, v1| Date.parse(v1.last[:date]).yday }
|
330
|
+
# fdays is the list of 'first days'
|
237
331
|
fdays = v0.map { |_, v1| Date.parse(v1.first[:date]).yday }.sort
|
238
332
|
# if the last ml day nears the end of the year, we must fix
|
239
|
-
|
333
|
+
ldays.map! { |x| x > 350 ? x - 366 : x } if ldays.min < 50
|
240
334
|
fday = fdays[fdays.size / 2]
|
241
|
-
|
242
|
-
# a contract is proposed to use after fday - 1, but before
|
243
|
-
# it is warned to user after fday - 1 but before
|
244
|
-
# it is warned red >=
|
245
|
-
color = if (ytoday >=
|
335
|
+
lavg = ldays.reduce(:+) / ldays.size
|
336
|
+
# a contract is proposed to use after fday - 1, but before ldays.min (green)
|
337
|
+
# it is warned to user after fday - 1 but before lavg - 1 (red)
|
338
|
+
# it is warned red >= lavg - 1 and <= lavg + 1
|
339
|
+
color = if (ytoday >= lavg - 1) && (ytoday <= lavg + 1)
|
246
340
|
:light_red
|
247
|
-
elsif (ytoday >
|
341
|
+
elsif (ytoday > ldays.min) && (ytoday < lavg - 1)
|
248
342
|
:light_yellow
|
249
|
-
elsif (ytoday >= (fday >
|
343
|
+
elsif (ytoday >= (fday > lavg ? 0 : fday - 5)) && (ytoday <= ldays.min)
|
250
344
|
:light_green
|
251
345
|
else
|
252
346
|
:white
|
253
347
|
end
|
254
348
|
# rubocop:disable Layout/ClosingParenthesisIndentation
|
349
|
+
long_output << {
|
350
|
+
month: month,
|
351
|
+
first_ml: fday,
|
352
|
+
last_min: ldays.min,
|
353
|
+
last_avg: lavg,
|
354
|
+
last_max: ldays.max }
|
255
355
|
output = "#{sym[:symbol]
|
256
356
|
}#{month
|
257
357
|
}\t#{format '%12s', sym[:type]
|
258
|
-
}\
|
259
|
-
|
260
|
-
}\
|
261
|
-
}: #{dfm.call(
|
262
|
-
}\t#{format '%5d',
|
263
|
-
}: #{dfm.call(
|
264
|
-
}\
|
265
|
-
}: #{dfm.call(
|
358
|
+
}\ttoday is #{ytoday
|
359
|
+
} -- median of first is #{fday
|
360
|
+
}\tlast ranges from #{format '%5d', ldays.min
|
361
|
+
}: #{dfm.call(ldays.min)
|
362
|
+
}\t#{format '%5d', lavg
|
363
|
+
}: #{dfm.call(lavg)
|
364
|
+
}\tto #{format '%5d', ldays.max
|
365
|
+
}: #{dfm.call(ldays.max)}".colorize(color)
|
266
366
|
if debug || (color != :white)
|
267
|
-
puts output
|
268
|
-
output_sent << "#{sym[:symbol]}#{month}" unless color == :white
|
367
|
+
puts output unless silent
|
269
368
|
end
|
369
|
+
output_sent << "#{sym[:symbol]}#{month}" unless color == :white
|
270
370
|
early_year ||= output
|
271
|
-
next
|
371
|
+
next if silent or not (debug and debuglevel >= 2)
|
272
372
|
|
273
373
|
v0.each do |contract, v1|
|
274
374
|
puts "\t#{contract
|
275
375
|
}\t#{v1.first[:date]
|
276
|
-
}
|
277
|
-
|
278
|
-
} (#{Date.parse(v1.last[:date]).yday}"
|
376
|
+
} (#{format '%3d', Date.parse(v1.first[:date]).yday
|
377
|
+
})\t#{Date.parse(v1.last[:date]).strftime('%a, %Y-%m-%d')
|
378
|
+
} (#{Date.parse(v1.last[:date]).yday})" unless silent
|
279
379
|
# rubocop:enable Layout/ClosingParenthesisIndentation
|
280
380
|
end
|
281
381
|
end
|
282
382
|
case output_sent.size
|
283
383
|
when 0
|
284
|
-
|
285
|
-
|
286
|
-
|
384
|
+
unless silent
|
385
|
+
puts "WARNING: No output was sent for symbol '#{sym[:symbol]}'.".colorize(:light_yellow)
|
386
|
+
puts " Assuming late-year-processing.".light_yellow
|
387
|
+
puts early_year.light_green
|
388
|
+
end
|
287
389
|
when 1
|
288
390
|
# all ok
|
289
391
|
true
|
290
392
|
else
|
291
|
-
puts "
|
292
|
-
|
393
|
+
puts "Continuous table show #{output_sent.size} active contracts ( #{output_sent} ) for #{sym[:symbol]} ---------------" unless silent
|
293
394
|
end
|
294
395
|
measuring.call("Finished processing")
|
295
|
-
output_sent
|
396
|
+
short ? output_sent : long_output
|
296
397
|
end
|
297
398
|
end
|
298
399
|
end
|
@@ -46,6 +46,10 @@ module Cotcube
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def get_id_set(symbol: nil, id: nil, contract: nil, config: init)
|
49
|
+
contract = contract.to_s.upcase if contract.is_a? Symbol
|
50
|
+
id = id.to_s.upcase if id.is_a? Symbol
|
51
|
+
symbol = symbol.to_s.upcase if symbol.is_a? Symbol
|
52
|
+
|
49
53
|
if contract.is_a?(String) && (contract.length == 5)
|
50
54
|
c_symbol = contract[0..1]
|
51
55
|
if (not symbol.nil?) && (symbol != c_symbol)
|
@@ -14,6 +14,7 @@ module Cotcube
|
|
14
14
|
filter: :full,
|
15
15
|
# TODO: for future compatibility and suggestion: planning to include a function to update
|
16
16
|
# with live data from broker
|
17
|
+
force_update: false,
|
17
18
|
force_recent: false)
|
18
19
|
|
19
20
|
sym = get_id_set(symbol: symbol, id: id, contract: contract, config: config)
|
@@ -33,7 +34,7 @@ module Cotcube
|
|
33
34
|
|
34
35
|
when :days, :weeks, :months
|
35
36
|
base = provide_cached contract: contract, symbol: symbol, id: id, config: config, filter: filter,
|
36
|
-
range: range, force_recent: force_recent
|
37
|
+
range: range, force_recent: force_recent, force_update: force_update
|
37
38
|
return base if %i[day days].include? interval
|
38
39
|
|
39
40
|
# TODO: Missing implementation to reduce cached days to weeks or months
|
@@ -42,7 +43,7 @@ module Cotcube
|
|
42
43
|
provide_daily contract: contract, symbol: symbol, id: id, config: config, range: range
|
43
44
|
when :synth, :synthetic, :synthetic_days
|
44
45
|
days = provide_cached contract: contract, symbol: symbol, id: id, config: config, filter: filter,
|
45
|
-
range: range, force_recent: force_recent
|
46
|
+
range: range, force_recent: force_recent, force_update: force_update
|
46
47
|
dailies = provide_daily contract: contract, symbol: symbol, id: id, config: config, range: range
|
47
48
|
if ((days.last[:datetime] > dailies.last[:datetime]) rescue false)
|
48
49
|
dailies[..-2] + days.select { |d| d[:datetime] > dailies[-2][:datetime] }
|
@@ -53,5 +54,11 @@ module Cotcube
|
|
53
54
|
raise ArgumentError, "Unsupported or unknown interval '#{interval}' in Bardata.provide"
|
54
55
|
end
|
55
56
|
end
|
57
|
+
|
58
|
+
def determine_significant_volume(base: , contract: )
|
59
|
+
set = Cotcube::Bardata.trading_hours(symbol: contract[0..1], filter: :rth)
|
60
|
+
prod = base - base.select_within(ranges: set ,attr: :datetime) {|x| x.to_datetime.to_sssm }
|
61
|
+
prod.group_by{|x| x[:volume] / 500 }
|
62
|
+
end
|
56
63
|
end
|
57
64
|
end
|
@@ -33,6 +33,7 @@ module Cotcube
|
|
33
33
|
%i[open high low close].map { |x| row[x] = row[x].to_f }
|
34
34
|
%i[volume day].map { |x| row[x] = row[x].to_i }
|
35
35
|
row[:datetime] = timezone.parse(row[:datetime])
|
36
|
+
row[:dist] = ((row[:high] - row[:low]) / sym[:ticksize] ).to_i
|
36
37
|
row[:type] = :quarter
|
37
38
|
row
|
38
39
|
end
|
@@ -38,7 +38,6 @@ module Cotcube
|
|
38
38
|
|
39
39
|
%i[days weeks months].each do |period|
|
40
40
|
next if days_only and %i[weeks months].include? period
|
41
|
-
p yield(source[period].first)
|
42
41
|
source[period].map! do |x|
|
43
42
|
x[:range] = block_given? ? yield(x) : (((x[:high] - x[:low]) / sym[:ticksize]).round)
|
44
43
|
x
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
# missing top level documentation
|
5
|
+
module Bardata
|
6
|
+
# based on day(of year) and symbol, suggest best fitting contract
|
7
|
+
def suggest_contract_for symbol:, date: Date.today, warnings: true
|
8
|
+
ml = Cotcube::Bardata.continuous_table symbol: symbol, date: date, silent: true
|
9
|
+
if ml.size != 1
|
10
|
+
puts "WARNING: No or no unique most liquid found for #{date}. please give :contract parameter".light_yellow if warnings
|
11
|
+
if ml.size > 1
|
12
|
+
puts "\tUsing #{ml.last}. Consider breaking here, if that is not acceptable.".light_yellow if warnings
|
13
|
+
sleep 1
|
14
|
+
else
|
15
|
+
puts "\tERROR: No suggestible contract found for #{symbol} and #{date}.".light_red
|
16
|
+
return
|
17
|
+
end
|
18
|
+
end
|
19
|
+
year = date.year % 100
|
20
|
+
if ml.last[2] < "K" and date.month > 9
|
21
|
+
"#{ml.last}#{year + 1}"
|
22
|
+
else
|
23
|
+
"#{ml.last}#{year}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -5,19 +5,45 @@ module Cotcube
|
|
5
5
|
module Bardata
|
6
6
|
# fetching official trade dates from CME
|
7
7
|
# it returns the current trade date or, if today isn't a trading day, the last trade date.
|
8
|
-
def last_trade_date
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
"
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
8
|
+
def last_trade_date(force_update: false)
|
9
|
+
const_LTD = :LAST_TRADE_DATE
|
10
|
+
const_LTDU = :LAST_TRADE_DATE_UPDATE
|
11
|
+
if force_update or not Object.const_defined?(const_LTD) or Object.const_get(const_LTD).nil? or Time.now - Object.const_get(const_LTDU) > 2.hours
|
12
|
+
result = nil
|
13
|
+
uri = 'https://www.cmegroup.com/CmeWS/mvc/Volume/TradeDates?exchange=CME'
|
14
|
+
headers = { "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
15
|
+
"Accept-Encoding" => "gzip, deflate, br",
|
16
|
+
"Accept-Language" => "en-US,en;q=0.9",
|
17
|
+
"Cache-Control" => "max-age=0",
|
18
|
+
"Connection" => "keep-alive",
|
19
|
+
# Cookie: ak_bmsc=602078F6DE40954BAA8C7E7D3815102CACE82AAFD237000084B5A460F4FBCA68~pluz010T49Xag3sXquUZtVJmFX701dzEgt5v6Ht1EZSLKE4HL+bgg1L9ePnL5I0mm7QWXe1qaLhUbX1IPrL/f20trRMMRlkC3UWXk27DY/EBCP4mRno8QQygLCwgs2B2AQHJyb63WwRihCko8UYUiIhb89ArPZM5OPraoKy3JU9oE9e+iERdARNZHLHqRiB1GnmbKUvQqos3sXaEe3GpoiTszzk8sHZs4ZKuoO/rvFHko=",
|
20
|
+
"Host" => "www.cmegroup.com",
|
21
|
+
"sec-ch-ua" => %q[" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"],
|
22
|
+
"sec-ch-ua-mobile" => "?0",
|
23
|
+
"Sec-Fetch-Dest" => "document",
|
24
|
+
"Sec-Fetch-Mode" => "navigate",
|
25
|
+
"Sec-Fetch-Site" => "none",
|
26
|
+
"Sec-Fetch-User" => "?1",
|
27
|
+
"Upgrade-Insecure-Requests" => "1",
|
28
|
+
"User-Agent" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36" }
|
29
|
+
begin
|
30
|
+
# HTTParty.get(uri, headers: { "User-Agent" => "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0"})
|
31
|
+
result = HTTParty.get(uri, headers: headers)
|
32
|
+
.parsed_response
|
33
|
+
.map do |x|
|
34
|
+
a = x['tradeDate'].chars.each_slice(2).map(&:join)
|
35
|
+
"#{a[0]}#{a[1]}-#{a[2]}-#{a[3]}"
|
36
|
+
end
|
37
|
+
.first
|
38
|
+
rescue StandardError
|
39
|
+
result = nil
|
40
|
+
end
|
41
|
+
oldverbose = $VERBOSE; $VERBOSE = nil
|
42
|
+
Object.const_set(const_LTD, result)
|
43
|
+
Object.const_set(const_LTDU, Time.now) unless result.nil?
|
44
|
+
$VERBOSE = oldverbose
|
20
45
|
end
|
46
|
+
Object.const_get(const_LTD)
|
21
47
|
end
|
22
48
|
|
23
49
|
def holidays(config: init)
|
@@ -12,14 +12,20 @@ module Cotcube
|
|
12
12
|
filter: ,
|
13
13
|
force_filter: false, # with force_filter one would avoid falling back
|
14
14
|
# to the contract_type based range set
|
15
|
+
headers_only: false, # return only headers instead of ranges
|
15
16
|
config: init, debug: false)
|
16
17
|
return (0...24 * 7 * 3600) if filter.to_s =~ /24x7/
|
17
18
|
|
18
19
|
prepare = lambda do |f|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
if headers_only
|
21
|
+
CSV.read(f)
|
22
|
+
.first
|
23
|
+
else
|
24
|
+
CSV.read(f, converters: :numeric)
|
25
|
+
.map(&:to_a)
|
26
|
+
.tap { |x| x.shift unless x.first.first.is_a?(Numeric) }
|
27
|
+
.map { |x| (x.first...x.last) }
|
28
|
+
end
|
23
29
|
end
|
24
30
|
|
25
31
|
sym = get_id_set(symbol: symbol, id: id)
|
data/lib/cotcube-bardata.rb
CHANGED
@@ -11,6 +11,7 @@ require 'date' unless defined?(DateTime)
|
|
11
11
|
require 'csv' unless defined?(CSV)
|
12
12
|
require 'yaml' unless defined?(YAML)
|
13
13
|
require 'cotcube-helpers'
|
14
|
+
require 'cotcube-indicators'
|
14
15
|
require 'parallel'
|
15
16
|
|
16
17
|
require_relative 'cotcube-bardata/constants'
|
@@ -22,6 +23,7 @@ require_relative 'cotcube-bardata/quarters'
|
|
22
23
|
require_relative 'cotcube-bardata/eods'
|
23
24
|
require_relative 'cotcube-bardata/cached'
|
24
25
|
require_relative 'cotcube-bardata/provide'
|
26
|
+
require_relative 'cotcube-bardata/suggest'
|
25
27
|
require_relative 'cotcube-bardata/range_matrix'
|
26
28
|
require_relative 'cotcube-bardata/trading_hours'
|
27
29
|
|
@@ -61,13 +63,17 @@ module Cotcube
|
|
61
63
|
# create an array of ranges based on specified source data
|
62
64
|
:trading_hours,
|
63
65
|
# receive id / symbol information on an uncertain set of parameters
|
66
|
+
# TODO: decommision sppearance of get_id_set and symbols in cotcube::bardata
|
64
67
|
:get_id_set,
|
68
|
+
:symbols,
|
69
|
+
#
|
65
70
|
:select_specific_date,
|
66
71
|
:extended_select_for_range,
|
67
72
|
:provide_cached,
|
73
|
+
:suggest_contract_for,
|
74
|
+
#
|
68
75
|
:compare,
|
69
|
-
:holidays
|
70
|
-
:symbols # reads and provides the symbols file
|
76
|
+
:holidays
|
71
77
|
|
72
78
|
# please note that module_functions of source provided in private files must be published there
|
73
79
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cotcube-bardata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.15.4
|
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-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -175,11 +175,12 @@ files:
|
|
175
175
|
- lib/cotcube-bardata/provide.rb
|
176
176
|
- lib/cotcube-bardata/quarters.rb
|
177
177
|
- lib/cotcube-bardata/range_matrix.rb
|
178
|
+
- lib/cotcube-bardata/suggest.rb
|
178
179
|
- lib/cotcube-bardata/trade_dates.rb
|
179
180
|
- lib/cotcube-bardata/trading_hours.rb
|
180
181
|
homepage: https://github.com/donkeybridge/cotcube-bardata
|
181
182
|
licenses:
|
182
|
-
- BSD-
|
183
|
+
- BSD-3-Clause
|
183
184
|
metadata:
|
184
185
|
homepage_uri: https://github.com/donkeybridge/cotcube-bardata
|
185
186
|
source_code_uri: https://github.com/donkeybridge/cotcube-bardata
|