cotcube-bardata 0.1.3 → 0.1.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/.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
|