spanbars 0.1.2beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7698dcae6b52fb67ba0636e83ed8c818bc517ed0ccce2559c4ae61e9c37bad62
4
+ data.tar.gz: c7472948c2e2f5809e910548677f016b510eeadd711414bae016c027e04b46a9
5
+ SHA512:
6
+ metadata.gz: cda1887acff3b220c43e0667202ccf6eaa83c1c9ccfa80b6610056e1eea1a786a83baa409f34b146b165a60a769c1b11caed87037a879725a7b42a9964ff6c0f
7
+ data.tar.gz: c9a98b5a5bf355ee6cec2c9602309d05da34f9293189c0fdfd9a4d1537de96eda539b116d5ec6438a62a3c72c8df4f987fedcc953e1c760272bd9559996d9a13
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # SpanBars
2
+
3
+ SpanBars is a tiny tool to produce span bars from time series data, either directly from a time
4
+ series or based on OHLC bars.
5
+
6
+ ## Description
7
+
8
+ SpanBars reads record-by-record (or line-by-line) and provides the according span bars.
9
+ The current bar is closed and a new bar is created as soon as the given span is _exceeded_.
10
+
11
+ Spanbars are comparable to classic OHLC bars or candle sticks, but have the major
12
+ advantage of a CLOSE value always equalling either HIGH or LOW. All data eliminated
13
+ can be considered as _noise_.
14
+
15
+ Therefore exists 4 types of bars:
16
+
17
+ * TOP: CLOSE == HIGH
18
+ * UP: CLOSE == HIGH and OPEN == LOW
19
+ * BOTTOM: CLOSE == LOW
20
+ * DOWN: CLOSE == LOW and OPEN == HIGH
21
+
22
+ When generating spanbars based on a plain time series, these bars can be sent to output using
23
+ the parameter _--simple_. Without it will
24
+ process the resulting data again, aggregating all bars created in the first run to start
25
+ at an absolute HIGH (or LOW) and end at an absolute LOW (or HIGH resp.). Although these
26
+ spans of second type might be much larger than _--span_, they can be considered to
27
+ contain no noise _smaller or equal than_ span.
28
+
29
+ Creating spanbars from given OHLCs is slightly inaccurate, as even one OHLC might
30
+ contain several spanbars, but that cannot be reflected by the given input data. Also, for
31
+ processing OHLC-input data, note that the entire algorithm (simple spanbars first, strict spanbars seconds) is run 3 times:
32
+ 1. using (span / 2) on input HIGHS
33
+ 2. using (span / 2) on input LOWS
34
+ 1. using (span) on [ resulting Highs, resulting Lows ].sorted\_by\_time
35
+
36
+ The application area this gem is written for is denoising data for trend recognition
37
+ within monitored timeseries.
38
+
39
+ ## Basic usage
40
+ processor = SpanBarProcessor.new(span: 5)
41
+ File.read("./timeseries.csv").map{|x| { t: x[0], v: x[1] } }.sort_by{|x| x[:t]}.each do |data|
42
+ processor.add(data)
43
+ end
44
+ processor.bars.each {|x| puts x}
45
+
46
+ ## Basic usage via commandline
47
+
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".
50
+
51
+ Provided output will be CSV as well, for
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"
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_).
58
+
59
+ $ cat timeseries.csv | spanbars
60
+ $ spanbars --input ./timeseries.csv --span 5
61
+
62
+ ## List of parameters
63
+
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 ADDED
@@ -0,0 +1,97 @@
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'
6
+
7
+
8
+ # prepare processing of incoming control commands (e.g. Ctrl-C)
9
+ Signal.trap("TERM") { local_interrupt }
10
+ Signal.trap("INT") { local_interrupt }
11
+
12
+ def local_interrupt
13
+ exit
14
+ end
15
+
16
+ # prepare command line parser and help text
17
+
18
+ op = Slop::Options.new
19
+ op.banner = "\n#{"Usage:".light_white} spanbars [options]"
20
+ op.separator ""
21
+ op.bool '--simple' , "Disable processing of strict StanBars", default: false
22
+ op.float '--ticksize' , "Set ticksize for processing", default: 1.0
23
+ op.integer '--span' , "Set span for processing", default: 10
24
+ op.boolean '--ohlc' , "Define OHLC input file instead of timeseries (overrides --simple and --both)", default: false
25
+ op.boolean '--human' , "Define human output", default: false
26
+ op.boolean '--intraday' , "Strip date portion (affects --human only)", default: false
27
+ op.boolean '--help' , "Print this help", default: false
28
+ op.boolean '--both' , "Returns both simple and strict bars (overrides simple)", default: false
29
+ op.separator ""
30
+ op.separator "#{"Please note:".light_white} spanbars relies on STDIN data, e.g. "
31
+ op.separator " #{"$".light_white} cat /tmp/timeseries.csv | spanbars --simple --span 5 --ticksize 0.1"
32
+ op.separator ""
33
+
34
+ optparser = Slop::Parser.new(op)
35
+
36
+ # print help upon unparsable commandline options
37
+ begin
38
+ opts = optparser.parse(ARGV)
39
+ rescue StandardError => e
40
+ puts e.inspect
41
+ puts op.to_s
42
+ exit
43
+ end
44
+
45
+ if opts.help?
46
+ puts op.to_s
47
+ exit
48
+ end
49
+
50
+ unless STDIN.tty?
51
+ unless opts.ohlc?
52
+ s = SpanBarProcessor.new(opts)
53
+ while csv = STDIN.gets
54
+ line = CSV.parse(csv.chomp).flatten
55
+ result = s.add(line[0].to_i, line[1].to_f)
56
+ if result
57
+ if opts[:human]
58
+ result.each {|r| r.set_intraday if opts[:intraday]; puts ([:up, :bottom].include? r.type) ? "#{r.to_human}".green : "#{r.to_human}".red }
59
+ else # CSV output
60
+ result.each {|r| CSV {|out| out << r.to_a } }
61
+ end
62
+ end
63
+ end
64
+ exit
65
+ else # if ohlc is given
66
+ data = [ ]
67
+ data << CSV.parse(csv.chomp).flatten while csv = STDIN.gets
68
+ opts = opts.to_hash
69
+ opts.delete(:simple)
70
+ opts.delete(:both)
71
+ highOpts = opts.dup
72
+ highOpts[:span] = (opts[:span] / 2.0).floor.to_i
73
+ highProc = SpanBarProcessor.new(highOpts)
74
+ lowProc = SpanBarProcessor.new(highOpts)
75
+ finProc = SpanBarProcessor.new(opts)
76
+ 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)
79
+ end
80
+ res = highProc.spanBars.map{|bar| bar.type == :up ? bar.highval : bar.lowval } #.map{|peak| bar.highval }
81
+ res.flatten!
82
+ res.sort!{|a,b| a[:t] <=> b[:t]}
83
+ res.each {|peak| finProc.add peak[:t],peak[:p]}
84
+ if opts[:human]
85
+ 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
+ else # CSV output
87
+ finProc.spanBars.each {|r| CSV {|out| out << r.to_a } }
88
+ end
89
+ end
90
+ else
91
+ puts op.to_s
92
+ end
93
+
94
+
95
+
96
+
97
+
@@ -0,0 +1,29 @@
1
+ Feature: Initializiation of SpanBarProcessor
2
+ Scenario: Testing provided arguments during initialization
3
+ * a new SpanBarProcessor created with span that is not Integer or <= 1 should raise ArgumentError
4
+ * a new SpanBarProcessor created with a valid span but non-Numeric ticksize or <= 0 should raise ArgumentError
5
+
6
+ Scenario Outline: Testing well initialized instance_variables
7
+ Given a SpanBarProcessor is initialized with "3", "3"
8
+ Then <instance_var> should be set to <value>
9
+ Examples:
10
+ | instance_var | value |
11
+ | @ticks | [] |
12
+ | @limit | 9 |
13
+ | @simpleMax | 0 |
14
+ | @simpleMin | Float::INFINITY |
15
+ | @simpleBar | [] |
16
+ | @simpleBars | [] |
17
+ | @intraday | false |
18
+ | @both | false |
19
+ | @simple | false |
20
+
21
+ #Scenario Outline: Testing methods and attr
22
+ #Given a SpanBarProcessor is initialized with "4", "3"
23
+ #Then it should respond to "<method>"
24
+ #Examples:
25
+ # | method |
26
+ # | add |
27
+ # | simpleBars |
28
+ # | create_strict_from |
29
+ #
@@ -0,0 +1,40 @@
1
+ Feature: Using #add
2
+
3
+ Scenario: Falsey input
4
+ Given a simple SpanBarProcessor is initialized with "3", "3"
5
+ Then it should raise ArgumentError when calling #add with less than 2 arguments
6
+ Then it should raise ArgumentError when calling #add and first argument is neither Integer nor Time
7
+ Then it should raise ArgumentError when calling #add and second argument is neither Numeric nor Nil
8
+
9
+ Scenario: Correct return values
10
+ Given a simple SpanBarProcessor is initialized with "3", "3"
11
+ Then calling #add with params 123 and nil should return nil
12
+ Then calling #add with params 123 and 2.5 should return false
13
+
14
+ Scenario: eesting a linear raising series
15
+ Given a simple SpanBarProcessor is initialized with "3", "1"
16
+ Then #add with params 1, 1 should return false
17
+ Then #add with params 2, 2 should return false
18
+ Then #add with params 3, 3 should return false
19
+ Then #add with params 4, 4 should return false
20
+ Then #add with params 5, 5 should return an Array
21
+
22
+ Scenario Outline: Running with reference data
23
+ Given a SpanBarProcessor is initialized with "<deviation>", "<ticksize>"
24
+ And a feeder with data from "<file>" is prepared
25
+ Then adding all data from file should not raise any error
26
+
27
+ Examples:
28
+ | file | deviation | ticksize |
29
+ | ./features/support/ref1.csv | 5 | 0.25 |
30
+ | ./features/support/ref1.csv | 8 | 0.1 |
31
+ | ./features/support/ref1.csv | 12 | 0.66 |
32
+ | ./features/support/ref2.csv | 5 | 1 |
33
+ | ./features/support/ref2.csv | 8 | 2 |
34
+ | ./features/support/ref2.csv | 12 | 5.5 |
35
+ | ./features/support/ref3.csv | 5 | 0.0000005 |
36
+ | ./features/support/ref3.csv | 8 | 0.000001 |
37
+ | ./features/support/ref3.csv | 12 | 0.00000025 |
38
+
39
+
40
+
@@ -0,0 +1,30 @@
1
+ Feature: Initializiation of SpanBar
2
+ Scenario: Testing provided arguments during initialization
3
+ * a new simple SpanBar created without options should raise ArgumentError
4
+ * a new simple SpanBar created by SpanBarProcessor should not raise
5
+
6
+ Scenario Outline: Testing well initialized instance_variables
7
+ Given a valid simple SpanBar is created by SpanBarProcessor
8
+ Then <instance_var> should be set to <value>
9
+ Examples:
10
+ | instance_var | value |
11
+ | @strict | false |
12
+ | @type | :up |
13
+ | @open | 1 |
14
+ | @momentum | Float::INFINITY |
15
+
16
+
17
+
18
+ Scenario Outline: Testing methods and attr of SpanBar
19
+ Given a valid simple SpanBar is created by SpanBarProcessor
20
+ Then it should respond to "<method>"
21
+ Examples:
22
+ | method |
23
+ | inspect |
24
+ | to_human |
25
+ | to_a |
26
+ | valid? |
27
+ | path |
28
+ | momentum |
29
+ | split_for|
30
+
@@ -0,0 +1,19 @@
1
+ Feature: Using spanbars on commandline
2
+ Scenario: When using bin/spanbars on the commandline without parameters
3
+ Given bin/spanbars is run on the commandline and neither parameters nor STDIN is given
4
+ #Then bin/spanbars should display help
5
+
6
+ Scenario Outline: Checking known input to produce known output
7
+ Given bin/spanbars is run with following parameters it should ouput
8
+ Then cat <input> | spanbars --both --ticksize <ticksize> --span <span> should produce <output>
9
+ Examples:
10
+ | input | ticksize | span | output |
11
+ | ref1 | 0.25 | 25 | ref1_out_0.25_25 |
12
+ | ref2 | 1 | 25 | ref2_out_1_25 |
13
+ | ref3 | 0.0000005 | 10 | ref3_out_0.0000005_10 |
14
+
15
+ Scenario: Checking whether false reference is recognized
16
+ Given bin/spanbars is run with following parameters it should ouput
17
+ Then 'cat ref3 | bin/spanbars --both --span 10 --ticksize 0.0000005' should not produce ref2_out_1_25
18
+
19
+
@@ -0,0 +1,36 @@
1
+ require './lib/spanbar.rb'
2
+ require './lib/spanbarprocessor.rb'
3
+
4
+ Given "a new SpanBarProcessor created with span that is not Integer or <= 1 should raise ArgumentError" do
5
+ expect{ SpanBarProcessor.new(span: "t") }.to raise_error(ArgumentError)
6
+ expect{ SpanBarProcessor.new(span: 1.6) }.to raise_error(ArgumentError)
7
+ expect{ SpanBarProcessor.new(span: 1) }.to raise_error(ArgumentError)
8
+ end
9
+
10
+ Given "a new SpanBarProcessor created with a valid span but non-Numeric ticksize or <= 0 should raise ArgumentError" do
11
+ expect{ SpanBarProcessor.new(span: 2, ticksize: "foo")}.to raise_error(ArgumentError)
12
+ expect{ SpanBarProcessor.new(span: 2, ticksize: 0)}.to raise_error(ArgumentError)
13
+ expect{ SpanBarProcessor.new(span: 2, ticksize: -1.2)}.to raise_error(ArgumentError)
14
+ end
15
+
16
+ Given /^a SpanBarProcessor is initialized with "([^"]*)", "([^"]*)"$/ do |span, ticksize|
17
+ @s = SpanBarProcessor.new(span: span.to_i, ticksize: ticksize.to_f)
18
+ end
19
+
20
+ Given /^a simple SpanBarProcessor is initialized with "([^"]*)", "([^"]*)"$/ do |span, ticksize|
21
+ @s = SpanBarProcessor.new(span: span.to_i, ticksize: ticksize.to_f, simple: true)
22
+ end
23
+
24
+
25
+ Then /^it should respond to "([^"]*)"$/ do |method|
26
+ expect(@s).to respond_to(method.to_sym)
27
+ end
28
+
29
+ Then /^([^\s]*) should be set to ([^\s]*)$/ do |var,value|
30
+ expect(@s.instance_variable_defined?(var.to_sym)).to be_truthy
31
+ res = nil
32
+ puts "#{var}---"
33
+ puts "#{value}---"
34
+ eval "res = @s.instance_variable_get(var.to_sym) == #{value}"
35
+ expect(res).to be_truthy
36
+ end
@@ -0,0 +1,42 @@
1
+ Then "it should raise ArgumentError when calling #add with less than 2 arguments" do
2
+ expect{ @s.add }.to raise_error(ArgumentError)
3
+ expect{ @s.add(1)}.to raise_error(ArgumentError)
4
+ end
5
+
6
+ Then "it should raise ArgumentError when calling #add and first argument is neither Integer nor Time" do
7
+ expect{ @s.add("foo", "bar") }.to raise_error(ArgumentError)
8
+ expect{ @s.add(124, 1.22) }.not_to raise_error
9
+ expect{ @s.add(Time.now, 1.22) }.not_to raise_error
10
+ end
11
+
12
+ Then "it should raise ArgumentError when calling #add and second argument is neither Numeric nor Nil" do
13
+ expect{ @s.add(123, "foo") }.to raise_error(ArgumentError)
14
+ expect{ @s.add(123, 11234) }.not_to raise_error
15
+ expect{ @s.add(123, nil ) }.not_to raise_error
16
+ end
17
+
18
+ Then /^calling #add with params (\d+) and nil should return nil$/ do |timestamp|
19
+ expect(@s.add(timestamp, nil)).to be_nil
20
+ end
21
+
22
+ Then /^calling #add with params (\d+) and ([0-9.]+) should return false$/ do |timestamp, value|
23
+ expect(@s.add(timestamp, value.to_f)).to be(false)
24
+ end
25
+
26
+ Then /^#add with params (\d+), ([0-9.]+) should return false$/ do |timestamp, value|
27
+ expect(@s.add(timestamp, value.to_f)).to be(false)
28
+ end
29
+
30
+ Then /^#add with params (\d+), ([0-9.]+) should return an Array$/ do |timestamp, value|
31
+ result = @s.add(timestamp, value.to_f)
32
+ expect(result.class).to be(Array)
33
+ end
34
+
35
+ Given /^a feeder with data from "([^"]+)" is prepared$/ do |filename|
36
+ require 'csv'
37
+ @data = CSV.read(filename).map{|x| [ x[0].to_i, x[1].to_f] }
38
+ end
39
+
40
+ Then "adding all data from file should not raise any error" do
41
+ expect { @data.each {|d| @s.add(d[0],d[1]) } } .not_to raise_error
42
+ end
@@ -0,0 +1,18 @@
1
+ Then "a new simple SpanBar created without options should raise ArgumentError" do
2
+ expect { SpanBar.new }.to raise_error(ArgumentError)
3
+ end
4
+
5
+ Then "a new simple SpanBar created by SpanBarProcessor should not raise" do
6
+ @p = SpanBarProcessor.new(simple: true)
7
+ expect { for i in (1..5); @p.add(i,i); end }.not_to raise_error
8
+ end
9
+
10
+ Given "a valid simple SpanBar is created by SpanBarProcessor" do
11
+ @p = SpanBarProcessor.new(simple: true); @s = true; i = 1;
12
+ while (not @s.is_a?(Array)) do @s = @p.add(i,i); i+=1; end
13
+ @s = @s[0]
14
+ end
15
+
16
+
17
+
18
+
@@ -0,0 +1,26 @@
1
+ Given /^bin\/spanbars is run on the commandline and neither parameters nor STDIN is given$/ do
2
+ end
3
+
4
+ Then /^bin\/spanbars should display help$/ do
5
+ expect{ system("bin/spanbars --help") }.to output(/usage/).to_stdout_from_any_process
6
+ end
7
+
8
+ Given /^bin\/spanbars is run with following parameters it should ouput$/ do
9
+ end
10
+
11
+ Then /^cat ([^ ]*) \| spanbars --both --ticksize ([^ ]*) --span ([^ ]*) should produce ([^ ]*)$/ do |input, ticksize, span, output|
12
+ inputfile = "features/support/#{input}.csv"
13
+ outputfile = "features/support/#{output}.csv"
14
+ result = `cat #{inputfile} | bin/spanbars --both --span #{span} --ticksize #{ticksize}`
15
+ reference = File.read(outputfile)
16
+ expect(result).to eq(reference)
17
+ end
18
+
19
+ Then /^'cat ([^ ]*) \| bin\/spanbars --both --span ([^ ]*) --ticksize ([^' ]*)' should not produce ([^"$ ]*)$/ do |input, span, ticksize, output|
20
+ inputfile = "features/support/#{input}.csv"
21
+ outputfile = "features/support/#{output}.csv"
22
+ result = `cat #{inputfile} | bin/spanbars --both --span #{span} --ticksize #{ticksize}`
23
+ reference = File.read(outputfile)
24
+ expect(result).not_to eq(reference)
25
+ end
26
+
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+ RSpec.configure do |config|
3
+ config.expect_with :rspec do |c|
4
+ c.syntax = :expect
5
+ end
6
+ end