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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5607a73308b8d9d7aff10caccdd3785189072a7d5b2a0b37ea0b6d2bd1ebf4f9
4
- data.tar.gz: 18bf8b90e2aad0d32a6608037de861d5383df1bd6d8c6b11e903724c91f172df
3
+ metadata.gz: 48c3d1d43865ad1b7bba959025aa38ecf0ea574a8a946740f8226aa06741e600
4
+ data.tar.gz: 4da3026ca588bd848648242c8f21d482e86bb8be72f4a6f0e45b543fca461e05
5
5
  SHA512:
6
- metadata.gz: 3589e6b49ba104aed0177032cd46299c2c0e1db37330e2b869ab6a97f8b81400917bf29ec631cb37b2f35245ece8dcdb147dd56194487d441773d8c79628d38e
7
- data.tar.gz: 3be51d9604d9fb1092939b30fa099801c989babae6ec528479d8af5c8b8750e087dc5965f8b05a3cc5d68281a58de93295cba1b2249030a106220b0eea9594bd
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 [MIT License](https://opensource.org/licenses/MIT).
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.12
1
+ 0.1.15.4
@@ -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-4-Clause'
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 unless marked
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
- row[k] = row[k].to_f if %i[open high low close].include? k
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
- data.pop if data.last[:high].zero?
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 '0,0,0,0' | sort -t, -k2 | cut -d, -f1,2,7,8 > #{c_file}`
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
- volume: row[2].to_i,
86
- oi: row[3].to_i
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
- result << {
161
+ avg_bar = {
95
162
  date: k,
96
- volume: v.map { |x| x[:volume] }.reduce(:+),
97
- oi: v.map { |x| x[:oi] }.reduce(:+)
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 == date.year % 2000 }
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
- ydays = v0.map { |_, v1| Date.parse(v1.last[:date]).yday }
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
- ydays.map! { |x| x > 350 ? x - 366 : x } if ydays.min < 50
333
+ ldays.map! { |x| x > 350 ? x - 366 : x } if ldays.min < 50
240
334
  fday = fdays[fdays.size / 2]
241
- yavg = ydays.reduce(:+) / ydays.size
242
- # a contract is proposed to use after fday - 1, but before ydays.min (green)
243
- # it is warned to user after fday - 1 but before yavg - 1 (red)
244
- # it is warned red >= yavg - 1 and <= yavg + 1
245
- color = if (ytoday >= yavg - 1) && (ytoday <= yavg + 1)
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 > ydays.min) && (ytoday < yavg - 1)
341
+ elsif (ytoday > ldays.min) && (ytoday < lavg - 1)
248
342
  :light_yellow
249
- elsif (ytoday >= (fday > yavg ? 0 : fday - 5)) && (ytoday <= ydays.min)
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
- }\t#{ytoday
259
- }/#{fday
260
- }\t#{format '%5d', ydays.min
261
- }: #{dfm.call(ydays.min)
262
- }\t#{format '%5d', yavg
263
- }: #{dfm.call(yavg)
264
- }\t#{format '%5d', ydays.max
265
- }: #{dfm.call(ydays.max)}".colorize(color)
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 unless (debug and debuglevel >= 2)
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
- }\t#{Date.parse(v1.last[:date]).strftime('%a')
277
- }, #{v1.last[:date]
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
- puts "WARNING: No output was sent for symbol '#{sym[:symbol]}'.".colorize(:light_yellow)
285
- puts " Assuming late-year-processing.".light_yellow
286
- puts early_year.light_green
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 "---- #{output_sent} for #{sym[:symbol]} ---------------"
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
- uri = 'https://www.cmegroup.com/CmeWS/mvc/Volume/TradeDates?exchange=CME'
10
- begin
11
- HTTParty.get(uri)
12
- .parsed_response
13
- .map do |x|
14
- a = x['tradeDate'].chars.each_slice(2).map(&:join)
15
- "#{a[0]}#{a[1]}-#{a[2]}-#{a[3]}"
16
- end
17
- .first
18
- rescue StandardError
19
- nil
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
- CSV.read(f, converters: :numeric)
20
- .map(&:to_a)
21
- .tap { |x| x.shift unless x.first.first.is_a?(Numeric) }
22
- .map { |x| (x.first...x.last) }
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)
@@ -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.12
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-03-13 00:00:00.000000000 Z
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-4-Clause
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