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 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