flashboy 0.1.0 → 0.2.0

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