spanbars 0.1.2beta → 0.2.0beta
Sign up to get free protection for your applications and to get access to all the features.
- 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
|