tickseries 0.1.1alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +50 -0
- data/LICENSE +29 -0
- data/README.md +19 -0
- data/features/01-ticks-init.feature +13 -0
- data/features/02-tickseries-init.feature +0 -0
- data/features/step_definitions/01-ticks-init_steps.rb +12 -0
- data/features/support/env.rb +6 -0
- data/lib/tickseries.rb +212 -0
- metadata +111 -0
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
|
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
|