cotcube-bardata 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.irbrc.rb +3 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +3 -2
- data/README.md +61 -28
- data/Rakefile +5 -3
- data/VERSION +1 -1
- data/cotcube-bardata.gemspec +10 -11
- data/lib/cotcube-bardata.rb +44 -18
- data/lib/cotcube-bardata/cached.rb +80 -0
- data/lib/cotcube-bardata/constants.rb +8 -8
- data/lib/cotcube-bardata/daily.rb +94 -59
- data/lib/cotcube-bardata/eods.rb +78 -55
- data/lib/cotcube-bardata/helpers.rb +104 -0
- data/lib/cotcube-bardata/init.rb +23 -27
- data/lib/cotcube-bardata/provide.rb +38 -12
- data/lib/cotcube-bardata/quarters.rb +29 -65
- data/lib/cotcube-bardata/range_matrix.rb +117 -0
- data/lib/cotcube-bardata/trade_dates.rb +19 -6
- data/lib/cotcube-bardata/trading_hours.rb +45 -0
- metadata +43 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad166746ea75e103e71ce235710267f98fc6a1b13705b73dc978bd0001b282ed
|
4
|
+
data.tar.gz: 94b63c1eab7181655f99484ffcb27a29eadef2e2befa03e0301e1f82dc5e2b42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07a2d4ffc5291c1474f14c49ce1d9d42804b2a5bb5ce9fabfef048904a7d8455a106381e76ec6c8bbe05feeda50afb9db9b6f56087c572b704bbce2e7ab313c9
|
7
|
+
data.tar.gz: d3e83e577ad407550ab590dcd8b5c2880c05939197b80d67a07de117d2ffba918adc852f1de87e16b7262e22d71c53a1bd789d700b6ae3ef82be645adc532b52
|
data/.irbrc.rb
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## 0.1.4 (January 02, 2021)
|
2
|
+
- two minor fixes (cached.rb, daily.rb)
|
3
|
+
- adding first (shy) specs ... to be continued
|
4
|
+
- cotcube-bardata.rb: added dependency parallel, added new module files and functions
|
5
|
+
- provide.rb: writing provide, the central accessor to actual bardata
|
6
|
+
- cached.rb: implementing 'provide_cached', which manages reduced and dimished subsets of 'quarters'
|
7
|
+
- helpers.rb: added get_id_set
|
8
|
+
- daily.rb: applied cops
|
9
|
+
- quarters.rb: applied cops, used new get_id_set, slimmed down content in favor of 'provide'
|
10
|
+
- eods.rb: renamed get_id_from to get_id_set
|
11
|
+
- added explanation to range_matrix.rb
|
12
|
+
- added 'holidays' to trade_dates.csv, depending on according CSV
|
13
|
+
- applied cops to init.rb
|
14
|
+
- changed name from get_range to trading_hours
|
15
|
+
- minor change in gemspec
|
16
|
+
- fixed typos in README
|
17
|
+
- added bounded versions to gemspec
|
18
|
+
- applied cops
|
19
|
+
- new file trading_hours.rb providing get_range(). Based on CSV data it provides a list of ranges depicting seconds since Sunday 0:00am, which in turn can be used with the helper Array.new.to_time_interval.
|
20
|
+
- new file and method 'range_matrix', investigating high-low ranges of entire daily
|
21
|
+
- Too bad, found copied README...fixing something quite embarassing.
|
22
|
+
|
1
23
|
## 0.1.3 (December 23, 2020)
|
2
24
|
- added .provide_most_liquids_by_eod which supports :age, filtering out files that have been updated more recently
|
3
25
|
- added 'type' to symbols to filter for e.g. currencies
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,36 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# cotcube-bardata
|
2
2
|
|
3
|
-
This gem
|
4
|
-
time series data into bitangents.
|
3
|
+
This gem is a versatile provider of bardata. It relies on a directory structure most probably saved to `/var/cotcube/bardata/`. The following directories and files contain data others might expect to be delivered by a database:
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
the bitangent in regard to the :fuzziness, which is at least 1 'tick'.
|
12
|
-
- Ocassionally a bitangent becomes an N-tangent, when multiple findings or clusters are
|
13
|
-
in one line resp. the fuzzied ranged on the sheared graph.
|
14
|
-
- Shearing is limited by reaching 0 degrees, so everything below the horizont (or above resp.)
|
15
|
-
is discarded.
|
16
|
-
|
17
|
-
After identifiying the angle of Z degress delivering N findings( actually N - 1, as the
|
18
|
-
last finding always is the last member of the series), the entire time series then is split
|
19
|
-
into N subranges, where each subrange is processed again until it reaches minimum size
|
20
|
-
(which defaults to 3 items).
|
21
|
-
|
22
|
-
Except for the very first range the challenge is to trim away the beginning of each sub
|
23
|
-
range.
|
24
|
-
|
25
|
-
The result of such a search is a tree, where it might be considerable to walk and change
|
26
|
-
this tree by adding elements to the time series instead of recalculating it completely.
|
5
|
+
1. `eods`: within *eods/<id or symbol>/<date>.csv*, for each date a list of contracts is located, to be applied with the list of headers `%i[ contract date open high low close volume oi ]`.
|
6
|
+
2. `daily`: within *daily/<id or symbol>/<contract>.csv*, for each contract all eods are provided. The list of headers is expected as `%i[ contract date open high low close volume oi ]`. Please note that it is not obvious whether `close` contains settlement or actual closing price, depending on the exchange and the broker providing the source data.
|
7
|
+
3. `quarters`: within *quarters/<id or symbol>/<contract>.csv*, for each contract a list of quarters (15 minute intervals) is provided, depending on the first occurrence of the contract within the topN volume segment. Note the different headers here: `%i[ contract date_alike day open high low close volume ]`.
|
8
|
+
4. `trading_hours`: within *trading_hours/<symbol or type>_<set>.csv* a list of intervals is provided, with the headers `%i[ interval_start interval_end ]` for each interval described by seconds since Sunday 0:00p.m. (as defaulted by Ruby's *DateTime.new.wday)*.
|
9
|
+
5. `trade_dates.csv`: A growing list of trade\_dates as provided by the CME.
|
27
10
|
|
28
11
|
## Installation
|
29
12
|
|
30
13
|
Add this line to your application's Gemfile:
|
31
14
|
|
32
15
|
```ruby
|
33
|
-
gem '
|
16
|
+
gem 'cotcube-bardata'
|
34
17
|
```
|
35
18
|
|
36
19
|
And then execute:
|
@@ -39,11 +22,60 @@ And then execute:
|
|
39
22
|
|
40
23
|
Or install it yourself as:
|
41
24
|
|
42
|
-
$ gem install
|
25
|
+
$ gem install cotcube-bardata
|
43
26
|
|
44
27
|
## Usage
|
45
28
|
|
46
|
-
|
29
|
+
### Configuration
|
30
|
+
|
31
|
+
The gem expects a configfile 'bardata.yml', located in '/etc/cotcube/' on linux platform and '/usr/local/etc/cotcube/' on FreeBSD. The location of the configfile can be overwritten by passing the according parameter to `init`.
|
32
|
+
|
33
|
+
### daily.rb
|
34
|
+
|
35
|
+
Provides
|
36
|
+
|
37
|
+
* `provide_daily(symbol: nil, id: nil, contract:, timezone: Time.find_zone('America/Chicago'), config: init)`
|
38
|
+
* `continuous(symbol: nil, id: nil, config: init, date: nil)`: Loads all dailies for given *id* and groups by date, hence providing a list of eods.
|
39
|
+
* `continuous_ml(symbol: nil, id: nil, base: nil)`: Provides a list of contracts, containing a list of most liquid by volume contracts as `{ date: , ml: }`
|
40
|
+
* `continuous_actual_ml(symbol: nil, id: nil)`: Same as above, but providing the succeeding trading day (as the signaled 'ML' is yet one day old before it can be used).
|
41
|
+
* `continuous_overview(symbol: nil, id: nil, config: init, selector: :volume, human: false, filter: nil)`: Several purposes, but most noticeable providing the range of first and last occurrence within the top N% by volume within eods.
|
42
|
+
|
43
|
+
### eods.rb
|
44
|
+
|
45
|
+
Provides
|
46
|
+
|
47
|
+
* `most_liquid_for(symbol: nil, id: nil, date: last_trade_date, config: init, quiet: false)`
|
48
|
+
* `provide_most_liquids_by_eod(config: init, date: last_trade_date, filter: :volume_part, age: 1.hour)`
|
49
|
+
* `provide_eods(**args)`
|
50
|
+
* `provide_quarters(**args)`
|
51
|
+
|
52
|
+
### quarters.rb
|
53
|
+
|
54
|
+
Provides `provide_quarters(**args)`.
|
55
|
+
|
56
|
+
### provide.rb
|
57
|
+
|
58
|
+
Provides `provide(**args)`.
|
59
|
+
|
60
|
+
### range\_matrix.rb
|
61
|
+
|
62
|
+
Provides `range_matrix`, a simple method processing data based on `Bardata.continuous_actual_ml` to return a statistical overview of daily high-low ranges (not True Ranges). It contains sets for
|
63
|
+
|
64
|
+
* all available data,
|
65
|
+
* a data subset containing the recent 12 months
|
66
|
+
* data diminished by `:dim` (top and bottom) based on all available data
|
67
|
+
|
68
|
+
and contains *max*, *avg*, *lower*, *median*, *upper* and *max* (where 'upper' and 'lower' are like the median but at the 25percentile and 75percentile resp.).
|
69
|
+
|
70
|
+
As a third dimension (sorry!) all of the above is applied to days, weeks as well as months.
|
71
|
+
|
72
|
+
### trade\_dates.rb
|
73
|
+
|
74
|
+
Provides `last_trade_date`, simple fetches the current 5 trade dates from CME and returns the very last.
|
75
|
+
|
76
|
+
### trading\_hours.rb
|
77
|
+
|
78
|
+
Provides `get_range(symbol: nil, set: :full, force_set: false, config: init, debug: false)`, loading a set of intervals. The sets are defaulting to :full when the requested set is not found--unless :force\_set is enabled. Furthermore, if symbol is not found, the type-based version is returned. Eventually, if neither could be returned, the 24x7 interval is returned.
|
47
79
|
|
48
80
|
## Development
|
49
81
|
|
@@ -53,9 +85,10 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
53
85
|
|
54
86
|
## Contributing
|
55
87
|
|
56
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
88
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/donkeybridge/bitangent.
|
57
89
|
|
58
90
|
|
59
91
|
## License
|
60
92
|
|
61
93
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
94
|
+
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/cotcube-bardata.gemspec
CHANGED
@@ -9,13 +9,13 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.summary = 'Functions to provide bardata; and some simple time series aggregations'
|
10
10
|
spec.description = 'Functions to provide bardata; and some simple time series aggregations '
|
11
11
|
|
12
|
-
spec.homepage =
|
12
|
+
spec.homepage = "https://github.com/donkeybridge/#{spec.name}"
|
13
13
|
spec.license = 'BSD-4-Clause'
|
14
14
|
spec.required_ruby_version = Gem::Requirement.new('~> 2.7')
|
15
15
|
|
16
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
17
17
|
spec.metadata['source_code_uri'] = spec.homepage
|
18
|
-
spec.metadata['changelog_uri'] = spec.homepage
|
18
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/CHANGELOG.md"
|
19
19
|
|
20
20
|
# Specify which files should be added to the gem when it is released.
|
21
21
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -26,16 +26,15 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
27
|
spec.require_paths = ['lib']
|
28
28
|
|
29
|
-
spec.add_dependency '
|
30
|
-
spec.add_dependency '
|
31
|
-
spec.add_dependency '
|
32
|
-
spec.add_dependency '
|
33
|
-
spec.add_dependency '
|
34
|
-
spec.add_dependency '
|
35
|
-
spec.add_dependency '
|
29
|
+
spec.add_dependency 'activesupport', '~> 6'
|
30
|
+
spec.add_dependency 'colorize', '~> 0.8'
|
31
|
+
spec.add_dependency 'cotcube-helpers', '~> 0.1'
|
32
|
+
spec.add_dependency 'cotcube-indicators', '~> 0.1'
|
33
|
+
spec.add_dependency 'httparty', '~> 0.18'
|
34
|
+
spec.add_dependency 'parallel', '~> 1'
|
35
|
+
spec.add_dependency 'yaml', '~> 0.1'
|
36
36
|
|
37
|
-
|
38
|
-
spec.add_development_dependency 'rake'
|
37
|
+
spec.add_development_dependency 'rake', '>= 12'
|
39
38
|
spec.add_development_dependency 'rspec', '~>3.6'
|
40
39
|
spec.add_development_dependency 'yard', '~>0.9'
|
41
40
|
end
|
data/lib/cotcube-bardata.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# rubocop:disable Naming/FileName
|
2
|
+
# rubocop:enable Naming/FileName
|
1
3
|
# frozen_string_literal: true
|
2
4
|
|
3
5
|
require 'active_support'
|
@@ -9,35 +11,59 @@ require 'date' unless defined?(DateTime)
|
|
9
11
|
require 'csv' unless defined?(CSV)
|
10
12
|
require 'yaml' unless defined?(YAML)
|
11
13
|
require 'cotcube-helpers'
|
12
|
-
|
13
|
-
|
14
|
+
require 'parallel'
|
14
15
|
|
15
16
|
require_relative 'cotcube-bardata/constants'
|
17
|
+
require_relative 'cotcube-bardata/helpers'
|
16
18
|
require_relative 'cotcube-bardata/init'
|
17
19
|
require_relative 'cotcube-bardata/trade_dates'
|
18
20
|
require_relative 'cotcube-bardata/daily'
|
19
21
|
require_relative 'cotcube-bardata/quarters'
|
20
22
|
require_relative 'cotcube-bardata/eods'
|
23
|
+
require_relative 'cotcube-bardata/cached'
|
21
24
|
require_relative 'cotcube-bardata/provide'
|
25
|
+
require_relative 'cotcube-bardata/range_matrix'
|
26
|
+
require_relative 'cotcube-bardata/trading_hours'
|
22
27
|
|
23
28
|
module Cotcube
|
24
29
|
module Bardata
|
25
|
-
|
26
30
|
module_function :config_path, # provides the path of configuration directory
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
# provides the prefix of the configuration directory according to OS-specific FSH
|
32
|
+
:config_prefix,
|
33
|
+
# checks whether environment is prepared and returns the config hash
|
34
|
+
:init,
|
35
|
+
# Provides the most recent trade date (today or maybe last friday before weekend)
|
36
|
+
:last_trade_date,
|
37
|
+
:provide,
|
38
|
+
# the most_liquid contract for a given symbol or id, based on date or last_trade_date
|
39
|
+
:most_liquid_for,
|
40
|
+
# provides the list of eods, either with data or just the contracts,
|
41
|
+
# filtered for liquidity threshold
|
42
|
+
:provide_eods,
|
43
|
+
:provide_most_liquids_by_eod,
|
44
|
+
# provides the list of dailies for a given symbol, which include OI.
|
45
|
+
# Note that the close is most probably settlement price.
|
46
|
+
:provide_daily,
|
47
|
+
# for a given date or range, provide all contracts that exceed a given threshold of volume share
|
48
|
+
:continuous,
|
49
|
+
# the list of most liquid contracts (by each days volume share)
|
50
|
+
:continuous_ml,
|
51
|
+
# same list but riped one day each
|
52
|
+
:continuous_actual_ml,
|
53
|
+
# based on continuous, create list of when which contract was most liquid
|
54
|
+
:continuous_overview,
|
55
|
+
# provide the list of quarters, possibly as hours or days.
|
56
|
+
:provide_quarters,
|
57
|
+
# some statistics to estimate daily volatility of specific contract
|
58
|
+
:range_matrix,
|
59
|
+
# create an array of ranges based on specified source data
|
60
|
+
:trading_hours,
|
61
|
+
# receive id / symbol information on an uncertain set of parameters
|
62
|
+
:get_id_set,
|
63
|
+
:compare,
|
64
|
+
:holidays,
|
65
|
+
:symbols # reads and provides the symbols file
|
66
|
+
|
67
|
+
# please note that module_functions of source provided in private files must be published there
|
41
68
|
end
|
42
69
|
end
|
43
|
-
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
# missing top level documentation
|
5
|
+
module Bardata
|
6
|
+
|
7
|
+
# send pre-created days based on quarters
|
8
|
+
def provide_cached(contract:,
|
9
|
+
symbol: nil, id: nil,
|
10
|
+
config: init,
|
11
|
+
timezone: Time.find_zone('America/Chicago'),
|
12
|
+
set: :full, # most probably either :full or :rth
|
13
|
+
force_update: false, # force reloading via provide_quarters
|
14
|
+
force_recent: false) #
|
15
|
+
|
16
|
+
headers = %i[contract datetime open high low close volume]
|
17
|
+
sym = get_id_set(symbol: symbol, id: id, contract: contract)
|
18
|
+
contract = contract[-3..-1]
|
19
|
+
dir = "#{config[:data_path]}/cached/#{sym[:id]}_#{set.to_s.downcase}"
|
20
|
+
symlink = "#{config[:data_path]}/cached/#{sym[:symbol]}_#{set.to_s.downcase}"
|
21
|
+
`mkdir -p #{dir}` unless Dir.exist? dir
|
22
|
+
`ln -s #{dir} #{symlink}` unless File.exist? symlink
|
23
|
+
file = "#{dir}/#{contract}.csv"
|
24
|
+
quarters_file = "#{config[:data_path]}/quarters/#{sym[:id]}/#{contract[-3..-1]}.csv"
|
25
|
+
if File.exist?(file) and not force_update
|
26
|
+
base = CSV.read(file, headers: headers).map{|x|
|
27
|
+
x = x.to_h
|
28
|
+
x[:datetime] = timezone.parse(x[:datetime])
|
29
|
+
%i[open high low close].each{|z| x[z] = x[z].to_f.round(9)}
|
30
|
+
x[:volume] = x[:volume].to_i
|
31
|
+
x[:type] = "#{set.to_s.downcase}_day".to_sym
|
32
|
+
x
|
33
|
+
}
|
34
|
+
if base.last[:high].zero?
|
35
|
+
# contract exists but is closed (has the CLOSED marker)
|
36
|
+
base.pop
|
37
|
+
return base
|
38
|
+
elsif File.mtime(file) < File.mtime(quarters_file)
|
39
|
+
return base
|
40
|
+
else
|
41
|
+
puts "File #{file} exists, but is neither closed nor current. Running update.".colorize(:light_green)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
begin
|
45
|
+
data = provide_quarters(contract: contract, id: sym[:id], keep_marker: true)
|
46
|
+
rescue
|
47
|
+
puts "Cannot provide quarters for requested contract #{sym[:symbol]}:#{contract}, returning '[ ]'".colorize(:light_red)
|
48
|
+
return []
|
49
|
+
end
|
50
|
+
|
51
|
+
# removing marker if existing
|
52
|
+
contract_is_marked = data.last[:high].zero?
|
53
|
+
data.pop if contract_is_marked
|
54
|
+
unless set == :full or data.size < 3
|
55
|
+
requested_set = trading_hours(symbol: sym[:symbol], set: set)
|
56
|
+
data = data.select_within(ranges: requested_set, attr: :datetime) {|x| x.to_datetime.to_sssm }
|
57
|
+
end
|
58
|
+
|
59
|
+
base = Cotcube::Helpers.reduce(bars: data, to: :days)
|
60
|
+
|
61
|
+
# remove last day of result if not marked
|
62
|
+
base.pop unless contract_is_marked or force_recent
|
63
|
+
|
64
|
+
base.map do |x|
|
65
|
+
x[:datetime] = x[:datetime].to_date
|
66
|
+
x[:type]= "#{set}_day".to_sym
|
67
|
+
x.delete(:day)
|
68
|
+
end
|
69
|
+
CSV.open(file, 'w') do |csv|
|
70
|
+
base.each{|b| csv << b.values_at(*headers) }
|
71
|
+
if contract_is_marked
|
72
|
+
marker = [ "#{sym[:symbol]}#{contract}",base.last[:datetime]+1.day,0,0,0,0,0 ]
|
73
|
+
csv << marker
|
74
|
+
end
|
75
|
+
end
|
76
|
+
base
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -2,16 +2,16 @@
|
|
2
2
|
|
3
3
|
module Cotcube
|
4
4
|
module Bardata
|
5
|
-
|
6
5
|
SYMBOL_EXAMPLES = [
|
7
|
-
{ id:
|
8
|
-
|
6
|
+
{ id: '13874U', symbol: 'ET', ticksize: 0.25, power: 1.25, months: 'HMUZ', bcf: 1.0, reports: 'LF',
|
7
|
+
name: 'S&P 500 MICRO' },
|
8
|
+
{ id: '209747', symbol: 'NM', ticksize: 0.25, power: 0.5, months: 'HMUZ', bcf: 1.0, reports: 'LF',
|
9
|
+
name: 'NASDAQ 100 MICRO' }
|
9
10
|
].freeze
|
10
11
|
|
11
|
-
MONTH_COLOURS
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
MONTH_COLOURS = { 'F' => :cyan, 'G' => :green, 'H' => :light_green,
|
13
|
+
'J' => :blue, 'K' => :yellow, 'M' => :light_yellow,
|
14
|
+
'N' => :cyan, 'Q' => :magenta, 'U' => :light_magenta,
|
15
|
+
'V' => :blue, 'X' => :red, 'Z' => :light_red }.freeze
|
16
16
|
end
|
17
17
|
end
|
@@ -1,92 +1,127 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Cotcube
|
4
|
+
# Missing top level documentation comment
|
4
5
|
module Bardata
|
5
|
-
|
6
6
|
# just reads bardata/daily/<id>/<contract>.csv
|
7
|
-
def provide_daily(symbol: nil, id: nil,
|
7
|
+
def provide_daily(contract:, symbol: nil, id: nil, timezone: Time.find_zone('America/Chicago'), config: init)
|
8
8
|
contract = contract.to_s.upcase
|
9
|
-
|
10
|
-
|
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
|
9
|
+
unless contract.is_a?(String) && [3, 5].include?(contract.size)
|
10
|
+
raise ArgumentError, "Contract '#{contract}' is bogus, should be like 'M21' or 'ESM21'"
|
19
11
|
end
|
20
|
-
|
12
|
+
|
13
|
+
sym = get_id_set(symbol: symbol, id: id, contract: contract)
|
14
|
+
contract = contract[2..4] if contract.to_s.size == 5
|
15
|
+
id = sym[:id]
|
21
16
|
id_path = "#{config[:data_path]}/daily/#{id}"
|
22
17
|
data_file = "#{id_path}/#{contract}.csv"
|
23
|
-
raise
|
24
|
-
|
25
|
-
data
|
18
|
+
raise "No data found for requested :id (#{id_path} does not exist)" unless Dir.exist?(id_path)
|
19
|
+
|
20
|
+
raise "No data found for requested contract #{symbol}:#{contract} in #{id_path}." unless File.exist?(data_file)
|
21
|
+
|
22
|
+
data = CSV.read(data_file, headers: %i[contract date open high low close volume oi]).map do |row|
|
26
23
|
row = row.to_h
|
27
|
-
row.each do |k, _|
|
28
|
-
row[k] = row[k].to_f if [
|
29
|
-
row[k] = row[k].to_i if [
|
24
|
+
row.each do |k, _|
|
25
|
+
row[k] = row[k].to_f if %i[open high low close].include? k
|
26
|
+
row[k] = row[k].to_i if %i[volume oi].include? k
|
30
27
|
end
|
31
|
-
row[:datetime] = timezone.parse(row[:date])
|
28
|
+
row[:datetime] = timezone.parse(row[:date]).to_date
|
29
|
+
row[:type] = :daily
|
32
30
|
row
|
33
31
|
end
|
32
|
+
data.pop if data.last[:high].zero?
|
34
33
|
data
|
35
34
|
end
|
36
|
-
|
37
|
-
# reads all files in bardata/daily/<id> and aggregates by date
|
35
|
+
|
36
|
+
# reads all files in bardata/daily/<id> and aggregates by date
|
37
|
+
# (what is a pre-stage of a continuous based on daily bars)
|
38
38
|
def continuous(symbol: nil, id: nil, config: init, date: nil)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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|
|
39
|
+
sym = get_id_set(symbol: symbol, id: id)
|
40
|
+
id = sym[:id]
|
41
|
+
id_path = "#{config[:data_path]}/daily/#{id}"
|
42
|
+
available_contracts = Dir["#{id_path}/*.csv"].map { |x| x.split('/').last.split('.').first }
|
43
|
+
available_contracts.sort_by! { |x| x[-7] }.sort_by! { |x| x[-6..-5] }
|
44
|
+
data = []
|
45
|
+
available_contracts.each do |c|
|
46
|
+
provide_daily(id: id, config: config, contract: c).each do |x|
|
51
47
|
data << x
|
52
48
|
end
|
53
49
|
end
|
54
|
-
result = []
|
55
|
-
data.sort_by{|x| x[:date]}.group_by{|x| x[:date]}.map
|
56
|
-
v.map{|x| x.delete(:date)}
|
50
|
+
result = []
|
51
|
+
data.sort_by { |x| x[:date] }.group_by { |x| x[:date] }.map do |k, v|
|
52
|
+
v.map { |x| x.delete(:date) }
|
57
53
|
result << {
|
58
54
|
date: k,
|
59
|
-
volume: v.map{|x| x[:volume]}.reduce(:+),
|
60
|
-
oi:
|
55
|
+
volume: v.map { |x| x[:volume] }.reduce(:+),
|
56
|
+
oi: v.map { |x| x[:oi] }.reduce(:+)
|
61
57
|
}
|
62
58
|
result.last[:contracts] = v
|
63
|
-
|
64
|
-
date.nil? ? result : result.select{|x| x[:date] == date}.first
|
59
|
+
end
|
60
|
+
date.nil? ? result : result.select { |x| x[:date] == date }.first
|
65
61
|
end
|
66
62
|
|
67
|
-
|
63
|
+
def continuous_ml(symbol: nil, id: nil, base: nil)
|
64
|
+
(base.nil? ? Cotcube::Bardata.continuous(symbol: symbol, id: id) : base).map do |x|
|
65
|
+
x[:ml] = x[:contracts].max_by { |z| z[:volume] }[:contract]
|
66
|
+
{ date: x[:date], ml: x[:ml] }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# the method above delivers the most_liquid as it is found at the end of the day. D
|
71
|
+
# during trading, the work is done with data
|
72
|
+
# that is already one day old. This is is fixed here:
|
73
|
+
def continuous_actual_ml(symbol: nil, id: nil)
|
74
|
+
continuous = Cotcube::Bardata.continuous symbol: symbol, id: id
|
75
|
+
continuous_ml = Cotcube::Bardata.continuous_ml base: continuous
|
76
|
+
continuous_hash = continuous.to_h { |x| [x[:date], x[:contracts]] }
|
77
|
+
actual_ml = continuous_ml.pairwise { |a, b| { date: b[:date], ml: a[:ml] } }
|
78
|
+
actual_ml.map do |x|
|
79
|
+
r = continuous_hash[x[:date]].select { |z| x[:ml] == z[:contract] }.first
|
80
|
+
r = continuous_hash[x[:date]].min_by { |z| -z[:volume] } if r.nil?
|
81
|
+
r
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# based on .continuous, this methods sorts the prepared dailies continuous for each date
|
86
|
+
# on either :volume (default) or :oi
|
68
87
|
# with this job done, it can provide the period for which a past contract was the most liquid
|
69
88
|
#
|
70
|
-
def continuous_overview(symbol: nil, id: nil,
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
data = continuous(id: id, config: config).map
|
89
|
+
def continuous_overview(symbol: nil, id: nil, # rubocop:disable Metrics/ParameterLists
|
90
|
+
config: init,
|
91
|
+
selector: :volume,
|
92
|
+
human: false,
|
93
|
+
filter: nil)
|
94
|
+
raise ArgumentError, 'Selector must be either :volume or :oi' unless selector.is_a?(Symbol) &&
|
95
|
+
%i[volume oi].include?(selector)
|
96
|
+
|
97
|
+
sym = get_id_set(symbol: symbol, id: id)
|
98
|
+
id = sym[:id]
|
99
|
+
data = continuous(id: id, config: config).map do |x|
|
81
100
|
{
|
82
|
-
date:
|
83
|
-
volume: x[:contracts].sort_by{|
|
84
|
-
oi:
|
101
|
+
date: x[:date],
|
102
|
+
volume: x[:contracts].sort_by { |z| - z[:volume] }[0..4].compact.reject { |z| z[:volume].zero? },
|
103
|
+
oi: x[:contracts].sort_by { |z| - z[:oi] }[0..4].compact.reject { |z| z[:oi].zero? }
|
85
104
|
}
|
86
|
-
|
87
|
-
|
105
|
+
end
|
106
|
+
data.reject! { |x| x[selector].empty? }
|
107
|
+
result = data.group_by { |x| x[selector].first[:contract] }
|
88
108
|
if human
|
89
|
-
result.each
|
109
|
+
result.each do |k, v|
|
110
|
+
next unless filter.nil? || v.first[selector].first[:contract][2..4] =~ (/#{filter}/)
|
111
|
+
|
112
|
+
# rubocop:disable Layout/ClosingParenthesisIndentation
|
113
|
+
puts "#{k
|
114
|
+
}\t#{v.first[:date]
|
115
|
+
}\t#{v.last[:date]
|
116
|
+
}\t#{format('%4d', (Date.parse(v.last[:date]) - Date.parse(v.first[:date])))
|
117
|
+
}\t#{result[k].map do |x|
|
118
|
+
x[:volume].select do
|
119
|
+
x[:contract] == k
|
120
|
+
end
|
121
|
+
end.size
|
122
|
+
}"
|
123
|
+
# rubocop:enable Layout/ClosingParenthesisIndentation
|
124
|
+
end
|
90
125
|
end
|
91
126
|
result
|
92
127
|
end
|