rtcbx 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ce01db70ca69c8e9533e06c5c2d221e9ce8b6ea4
4
+ data.tar.gz: 32e30472d2c289cb46dbeeed4f37b5779fb52b39
5
+ SHA512:
6
+ metadata.gz: 7b0261d1e4888042a3297e8b53216b51a733345b37b01ff47c795d23ec97e82b48f323b9550a9bbc10f39dcc004911e39a976766c8df48ff6a656e1fba011cea
7
+ data.tar.gz: c84d05b67756afb44a56b5cf69a3b32dd13a0de5701116b6bcbd313e5493106d35474a0cd7e952db4788bfba744b4db8fdb1f4b1408102f83540aae00575eac1
@@ -0,0 +1,19 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+ *config.yml
15
+ *config.yaml
16
+ *config.rb
17
+ *.gem
18
+ bin
19
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in orderbook.gemspec
4
+ gemspec
5
+ gem 'coinbase-exchange', git: 'https://github.com/coinbase/coinbase-exchange-ruby.git', branch: 'master'
6
+ gem 'json'
7
+ gem 'eventmachine'
8
+
9
+ group :test, :development do
10
+ gem 'pry'
11
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Michael Rodrigues
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,143 @@
1
+ # RTCBX
2
+
3
+ RTCBX uses the Coinbase (now GDAX) Exchange websocket feed to provide immediate access to
4
+ the current state of the exchange without repeatedly polling parts of the
5
+ RESTful API. It can:
6
+ * Keep a synchronized copy of the entire orderbook - `RTCBX::Orderbook`
7
+ * Calculate historic rates (candles) by the minute - `RTCBX::Candles`
8
+ * Place and track orders for an account - `RTCBX::Trader`
9
+
10
+ Each type of RTCBX object will supports defining callbacks to run when:
11
+ * The `Orderbook` changes.
12
+ * A new candle is generated.
13
+ * Your order(s) change status.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'rtcbx'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install rtcbx
30
+
31
+ ## Usage
32
+
33
+ ```ruby
34
+ require 'rtcbx'
35
+ ```
36
+ RTCBX objects share a common interface:
37
+ ```ruby
38
+ #
39
+ # :product_id
40
+ # sets the currency (defaults to 'BTC-USD)
41
+ #
42
+ # :start
43
+ # run #start! at creation? (defaults to true)
44
+ #
45
+
46
+ rtcbx = RTCBX.new({product_id: 'BTC-GPB', start: false}) do |change|
47
+ # check some values, do some stuff
48
+ end
49
+
50
+ rtcbx.start! # Starts the websocket feed and tracking/update threads.
51
+
52
+ rtcbx.stop! # Stops the websocket feed and any tracking/update threads.
53
+
54
+ rtcbx.reset! # Calls #stop! then calls #start!.
55
+
56
+ ```
57
+
58
+
59
+
60
+
61
+ * Create a live updating Orderbook:
62
+ ```ruby
63
+ ob = RTCBX::Orderbook.new
64
+ ```
65
+
66
+ * Create an Orderbook object but don't fetch an orderbook or start live
67
+ updating.
68
+ ```ruby
69
+ ob = RTCBX::Orderbook.new(start: false)
70
+
71
+ # When you want it to go live:
72
+
73
+ ob.start!
74
+
75
+ # When you want to stop it:
76
+
77
+ ob.stop!
78
+
79
+ # Reset the orderbook by fetching a fresh orderbook snapshot. This just calls
80
+ # `stop!` and then `start!` again:
81
+
82
+ ob.reset!
83
+ ```
84
+
85
+ * Get the "BTC-GBP" orderbook instead of "BTC-USD":
86
+ ```ruby
87
+ ob = RTCBX::Orderbook.new(product_id: "BTC-GBP")
88
+ ```
89
+
90
+ * Get the "BTC-GBP" orderbook instead of "BTC-USD":
91
+ ```ruby
92
+ ob = RTCBX::Orderbook.new(product_id: "BTC-GBP")
93
+ ```
94
+
95
+ * Create a live Orderbook with a callback to fire on each message:
96
+ ```ruby
97
+ ob = RTCBX::Orderbook.new do |message|
98
+ if message.fetch 'type' == 'match'
99
+ puts ob.spread.to_f('s')
100
+ end
101
+ end
102
+ ```
103
+
104
+ * Create or reset the message callback:
105
+ ```ruby
106
+ ob.on_message do |message|
107
+ puts ob.count
108
+ end
109
+ ```
110
+
111
+ * List current bids:
112
+ ```ruby
113
+ ob.bids
114
+ ```
115
+
116
+ * List current asks:
117
+ ```ruby
118
+ ob.asks
119
+ ```
120
+
121
+ * Show sequence number for initial level 3 snapshot:
122
+ ```ruby
123
+ ob.snapshot_sequence
124
+ ```
125
+
126
+ * Show sequence number for the last message received
127
+ ```ruby
128
+ ob.last_sequence
129
+ ```
130
+
131
+ * Show the last Time a pong was received after a ping (ensures the connection is
132
+ still alive):
133
+ ```ruby
134
+ ob.last_pong
135
+ ```
136
+
137
+ ## Contributing
138
+
139
+ 1. Fork it ( https://github.com/mikerodrigues/orderbook/fork )
140
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
141
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
142
+ 4. Push to the branch (`git push origin my-new-feature`)
143
+ 5. Create a new Pull Request
@@ -0,0 +1,36 @@
1
+ require 'rake/testtask'
2
+ require 'bundler'
3
+ require_relative './lib/rtcbx/version.rb'
4
+
5
+ task :build do
6
+ begin
7
+ puts 'building gem...'
8
+ `gem build rtcbx.gemspec`
9
+ rescue
10
+ puts 'build failed.'
11
+ end
12
+ end
13
+
14
+ task :install do
15
+ begin
16
+ puts 'installing gem...'
17
+ `gem install --local rtcbx`
18
+ rescue
19
+ puts 'install failed.'
20
+ end
21
+ end
22
+
23
+ task :console do
24
+ require 'rubygems'
25
+ require 'pry'
26
+ ARGV.clear
27
+ PRY.start
28
+ end
29
+
30
+ task default: %w(build install)
31
+
32
+ Rake::TestTask.new do |t|
33
+ t.libs << 'test'
34
+ t.test_files = FileList['test/tc*.rb']
35
+ t.verbose = true
36
+ end
@@ -0,0 +1,95 @@
1
+ require 'coinbase/exchange'
2
+ require 'rtcbx/orderbook'
3
+ require 'rtcbx/candles'
4
+ require 'rtcbx/trader'
5
+ require 'rtcbx/version'
6
+ require 'eventmachine'
7
+
8
+ class RTCBX
9
+ # seconds in between pinging the connection.
10
+ #
11
+ PING_INTERVAL = 15
12
+
13
+ attr_reader :product_id
14
+ attr_reader :start
15
+ attr_reader :api_key
16
+ attr_reader :api_secret
17
+ attr_reader :api_passphrase
18
+
19
+ attr_reader :message_callbacks
20
+ attr_reader :websocket
21
+ attr_reader :client
22
+ attr_reader :queue
23
+ attr_reader :last_pong
24
+ attr_reader :websocket_thread
25
+
26
+ def initialize(options = {}, &block)
27
+ @product_id = options.fetch(:product_id, 'BTC-USD')
28
+ @start = options.fetch(:start, true)
29
+ @api_key = options.fetch(:api_key, '')
30
+ @api_secret = options.fetch(:api_secret, '')
31
+ @api_passphrase = options.fetch(:api_passphrase, '')
32
+ @message_callbacks = []
33
+ @message_callbacks << block
34
+ @client = Coinbase::Exchange::Client.new(
35
+ api_key,
36
+ api_secret,
37
+ api_passphrase,
38
+ product_id: product_id
39
+ )
40
+ @websocket = Coinbase::Exchange::Websocket.new(
41
+ keepalive: true,
42
+ product_id: product_id
43
+ )
44
+ @queue = Queue.new
45
+ start! if start
46
+ end
47
+
48
+ def start!
49
+ start_websocket_thread
50
+ end
51
+
52
+ def stop!
53
+ websocket_thread.kill
54
+ websocket.stop!
55
+ end
56
+
57
+ def reset!
58
+ stop!
59
+ start!
60
+ end
61
+
62
+ private
63
+
64
+ def setup_websocket_callback
65
+ websocket.message do |message|
66
+ queue.push(message)
67
+ message_callbacks.each { |b| b.call(message) unless b.nil? }
68
+ end
69
+ end
70
+
71
+ def start_websocket_thread
72
+ @websocket_thread = Thread.new do
73
+ setup_websocket_callback
74
+ EM.run do
75
+ websocket.start!
76
+ setup_ping_timer
77
+ setup_error_handler
78
+ end
79
+ end
80
+ end
81
+
82
+ def setup_ping_timer
83
+ EM.add_periodic_timer(PING_INTERVAL) do
84
+ websocket.ping do
85
+ last_pong = Time.now
86
+ end
87
+ end
88
+ end
89
+
90
+ def setup_error_handler
91
+ EM.error_handler do |e|
92
+ print "Websocket Error: #{e.message} - #{e.backtrace.join("\n")}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,91 @@
1
+ require 'rtcbx/candles/candle'
2
+
3
+ class RTCBX
4
+ class Candles < RTCBX
5
+
6
+ attr_reader :buckets
7
+ attr_reader :history_queue
8
+ attr_reader :update_thread
9
+ attr_reader :bucket_thread
10
+ attr_reader :candle_thread
11
+ attr_reader :current_bucket
12
+ attr_reader :start_minute
13
+ attr_reader :candles
14
+
15
+ attr_reader :initial_time
16
+ attr_reader :first_bucket
17
+ attr_reader :bucket_lock
18
+
19
+
20
+ def initialize(options = {}, &block)
21
+ super(options, &block)
22
+ @buckets_lock = Mutex.new
23
+ end
24
+
25
+ def start!
26
+ super
27
+ #
28
+ # Calculate the first minute to start relying on just the websocket for
29
+ # data.
30
+ #
31
+ @initial_time = Time.now
32
+ @first_bucket = initial_time.to_i + (60 - initial_time.sec)
33
+ @history_queue = Queue.new
34
+
35
+ start_bucket_thread
36
+ start_candle_thread
37
+ end
38
+
39
+ private
40
+
41
+ def start_bucket_thread
42
+ @bucket_thread = Thread.new do
43
+ @buckets = {}
44
+ @current_bucket = first_bucket
45
+ @buckets[current_bucket.to_i] = []
46
+
47
+ loop do
48
+ message = queue.pop
49
+ if message.fetch('type') == 'match'
50
+ if Time.parse(message.fetch('time')) >= Time.at(first_bucket)
51
+ timestamp = Time.parse(message.fetch('time'))
52
+ bucket = timestamp.to_i - timestamp.sec
53
+ @buckets_lock.synchronize do
54
+ if bucket > current_bucket
55
+ @current_bucket = bucket
56
+ @buckets[current_bucket.to_i] = []
57
+ @buckets[current_bucket.to_i] << message
58
+ else
59
+ @buckets[current_bucket.to_i] << message
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def start_candle_thread
69
+ @candle_thread = Thread.new do
70
+ @candles = []
71
+ sleep(60 - Time.now.sec)
72
+ loop do
73
+ buckets.keys.each do |key|
74
+ if key + 60 <= Time.now.to_i
75
+ @buckets_lock.synchronize do
76
+ candle = Candle.new(key, buckets[key]) unless buckets[key].empty?
77
+ @candles << candle
78
+ # Run candle callback
79
+ #
80
+ @message_callbacks.each{|c| c.call(candle)}
81
+ buckets.delete(key)
82
+ end
83
+ end
84
+ end
85
+
86
+ sleep(60 - Time.now.sec)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,28 @@
1
+ class RTCBX
2
+ class Candles < RTCBX
3
+ class Candle
4
+
5
+ attr_reader :time, :low, :high, :open, :close, :volume
6
+
7
+ def initialize(epoch, matches)
8
+ @time = epoch
9
+ @low = matches.map {|message| BigDecimal.new(message.fetch('price'))}.min
10
+ @high = matches.map {|message| BigDecimal.new(message.fetch('price'))}.max
11
+ @open = BigDecimal.new(matches.first.fetch('price'))
12
+ @close = BigDecimal.new(matches.last.fetch('price'))
13
+ @volume = matches.reduce(BigDecimal(0)) {|sum, message| sum + BigDecimal.new(message.fetch('size'))}
14
+ end
15
+
16
+ def to_h
17
+ {
18
+ start: Time.at(@time),
19
+ low: @low.to_s("F"),
20
+ high: @high.to_s("F"),
21
+ open: @open.to_s("F"),
22
+ close: @close.to_s("F"),
23
+ volume: @volume.to_s("F"),
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,98 @@
1
+ require 'rtcbx/orderbook/book_methods'
2
+ require 'rtcbx/orderbook/book_analysis'
3
+
4
+ # This class represents the current state of the CoinBase Exchange orderbook.
5
+ #
6
+ class RTCBX
7
+ class Orderbook < RTCBX
8
+ include BookMethods
9
+ include BookAnalysis
10
+
11
+ # Array of bids
12
+ #
13
+ attr_reader :bids
14
+
15
+ # Array of asks
16
+ #
17
+ attr_reader :asks
18
+
19
+ # Sequence number from the initial level 3 snapshot
20
+ #
21
+ attr_reader :snapshot_sequence
22
+
23
+ # Sequence number of most recently received message
24
+ #
25
+ attr_reader :last_sequence
26
+
27
+ # Reads from the queue and updates the Orderbook.
28
+ #
29
+ attr_reader :update_thread
30
+
31
+ # Creates a new live copy of the orderbook.
32
+ #
33
+ # If +start+ is set to false, the orderbook will not start automatically.
34
+ #
35
+ # If a +block+ is given it is passed each message as it is received.
36
+ #
37
+ def initialize(options={}, &block)
38
+ @bids = []
39
+ @asks = []
40
+ @snapshot_sequence = 0
41
+ @last_sequence = 0
42
+ super(options, &block)
43
+ end
44
+
45
+ # Used to start the thread that listens to updates on the websocket and
46
+ # applies them to the current orderbook to create a live book.
47
+ #
48
+ def start!
49
+ super
50
+ sleep 0.3
51
+ apply_orderbook_snapshot
52
+ start_update_thread
53
+ end
54
+
55
+ def stop!
56
+ super
57
+ update_thread.kill
58
+ end
59
+
60
+ private
61
+
62
+ # Converts an order array from the API into a hash.
63
+ #
64
+ def order_to_hash(price, size, order_id)
65
+ { price: BigDecimal.new(price),
66
+ size: BigDecimal.new(size),
67
+ order_id: order_id
68
+ }
69
+ end
70
+
71
+ # Fetch orderbook snapshot from API and convert order arrays to hashes.
72
+ #
73
+ def apply_orderbook_snapshot
74
+ client.orderbook(level: 3) do |resp|
75
+ @bids = resp['bids'].map { |b| order_to_hash(*b) }
76
+ @asks = resp['asks'].map { |a| order_to_hash(*a) }
77
+ @snapshot_sequence = resp['sequence']
78
+ @last_sequence = resp['sequence']
79
+ end
80
+ end
81
+
82
+ def start_update_thread
83
+ @update_thread = Thread.new do
84
+ begin
85
+ loop do
86
+ message = queue.pop
87
+ apply(message)
88
+ end
89
+
90
+ rescue => e
91
+ puts e
92
+ end
93
+ end
94
+ end
95
+
96
+ # apply(message)
97
+ end
98
+ end
@@ -0,0 +1,67 @@
1
+ class RTCBX
2
+ class Orderbook < RTCBX
3
+ # Simple collection of commands to get info about the orderbook. Add our own
4
+ # methods for calculating whatever it is you feel like calculating.
5
+ #
6
+ module BookAnalysis
7
+ def bid_count
8
+ @bids.count
9
+ end
10
+
11
+ def ask_count
12
+ @asks.count
13
+ end
14
+
15
+ def count
16
+ { bid: bid_count, ask: ask_count }
17
+ end
18
+
19
+ def bid_volume
20
+ @bids.map { |x| x.fetch(:size) }.inject(:+)
21
+ end
22
+
23
+ def ask_volume
24
+ @asks.map { |x| x.fetch(:size) }.inject(:+)
25
+ end
26
+
27
+ def volume
28
+ { bid: bid_volume, ask: ask_volume }
29
+ end
30
+
31
+ def average_bid
32
+ bids = @bids.map { |x| x.fetch(:price) }
33
+ bids.inject(:+) / bids.count
34
+ end
35
+
36
+ def average_ask
37
+ asks = @asks.map { |x| x.fetch(:price) }
38
+ asks.inject(:+) / asks.count
39
+ end
40
+
41
+ def average
42
+ { bid: average_bid, ask: average_ask }
43
+ end
44
+
45
+ def best_bid
46
+ @bids.sort_by { |x| x.fetch(:price) }.last
47
+ end
48
+
49
+ def best_ask
50
+ @asks.sort_by { |x| x.fetch(:price) }.first
51
+ end
52
+
53
+ def best
54
+ { bid: best_bid, ask: best_ask }
55
+ end
56
+
57
+ def spread
58
+ best_ask.fetch(:price) - best_bid.fetch(:price)
59
+ end
60
+
61
+ def summarize
62
+ print "# of asks: #{ask_count}\n# of bids: #{bid_count}\nAsk volume: #{ask_volume.to_s('F')}\nBid volume: #{bid_volume.to_s('F')}\n"
63
+ $stdout.flush
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,76 @@
1
+ require 'bigdecimal'
2
+ class RTCBX
3
+ class Orderbook < RTCBX
4
+
5
+ # This class provides methods to apply updates to the state of the orderbook
6
+ # as they are received by the websocket.
7
+ #
8
+ module BookMethods
9
+ BIGDECIMAL_KEYS = %w(size old_size new_size remaining_size price)
10
+
11
+ # Applies a message to an Orderbook object by making relevant changes to
12
+ # @bids, @asks, and @last_sequence.
13
+ #
14
+ def apply(msg)
15
+ return if msg.fetch('sequence') != @last_sequence + 1
16
+ #if msg.fetch('sequence') != @last_sequence + 1
17
+ # puts "Expected #{@last_sequence + 1}, got #{msg.fetch('sequence')}"
18
+ # @websocket.stop!
19
+ #end
20
+
21
+ @last_sequence = msg.fetch('sequence')
22
+ BIGDECIMAL_KEYS.each do |key|
23
+ msg[key] = BigDecimal.new(msg.fetch(key)) if msg.fetch(key, false)
24
+ end
25
+
26
+ __send__(msg.fetch('type'), msg)
27
+ end
28
+
29
+ private
30
+
31
+ def open(msg)
32
+ order = {
33
+ price: msg.fetch('price'),
34
+ size: msg.fetch('remaining_size'),
35
+ order_id: msg.fetch('order_id')
36
+ }
37
+
38
+ @bids << order if msg.fetch('side') == 'buy'
39
+ @asks << order if msg.fetch('side') == 'sell'
40
+ end
41
+
42
+ def match(msg)
43
+ decrement_match = lambda do |o|
44
+ if o.fetch(:order_id) == msg.fetch('maker_order_id')
45
+ o[:size] = o.fetch(:size) - msg.fetch('size')
46
+ end
47
+ end
48
+
49
+ @asks.map(&decrement_match) if msg.fetch('side') == 'sell'
50
+ @bids.map(&decrement_match) if msg.fetch('side') == 'buy'
51
+ end
52
+
53
+ def done(msg)
54
+ matching_order = ->(o) { o.fetch(:order_id) == msg.fetch('order_id') }
55
+
56
+ @asks.reject!(&matching_order) if msg.fetch('side') == 'sell'
57
+ @bids.reject!(&matching_order) if msg.fetch('side') == 'buy'
58
+ end
59
+
60
+ def change(msg)
61
+ change_order = lambda do |o|
62
+ if o.fetch(:order_id) == msg.fetch('order_id')
63
+ o[:size] = msg.fetch('new_size')
64
+ end
65
+ end
66
+
67
+ @asks.map(&change_order) if msg.fetch('side') == 'sell'
68
+ @bids.map(&change_order) if msg.fetch('side') == 'buy'
69
+ end
70
+
71
+ def received(_)
72
+ # The book doesn't change for this message type.
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ class RTCBX
2
+ class Trader < RTCBX
3
+
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ # Orderbook version number. I try to keep it semantic.
2
+ #
3
+ class RTCBX
4
+ VERSION = '0.0.2'
5
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rtcbx/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rtcbx'
8
+ spec.version = RTCBX::VERSION
9
+ spec.authors = ['Michael Rodrigues']
10
+ spec.email = ['mikebrodrigues@gmail.com']
11
+ spec.summary = %q(Maintains an real-time copy of the GDAX (Coinbase) Exchange order book.)
12
+ spec.description = %q(Uses the GDAX (Coinbase) Exchange Websocket stream
13
+ to maintain a real-time copy of the order book, place and track orders,
14
+ and calculate historic rates by the minute in real-time.)
15
+ spec.homepage = 'https://github.com/mikerodrigues/rtcbx'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rtcbx
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Michael Rodrigues
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: |-
42
+ Uses the GDAX (Coinbase) Exchange Websocket stream
43
+ to maintain a real-time copy of the order book, place and track orders,
44
+ and calculate historic rates by the minute in real-time.
45
+ email:
46
+ - mikebrodrigues@gmail.com
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - ".gitignore"
52
+ - Gemfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - lib/rtcbx.rb
57
+ - lib/rtcbx/candles.rb
58
+ - lib/rtcbx/candles/candle.rb
59
+ - lib/rtcbx/orderbook.rb
60
+ - lib/rtcbx/orderbook/book_analysis.rb
61
+ - lib/rtcbx/orderbook/book_methods.rb
62
+ - lib/rtcbx/trader.rb
63
+ - lib/rtcbx/version.rb
64
+ - rtcbx.gemspec
65
+ homepage: https://github.com/mikerodrigues/rtcbx
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.4.6
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Maintains an real-time copy of the GDAX (Coinbase) Exchange order book.
89
+ test_files: []
90
+ has_rdoc: