cotcube-bardata 0.1.1 → 0.1.2
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 +9 -0
- data/VERSION +1 -1
- data/cotcube-bardata.gemspec +1 -0
- data/lib/cotcube-bardata.rb +17 -12
- data/lib/cotcube-bardata/daily.rb +94 -0
- data/lib/cotcube-bardata/eods.rb +79 -0
- data/lib/cotcube-bardata/provide.rb +14 -78
- data/lib/cotcube-bardata/quarters.rb +80 -0
- data/lib/cotcube-bardata/trade_dates.rb +15 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cc8f5a19330f55ec26073d90b2c7da30547b61903daa93f8b2977c05ad308f8
|
4
|
+
data.tar.gz: 3dac9e49fb0143d8dd91a86f5a7779dcb25a5c8fca719de5b23a46a5e742380f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aafb4160d474f4f4d0694faee22821f1e8a65a510dcc2c84999acce0e0f3b7511dc52cd2d28cb21856162fbed9c5fa2e9b8e65b8065b7c1603128f8ef1cdea75
|
7
|
+
data.tar.gz: 51ba4678419a6daf9f4d2a18810432f2f3c93c7a6417e8d0e7beae822e76ad9214450bfb1bf4479fc30c4032b51e4504c923e707f6ea8feff295f16071040df3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 0.1.2 (December 22, 2020)
|
2
|
+
- created and added .provide_quarters
|
3
|
+
- added cotcube-helpers as new dependency to gempsec
|
4
|
+
- added timezone to dailies and :datetime to each row
|
5
|
+
- updated master file with new methods and sub-file
|
6
|
+
- added eods with 2 simple mathods .most_liquid_for and .provide_eods
|
7
|
+
- added a simple getter to access CME tradedates (to fetch last_trade_date)
|
8
|
+
- moved daily stuff into new file, prepare .provide to become a major accessor method
|
9
|
+
|
1
10
|
## 0.1.1 (December 16, 2020)
|
2
11
|
|
3
12
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/cotcube-bardata.gemspec
CHANGED
data/lib/cotcube-bardata.rb
CHANGED
@@ -1,34 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_support'
|
4
|
+
require 'active_support/core_ext/time'
|
5
|
+
require 'active_support/core_ext/numeric'
|
4
6
|
require 'colorize'
|
7
|
+
require 'httparty'
|
5
8
|
require 'date' unless defined?(DateTime)
|
6
9
|
require 'csv' unless defined?(CSV)
|
7
10
|
require 'yaml' unless defined?(YAML)
|
8
|
-
require '
|
9
|
-
|
11
|
+
require 'cotcube-helpers'
|
12
|
+
|
10
13
|
|
11
14
|
|
12
15
|
require_relative 'cotcube-bardata/constants'
|
13
16
|
require_relative 'cotcube-bardata/init'
|
17
|
+
require_relative 'cotcube-bardata/trade_dates'
|
18
|
+
require_relative 'cotcube-bardata/daily'
|
19
|
+
require_relative 'cotcube-bardata/quarters'
|
20
|
+
require_relative 'cotcube-bardata/eods'
|
14
21
|
require_relative 'cotcube-bardata/provide'
|
15
22
|
|
16
|
-
private_files = Dir[__dir__ + '/cotcube-bardata/private/*.rb']
|
17
|
-
private_files.each do |file|
|
18
|
-
# puts 'Loading private module extension ' + file.chomp
|
19
|
-
require file.chomp
|
20
|
-
end
|
21
|
-
|
22
23
|
module Cotcube
|
23
24
|
module Bardata
|
24
25
|
|
25
26
|
module_function :config_path, # provides the path of configuration directory
|
26
27
|
:config_prefix, # provides the prefix of the configuration directory according to OS-specific FSH
|
27
28
|
:init, # checks whether environment is prepared and returns the config hash
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
|
29
|
+
:last_trade_date, # Provides the most recent trade date (today or maybe last friday before weekend)
|
30
|
+
:provide, #
|
31
|
+
:most_liquid_for, # the most_liquid contract for a given symbol or id, based on date or last_trade_date
|
32
|
+
:provide_eods, # provides the list of eods, either with data or just the contracts, filtered for liquidity threshold
|
33
|
+
:provide_daily, # provides the list of dailies for a given symbol, which include OI. Note that the close is settlement price.
|
34
|
+
:continuous, # for a given date or range, provide all contracts that exceed a given threshold of volume share
|
35
|
+
:continuous_overview, # based on continuous, create list of when which contract was most liquid
|
36
|
+
:provide_quarters, # provide the list of quarters, possibly as hours or days.
|
32
37
|
:symbols # reads and provides the symbols file
|
33
38
|
|
34
39
|
# please not that module_functions of source provided in private files must be published there
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
module Bardata
|
5
|
+
|
6
|
+
# just reads bardata/daily/<id>/<contract>.csv
|
7
|
+
def provide_daily(symbol: nil, id: nil, contract:, timezone: Time.find_zone('America/Chicago'), config: init)
|
8
|
+
contract = contract.to_s.upcase
|
9
|
+
raise ArgumentError, "Contract '#{contract}' is bogus, should be like 'M21' or 'ESM21'" unless contract.is_a? String and [3,5].include? contract.size
|
10
|
+
if contract.to_s.size == 5
|
11
|
+
symbol = contract[0..1]
|
12
|
+
contract = contract[2..4]
|
13
|
+
end
|
14
|
+
unless symbol.nil?
|
15
|
+
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
16
|
+
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
17
|
+
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
18
|
+
id = symbol_id
|
19
|
+
end
|
20
|
+
raise ArgumentError, "Could not guess :id or :symbol from 'contract: #{contract}', please clarify." if id.nil?
|
21
|
+
id_path = "#{config[:data_path]}/daily/#{id}"
|
22
|
+
data_file = "#{id_path}/#{contract}.csv"
|
23
|
+
raise RuntimeError, "No data found for requested :id (#{id_path} does not exist)" unless Dir.exist?(id_path)
|
24
|
+
raise RuntimeError, "No data found for requested contract #{symbol}:#{contract} in #{id_path}." unless File.exist?(data_file)
|
25
|
+
data = CSV.read(data_file, headers: %i[contract date open high low close volume oi] ).map do |row|
|
26
|
+
row = row.to_h
|
27
|
+
row.each do |k, _|
|
28
|
+
row[k] = row[k].to_f if [:open, :high, :low, :close].include? k
|
29
|
+
row[k] = row[k].to_i if [:volume, :oi].include? k
|
30
|
+
end
|
31
|
+
row[:datetime] = timezone.parse(row[:date])
|
32
|
+
row
|
33
|
+
end
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
# reads all files in bardata/daily/<id> and aggregates by date (what is a pre-stage of a continuous based on daily bars)
|
38
|
+
def continuous(symbol: nil, id: nil, config: init, date: nil)
|
39
|
+
unless symbol.nil?
|
40
|
+
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
41
|
+
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
42
|
+
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
43
|
+
id = symbol_id
|
44
|
+
end
|
45
|
+
raise ArgumentError, "Could not guess :id or :symbol, please clarify." if id.nil?
|
46
|
+
id_path = "#{config[:data_path]}/daily/#{id}"
|
47
|
+
available_contracts = Dir[id_path + '/*.csv'].map{|x| x.split('/').last.split('.').first}.sort_by{|x| x[-7]}.sort_by{|x| x[-6..-5]}
|
48
|
+
data = []
|
49
|
+
available_contracts.each do |c|
|
50
|
+
provide_daily(id: id, config: config, contract: c).each do |x|
|
51
|
+
data << x
|
52
|
+
end
|
53
|
+
end
|
54
|
+
result = []
|
55
|
+
data.sort_by{|x| x[:date]}.group_by{|x| x[:date]}.map{|k,v|
|
56
|
+
v.map{|x| x.delete(:date)}
|
57
|
+
result << {
|
58
|
+
date: k,
|
59
|
+
volume: v.map{|x| x[:volume]}.reduce(:+),
|
60
|
+
oi: v.map{|x| x[:oi ]}.reduce(:+)
|
61
|
+
}
|
62
|
+
result.last[:contracts] = v
|
63
|
+
}
|
64
|
+
date.nil? ? result : result.select{|x| x[:date] == date}.first
|
65
|
+
end
|
66
|
+
|
67
|
+
# based on .continuous, this methods sorts the prepared dailies continuous for each date on either :volume (default) or :oi
|
68
|
+
# with this job done, it can provide the period for which a past contract was the most liquid
|
69
|
+
#
|
70
|
+
def continuous_overview(symbol: nil, id: nil, config: init, selector: :volume, human: false, filter: nil)
|
71
|
+
raise ArgumentError, "Selector must be either :volume or :oi" unless selector.is_a? Symbol and [:volume, :oi].include? selector
|
72
|
+
|
73
|
+
unless symbol.nil?
|
74
|
+
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
75
|
+
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
76
|
+
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
77
|
+
id = symbol_id
|
78
|
+
end
|
79
|
+
raise ArgumentError, "Could not guess :id or :symbol, please clarify." if id.nil?
|
80
|
+
data = continuous(id: id, config: config).map{|x|
|
81
|
+
{
|
82
|
+
date: x[:date],
|
83
|
+
volume: x[:contracts].sort_by{|x| - x[:volume]}[0..4].compact.select{|x| not x[:volume].zero?},
|
84
|
+
oi: x[:contracts].sort_by{|x| - x[:oi]}[0..4].compact.select{|x| not x[:oi].zero?}
|
85
|
+
}
|
86
|
+
}.select{|x| not x[selector].empty? }
|
87
|
+
result = data.group_by{|x| x[selector].first[:contract]}
|
88
|
+
if human
|
89
|
+
result.each {|k,v| puts "#{k}\t#{v.first[:date]}\t#{v.last[:date]}" if filter.nil? or v.first[selector].first[:contract][2..4]=~ /#{filter}/ }
|
90
|
+
end
|
91
|
+
result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
module Bardata
|
5
|
+
|
6
|
+
def most_liquid_for(symbol: nil, id: nil, date: last_trade_date, config: init)
|
7
|
+
unless symbol.nil?
|
8
|
+
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
9
|
+
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
10
|
+
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
11
|
+
id = symbol_id
|
12
|
+
end
|
13
|
+
raise ArgumentError, "Need :id or :symbol." if id.nil?
|
14
|
+
provide_eods(id: id, dates: date, contracts_only: true).first
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
def provide_eods(symbol: nil,
|
20
|
+
id: nil,
|
21
|
+
contract: nil,
|
22
|
+
config: init,
|
23
|
+
dates: last_trade_date, # should accept either a date or datelike or date string OR a range of 2 datelike
|
24
|
+
# if omitted returns the eods of last trading date
|
25
|
+
threshold: 0.1, # set threshold to 0 to disable filtering at all. otherwise only contracts with partial of >= threshold are returned
|
26
|
+
filter: :volume_part, # filter can be set to volume_part and oi_part. determines, which property is used for filtering.
|
27
|
+
contracts_only: true # set to false to return the complete row instead of just the contracts matching filter and threshold
|
28
|
+
)
|
29
|
+
raise ArgumentError, "Contract '#{contract}' is bogus, should be like 'M21' or 'ESM21'" unless contract.nil? or (contract.is_a? String and [3,5].include? contract.size)
|
30
|
+
if contract.to_s.size == 5
|
31
|
+
symbol = contract[0..1]
|
32
|
+
contract = contract[2..4]
|
33
|
+
end
|
34
|
+
unless symbol.nil?
|
35
|
+
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
36
|
+
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
37
|
+
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
38
|
+
id = symbol_id
|
39
|
+
end
|
40
|
+
# if no id can be clarified from given arguments, return all matching contracts from all available symbols
|
41
|
+
# raise ArgumentError, "Could not guess :id or :symbol from 'contract: #{contract}', please clarify." if id.nil?
|
42
|
+
raise ArgumentError, ":filter must be in [:volume_part, :oi_part]" unless [:volume_part, :oi_part].include? filter
|
43
|
+
|
44
|
+
ids = id.nil? ? symbols.map{|x| x[:id]} : [ id ]
|
45
|
+
dates = [ dates ] unless dates.is_a? Array or dates.nil?
|
46
|
+
|
47
|
+
id_path_get = lambda {|_id| "#{config[:data_path]}/eods/#{_id}" }
|
48
|
+
|
49
|
+
process_date_for_id = lambda do |d,i|
|
50
|
+
sym = symbols.select{|s| s[:id] == i}.first
|
51
|
+
symbol = sym[:symbol]
|
52
|
+
id_path = id_path_get.call(i)
|
53
|
+
data_file = "#{id_path}/#{d}.csv"
|
54
|
+
raise RuntimeError, "No data found for requested :id (#{id_path} does not exist)" unless Dir.exist?(id_path)
|
55
|
+
raise RuntimeError, "No data found for requested contract #{symbol}:#{contract} in #{id_path}." unless File.exist?(data_file)
|
56
|
+
data = CSV.read(data_file, headers: %i[contract date open high low close volume oi] ).map do |row|
|
57
|
+
row = row.to_h
|
58
|
+
row.each do |k, _|
|
59
|
+
row[k] = row[k].to_f if [:open, :high, :low, :close].include? k
|
60
|
+
row[k] = row[k].to_i if [:volume, :oi].include? k
|
61
|
+
end
|
62
|
+
row
|
63
|
+
end
|
64
|
+
all_volume = data.map{|x| x[:volume] }.reduce(:+)
|
65
|
+
all_oi = data.map{|x| x[:oi] }.reduce(:+)
|
66
|
+
data.map{|x| x[:volume_part] = (x[:volume] / all_volume.to_f).round(4); x[:oi_part] = (x[:oi] / all_oi.to_f).round(4) }
|
67
|
+
data.select{|x| x[filter] >= threshold}.sort_by{|x| -x[filter]}.tap{|x| x.map!{|y| y[:contract]} if contracts_only}
|
68
|
+
end
|
69
|
+
if dates
|
70
|
+
dates.map do |date|
|
71
|
+
ids.map{|id| process_date_for_id.call(date, id) }
|
72
|
+
end.flatten
|
73
|
+
else
|
74
|
+
raise ArgumentError, "Sorry, support for unlimited dates is not implemented yet. Please send array of dates or single date"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -3,85 +3,21 @@
|
|
3
3
|
module Cotcube
|
4
4
|
module Bardata
|
5
5
|
|
6
|
-
def provide(symbol: nil, id: nil, contract:, config: init)
|
7
|
-
|
8
|
-
|
9
|
-
symbol
|
10
|
-
|
6
|
+
def provide(symbol: nil, id: nil, contract:, config: init, date: Date.today - 1, type: nil, fill: :none)
|
7
|
+
case type
|
8
|
+
when :eod, :eods
|
9
|
+
provide_eods(symbol: symbol, id: id, contract: contract, date: date)
|
10
|
+
when :quarters
|
11
|
+
print :quarters
|
12
|
+
when :hours
|
13
|
+
print :hours
|
14
|
+
when :daily, :dailies
|
15
|
+
print :dailies
|
16
|
+
else
|
17
|
+
puts "WARNING: Using provide without :type is for legacy support pointing to .provide_daily".light_yellow
|
18
|
+
provide_daily(symbol: symbol, id: id, contract: contract, config: config)
|
11
19
|
end
|
12
|
-
unless symbol.nil?
|
13
|
-
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
14
|
-
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
15
|
-
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
16
|
-
id = symbol_id
|
17
|
-
end
|
18
|
-
raise ArgumentError, "Could not guess :id or :symbol from 'contract: #{contract}', please clarify." if id.nil?
|
19
|
-
id_path = "#{config[:data_path]}/daily/#{id}"
|
20
|
-
data_file = "#{id_path}/#{contract}.csv"
|
21
|
-
raise RuntimeError, "No data found for requested :id (#{id_path} does not exist)" unless Dir.exist?(id_path)
|
22
|
-
raise RuntimeError, "No data found for requested contract #{symbol}:#{contract} in #{id_path}." unless File.exist?(data_file)
|
23
|
-
data = CSV.read(data_file, headers: %i[contract date open high low close volume oi] ).map do |row|
|
24
|
-
row = row.to_h
|
25
|
-
row.each do |k, _|
|
26
|
-
row[k] = row[k].to_f if [:open, :high, :low, :close].include? k
|
27
|
-
row[k] = row[k].to_i if [:volume, :oi].include? k
|
28
|
-
end
|
29
|
-
row
|
30
|
-
end
|
31
|
-
data
|
32
|
-
end
|
33
|
-
|
34
|
-
def continuous(symbol: nil, id: nil, config: init)
|
35
|
-
unless symbol.nil?
|
36
|
-
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
37
|
-
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
38
|
-
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
39
|
-
id = symbol_id
|
40
|
-
end
|
41
|
-
raise ArgumentError, "Could not guess :id or :symbol, please clarify." if id.nil?
|
42
|
-
id_path = "#{config[:data_path]}/daily/#{id}"
|
43
|
-
available_contracts = Dir[id_path + '/*.csv'].map{|x| x.split('/').last.split('.').first}.sort_by{|x| x[-7]}.sort_by{|x| x[-6..-5]}
|
44
|
-
data = []
|
45
|
-
available_contracts.each do |c|
|
46
|
-
provide(id: id, config: config, contract: c).each do |x|
|
47
|
-
data << x
|
48
|
-
end
|
49
|
-
end
|
50
|
-
result = []
|
51
|
-
data.sort_by{|x| x[:date]}.group_by{|x| x[:date]}.map{|k,v|
|
52
|
-
v.map{|x| x.delete(:date)}
|
53
|
-
result << {
|
54
|
-
date: k,
|
55
|
-
volume: v.map{|x| x[:volume]}.reduce(:+),
|
56
|
-
oi: v.map{|x| x[:oi ]}.reduce(:+)
|
57
|
-
}
|
58
|
-
result.last[:contracts] = v
|
59
|
-
}
|
60
|
-
result
|
61
|
-
end
|
62
|
-
|
63
|
-
def continuous_overview(symbol: nil, id: nil, config: init, selector: :volume, human: false, filter: nil)
|
64
|
-
raise ArgumentError, "Selector must be either :volume or :oi" unless selector.is_a? Symbol and [:volume, :oi].include? selector
|
65
|
-
|
66
|
-
unless symbol.nil?
|
67
|
-
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
68
|
-
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
69
|
-
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
70
|
-
id = symbol_id
|
71
|
-
end
|
72
|
-
raise ArgumentError, "Could not guess :id or :symbol, please clarify." if id.nil?
|
73
|
-
data = continuous(id: id, config: config).map{|x|
|
74
|
-
{
|
75
|
-
date: x[:date],
|
76
|
-
volume: x[:contracts].sort_by{|x| - x[:volume]}[0..4].compact.select{|x| not x[:volume].zero?},
|
77
|
-
oi: x[:contracts].sort_by{|x| - x[:oi]}[0..4].compact.select{|x| not x[:oi].zero?}
|
78
|
-
}
|
79
|
-
}.select{|x| not x[selector].empty? }
|
80
|
-
result = data.group_by{|x| x[selector].first[:contract]}
|
81
|
-
if human
|
82
|
-
result.each {|k,v| puts "#{k}\t#{v.first[:date]}\t#{v.last[:date]}" if filter.nil? or v.first[selector].first[:contract][2..4]=~ /#{filter}/ }
|
83
|
-
end
|
84
|
-
result
|
85
20
|
end
|
86
21
|
end
|
22
|
+
|
87
23
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
module Bardata
|
5
|
+
|
6
|
+
def provide_quarters(
|
7
|
+
symbol: nil, id: nil,
|
8
|
+
contract:,
|
9
|
+
as: :quarters,
|
10
|
+
range: nil, date: nil,
|
11
|
+
timezone: Time.find_zone('America/Chicago'),
|
12
|
+
config: init,
|
13
|
+
quiet: false
|
14
|
+
)
|
15
|
+
date = timezone.parse(date) if date.is_a? String
|
16
|
+
raise ArgumentError, ":range and :date are mutually exclusive" if range and date
|
17
|
+
raise ArgumentError, "Contract '#{contract}' is bogus, should be like 'M21' or 'ESM21'" unless contract.is_a? String and [3,5].include? contract.size
|
18
|
+
if contract.to_s.size == 5
|
19
|
+
symbol = contract[0..1]
|
20
|
+
contract = contract[2..4]
|
21
|
+
end
|
22
|
+
unless symbol.nil?
|
23
|
+
symbol_id = symbols.select{|s| s[:symbol] == symbol.to_s.upcase}.first[:id]
|
24
|
+
raise ArgumentError, "Could not find match in #{config[:symbols_file]} for given symbol #{symbol}" if symbol_id.nil?
|
25
|
+
raise ArgumentError, "Mismatching symbol #{symbol} and given id #{id}" if not id.nil? and symbol_id != id
|
26
|
+
id = symbol_id
|
27
|
+
end
|
28
|
+
raise ArgumentError, ":as can only be in [:quarters, :hours, :days]" unless %i[quarters hours days].include?(as)
|
29
|
+
raise ArgumentError, "Could not guess :id or :symbol from 'contract: #{contract}', please clarify." if id.nil?
|
30
|
+
id_path = "#{config[:data_path]}/quarters/#{id}"
|
31
|
+
data_file = "#{id_path}/#{contract}.csv"
|
32
|
+
raise RuntimeError, "No data found for requested :id (#{id_path} does not exist)" unless Dir.exist?(id_path)
|
33
|
+
raise RuntimeError, "No data found for requested contract #{symbol}:#{contract} in #{id_path}." unless File.exist?(data_file)
|
34
|
+
data = CSV.read(data_file, headers: %i[contract datetime day open high low close volume]).map do |row|
|
35
|
+
row = row.to_h
|
36
|
+
%i[open high low close].map{|x| row[x] = row[x].to_f}
|
37
|
+
%i[volume day].map{|x| row[x] = row[x].to_i}
|
38
|
+
row[:datetime] = timezone.parse(row[:datetime])
|
39
|
+
row
|
40
|
+
end
|
41
|
+
select_specific_date = lambda do |specific_date|
|
42
|
+
data.select{|d| d[:day] == specific_date.day and specific_date.year == d[:datetime].year and (
|
43
|
+
if specific_date.day > 1
|
44
|
+
specific_date.month == d[:datetime].month
|
45
|
+
else
|
46
|
+
((specific_date.month == d[:datetime].month and d[:datetime].day == 1) or
|
47
|
+
(specific_date.month == d[:datetime].month + 1 and d[:datetime].day > 25) )
|
48
|
+
end
|
49
|
+
)}
|
50
|
+
end
|
51
|
+
if range
|
52
|
+
starting = range.begin
|
53
|
+
starting = timezone.parse(starting) if starting.is_a? String
|
54
|
+
ending = range.end
|
55
|
+
ending = timezone.parse( ending) if ending.is_a? String
|
56
|
+
if starting.hour == 0 and starting.min == 0 and ending.hour == 0 and ending.min == 0
|
57
|
+
puts "WARNING: When sending midnight, full trading day is assumed (starting 5 pm yesterday, ending 4 pm today)".light_yellow unless quiet
|
58
|
+
result = select_specific_date.call(starting)
|
59
|
+
result += data.select{|d| d[:datetime] > starting and d[:datetime] < ending.to_date }
|
60
|
+
result += select_specific_date.call(ending)
|
61
|
+
result.uniq!
|
62
|
+
else
|
63
|
+
result = data.select{|x| x[:datetime] >= starting and x[:datetime] < ending }
|
64
|
+
end
|
65
|
+
elsif date
|
66
|
+
result = select_specific_date.call(date)
|
67
|
+
else
|
68
|
+
result = data
|
69
|
+
end
|
70
|
+
return case as
|
71
|
+
when :hours
|
72
|
+
Cotcube::Helpers.reduce(bars: result, to: 1.hour){|c,b| c[:day] == b[:day] and c[:datetime].hour == b[:datetime].hour }
|
73
|
+
when :days
|
74
|
+
Cotcube::Helpers.reduce(bars: result, to: 1.day ){|c,b| c[:day] == b[:day] }
|
75
|
+
else
|
76
|
+
result
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
module Bardata
|
5
|
+
|
6
|
+
# fetching official tradedates from CME
|
7
|
+
def last_trade_date
|
8
|
+
uri = "https://www.cmegroup.com/CmeWS/mvc/Volume/TradeDates?exchange=CME"
|
9
|
+
res = nil
|
10
|
+
res = HTTParty.get(uri).parsed_response
|
11
|
+
res.map{|x| a = x["tradeDate"].chars.each_slice(2).map(&:join); "#{a[0]}#{a[1]}-#{a[2]}-#{a[3]}"}.first
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
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.2
|
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: 2020-12-
|
11
|
+
date: 2020-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cotcube-indicators
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cotcube-helpers
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: yaml
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -153,8 +167,12 @@ files:
|
|
153
167
|
- cotcube-bardata.gemspec
|
154
168
|
- lib/cotcube-bardata.rb
|
155
169
|
- lib/cotcube-bardata/constants.rb
|
170
|
+
- lib/cotcube-bardata/daily.rb
|
171
|
+
- lib/cotcube-bardata/eods.rb
|
156
172
|
- lib/cotcube-bardata/init.rb
|
157
173
|
- lib/cotcube-bardata/provide.rb
|
174
|
+
- lib/cotcube-bardata/quarters.rb
|
175
|
+
- lib/cotcube-bardata/trade_dates.rb
|
158
176
|
homepage: https://github.com/donkeybridge/cotcube-bardata
|
159
177
|
licenses:
|
160
178
|
- BSD-4-Clause
|