cotcube-helpers 0.1.1 → 0.1.5.1
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 +20 -0
- data/Gemfile +3 -2
- data/LICENSE.txt +21 -0
- data/README.md +107 -0
- data/VERSION +1 -1
- data/cotcube-helpers.gemspec +2 -3
- data/lib/cotcube-helpers.rb +9 -9
- data/lib/cotcube-helpers/array_ext.rb +34 -10
- data/lib/cotcube-helpers/datetime_ext.rb +12 -0
- data/lib/cotcube-helpers/enum_ext.rb +5 -3
- data/lib/cotcube-helpers/hash_ext.rb +8 -6
- data/lib/cotcube-helpers/input.rb +19 -15
- data/lib/cotcube-helpers/parallelize.rb +21 -26
- data/lib/cotcube-helpers/range_ext.rb +62 -26
- data/lib/cotcube-helpers/reduce.rb +56 -0
- data/lib/cotcube-helpers/string_ext.rb +11 -6
- data/lib/cotcube-helpers/subpattern.rb +4 -2
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3d088e43fd870ff67a238501fd680f1c5688c7108bc90ace92e01cd8c33627b
|
4
|
+
data.tar.gz: 72b07a1f0e502a29e9cb818d62a8d398fe5e094efade358300eab4578b64ceae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b92ec6b178328bd6944f61828848d1d1c4aa164e9a3680054345189503d237a809ea082441eeab476ff693eeebdf5bf27bec300b3606af589598da946ce1c0a
|
7
|
+
data.tar.gz: 00d63e82c5ed3cfcc9986f4ec3222e2282a692ed0ad54602cb4e212b9285d8c51182d9c87a4e8b9672d2f305409b6f01d27437b73aa40df6872a35e4ea962b8e
|
data/.irbrc.rb
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## 0.1.5.1 (January 02, 2021)
|
2
|
+
- Hotfixing parallelize
|
3
|
+
|
4
|
+
## 0.1.5 (January 02, 2021)
|
5
|
+
- applied new datetime helper to Range#to_time_intervals
|
6
|
+
- added new DateTime extension, containing 'to_seconds_since_sunday_morning'
|
7
|
+
- added #select_within to array_ext
|
8
|
+
|
9
|
+
## 0.1.4 (December 27, 2020)
|
10
|
+
- applied cops
|
11
|
+
- added README for reduce; minor changes
|
12
|
+
|
13
|
+
## 0.1.3 (December 22, 2020)
|
14
|
+
- added .reduce(bars: , to: ,*args, &block) to reduce a series of bars to a higher timeframe (though only 1hour and 1day are supported yet)
|
15
|
+
|
16
|
+
## 0.1.2 (December 21, 2020)
|
17
|
+
- minor changes
|
18
|
+
- minor fix to parallelize and application of positional arguments
|
19
|
+
- added license and README
|
20
|
+
|
1
21
|
## 0.1.1 (December 21, 2020)
|
2
22
|
|
3
23
|
|
data/Gemfile
CHANGED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Benjamin L. Tischendorf
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
## Cotcube::Helpers
|
2
|
+
|
3
|
+
Just a collection of helpers not fitting into other repositories of the Cotcube suite. Also usable aside of Cotcube. Where appropriate, these are provided as core\_extensions, otherwise within the model Cotcube::Helpers
|
4
|
+
|
5
|
+
#### Extending Array
|
6
|
+
|
7
|
+
##### compact\_or\_nil
|
8
|
+
|
9
|
+
Returns the array compacted--or just nil, when the result is empty?
|
10
|
+
|
11
|
+
##### split\_by(attr)
|
12
|
+
|
13
|
+
This is a rather old implementation. Most probably something I developed before I met Array#group\_by.
|
14
|
+
|
15
|
+
##### pairwise(&block) / one_by_one(&block)
|
16
|
+
|
17
|
+
Yields block on each consecutive pair of the array, hence returning array.size-1 results.
|
18
|
+
|
19
|
+
##### triplewise(&block)
|
20
|
+
|
21
|
+
Yields block on each consecutive triple of the array, hence returning array.size-2 results.
|
22
|
+
|
23
|
+
#### Extending Enumerator
|
24
|
+
|
25
|
+
##### shy\_peek
|
26
|
+
|
27
|
+
Peeks into the successor without raising if there is none available--returning nil instead.
|
28
|
+
|
29
|
+
#### Extending Hash
|
30
|
+
|
31
|
+
##### keys\_to\_sym
|
32
|
+
|
33
|
+
Transforms the keys of a Hash (recursivly for subsequent Arrays and Hashes) into symbols.
|
34
|
+
|
35
|
+
#### Extending Range
|
36
|
+
|
37
|
+
##### to\_time\_intervals(timezone: Time.find\_zone('America/Chicago'), step:, ranges: nil)
|
38
|
+
|
39
|
+
Uses the range of *date alikes* delivered to create an Array of time periods of length :step
|
40
|
+
(which is a ActiveSupport::Duration, e.g. 5.minutes or 1.hour).
|
41
|
+
|
42
|
+
When the step is sub-day, the periods are cleared for DST-changes.
|
43
|
+
|
44
|
+
When ranges are nil, only periods are returned that are within the full trading hours.
|
45
|
+
|
46
|
+
This is implemented with DateTime's propery, that Sunday is wday==0. If you want to use ranges, just send a an array
|
47
|
+
of ranges, providing seconds starting at Sunday morning midnight. Here is the default as example:
|
48
|
+
|
49
|
+
```
|
50
|
+
ranges ||= [
|
51
|
+
61200...144000, # Sun 5pm .. Mon 4pm
|
52
|
+
147600...230400, # Mon 5pm .. Tue 4pm
|
53
|
+
234000...316800, # ...
|
54
|
+
320400...403300,
|
55
|
+
406800...489600
|
56
|
+
]
|
57
|
+
```
|
58
|
+
|
59
|
+
#### Extending String
|
60
|
+
|
61
|
+
##### is\_valid\_json?
|
62
|
+
|
63
|
+
#### Subpattern
|
64
|
+
|
65
|
+
##### sub(minimum: 1) { [:hyper, :mega] }
|
66
|
+
|
67
|
+
sub (should be 'subpattern', but too long) is for use in `case / when` statements.
|
68
|
+
It returns a lambda, that checks the *case'd* expression for matching subpatterns
|
69
|
+
based on the the giving minimum. E.g. 'a', 'ab' .. 'abcd' .. 'abcd<any_garbage> will match sub(1){'abcd'}
|
70
|
+
but only 'abc' and 'abcd' will match sub(3){'abcd'}
|
71
|
+
|
72
|
+
It is developed for evaluating user input, where abbreviation of incoming commands
|
73
|
+
is desirable (h for hoover and hyper, what will translate to sub(2){'hoover'} and sub(2){hyper})
|
74
|
+
|
75
|
+
To extend functionality even more, it is possible to send array of patterns like
|
76
|
+
sub(2){[:hyper,:mega]}, what will respond truthy to "hy" and "meg" but not to "m" or "hypo" (sadly, you can
|
77
|
+
set `:minimum` only once).
|
78
|
+
|
79
|
+
*Paired with keystroke() it allows an easy build of an inputhandler.*
|
80
|
+
|
81
|
+
#### SimpleOutput
|
82
|
+
|
83
|
+
This is a very simple class, that mocks a more complex output handler. It provides puts, print,
|
84
|
+
puts! and print!. The actual OutputHandler is another project that needs to be rewritten. Once
|
85
|
+
that is done, SimpleOutput will be replaced. The new OutputHandler is a tool to handle information
|
86
|
+
flow like logs, to pause and continue output.
|
87
|
+
|
88
|
+
#### Input
|
89
|
+
|
90
|
+
##### keystroke(quit: false)
|
91
|
+
|
92
|
+
A version of STDIN.gets, that does not wait for pressing 'enter' but instantly returns the content
|
93
|
+
of the keystroke.
|
94
|
+
|
95
|
+
*Paired with subpattern it allows an easy build of an InputHandler.*
|
96
|
+
|
97
|
+
#### Parallelize
|
98
|
+
|
99
|
+
##### parallelize(ary, processes: 1, threads: 1, progress: "", &block)
|
100
|
+
|
101
|
+
Based on https://github.com/grosser/parallel, it is a quite convenient way to parallelize tasks.
|
102
|
+
|
103
|
+
#### Reduce
|
104
|
+
|
105
|
+
##### reduce(bars: , to: nil, datelike: :datetime, &block)
|
106
|
+
|
107
|
+
Given a series resp. an array of bars (a bar is a set of contract/symbol, datelike, OHLC, volume) respresenting periods of e.g. 1.minute or 15.minutes, these are 'reduced' to an new series of a higher timeframe like hours or days. More target periods might be added in the futures.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.1
|
1
|
+
0.1.5.1
|
data/cotcube-helpers.gemspec
CHANGED
@@ -9,13 +9,13 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.summary = 'Some helpers and core extensions as part of the Cotcube Suite.'
|
10
10
|
spec.description = 'Some helpers and core extensions as part of the Cotcube Suite...'
|
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.
|
@@ -28,7 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency 'activesupport'
|
30
30
|
|
31
|
-
|
32
31
|
spec.add_development_dependency 'rake'
|
33
32
|
spec.add_development_dependency 'rspec', '~>3.6'
|
34
33
|
spec.add_development_dependency 'yard', '~>0.9'
|
data/lib/cotcube-helpers.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'
|
@@ -10,22 +12,20 @@ require_relative 'cotcube-helpers/enum_ext'
|
|
10
12
|
require_relative 'cotcube-helpers/hash_ext'
|
11
13
|
require_relative 'cotcube-helpers/range_ext'
|
12
14
|
require_relative 'cotcube-helpers/string_ext'
|
13
|
-
require_relative 'cotcube-helpers/
|
15
|
+
require_relative 'cotcube-helpers/datetime_ext'
|
16
|
+
require_relative 'cotcube-helpers/subpattern'
|
14
17
|
require_relative 'cotcube-helpers/parallelize'
|
15
18
|
require_relative 'cotcube-helpers/simple_output'
|
16
19
|
require_relative 'cotcube-helpers/input'
|
17
|
-
|
20
|
+
require_relative 'cotcube-helpers/reduce'
|
18
21
|
|
19
22
|
module Cotcube
|
20
23
|
module Helpers
|
24
|
+
module_function :sub,
|
25
|
+
:parallelize,
|
26
|
+
:reduce,
|
27
|
+
:keystroke
|
21
28
|
|
22
|
-
module_function :sub,
|
23
|
-
:parallelize,
|
24
|
-
:keystroke
|
25
|
-
|
26
|
-
|
27
|
-
|
28
29
|
# please not that module_functions of source provided in private files must be published there
|
29
30
|
end
|
30
31
|
end
|
31
|
-
|
@@ -1,22 +1,25 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Monkey patching the Ruby Core class Array
|
4
|
+
class Array
|
3
5
|
# returns nil if the compacted array is empty, otherwise returns the compacted array
|
4
|
-
def compact_or_nil(*
|
5
|
-
return nil if
|
6
|
-
|
6
|
+
def compact_or_nil(*_args)
|
7
|
+
return nil if compact == []
|
8
|
+
|
9
|
+
yield compact
|
7
10
|
end
|
8
11
|
|
9
12
|
# sorts by a given attribute and then returns groups of where this attribute is equal
|
10
13
|
# .... seems like some_array.group_by(&attr).values
|
11
14
|
def split_by(attrib)
|
12
15
|
res = []
|
13
|
-
sub = []
|
14
|
-
|
15
|
-
if sub.empty?
|
16
|
+
sub = []
|
17
|
+
sort_by(&attrib).each do |elem|
|
18
|
+
if sub.empty? || (sub.last[attrib] == elem[attrib])
|
16
19
|
sub << elem
|
17
20
|
else
|
18
21
|
res << sub
|
19
|
-
sub = [
|
22
|
+
sub = [elem]
|
20
23
|
end
|
21
24
|
end
|
22
25
|
res << sub
|
@@ -37,7 +40,7 @@ class Array
|
|
37
40
|
end.compact
|
38
41
|
end
|
39
42
|
|
40
|
-
|
43
|
+
alias one_by_one pairwise
|
41
44
|
|
42
45
|
# same as pairwise, but with arity of three
|
43
46
|
def triplewise(&block)
|
@@ -47,7 +50,28 @@ class Array
|
|
47
50
|
each_with_index.map do |_, i|
|
48
51
|
next if i < 2
|
49
52
|
|
50
|
-
block.call(self[i - 2], self[i-1], self[i])
|
53
|
+
block.call(self[i - 2], self[i - 1], self[i])
|
51
54
|
end.compact
|
52
55
|
end
|
56
|
+
|
57
|
+
# selects all elements from array that fit in given ranges.
|
58
|
+
# if :attr is given, selects all elements, where elem[:attr] fit
|
59
|
+
# raises if elem.first[attr].nil?
|
60
|
+
def select_within(ranges:, attr: nil, &block)
|
61
|
+
unless attr.nil? || first[attr]
|
62
|
+
raise ArgumentError,
|
63
|
+
"At least first element of Array '#{first}' does not contain attr '#{attr}'!"
|
64
|
+
end
|
65
|
+
raise ArgumentError, 'Ranges should be an Array or, more precisely, respond_to :map' unless ranges.respond_to? :map
|
66
|
+
raise ArgumentError, 'Each range in :ranges should respond to .include!' unless ranges.map do |x|
|
67
|
+
x.respond_to? :include?
|
68
|
+
end.reduce(:&)
|
69
|
+
|
70
|
+
select do |el|
|
71
|
+
value = attr.nil? ? el : el[attr]
|
72
|
+
ranges.map do |range|
|
73
|
+
range.include?(block.nil? ? value : block.call(value))
|
74
|
+
end.reduce(:|)
|
75
|
+
end
|
76
|
+
end
|
53
77
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patching the Ruby class DateTime
|
4
|
+
class DateTime
|
5
|
+
# based on the fact that sunday is 'wday 0' plus that trading week starts
|
6
|
+
# sunday 0:00 (as trading starts sunday 5pm CT to fit tokyo monday morning)
|
7
|
+
def to_seconds_since_sunday_morning
|
8
|
+
wday * 86_400 + hour * 3600 + min * 60 + sec
|
9
|
+
end
|
10
|
+
|
11
|
+
alias to_sssm to_seconds_since_sunday_morning
|
12
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Monkey patching the Ruby Core class Hash
|
4
|
+
class Hash
|
3
5
|
def keys_to_sym
|
4
|
-
|
6
|
+
each_key do |key|
|
5
7
|
case self[key].class.to_s
|
6
|
-
when
|
8
|
+
when 'Hash'
|
7
9
|
self[key].keys_to_sym
|
8
|
-
when
|
9
|
-
self[key].map {|el| el.is_a?(Hash) ? el.keys_to_sym : el}
|
10
|
+
when 'Array'
|
11
|
+
self[key].map { |el| el.is_a?(Hash) ? el.keys_to_sym : el }
|
10
12
|
end
|
11
|
-
self[key.to_sym] =
|
13
|
+
self[key.to_sym] = delete(key)
|
12
14
|
end
|
13
15
|
self
|
14
16
|
end
|
@@ -1,40 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Top level comment missing!
|
1
4
|
module Cotcube
|
5
|
+
# noinspection ALL
|
2
6
|
module Helpers
|
3
7
|
def keystroke(quit: false)
|
4
8
|
begin
|
5
9
|
# save previous state of stty
|
6
10
|
old_state = `stty -g`
|
7
11
|
# disable echoing and enable raw (not having to press enter)
|
8
|
-
system
|
9
|
-
c =
|
12
|
+
system 'stty raw -echo'
|
13
|
+
c = $stdin.getc.chr rescue '_' # rubocop:disable Style/RescueModifier
|
10
14
|
# gather next two characters of special keys
|
11
|
-
if
|
12
|
-
extra_thread = Thread.new
|
13
|
-
c
|
14
|
-
c
|
15
|
-
|
15
|
+
if c == "\e"
|
16
|
+
extra_thread = Thread.new do
|
17
|
+
c += $stdin.getc.chr
|
18
|
+
c += $stdin.getc.chr
|
19
|
+
end
|
16
20
|
# wait just long enough for special keys to get swallowed
|
17
21
|
extra_thread.join(0.00001)
|
18
22
|
# kill thread so not-so-long special keys don't wait on getc
|
19
23
|
extra_thread.kill
|
20
24
|
end
|
21
|
-
rescue =>
|
22
|
-
puts "#{
|
23
|
-
puts
|
25
|
+
rescue StandardError => e
|
26
|
+
puts "#{e.class}: #{e.message}"
|
27
|
+
puts e.backtrace
|
24
28
|
ensure
|
25
29
|
# restore previous state of stty
|
26
30
|
system "stty #{old_state}"
|
27
31
|
end
|
28
|
-
c.each_byte do |x|
|
32
|
+
c.each_byte do |x| # rubocop:disable Lint/UnreachableLoop
|
29
33
|
case x
|
30
34
|
when 3
|
31
|
-
puts
|
35
|
+
puts 'Strg-C captured, exiting...'
|
32
36
|
quit ? exit : (return true)
|
33
37
|
when 13
|
34
|
-
return
|
38
|
+
return '_return_'
|
35
39
|
when 27
|
36
|
-
puts
|
37
|
-
return
|
40
|
+
puts 'ESCAPE gathered'
|
41
|
+
return '_esc_'
|
38
42
|
else
|
39
43
|
return c
|
40
44
|
end
|
@@ -1,33 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: Missing top level documentation!
|
1
4
|
module Cotcube
|
5
|
+
# TODO: Missing top level documentation!
|
2
6
|
module Helpers
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
if processes == 0 or processes == 1
|
10
|
-
r = Parallel.map(ary, in_threads: threads_per_process) {|u| v = yield(u); v}
|
11
|
-
elsif [0,1].include?(threads_per_process)
|
12
|
-
r = Parallel.map(ary, in_processes: processes) {|u| v = yield(u)}
|
7
|
+
def parallelize(ary, processes: 1, threads: 1, progress: '', &block)
|
8
|
+
chunks = []
|
9
|
+
if [0, 1].include? processes
|
10
|
+
result = Parallel.map(ary, in_threads: threads, &block)
|
11
|
+
elsif [0, 1].include? threads
|
12
|
+
result = Parallel.map(ary, in_processes: processes, &block)
|
13
13
|
else
|
14
|
-
ary.each_slice(
|
15
|
-
if progress ==
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
yield(unit)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
14
|
+
ary.each_slice(threads) { |chunk| chunks << chunk }
|
15
|
+
result = if progress == ''
|
16
|
+
Parallel.map(chunks, in_processes: processes) do |chunk|
|
17
|
+
Parallel.map(chunk, in_threads: threads, &block)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
Parallel.map(chunks, progress: progress, in_processes: processes) do |chunk|
|
21
|
+
Parallel.map(chunk, in_threads: threads, &block)
|
22
|
+
end
|
23
|
+
end
|
28
24
|
end
|
29
|
-
|
25
|
+
result
|
30
26
|
end
|
31
|
-
|
32
27
|
end
|
33
28
|
end
|
@@ -1,52 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patching the Ruby Core class Range
|
1
4
|
class Range
|
2
|
-
def to_time_intervals(timezone: Time.find_zone('America/Chicago'),
|
5
|
+
def to_time_intervals(step:, timezone: Time.find_zone('America/Chicago'), ranges: nil)
|
6
|
+
unless step.is_a? ActiveSupport::Duration
|
7
|
+
raise ArgumentError,
|
8
|
+
":step must be a 'ActiveSupport::Duration', like '15.minutes', but '#{step}' is a '#{step.class}'"
|
9
|
+
end
|
3
10
|
|
4
|
-
|
11
|
+
valid_classes = [ActiveSupport::TimeWithZone, Time, Date, DateTime]
|
12
|
+
unless timezone.is_a? ActiveSupport::TimeZone
|
13
|
+
raise "Expecting 'ActiveSupport::TimeZone' for :timezone, got '#{timezone.class}"
|
14
|
+
end
|
5
15
|
|
6
|
-
valid_classes = [ ActiveSupport::TimeWithZone, Time, Date, DateTime ]
|
7
|
-
raise "Expecting 'ActiveSupport::TimeZone' for :timezone, got '#{timezone.class}" unless timezone.is_a? ActiveSupport::TimeZone
|
8
16
|
starting = self.begin
|
9
17
|
ending = self.end
|
10
18
|
starting = timezone.parse(starting) if starting.is_a? String
|
11
19
|
ending = timezone.parse(ending) if ending.is_a? String
|
12
|
-
|
13
|
-
|
20
|
+
unless valid_classes.include? starting.class
|
21
|
+
raise ArgumentError,
|
22
|
+
":self.begin seems not to be proper time value: #{starting} is a #{starting.class}"
|
23
|
+
end
|
24
|
+
unless valid_classes.include? ending.class
|
25
|
+
raise ArgumentError,
|
26
|
+
":self.end seems not to be proper time value: #{ending} is a #{ending.class}"
|
27
|
+
end
|
14
28
|
|
15
|
-
##### The following is the actual big magic line:
|
16
|
-
#
|
17
|
-
result = (starting.to_time.to_i..ending.to_time.to_i).step(step).to_a.map{|x| timezone.at(x)}
|
29
|
+
##### The following is the actual big magic line, as it creates the raw target array:
|
18
30
|
#
|
19
|
-
|
20
|
-
|
31
|
+
result = (starting.to_time.to_i..ending.to_time.to_i).step(step).to_a.map { |x| timezone.at(x) }
|
32
|
+
#
|
33
|
+
# ###################<3##
|
21
34
|
|
22
35
|
# with step.to_i >= 86400 we are risking stuff like 25.hours to return bogus
|
23
36
|
# also notice: When using this with swaps, you will loose 1 hour (#f**k_it)
|
24
37
|
#
|
25
38
|
# eventually, for dailies and above, return M-F default, return S-S when forced by empty ranges
|
26
|
-
|
39
|
+
if step.to_i >= 86_400
|
40
|
+
return result.select do |x|
|
41
|
+
(not ranges.nil?) && ranges.empty? ? true : (not [6, 0].include?(x.wday))
|
42
|
+
end
|
43
|
+
end
|
27
44
|
|
28
45
|
# sub-day is checked for DST and filtered along provided ranges
|
46
|
+
# noinspection RubyNilAnalysis
|
29
47
|
starting_with_dst = result.first.dst?
|
30
|
-
|
48
|
+
|
49
|
+
# The following lambda is completely misplaces here.
|
50
|
+
# It should probably relocated to Cotcube::Bardata
|
51
|
+
# NOTE: In this current version 12 belongs to it succeeding hour
|
52
|
+
# i.e. 12am is right before 1am and 12pm right before 1pm
|
53
|
+
convert_to_sec_since = lambda do |clocking|
|
54
|
+
from_src, to_src = clocking.split(' - ')
|
55
|
+
regex = /^(?<hour>\d+):(?<minute>\d+)(?<morning>[pa]).?m.*/
|
56
|
+
|
57
|
+
from = from_src.match(regex)
|
58
|
+
to = to_src.match(regex)
|
59
|
+
|
60
|
+
from_i = from[:hour].to_i * 3600 + from[:minute].to_i * 60 + (from[:morning] == 'a' ? 2 : 1) * 12 * 3600
|
61
|
+
to_i = to[:hour].to_i * 3600 + to[:minute].to_i * 60 + (to[:morning] == 'a' ? 2 : 3) * 12 * 3600
|
62
|
+
|
63
|
+
(0...5).to_a.map { |i| [from_i + i * 24 * 3600, to_i + i * 24 * 3600] }
|
64
|
+
end
|
65
|
+
convert_to_sec_since.call('9:00a.m - 5:00p.m.')
|
66
|
+
|
31
67
|
ranges ||= [
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
68
|
+
61_200...144_000, # Sun 5pm .. Mon 4pm
|
69
|
+
147_600...230_400, # Mon 5pm .. Tue 4pm
|
70
|
+
234_000...316_800, # ...
|
71
|
+
320_400...403_200,
|
72
|
+
406_800...489_600
|
37
73
|
]
|
38
74
|
|
39
|
-
# if there was a change towards daylight saving time,
|
75
|
+
# if there was a change towards daylight saving time, subtract 1 hour, otherwise add 1 hour
|
40
76
|
result.map! do |time|
|
41
|
-
if
|
42
|
-
|
43
|
-
elsif
|
44
|
-
|
77
|
+
if (not starting_with_dst) && time.dst?
|
78
|
+
time - 3600
|
79
|
+
elsif starting_with_dst && (not time.dst?)
|
80
|
+
time + 3600
|
45
81
|
else
|
46
|
-
|
82
|
+
time
|
47
83
|
end
|
48
84
|
end
|
49
|
-
|
50
|
-
result.
|
85
|
+
|
86
|
+
result.select_within(ranges: ranges) { |x| x.to_datetime.to_seconds_since_monday_morning }
|
51
87
|
end
|
52
88
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cotcube
|
4
|
+
# Missing top level documentation
|
5
|
+
module Helpers
|
6
|
+
def reduce(bars:, to: nil, date_alike: :datetime, &block)
|
7
|
+
case to
|
8
|
+
when :days
|
9
|
+
terminators = %i[last daily beginning_of_day]
|
10
|
+
block = proc { |c, b| c[:day] == b[:day] } unless block_given?
|
11
|
+
when :hours
|
12
|
+
terminators = %i[first hours beginning_of_hour]
|
13
|
+
block = proc { |c, b| c[:day] == b[:day] and c[:datetime].hour == b[:datetime].hour } unless block_given?
|
14
|
+
when :weeks
|
15
|
+
terminators = %i[first weeks beginning_of_week]
|
16
|
+
block = proc { |a, b| a[:datetime].to_datetime.cweek == b[:datetime].to_datetime.cweek } unless block_given?
|
17
|
+
when :months
|
18
|
+
terminators = %i[first months beginning_of_month]
|
19
|
+
block = proc { |a, b| a[:datetime].to_datetime.month == b[:datetime].to_datetime.month } unless block_given?
|
20
|
+
else
|
21
|
+
raise ArgumentError, 'Currently supported are reductions to :hours, :days, :weeks, :months '
|
22
|
+
end
|
23
|
+
determine_date_alike = ->(ary) { ary.send(terminators.first)[date_alike].send(terminators.last) }
|
24
|
+
make_new_bar = lambda do |ary, _date = nil|
|
25
|
+
result = {
|
26
|
+
contract: ary.first[:contract],
|
27
|
+
symbol: ary.first[:symbol],
|
28
|
+
datetime: determine_date_alike.call(ary),
|
29
|
+
day: ary.first[:day],
|
30
|
+
open: ary.first[:open],
|
31
|
+
high: ary.map { |x| x[:high] }.max,
|
32
|
+
low: ary.map { |x| x[:low] }.min,
|
33
|
+
close: ary.last[:close],
|
34
|
+
volume: ary.map { |x| x[:volume] }.reduce(:+),
|
35
|
+
type: terminators[1]
|
36
|
+
}
|
37
|
+
result.map { |k, v| result.delete(k) if v.nil? }
|
38
|
+
result
|
39
|
+
end
|
40
|
+
collector = []
|
41
|
+
final = []
|
42
|
+
bars.each do |bar|
|
43
|
+
if collector.empty? || block.call(collector.last, bar)
|
44
|
+
collector << bar
|
45
|
+
else
|
46
|
+
new_bar = make_new_bar.call(collector)
|
47
|
+
final << new_bar
|
48
|
+
collector = [bar]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
new_bar = make_new_bar.call(collector)
|
52
|
+
final << new_bar
|
53
|
+
final
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,9 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patching the Ruby Core class String
|
1
4
|
class String
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
# ...
|
6
|
+
def valid_json?
|
7
|
+
JSON.parse(self)
|
8
|
+
true
|
9
|
+
rescue JSON::ParserError
|
10
|
+
false
|
7
11
|
end
|
8
|
-
end
|
9
12
|
|
13
|
+
alias is_valid_json? valid_json?
|
14
|
+
end
|
@@ -1,18 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# ...
|
3
4
|
module Cotcube
|
5
|
+
# ...
|
4
6
|
module Helpers
|
5
7
|
# sub (should be 'subpattern', but too long) is for use in case / when statements
|
6
8
|
# it returns a lambda, that checks the case'd expression for matching subpattern
|
7
9
|
# based on the the giving minimum. E.g. 'a', 'ab' .. 'abcd' will match sub(1){'abcd'}
|
8
|
-
# but only 'abc' and 'abcd' will match sub(3){'abcd'}
|
10
|
+
# but only 'abc' and 'abcd' will match sub(3){'abcd'}.:
|
9
11
|
#
|
10
12
|
# The recommended use within evaluating user input, where abbreviation of incoming commands
|
11
13
|
# is desirable (h for hoover and hyper, what will translate to sub(2){'hoover'} and sub(2){hyper})
|
12
14
|
#
|
13
15
|
# To extend functionality even more, it is possible to send a group of patterns to, like
|
14
16
|
# sub(2){[:hyper,:mega]}, what will respond truthy to "hy" and "meg" but not to "m" or "hypo"
|
15
|
-
def sub(minimum
|
17
|
+
def sub(minimum: 1)
|
16
18
|
pattern = yield
|
17
19
|
case pattern
|
18
20
|
when String, Symbol, NilClass
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cotcube-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.1
|
4
|
+
version: 0.1.5.1
|
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:
|
11
|
+
date: 2021-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -76,15 +76,19 @@ files:
|
|
76
76
|
- ".irbrc.rb"
|
77
77
|
- CHANGELOG.md
|
78
78
|
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
79
81
|
- VERSION
|
80
82
|
- cotcube-helpers.gemspec
|
81
83
|
- lib/cotcube-helpers.rb
|
82
84
|
- lib/cotcube-helpers/array_ext.rb
|
85
|
+
- lib/cotcube-helpers/datetime_ext.rb
|
83
86
|
- lib/cotcube-helpers/enum_ext.rb
|
84
87
|
- lib/cotcube-helpers/hash_ext.rb
|
85
88
|
- lib/cotcube-helpers/input.rb
|
86
89
|
- lib/cotcube-helpers/parallelize.rb
|
87
90
|
- lib/cotcube-helpers/range_ext.rb
|
91
|
+
- lib/cotcube-helpers/reduce.rb
|
88
92
|
- lib/cotcube-helpers/simple_output.rb
|
89
93
|
- lib/cotcube-helpers/string_ext.rb
|
90
94
|
- lib/cotcube-helpers/subpattern.rb
|