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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7698dcae6b52fb67ba0636e83ed8c818bc517ed0ccce2559c4ae61e9c37bad62
4
- data.tar.gz: c7472948c2e2f5809e910548677f016b510eeadd711414bae016c027e04b46a9
3
+ metadata.gz: 77c49955cc21152aa8b11ce9f3b34eb106e5676d26613f9734f68c155fdb6df1
4
+ data.tar.gz: 2c563526e765155d3a25424f832ad966034b86ecab8bc4f2c678611f1a187896
5
5
  SHA512:
6
- metadata.gz: cda1887acff3b220c43e0667202ccf6eaa83c1c9ccfa80b6610056e1eea1a786a83baa409f34b146b165a60a769c1b11caed87037a879725a7b42a9964ff6c0f
7
- data.tar.gz: c9a98b5a5bf355ee6cec2c9602309d05da34f9293189c0fdfd9a4d1537de96eda539b116d5ec6438a62a3c72c8df4f987fedcc953e1c760272bd9559996d9a13
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_: "timestamp\_open,open, timestamp\_high,high, timestamp\_low, low, timestamp\_close, close, direction, path, momentum, direction"
54
- * _strict_: "timestamp\_open,open, timestamp\_close, close, duration, path, momentum, effective\_span, direction"
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
- I currently plan to implement 2 more parameters: _--human_ to create a human-readable table
57
- (particularly concerning the time format), and _--intraday_ (ommiting the date part when using _--human_).
56
+ ## List of parameters
58
57
 
59
- $ cat timeseries.csv | spanbars
60
- $ spanbars --input ./timeseries.csv --span 5
58
+ Usage: spanbars [options]
61
59
 
62
- ## List of parameters
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
- require File.dirname(THIS_FILE) + '/../lib/spanbarprocessor.rb'
5
- require File.dirname(THIS_FILE) + '/../lib/spanbar.rb'
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[0].to_i, line[1].to_f)
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
- res = highProc.spanBars.map{|bar| bar.type == :up ? bar.highval : bar.lowval } #.map{|peak| bar.highval }
81
- res.flatten!
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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
4
+ require File.dirname(THIS_FILE) + '/../lib/spanbarprocessor.rb'
5
+ require File.dirname(THIS_FILE) + '/../lib/spanbar.rb'
@@ -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
- return "<#SpanBar:0x00#{self.object_id.to_s(16)}, #{@strict ? "strict" : "simple"}, :#{@type
96
- },\tpath: #{"%g" % self.path}, momentum: #{"%g" % self.momentum
97
- }, open: #{pval.(@openval)}, close: #{pval.(@closeval)}>"
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
- }, MOM: #{"%g" % (@momentum / @ticksize) },\tDUR: #{@duration
117
- },\tEFF: #{((@close - @open) / @ticksize).to_i}, :#{@type.to_s.upcase}"
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.upcase}"
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 [ "strict",
130
- @openval[:t], @openval[ :p].round(8),
131
- @closeval[:t], @closeval[:p].round(8),
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
- ((@close - @open) / @ticksize).to_i,
134
- @type ]
156
+ ((@close - @open) / @ticksize).to_i, @overdrive
157
+ ]
135
158
  else
136
- return [ "simple",
137
- @openval[:t], @openval[ :p].round(8),
138
- @highval[:t], @highval[ :p].round(8),
139
- @lowval[:t], @lowval[ :p].round(8),
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
- @type ]
170
+ ((@close - @open) / @ticksize).to_i, nil
171
+ ]
144
172
  end
145
173
  end
146
174
  end
@@ -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.1.2beta
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-15 00:00:00.000000000 Z
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