flashboy 0.1.0 → 0.2.0

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
  SHA1:
3
- metadata.gz: 5bdef52969250e0352db625ab7d35e59f36f6479
4
- data.tar.gz: e4a75faf8e32ff53e4124c125a08a8d706800658
3
+ metadata.gz: 6ed0ad1eabe895752f900b3db82b730d66a64c92
4
+ data.tar.gz: 205d2358756acc3908552d6fccc65c73996f2a16
5
5
  SHA512:
6
- metadata.gz: 273be5d6cfa97257f2a3246308d88442b58d7452fe59963e935f7d6fb8be0bec3c1f02253e0f478c60cee01e0d39eaec582ca494af255b6c9adf0bf7ab814059
7
- data.tar.gz: 6bb6c3ff510472947d1d61518883be46224d1d0ab814abc3662c4290704e947beda9dd8589260b6a9d28e026840e4f180d580e0a780aa02ba047f7bc38cf5455
6
+ metadata.gz: ef5a71afb4db214c7fb945e1d5ea7d4ab0ccef8ed9aca91a7680a2a479a772d9229c24c8ae9dbb5a8ef5051624e056c2288cadc6663c71b0d71c841c97f5d77a
7
+ data.tar.gz: 5279f96044882995075099a7d1c61a2d44782f7ee0b9711ab647934a4b7df0f1a1f9365cf812c13792c556564d89024eaa92e93b60758bfdf94b63b4735b63ab
@@ -1,6 +1,7 @@
1
1
  require 'flashboy/version'
2
2
  require 'faraday'
3
3
  require 'json'
4
+ require 'logger'
4
5
 
5
6
  require 'ext/hash'
6
7
 
@@ -12,6 +13,7 @@ module Flashboy
12
13
  autoload :Order, 'flashboy/order'
13
14
  autoload :OrderBook, 'flashboy/order_book'
14
15
  autoload :Quote, 'flashboy/quote'
16
+ autoload :Trade, 'flashboy/trade'
15
17
 
16
18
  autoload :Bitfinex, 'flashboy/bitfinex'
17
19
  autoload :Bittrex, 'flashboy/bittrex'
@@ -22,12 +24,22 @@ module Flashboy
22
24
  autoload :Poloniex, 'flashboy/poloniex'
23
25
  autoload :Quoine, 'flashboy/quoine'
24
26
 
27
+ def self.logger
28
+ @logger ||= Logger.new(STDOUT)
29
+ end
30
+
25
31
  def self.quote(pair)
26
32
  exchanges.map do |exchange|
27
33
  exchange.new.quote(pair)
28
34
  end
29
35
  end
30
36
 
37
+ def self.order_book(pair)
38
+ exchanges.map do |exchange|
39
+ exchange.new.order_book(pair)
40
+ end
41
+ end
42
+
31
43
  def self.exchanges
32
44
  [Bitfinex, Bittrex, Cex, Gdax, Gemini, Kraken, Poloniex, Quoine]
33
45
  end
@@ -5,6 +5,11 @@ module Flashboy
5
5
  host 'https://api.bitfinex.com'
6
6
  quote_path { |pair| "v1/pubticker/#{pair}.json" } #ethbtc
7
7
  order_book_path { |pair| "v1/book/#{pair}.json" }
8
+ trades_path { |pair, params| "v1/trades/#{pair}?timestamp=#{params[:start]}" }
9
+
10
+ def self.parse_cursor(params)
11
+ { start: params[:start] }
12
+ end
8
13
 
9
14
  private
10
15
 
@@ -13,6 +18,18 @@ module Flashboy
13
18
  "#{currency}#{base}".upcase
14
19
  end
15
20
 
21
+ def parse_trades(trades, exchange, pair)
22
+ [*trades].map do |trade|
23
+ {
24
+ global_id: "#{exchange.key}-#{trade[:tid]}",
25
+ type: trade[:type],
26
+ price: trade[:price],
27
+ amount: trade[:amount],
28
+ executed_at: trade[:timestamp]
29
+ }
30
+ end
31
+ end
32
+
16
33
  def parse_quote(quote, pair)
17
34
  {
18
35
  bid: quote[:bid],
@@ -25,8 +42,8 @@ module Flashboy
25
42
 
26
43
  def parse_order_book(order, pair)
27
44
  {
28
- bids: order[:bids].map{|order| { price: order[:price], amount: order[:amount] } },
29
- asks: order[:asks].map{|order| { price: order[:price], amount: order[:amount] } }
45
+ bids: (order[:bids] || []).map{|order| { price: order[:price], amount: order[:amount] } },
46
+ asks: (order[:asks] || []).map{|order| { price: order[:price], amount: order[:amount] } }
30
47
  }
31
48
  end
32
49
  end
@@ -5,6 +5,11 @@ module Flashboy
5
5
  host 'https://cex.io/api'
6
6
  quote_path { |pair| "ticker/#{pair}" } #ETH/BTC
7
7
  order_book_path { |pair| "order_book/#{pair}/?depth=10" }
8
+ trades_path { |pair, params| "trade_history/#{pair}/?since=#{params[:since]}" }
9
+
10
+ def self.parse_cursor(params)
11
+ { since: params[:since] }
12
+ end
8
13
 
9
14
  private
10
15
 
@@ -13,6 +18,19 @@ module Flashboy
13
18
  "#{currency}/#{base}".upcase
14
19
  end
15
20
 
21
+ def parse_trades(trades, exchange, _pair)
22
+ [*trades].map do |trade|
23
+ {
24
+ global_id: "#{exchange.key}-#{trade[:tid]}",
25
+ type: trade[:type],
26
+ price: trade[:price],
27
+ amount: trade[:amount],
28
+ executed_at: trade[:date]
29
+ }
30
+ end
31
+ end
32
+
33
+
16
34
  def parse_quote(quote, pair)
17
35
  {
18
36
  bid: quote[:bid],
@@ -25,8 +43,8 @@ module Flashboy
25
43
 
26
44
  def parse_order_book(order, pair)
27
45
  {
28
- bids: order[:bids].map{|(p, a)| { price: p, amount: a } },
29
- asks: order[:asks].map{|(p, a)| { price: p, amount: a } }
46
+ bids: (order[:bids] || []).map{|(p, a)| { price: p, amount: a } },
47
+ asks: (order[:asks] || []).map{|(p, a)| { price: p, amount: a } }
30
48
  }
31
49
  end
32
50
  end
@@ -44,6 +44,19 @@ module Flashboy
44
44
  self.class.quote_path(pair)
45
45
  end
46
46
 
47
+ def self.trades_path(pair = nil, params = {}, &path)
48
+ if !block_given?
49
+ raise RuntimeError, "You must define the trades_path on your exchange" if @trades_path.nil?
50
+ return @trades_path.call(pair, params)
51
+ end
52
+
53
+ @trades_path = path
54
+ end
55
+
56
+ def trades_path(pair, params = {})
57
+ self.class.trades_path(pair, params)
58
+ end
59
+
47
60
  def self.order_book_path(pair = nil, &path)
48
61
  return @order_book_path.call(pair) unless block_given?
49
62
  @order_book_path = path
@@ -9,6 +9,24 @@ module Flashboy
9
9
  )
10
10
  end
11
11
 
12
+ def trades(pair, options = {})
13
+ start, finish, cursor = nil
14
+ if options[:start] || options[:finish]
15
+ finish = options[:finish] || Time.now.to_i
16
+ start = options[:start] || finish - (60 * 60)
17
+ elsif options[:since]
18
+ cursor = options[:since] || 0
19
+ end
20
+
21
+ id = formatted_pair(pair)
22
+ url = trades_path(id, {start: start, finish: finish, since: cursor})
23
+ # puts url
24
+ data = request url
25
+ parse_trades(data, self, id).map do |trade|
26
+ Trade.new(trade.merge(pair: pair, exchange: name))
27
+ end
28
+ end
29
+
12
30
  def order_book(pair)
13
31
  id = formatted_pair(pair)
14
32
  data = request order_book_path(id)
@@ -29,9 +47,15 @@ module Flashboy
29
47
  end
30
48
 
31
49
  def request(path)
32
- response = connection.get(path).body
33
- data = JSON.parse(response)
34
- data.recursively_symbolize_keys!
50
+ begin
51
+ response = connection.get(path).body
52
+ data = JSON.parse(response)
53
+ rescue JSON::ParserError => e
54
+ Flashboy.logger.warn "Failed to parse response from #{host}/#{path}"
55
+ data = {}
56
+ end
57
+
58
+ data.is_a?(Hash) ? data.recursively_symbolize_keys! : data.map(&:recursively_symbolize_keys!)
35
59
  end
36
60
 
37
61
  def connection
@@ -5,6 +5,11 @@ module Flashboy
5
5
  host 'https://api.gdax.com'
6
6
  quote_path { |pair| "products/#{pair}/ticker" } #ETH-BTC
7
7
  order_book_path { |pair| "products/#{pair}/book" }
8
+ trades_path { |pair, params| "products/#{pair}/trades?#{params[:since] ? ('before=' + params[:since].to_s) : ''}" }
9
+
10
+ def self.parse_cursor(params)
11
+ { since: params[:since] }
12
+ end
8
13
 
9
14
  private
10
15
 
@@ -13,6 +18,18 @@ module Flashboy
13
18
  "#{currency}-#{base}".upcase
14
19
  end
15
20
 
21
+ def parse_trades(trades, exchange, _pair)
22
+ [*trades].map do |trade|
23
+ {
24
+ global_id: "#{exchange.key}-#{trade[:trade_id]}",
25
+ type: trade[:side],
26
+ price: trade[:price],
27
+ amount: trade[:size],
28
+ executed_at: trade[:time]
29
+ }
30
+ end
31
+ end
32
+
16
33
  def parse_quote(quote, pair)
17
34
  {
18
35
  bid: quote[:bid],
@@ -25,8 +42,8 @@ module Flashboy
25
42
 
26
43
  def parse_order_book(order, pair)
27
44
  {
28
- bids: order[:bids].map{|order| { price: order[:price], amount: order[:amount] } },
29
- asks: order[:asks].map{|order| { price: order[:price], amount: order[:amount] } }
45
+ bids: (order[:bids] || []).map{|order| { price: order[:price], amount: order[:amount] } },
46
+ asks: (order[:asks] || []).map{|order| { price: order[:price], amount: order[:amount] } }
30
47
  }
31
48
  end
32
49
  end
@@ -5,6 +5,11 @@ module Flashboy
5
5
  host 'https://api.gemini.com/v1'
6
6
  quote_path { |pair| "pubticker/#{pair}" } #ethbtc
7
7
  order_book_path { |pair| "book/#{pair}?depth=10" }
8
+ trades_path { |pair, params| "trades/#{pair}#{params[:start] ? ('?since=' + params[:start].to_s) : ''}" }
9
+
10
+ def self.parse_cursor(params)
11
+ { start: params[:start] }
12
+ end
8
13
 
9
14
  private
10
15
 
@@ -13,6 +18,18 @@ module Flashboy
13
18
  "#{currency}#{base}".downcase
14
19
  end
15
20
 
21
+ def parse_trades(trades, exchange, pair)
22
+ [*trades].map do |trade|
23
+ {
24
+ global_id: "#{exchange.key}-#{trade[:tid]}",
25
+ type: trade[:type],
26
+ price: trade[:price],
27
+ amount: trade[:amount],
28
+ executed_at: trade[:timestamp].to_i
29
+ }
30
+ end
31
+ end
32
+
16
33
  def parse_quote(quote, pair)
17
34
  {
18
35
  bid: quote[:bid],
@@ -1,18 +1,40 @@
1
1
  module Flashboy
2
2
  class Kraken < Exchange
3
3
  name 'Kraken'
4
- docs 'http://docs.bitfinex.com'
4
+ docs 'https://www.kraken.com/help/api'
5
5
  host 'https://api.kraken.com'
6
6
  quote_path { |pair| "0/public/Ticker?pair=#{pair}" } #ETHXBT
7
7
  order_book_path { |pair| "0/public/Depth?pair=#{pair}" }
8
+ trades_path { |pair, params| "0/public/Trades?pair=#{pair}#{params[:since] ? ('&since=' + params[:since].to_s) : ''}" }
9
+
10
+ def self.parse_cursor(params)
11
+ { since: params[:since] }
12
+ end
8
13
 
9
14
  private
10
15
 
11
16
  def formatted_pair(pair)
12
- currency, base = super(pair).map { |curr| curr == 'BTC' ? 'XBT' : curr }
17
+ currency, base = super(pair).map { |curr| curr.upcase == 'BTC' ? 'XBT' : curr }
13
18
  "#{currency}#{base}".upcase
14
19
  end
15
20
 
21
+ def parse_trades(trades, exchange, pair)
22
+ last_id = trades[:result][:last]
23
+ pair_id = trades[:result].keys.first
24
+ trades = trades[:result][pair_id]
25
+
26
+ [*trades].map do |trade|
27
+ price, amount, time, type = trade
28
+ {
29
+ global_id: "#{exchange.key}-#{time}-#{last_id}",
30
+ type: type == 'b' ? 'buy' : 'sell',
31
+ price: price,
32
+ amount: amount,
33
+ executed_at: time
34
+ }
35
+ end
36
+ end
37
+
16
38
  def parse_quote(quote, pair)
17
39
  data = quote[:result].values.first
18
40
  {
@@ -1,11 +1,12 @@
1
1
  module Flashboy
2
2
  class OrderBook
3
- attr_reader :bids, :asks, :data
3
+ attr_reader :pair, :bids, :asks, :exchange
4
4
 
5
5
  def initialize(data)
6
- @data = data
7
- @bids = data[:bids].map { |bid| Bid.new(bid) }
8
- @asks = data[:asks].map { |ask| Ask.new(ask) }
6
+ @pair = data[:pair]
7
+ @exchange = data[:exchange]
8
+ @bids = (data[:bids] || []).map { |bid| Bid.new(bid) }
9
+ @asks = (data[:asks] || []).map { |ask| Ask.new(ask) }
9
10
  end
10
11
  end
11
12
  end
@@ -5,6 +5,15 @@ module Flashboy
5
5
  host 'https://poloniex.com'
6
6
  quote_path { |pair| "public?command=returnTicker" } #BTC_ETH
7
7
  order_book_path { |pair| "book/#{pair}?depth=10" }
8
+ trades_path do |pair, params|
9
+ url = "public?command=returnTradeHistory&currencyPair=#{pair}"
10
+ url += "&start=#{params[:start]}" unless params[:start].nil?
11
+ url
12
+ end
13
+
14
+ def self.parse_cursor(params)
15
+ { start: params[:start] }
16
+ end
8
17
 
9
18
  private
10
19
 
@@ -13,6 +22,18 @@ module Flashboy
13
22
  "#{base}_#{currency}".upcase
14
23
  end
15
24
 
25
+ def parse_trades(trades, exchange, _pair)
26
+ [*trades].map do |trade|
27
+ {
28
+ global_id: "#{exchange.key}-#{trade[:globalTradeID]}",
29
+ type: trade[:type],
30
+ price: trade[:rate],
31
+ amount: trade[:amount],
32
+ executed_at: trade[:date]
33
+ }
34
+ end
35
+ end
36
+
16
37
  def parse_quote(quote, pair)
17
38
  data = quote[pair.to_sym]
18
39
  {
@@ -1,10 +1,19 @@
1
1
  module Flashboy
2
2
  class Quoine < Exchange
3
3
  name 'Quoine'
4
- docs 'https://developers.quoine.com/#introduction'
4
+ docs 'https://developers.quoine.com'
5
5
  host 'https://api.quoine.com'
6
6
  quote_path { |pair| "products/code/CASH/#{pair}" } #ETHBTC
7
7
  order_book_path { |pair| "products/#{pair}/price_levels" }
8
+ trades_path do |pair, params|
9
+ url = "executions?currency_pair_code=#{pair}"
10
+ url += "&timestamp=#{params[:start]}" if params[:start]
11
+ url
12
+ end
13
+
14
+ def self.parse_cursor(params)
15
+ { start: params[:start] }
16
+ end
8
17
 
9
18
  private
10
19
 
@@ -13,6 +22,20 @@ module Flashboy
13
22
  "#{currency}#{base}".upcase
14
23
  end
15
24
 
25
+ def parse_trades(trades, exchange, pair)
26
+ trades = trades.is_a?(Hash) ? trades[:models] : trades
27
+ [*trades].map do |trade|
28
+ trade.recursively_symbolize_keys!
29
+ {
30
+ global_id: "#{exchange.key}-#{trade[:id]}",
31
+ type: trade[:taker_side],
32
+ price: trade[:price],
33
+ amount: trade[:quantity],
34
+ executed_at: trade[:created_at]
35
+ }
36
+ end
37
+ end
38
+
16
39
  def parse_quote(quote, pair)
17
40
  {
18
41
  bid: quote[:market_bid],
@@ -0,0 +1,31 @@
1
+ module Flashboy
2
+ class Trade
3
+ attr_reader :global_id, :type, :exchange, :pair, :price, :amount, :executed_at
4
+
5
+ def initialize(attrs)
6
+ @global_id = attrs[:global_id]
7
+ @type = attrs[:type]
8
+ @exchange = attrs[:exchange]
9
+ @pair = attrs[:pair]
10
+ @price = attrs[:price].to_s.to_f
11
+ @amount = attrs[:amount].to_s.to_f
12
+ @executed_at = attrs[:executed_at]
13
+ end
14
+
15
+ def id
16
+ global_id.split('-').last
17
+ end
18
+
19
+ def to_json(_opts)
20
+ {
21
+ global_id: global_id,
22
+ type: type,
23
+ exchange: exchange,
24
+ pair: pair,
25
+ price: price,
26
+ amount: amount,
27
+ executed_at: executed_at
28
+ }.to_json
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  module Flashboy
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flashboy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Werner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-02 00:00:00.000000000 Z
11
+ date: 2017-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -101,6 +101,7 @@ files:
101
101
  - lib/flashboy/poloniex.rb
102
102
  - lib/flashboy/quoine.rb
103
103
  - lib/flashboy/quote.rb
104
+ - lib/flashboy/trade.rb
104
105
  - lib/flashboy/version.rb
105
106
  homepage: https://github.com/mwerner/flashboy
106
107
  licenses: