spanbars 0.1.2beta → 0.2.0beta
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/README.md +22 -15
- data/bin/spanbars +16 -9
- data/bin/spanbars.rb +5 -0
- data/features/step_definitions/01_spanbar_processor_initialization_steps.rb +0 -2
- data/lib/spanbar.rb +50 -22
- data/lib/spanbarprocessor.rb +7 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77c49955cc21152aa8b11ce9f3b34eb106e5676d26613f9734f68c155fdb6df1
|
4
|
+
data.tar.gz: 2c563526e765155d3a25424f832ad966034b86ecab8bc4f2c678611f1a187896
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d82b58f1aa81250c9104486644b32350c135b349d9ea2836545adbbd3add2fe2f20a2b5dacce0eb4642fb93078e32ebf729bc8045f6f2b14109d1c98c6cc062
|
7
|
+
data.tar.gz: bf85d6b41f4e67a8da95b67c33018498ffc1bcba6affcbf6489b63e635b7bffe704db9209d3bf55daae4e8de15d256400cafb1810e267cc9327576660a6b2ec9
|
data/README.md
CHANGED
@@ -46,24 +46,31 @@ within monitored timeseries.
|
|
46
46
|
## Basic usage via commandline
|
47
47
|
|
48
48
|
Using _spanbars_ on the commandline expects data on STDIN as CSV with timestamps on column 1 and
|
49
|
-
values on column 2. With _--ohlc_ enabled, it expects CSV with "timestamps,open,high,low,close".
|
49
|
+
values on column 2. With _--ohlc_ enabled, it expects CSV with "timestamps,open,high,low,close,volume".
|
50
50
|
|
51
|
-
Provided output will be CSV as well, for
|
51
|
+
Provided output will be CSV as well, using UPPERCASE type for strict bars
|
52
52
|
|
53
|
-
* _simple_: "
|
54
|
-
* _strict_: "
|
53
|
+
* _simple_: "close-time, closeval, vol, type, peaktime or nil, peakval or nil, duration, path, momentum, speed, nil"
|
54
|
+
* _strict_: "close-time, closeval, vol, TYPE, nil, nil, duration, path, momentum, speed, overdrive"
|
55
55
|
|
56
|
-
|
57
|
-
(particularly concerning the time format), and _--intraday_ (ommiting the date part when using _--human_).
|
56
|
+
## List of parameters
|
58
57
|
|
59
|
-
|
60
|
-
$ spanbars --input ./timeseries.csv --span 5
|
58
|
+
Usage: spanbars [options]
|
61
59
|
|
62
|
-
|
60
|
+
--simple Disable processing of strict StanBars
|
61
|
+
--ticksize Set ticksize for processing
|
62
|
+
--span Set span for processing
|
63
|
+
--ohlc Define OHLC input file instead of timeseries (overrides --simple and --both)
|
64
|
+
--human Define human output
|
65
|
+
--intraday Strip date portion (affects --human only)
|
66
|
+
--help Print this help
|
67
|
+
--both Returns both simple and strict bars (overrides simple)
|
68
|
+
--time Location of timestamp in source (defaults to first)
|
69
|
+
--value Location of value in source (defaults to second)
|
70
|
+
--volume Location of volume in source (defaults to third)
|
71
|
+
|
72
|
+
|
73
|
+
NOTE: spanbars relies on STDIN data, e.g.
|
74
|
+
|
75
|
+
$ cat /tmp/timeseries.csv | spanbars --simple --span 5 --ticksize 0.1
|
63
76
|
|
64
|
-
* --span (defaults to 10)
|
65
|
-
* --ticksize (default to 1.0)
|
66
|
-
* --ohlc (defaults to false)
|
67
|
-
* --simple (defaults to false)
|
68
|
-
* --human (planned, defaults to false)
|
69
|
-
* --intraday (planned, defaults to false)
|
data/bin/spanbars
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
|
4
|
-
|
5
|
-
|
4
|
+
THIS_PATH_REL = File.dirname(THIS_FILE)
|
5
|
+
THIS_PATH = File.absolute_path(THIS_PATH_REL)
|
6
|
+
|
7
|
+
require THIS_PATH + '/../lib/spanbarprocessor.rb'
|
8
|
+
require THIS_PATH + '/../lib/spanbar.rb'
|
6
9
|
|
7
10
|
|
8
11
|
# prepare processing of incoming control commands (e.g. Ctrl-C)
|
@@ -25,7 +28,10 @@ op.boolean '--ohlc' , "Define OHLC input file instead of timeseries (overri
|
|
25
28
|
op.boolean '--human' , "Define human output", default: false
|
26
29
|
op.boolean '--intraday' , "Strip date portion (affects --human only)", default: false
|
27
30
|
op.boolean '--help' , "Print this help", default: false
|
28
|
-
op.boolean '--both' , "Returns both simple and strict bars (overrides simple)", default: false
|
31
|
+
op.boolean '--both' , "Returns both simple and strict bars (overrides --simple)", default: false
|
32
|
+
op.integer '--time' , "The column in CSV providing the timestamp, defaults to the first", default: 0
|
33
|
+
op.integer '--value' , "The column in CSV providing the value, defaults to the second", default: 1
|
34
|
+
op.integer '--volume' , "The column in CSV providing the volume, default to the third", default: 2
|
29
35
|
op.separator ""
|
30
36
|
op.separator "#{"Please note:".light_white} spanbars relies on STDIN data, e.g. "
|
31
37
|
op.separator " #{"$".light_white} cat /tmp/timeseries.csv | spanbars --simple --span 5 --ticksize 0.1"
|
@@ -52,7 +58,7 @@ unless STDIN.tty?
|
|
52
58
|
s = SpanBarProcessor.new(opts)
|
53
59
|
while csv = STDIN.gets
|
54
60
|
line = CSV.parse(csv.chomp).flatten
|
55
|
-
result = s.add(line[
|
61
|
+
result = s.add(line[opts[:time]].to_i, line[opts[:value]].to_f, line[opts[:volume]].to_i)
|
56
62
|
if result
|
57
63
|
if opts[:human]
|
58
64
|
result.each {|r| r.set_intraday if opts[:intraday]; puts ([:up, :bottom].include? r.type) ? "#{r.to_human}".green : "#{r.to_human}".red }
|
@@ -74,13 +80,14 @@ unless STDIN.tty?
|
|
74
80
|
lowProc = SpanBarProcessor.new(highOpts)
|
75
81
|
finProc = SpanBarProcessor.new(opts)
|
76
82
|
data.each do |d|
|
77
|
-
highProc.add(d[0].to_i, d[2].to_f)
|
78
|
-
lowProc. add(d[0].to_i, d[3].to_f)
|
83
|
+
highProc.add(d[0].to_i, d[2].to_f, d[5].to_i)
|
84
|
+
lowProc. add(d[0].to_i, d[3].to_f, d[5].to_i)
|
79
85
|
end
|
80
|
-
|
81
|
-
|
86
|
+
highs = highProc.spanBars.map{|bar| bar.type.to_s.downcase == "up" ? bar.highval : nil }
|
87
|
+
lows = lowProc.spanBars.map{|bar| bar.type.to_s.downcase == "down" ? nil : bar.lowval }
|
88
|
+
res = [ highs, lows ].flatten.compact
|
82
89
|
res.sort!{|a,b| a[:t] <=> b[:t]}
|
83
|
-
res.each {|peak| finProc.add peak[:t],peak[:p]}
|
90
|
+
res.each {|peak| finProc.add peak[:t],peak[:p], peak[:v]}
|
84
91
|
if opts[:human]
|
85
92
|
finProc.spanBars.each {|r| r.set_intraday if opts[:intraday]; puts ([:up, :bottom].include? r.type) ? "#{r.to_human}".green : "#{r.to_human}".red }
|
86
93
|
else # CSV output
|
data/bin/spanbars.rb
ADDED
@@ -29,8 +29,6 @@ end
|
|
29
29
|
Then /^([^\s]*) should be set to ([^\s]*)$/ do |var,value|
|
30
30
|
expect(@s.instance_variable_defined?(var.to_sym)).to be_truthy
|
31
31
|
res = nil
|
32
|
-
puts "#{var}---"
|
33
|
-
puts "#{value}---"
|
34
32
|
eval "res = @s.instance_variable_get(var.to_sym) == #{value}"
|
35
33
|
expect(res).to be_truthy
|
36
34
|
end
|
data/lib/spanbar.rb
CHANGED
@@ -43,12 +43,14 @@ class SpanBar
|
|
43
43
|
def decimals(a); num = 0; while (a != a.to_i); num += 1; a *= 10; end; num; end
|
44
44
|
|
45
45
|
@ticksize = ticksize
|
46
|
+
@intraday = false
|
46
47
|
@format = "%1.#{decimals(@ticksize)}f"
|
47
48
|
@strict = strict
|
48
49
|
@openval = a[0]
|
49
50
|
@closeval = a[-1]
|
50
51
|
@highval = a.reverse.max_by{|x| x[:p]}
|
51
52
|
@lowval = a.reverse.min_by{|x| x[:p]}
|
53
|
+
@vol = a.map{|x| x[:v]}.reduce(:+)
|
52
54
|
@open = @openval[:p]
|
53
55
|
@high = @highval[:p]
|
54
56
|
@low = @lowval[:p]
|
@@ -64,6 +66,7 @@ class SpanBar
|
|
64
66
|
else
|
65
67
|
@type = :error
|
66
68
|
end
|
69
|
+
#puts self.inspect
|
67
70
|
raise "Validation error: Type must be :up or :down for #{self.inspect}" if @strict and not [:up,:down].include?(@type)
|
68
71
|
end
|
69
72
|
|
@@ -83,6 +86,12 @@ class SpanBar
|
|
83
86
|
return [ @resources, tmp0 ]
|
84
87
|
end
|
85
88
|
|
89
|
+
# Introduction of @overdrive (means the amount, that the current bar EXCEEDs span) needed this late injection
|
90
|
+
def inject_span(span)
|
91
|
+
@span = span
|
92
|
+
@overdrive = ((@openval[:p] - @closeval[:p]).abs / @ticksize - @span).to_i
|
93
|
+
end
|
94
|
+
|
86
95
|
# For human output, set output
|
87
96
|
def set_intraday
|
88
97
|
@intraday = true
|
@@ -91,16 +100,16 @@ class SpanBar
|
|
91
100
|
# Returns an inspection string
|
92
101
|
def inspect
|
93
102
|
pval = lambda {|val| "#{val[:t]}::#{@format % val[:p]}" }
|
94
|
-
if @strict
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
else
|
103
|
+
#if @strict
|
104
|
+
# return "<#SpanBar:0x00#{self.object_id.to_s(16)}, #{@strict ? "strict" : "simple"}, :#{@type
|
105
|
+
# },\tpath: #{"%g" % self.path}, momentum: #{"%g" % self.momentum
|
106
|
+
# }, open: #{pval.(@openval)}, close: #{pval.(@closeval)}>"
|
107
|
+
#else
|
99
108
|
return "<#SpanBar:0x00#{self.object_id.to_s(16)}, #{@strict ? "strict" : "simple"}, :#{@type
|
100
109
|
},\tpath: #{"%g" % self.path}, momentum: #{"%g" % self.momentum
|
101
110
|
}, open: #{pval.(@openval)}, high: #{pval.(@highval)
|
102
111
|
}, low: #{ pval.(@lowval)}, close: #{pval.(@closeval)}>"
|
103
|
-
end
|
112
|
+
#end
|
104
113
|
end
|
105
114
|
|
106
115
|
# Return human readable output of instance
|
@@ -112,35 +121,54 @@ class SpanBar
|
|
112
121
|
end
|
113
122
|
pval = lambda {|v| "[#{time.(v[:t])}, #{@format % v[:p]}]" }
|
114
123
|
if @strict
|
115
|
-
return "STRICT, OPEN: #{pval.(@openval)}, CLOSE: #{pval.(@closeval)
|
116
|
-
|
117
|
-
},\
|
124
|
+
#return "STRICT, OPEN: #{pval.(@openval)}, CLOSE: #{pval.(@closeval)
|
125
|
+
return "STRICT, #{pval.(@closeval)
|
126
|
+
},\tMOM: #{"%g" % (@momentum / @ticksize) }, \tDUR: #{@duration
|
127
|
+
},\tEFF: #{((@close - @open) / @ticksize).to_i}, OVER: #{@overdrive}, \t:#{@type.to_s.upcase}"
|
118
128
|
else
|
119
129
|
return "SIMPLE, OPEN: #{pval.(@openval)
|
120
130
|
}, #{ ([:up, :bottom].include? @type) ? "LOW: #{pval.(@lowval)}" : "HIGH #{pval.(@highval)}"
|
121
131
|
}, CLOSE: #{pval.(@closeval)}, MOM: #{"%g" % (@momentum / @ticksize)
|
122
|
-
},\tDUR: #{@duration},\tEFF: #{((@close - @open) / @ticksize).to_i}, :#{@type.to_s
|
132
|
+
},\tDUR: #{@duration},\tEFF: #{((@close - @open) / @ticksize).to_i}, :#{@type.to_s}"
|
123
133
|
end
|
124
134
|
end
|
125
135
|
|
126
136
|
# Returns an array containing instance values as needed for CSV output
|
137
|
+
#
|
138
|
+
# Format is
|
139
|
+
# closetime,
|
140
|
+
# closeval,
|
141
|
+
# volume,
|
142
|
+
# type (UPCASE for strict),
|
143
|
+
# high/low-time for top/bottom OR nil,
|
144
|
+
# high/low-val for top/bottom OR nil,
|
145
|
+
# duration in ms
|
146
|
+
# path
|
147
|
+
# momentum
|
148
|
+
# speed
|
149
|
+
# overdrive (if STRICT) or NIL
|
127
150
|
def to_a
|
128
151
|
if @strict
|
129
|
-
return [
|
130
|
-
@
|
131
|
-
@
|
152
|
+
return [
|
153
|
+
@closeval[:t], @closeval[:p].round(8), @vol, # so far it is the same as each other tick !!
|
154
|
+
@type.to_s.upcase.to_sym,nil,nil,
|
132
155
|
@duration, @path.round(8), @momentum.round(8),
|
133
|
-
|
134
|
-
|
156
|
+
((@close - @open) / @ticksize).to_i, @overdrive
|
157
|
+
]
|
135
158
|
else
|
136
|
-
return [
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
@closeval[:t], @closeval[:p].round(8),
|
159
|
+
return [
|
160
|
+
#@@openval[:t], @openval[ :p].round(8),
|
161
|
+
#@highval[:t], @highval[ :p].round(8),
|
162
|
+
#@lowval[:t], @lowval[ :p].round(8),
|
163
|
+
@closeval[:t], @closeval[:p].round(8), @vol, # so far it is the same as each other tick
|
164
|
+
@type,
|
165
|
+
[:top,:bottom].include?(@type.to_sym) ?
|
166
|
+
( @type.to_sym == :top ? @highval[:t] : @lowval[:t] ) : nil,
|
167
|
+
[:top,:bottom].include?(@type.to_sym) ?
|
168
|
+
( @type.to_sym == :top ? @highval[:p] : @lowval[:p] ) : nil,
|
141
169
|
@duration, @path.round(8), @momentum.round(8),
|
142
|
-
((@close - @open) / @ticksize).to_i,
|
143
|
-
|
170
|
+
((@close - @open) / @ticksize).to_i, nil
|
171
|
+
]
|
144
172
|
end
|
145
173
|
end
|
146
174
|
end
|
data/lib/spanbarprocessor.rb
CHANGED
@@ -11,7 +11,7 @@ module SpanBarHelpers
|
|
11
11
|
# @param a [Float]
|
12
12
|
# @param t [Integer]
|
13
13
|
def tickup(a,t=1)
|
14
|
-
{ p: a[:p] + 0.000000001, t: a[:t] + t }
|
14
|
+
{ p: a[:p] + 0.000000001, t: a[:t] + t, v: 0 }
|
15
15
|
end
|
16
16
|
|
17
17
|
# creates a new upfollowing tick that has a minimum lower value than the preceeding one
|
@@ -19,7 +19,7 @@ module SpanBarHelpers
|
|
19
19
|
# @param a [Float]
|
20
20
|
# @param t [Integer]
|
21
21
|
def tickdown(a,t=1)
|
22
|
-
{ p: a[:p] - 0.000000001, t: a[:t] + t }
|
22
|
+
{ p: a[:p] - 0.000000001, t: a[:t] + t, v: 0 }
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -71,17 +71,18 @@ class SpanBarProcessor
|
|
71
71
|
@simpleBars = []
|
72
72
|
@spanBars = []
|
73
73
|
@ticks = []
|
74
|
+
@intraday = false
|
74
75
|
end
|
75
76
|
|
76
77
|
# Sends a new items of the timeseries
|
77
78
|
#
|
78
79
|
# @option t [Integer] The timestamp (preferrably in JS format, i.e. Milliseconds since 1970-01-01)
|
79
80
|
# @option p [Float] The value
|
80
|
-
def add(t, p)
|
81
|
+
def add(t, p, v = 0)
|
81
82
|
raise ArgumentError, "SpanBar#add requires either an Integer (Timestamp) or a Time object as first argument" unless [Integer, Time].include?(t.class)
|
82
83
|
raise ArgumentError, "SpanBar#add requires either a Numeric or NilClass as second argument" unless p.is_a? Numeric or p.nil?
|
83
84
|
return nil if p.nil?
|
84
|
-
tick = {t: (t.class == Integer ? t : t.to_i), p: p.to_f}
|
85
|
+
tick = {t: (t.class == Integer ? t : t.to_i), p: p.to_f, v: v.to_i}
|
85
86
|
@simpleBar << tick
|
86
87
|
@simpleMax = [tick[:p],@simpleMax].max
|
87
88
|
@simpleMin = [tick[:p],@simpleMin].min
|
@@ -89,6 +90,7 @@ class SpanBarProcessor
|
|
89
90
|
simple = SpanBar.new(@simpleBar, @ts, false)
|
90
91
|
unless @simple
|
91
92
|
result = self.create_strict_from(simple)
|
93
|
+
result.map{|x| x.inject_span(@span)} if result
|
92
94
|
end
|
93
95
|
@simpleBars << simple
|
94
96
|
@simpleMax, @simpleMin = 0, Float::INFINITY
|
@@ -124,7 +126,7 @@ class SpanBarProcessor
|
|
124
126
|
tmp0, tmp1 = elem1.split_for :high
|
125
127
|
@currentBar = SpanBar.new([tmp0.last, tmp1], @ts)
|
126
128
|
when *[:up,:down]
|
127
|
-
@currentBar = elem1
|
129
|
+
@currentBar = SpanBar.new(elem1.resources, @ts)
|
128
130
|
else
|
129
131
|
raise "Invalid type for initial simple SpanBar #{elem0}"
|
130
132
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spanbars
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0beta
|
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: 2019-04-
|
11
|
+
date: 2019-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: slop
|
@@ -98,11 +98,13 @@ description: 'Tiny tool to process input CSV data as timeseries to span bars '
|
|
98
98
|
email: donkeybridge@jtown.eu
|
99
99
|
executables:
|
100
100
|
- spanbars
|
101
|
+
- spanbars.rb
|
101
102
|
extensions: []
|
102
103
|
extra_rdoc_files: []
|
103
104
|
files:
|
104
105
|
- README.md
|
105
106
|
- bin/spanbars
|
107
|
+
- bin/spanbars.rb
|
106
108
|
- features/01_spanbarprocessor_initialization.feature
|
107
109
|
- features/02_spanbarprocessor_add.feature
|
108
110
|
- features/03_spanbar_initialization.feature
|