tickseries 0.1.1alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d71caec6e9a6de66e0d18b58a6dba5ef5d021d6ebf2c6b6b71fad151344676f3
4
+ data.tar.gz: ad25827c08d77046112df71758f920635d0ecb572ab8f8ca1e88eb3167257b14
5
+ SHA512:
6
+ metadata.gz: 175441d690488c3e66bf3682a03220bada56eed28fe3319450be662129aae245054fd85a6246a35ef871bd29de566de2c0d6a8b678c3cb4a9e10cb9386a5658d
7
+ data.tar.gz: bede548c0567dc07643538e3a905bd36e144854c82b3b3d56c99ff88d3123dc5a60e0bbe7c63db62c020acd74b9d6f9b94082f89f8fe29639167076006d839b8
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2019, Donkeybridge
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # TickSeries
2
+
3
+ TickSeries is a Ruby module including the classes
4
+ * Tick
5
+ * Series
6
+
7
+ It's purpose is to build a robust layer to working with single-valued timeseries.
8
+
9
+ ## Description
10
+
11
+ Description pending (as are tests).
12
+
13
+ ## Basic usage
14
+
15
+ > series = Series.load(file: "ticks-2019-04-01", symbol: "Voltage")
16
+ > series.select {|x| x.p > 5.1 }.each {|x| p x}
17
+
18
+
19
+
@@ -0,0 +1,13 @@
1
+ Feature: Initialization of Tick
2
+ Scenario: Initialization of Tick
3
+ When creating a tick without parameters it should raise ArgumentError
4
+
5
+ Scenario Outline: Initialization of Tick with different valid parameter types
6
+ When creating a tick with '<params>' it should result in <timestamp> and <price>
7
+ Examples:
8
+ | params | timestamp | price |
9
+ | {s: :foo, t: 1555767235, p: 123 } | 1555767235000 | 123.0 |
10
+ | {s: "foo", t: 1555767235123, p: 1.21 } | 1555767235123 | 1.21 |
11
+ | ["foo", "1555767235", "225" ] | 1555767235000 | 225.0 |
12
+ | :foo, 1555767235, 225 | 1555767235000 | 225.0 |
13
+ | 1555767235, 225 | 1555767235000 | 225.0 |
File without changes
@@ -0,0 +1,12 @@
1
+ require './lib/tickseries.rb'
2
+ include TickSeries
3
+
4
+ When "creating a tick without parameters it should raise ArgumentError" do
5
+ expect { Tick.new }.to raise_error(ArgumentError)
6
+ end
7
+
8
+ When /^creating a tick with '([^']*)' it should result in (\S*) and (\S*)$/ do |params, timestamp, price|
9
+ eval "@tick = Tick.new(#{params})"
10
+ expect(@tick.t).to eq(Integer(timestamp))
11
+ expect(@tick.p).to eq(BigDecimal(price,8))
12
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+ RSpec.configure do |config|
3
+ config.expect_with :rspec do |c|
4
+ c.syntax = :expect
5
+ end
6
+ end
data/lib/tickseries.rb ADDED
@@ -0,0 +1,212 @@
1
+ require 'yaml'
2
+ require 'date'
3
+ require 'csv'
4
+ require 'bigdecimal'
5
+
6
+ # The module proveds TickSeries::Tick and TickSeries::Series
7
+ module TickSeries
8
+
9
+ # TickSeries::Series provides a TimeSeries (aka TickSeries) based on single Ticks (aka Measurements)
10
+ #
11
+ class Series
12
+
13
+ attr_reader :date, :ticks
14
+
15
+ # TickSeries::CONFIGFILEPATH
16
+ CONFIGFILEPATH = "~/.config/ez"
17
+ # TickSeries::CONFIGFILE basically contains the location of tickfiles as well as the location of symbol / contract information
18
+ #
19
+ CONFIGFILE = "#{CONFIGFILEPATH}/tickseries.conf"
20
+
21
+ # Helper method the read the config file
22
+ def self.get_config
23
+ begin
24
+ return YAML.load(File.read("#{`echo $HOME`.chomp}/.config/ez/tickseries.yml"))
25
+ rescue Errno::ENOENT
26
+ return {}
27
+ end
28
+ end
29
+
30
+ # Helper method the read the symbol config
31
+ def get_symbol_config
32
+ begin
33
+ return YAML.load(File.read("#{@symbolspath}/#{@symbol}.yml"))
34
+ #rescue Errno::ENOENT
35
+ # return {}
36
+ end
37
+ end
38
+
39
+ # Creates a new instance of TickSeries::Series. Accepts optionshash.
40
+ #
41
+ # @param opts [Hash]
42
+ def initialize(opts = {})
43
+
44
+ # Reading configfile and setting provided instance variables
45
+ `mkdir -p #{CONFIGFILEPATH} > /dev/null`
46
+ @config = Series.get_config
47
+ [:tickfilepath, :symbolspath].each do |param|
48
+ from_conf = @config[param.to_s] || @config[param]
49
+ from_opts = opts[param]
50
+ instance_variable_set("@#{param.to_s}", from_opts || from_conf)
51
+ end
52
+ p self.instance_variables
53
+
54
+ # Reading symbolconfig and providing instance variables
55
+ @symbol = opts[:symbol]
56
+ unless @symbol.nil?
57
+ @symbol = @symbol.upcase
58
+ @symbolconfig = self.get_symbol_config
59
+ p @symbolconfig
60
+ [:symbol, :ticksize].each do |param|
61
+ from_conf = @config[param.to_s] || @config[param]
62
+ from_opts = opts[param]
63
+ instance_variable_set("@#{param.to_s}", from_opts || from_conf)
64
+ end
65
+ end
66
+ @date ||= Date.today
67
+ @ticks = []
68
+ end
69
+
70
+ # Receptor from Enumerable#to_series
71
+ def self.from_enumerable(arr)
72
+ t = Series.new
73
+ arr.each{|x| t.add(x)}
74
+ t
75
+ end
76
+
77
+ # TickSeries::Series.load opens a 'tickfile' containing data saved as CSV.
78
+ def self.load(opts = {})
79
+ begin
80
+ config = Series.get_config
81
+ rescue Errno::ENOENT
82
+ config = {}
83
+ end
84
+ symbol = opts[:symbol]
85
+ file = opts[:file]
86
+ unless file.nil?
87
+ *path, filename = file.split('/')
88
+ if path.empty?
89
+ path = config[:tickfilepath] || config["tickfilepath"]
90
+ else
91
+ path = File.absolute_path(path.join('/'))
92
+ end
93
+ raise "Cannot guess tickfilepath from #{file}, please provide in #{CONFIGFILE}" if path.nil?
94
+
95
+ filetype = opts[:filetype] || filename.split('.').last
96
+ end
97
+ raise ArgumentError, "Cannot guess filetype for loading Timeseries from file #{path}/#{filename}" if filetype.nil?
98
+ raise ArgumentError, "Seems provided file for loading timeseries does not exist #{path}/#{filename}" unless File.file?("#{path}/#{filename}")
99
+
100
+ series = Series.new( symbol: opts[:symbol])
101
+
102
+ case filetype.downcase
103
+ when 'csv'
104
+ CSV.parse(`cat #{path}/#{filename} #{symbol.nil? ? "" : "| grep -i #{symbol}"}`).sort_by{|x| x[1] }.each{|x| series.add x}
105
+ else
106
+ raise(ArgumentError, "Unsupported filetype '.#{filetype}'")
107
+ end
108
+ series
109
+ end
110
+
111
+ # TickSeries::Series#add adds an element to series.
112
+ #
113
+ # element can be provided as TimeSeries::Tick, or as Hash or Array, that sufficises format requirements.
114
+ def add(element)
115
+ @ticks << ((element.is_a? Tick) ? element : Tick.new(element))
116
+ end
117
+
118
+ # @!visibility private
119
+ def map(&block); @ticks.map {|x| block.call(x)} ;end
120
+ # @!visibility private
121
+ def each(&block); @ticks.each {|x| block.call(x)} ;end
122
+ # @!visibility private
123
+ def each_with_index(&block); @ticks.each_with_index {|x,i| block.call(x,i)} ;end
124
+ # @!visibility private
125
+ def select(&block); @ticks.select {|x| block.call(x)} ;end
126
+ # @!visibility private
127
+ def reduce(c = 0, &block); @ticks.reduce(c) {|x,i| block.call(x,i)} ;end
128
+
129
+ # @!visibility private
130
+ def inspect
131
+ "<#TimeSeries::Series:0x#{self.object_id.to_s(16)} ticks: #{@ticks.size}, #{"symbol: #{@symbol}, " if @symbol}ticksize: #{@ticksize}, date: #{@date}>"
132
+ end
133
+ end
134
+
135
+ # TimeSeries::Series is the second part of the module. It contains a single measurement ("Messpunkt") of the series.
136
+ class Tick
137
+ attr_reader :t, :p, :v, :k
138
+
139
+ # The constructor is build as flexible as I was able to. It accepts
140
+ # * a Hash containing < :t | :time | :timestamp > with an integer or string value required in seconds or milliseconds
141
+ # containing < :m | :measurement | :price > with a Numeric value (can also be given as string)
142
+ # arbitrary information like or symbol, volume or peak information.
143
+ # * an Array that sufficises the expected order: < [ symbol,] timestamp, measure, frequency / volume, peak information >
144
+ def initialize(*args, &block)
145
+ raise ArgumentError, "Creating ticks without arguments is not supported" if args.empty?
146
+ opts = args[0] if args[0].is_a? Hash
147
+ args = args[0] if args[0].is_a? Array
148
+ if opts.nil?
149
+ prefix = 0
150
+ begin # if symbol is given on args[0], the Integer() will raise
151
+ timestamp = Integer(args[0])
152
+ rescue
153
+ prefix = 1
154
+ timestamp = Integer(args[1])
155
+ end
156
+ opts = {t: timestamp,
157
+ m: args[prefix + 1],
158
+ v: args[prefix + 2],
159
+ p: args[prefix + 3]}
160
+ end
161
+ @t = opts[:t] || opts[:time] || opts[:timestamp]
162
+ case @t
163
+ when *[Date, DateTime, Time]
164
+ @t = @t.to_time.to_i
165
+ when *[Integer, Float, BigDecimal]
166
+ @t = Integer(@t)
167
+ when String
168
+ failed = false
169
+ begin; @t = DateTime.parse(@t).to_time.to_i * 1000; rescue; failed = true; end
170
+ if failed; begin; @t = Integer(@t); failed = false; rescue; failed = true; end; end
171
+ raise ArgumentError, "Could not get Timestamp from given String #{@t}" if failed
172
+ else
173
+ raise ArgumentError, "Tick.new cannot continue without timestamp given as t:, time: timestamp: or within array."
174
+ end
175
+ if @t < 500000000000 # Tue, 05 Nov 1985 00:53:20 GMT in ms
176
+ # assume it was provided as second_based timestamp
177
+ @t *= 1000
178
+ end
179
+ raise ArgumentError, "Invalid timestamp, too small: #{@t} < 500000000000" if @t < 500000000000
180
+ @m = opts[:m] || opts[:measurement]
181
+ @m = BigDecimal(@m,8) unless @m.nil?
182
+ @v = opts[:v] || opts[:vol]
183
+ @v = @v.to_i unless @v.nil?
184
+ # :peak should only be set for peaks pointing out a deviation > 5 * @ticksize
185
+ @p = opts[:p] || opts[:peak]
186
+ @p = @p.to_i unless @p.nil?
187
+ end
188
+
189
+
190
+ # Convenient way to create a string representation of tick.
191
+ def to_s
192
+ "#{@t},#{@m.to_f},#{@v},#{@p}"
193
+ end
194
+
195
+ # TickSeries::Tick#human_time returns the time converted to a human readable format
196
+ #
197
+ # @param with_date [Boolean] whether or not to include Date information (default: false)
198
+ def human_time(with_date = false)
199
+ t = Time.at(@t / 1000)
200
+ t.strftime("#{ "%Y-%m-%d " if with_date }%H:%M:%S")
201
+ end
202
+ end
203
+ end
204
+
205
+ # Reopens enumerable to allow retransport into Series
206
+ # @!visibility private
207
+ module Enumerable
208
+ # @!visibility private
209
+ def to_series
210
+ TickSeries::Series.from_enumerable(self)
211
+ end
212
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tickseries
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1alpha
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin L. Tischendorf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: csv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.6'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: cucumber
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ description: 'Ruby Module providing TickSeries::Tick and TickSeries::Series '
70
+ email: donkeybridge@jtown.eu
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".gitignore"
76
+ - LICENSE
77
+ - README.md
78
+ - features/01-ticks-init.feature
79
+ - features/02-tickseries-init.feature
80
+ - features/step_definitions/01-ticks-init_steps.rb
81
+ - features/support/env.rb
82
+ - lib/tickseries.rb
83
+ homepage: https://github.com/donkeybridge/tickseries
84
+ licenses:
85
+ - BSD-4-Clause
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">"
99
+ - !ruby/object:Gem::Version
100
+ version: 1.3.1
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.7.8
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Ruby Module providing TickSeries::Tick and TickSeries::Series
107
+ test_files:
108
+ - features/01-ticks-init.feature
109
+ - features/support/env.rb
110
+ - features/02-tickseries-init.feature
111
+ - features/step_definitions/01-ticks-init_steps.rb