bitbot-trader 0.0.1
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 +7 -0
- data/.gitignore +18 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.devtools +55 -0
- data/LICENSE.txt +22 -0
- data/README.md +28 -0
- data/Rakefile +4 -0
- data/bitbot-trader.gemspec +22 -0
- data/config/devtools.yml +4 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +103 -0
- data/config/yardstick.yml +2 -0
- data/examples/account_info.rb +16 -0
- data/examples/open_orders.rb +16 -0
- data/lib/bitbot/trader.rb +32 -0
- data/lib/bitbot/trader/account.rb +29 -0
- data/lib/bitbot/trader/amount.rb +15 -0
- data/lib/bitbot/trader/api_methods.rb +33 -0
- data/lib/bitbot/trader/open_order.rb +29 -0
- data/lib/bitbot/trader/price.rb +15 -0
- data/lib/bitbot/trader/provider.rb +19 -0
- data/lib/bitbot/trader/providers/bitstamp.rb +29 -0
- data/lib/bitbot/trader/providers/bitstamp/http_client.rb +101 -0
- data/lib/bitbot/trader/providers/bitstamp/open_orders_parser.rb +33 -0
- data/lib/bitbot/trader/providers/bitstamp/open_orders_request.rb +25 -0
- data/lib/bitbot/trader/providers/mt_gox.rb +29 -0
- data/lib/bitbot/trader/providers/mt_gox/account_info_parser.rb +45 -0
- data/lib/bitbot/trader/providers/mt_gox/account_info_request.rb +24 -0
- data/lib/bitbot/trader/providers/mt_gox/http_client.rb +98 -0
- data/lib/bitbot/trader/providers/mt_gox/http_client/hmac_middleware.rb +118 -0
- data/lib/bitbot/trader/providers/mt_gox/open_orders_parser.rb +35 -0
- data/lib/bitbot/trader/providers/mt_gox/open_orders_request.rb +25 -0
- data/lib/bitbot/trader/providers/mt_gox/value_with_currency_coercer.rb +28 -0
- data/lib/bitbot/trader/request.rb +41 -0
- data/lib/bitbot/trader/utils/nonce_generator.rb +21 -0
- data/lib/bitbot/trader/version.rb +5 -0
- data/lib/bitbot/trader/wallet.rb +25 -0
- data/spec/integration/bitstamp/open_orders_spec.rb +28 -0
- data/spec/integration/mt_gox/account_spec.rb +28 -0
- data/spec/integration/mt_gox/open_orders_spec.rb +29 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/big_decimal_matcher.rb +5 -0
- data/spec/support/http_connection_helpers.rb +15 -0
- data/spec/support/http_request_mock.rb +7 -0
- data/spec/support/provider_mock.rb +5 -0
- data/spec/unit/bitbot/trader/account_spec.rb +28 -0
- data/spec/unit/bitbot/trader/api_methods_spec.rb +43 -0
- data/spec/unit/bitbot/trader/open_order_spec.rb +19 -0
- data/spec/unit/bitbot/trader/provider_spec.rb +18 -0
- data/spec/unit/bitbot/trader/providers/bitstamp/http_client_spec.rb +75 -0
- data/spec/unit/bitbot/trader/providers/bitstamp/open_order_parser_spec.rb +69 -0
- data/spec/unit/bitbot/trader/providers/bitstamp/open_orders_request_spec.rb +24 -0
- data/spec/unit/bitbot/trader/providers/bitstamp_spec.rb +45 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/account_info_parser_spec.rb +58 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/account_info_request_spec.rb +24 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/http_client/hmac_middleware_spec.rb +55 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/http_client_spec.rb +100 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/open_order_parser_spec.rb +95 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/open_orders_request_spec.rb +24 -0
- data/spec/unit/bitbot/trader/providers/mt_gox/value_with_currency_coercer_spec.rb +21 -0
- data/spec/unit/bitbot/trader/providers/mt_gox_spec.rb +26 -0
- data/spec/unit/bitbot/trader/request_spec.rb +39 -0
- data/spec/unit/bitbot/trader/utils/nonce_generator_spec.rb +19 -0
- metadata +166 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require "virtus"
|
2
|
+
|
3
|
+
module Bitbot
|
4
|
+
module Trader
|
5
|
+
# Value object for user open order
|
6
|
+
#
|
7
|
+
class OpenOrder
|
8
|
+
include Virtus.model
|
9
|
+
|
10
|
+
attribute :id, String
|
11
|
+
attribute :price, Price
|
12
|
+
attribute :amount, Amount
|
13
|
+
attribute :bid, Boolean
|
14
|
+
|
15
|
+
# Checks if order is a ask order
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# OpenOrder.new(bid: true).ask? #=> false
|
19
|
+
#
|
20
|
+
# @return [Boolean]
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
#
|
24
|
+
def ask?
|
25
|
+
!bid?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "virtus"
|
2
|
+
|
3
|
+
module Bitbot
|
4
|
+
module Trader
|
5
|
+
# Price value object. Every price has a value and a currency.
|
6
|
+
# e.g 5.6 USD
|
7
|
+
#
|
8
|
+
class Price
|
9
|
+
include Virtus.model
|
10
|
+
|
11
|
+
attribute :value, BigDecimal
|
12
|
+
attribute :currency, String
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bitbot
|
2
|
+
module Trader
|
3
|
+
# Common Provider methods
|
4
|
+
#
|
5
|
+
# @abstract
|
6
|
+
#
|
7
|
+
class Provider
|
8
|
+
include ApiMethods
|
9
|
+
|
10
|
+
# Object that communicates with external API
|
11
|
+
#
|
12
|
+
# @return [Object]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
attr_reader :client
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "virtus"
|
2
|
+
|
3
|
+
module Bitbot
|
4
|
+
module Trader
|
5
|
+
module Providers
|
6
|
+
# Provider for Bitstamp API
|
7
|
+
#
|
8
|
+
# @see https://www.bitstamp.net/api/
|
9
|
+
#
|
10
|
+
class Bitstamp < Provider
|
11
|
+
# Initializes Bitstamp provider
|
12
|
+
#
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [String] :username
|
15
|
+
# @option options [String] :password
|
16
|
+
#
|
17
|
+
# @param [HttpClient] client
|
18
|
+
#
|
19
|
+
# @return [undefined]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
#
|
23
|
+
def initialize(options, client = HttpClient.build(options))
|
24
|
+
@client = client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Bitbot
|
5
|
+
module Trader
|
6
|
+
module Providers
|
7
|
+
class Bitstamp
|
8
|
+
# Bitstamp specific http client
|
9
|
+
#
|
10
|
+
class HttpClient
|
11
|
+
HOST = "https://www.bitstamp.net/api/"
|
12
|
+
|
13
|
+
# Build a new HttpClient object
|
14
|
+
#
|
15
|
+
# @param [Hash] options
|
16
|
+
# @option options [String] :username
|
17
|
+
# @option options [String] :password
|
18
|
+
#
|
19
|
+
# @return [HttpClient]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
def self.build(options)
|
24
|
+
username = options.fetch(:username)
|
25
|
+
password = options.fetch(:password)
|
26
|
+
|
27
|
+
connection = make_connection
|
28
|
+
new(connection, username, password)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates a new faraday connection object
|
32
|
+
#
|
33
|
+
# @return [Faraday]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def self.make_connection
|
38
|
+
Faraday.new(url: HOST)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return connection
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
#
|
47
|
+
attr_reader :connection
|
48
|
+
|
49
|
+
# API username
|
50
|
+
#
|
51
|
+
# @return [String]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
attr_reader :username
|
56
|
+
|
57
|
+
# API passsword
|
58
|
+
#
|
59
|
+
# @return [String]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
#
|
63
|
+
attr_reader :password
|
64
|
+
|
65
|
+
# Initializes HttpClient object
|
66
|
+
#
|
67
|
+
# @param [#post] connection
|
68
|
+
# @param [String] username
|
69
|
+
# @param [String] password
|
70
|
+
#
|
71
|
+
# @return [undefined]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
#
|
75
|
+
def initialize(connection, username, password)
|
76
|
+
@connection = connection
|
77
|
+
@username = username
|
78
|
+
@password = password
|
79
|
+
end
|
80
|
+
|
81
|
+
# Sends post request to given path
|
82
|
+
#
|
83
|
+
# @param [String] path
|
84
|
+
# e.g. "open_orders"
|
85
|
+
# @param [Hash] options
|
86
|
+
# post request parameters
|
87
|
+
#
|
88
|
+
# @return [Hash]
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
#
|
92
|
+
def post(path, options = {})
|
93
|
+
options = options.merge(user: @username, password: @password)
|
94
|
+
result = @connection.post("#{path}/", options).body
|
95
|
+
JSON.parse(result)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Bitbot
|
2
|
+
module Trader
|
3
|
+
module Providers
|
4
|
+
class Bitstamp
|
5
|
+
# Parses raw open orders
|
6
|
+
#
|
7
|
+
class OpenOrderParser
|
8
|
+
include Virtus.model
|
9
|
+
|
10
|
+
attribute :id, Integer
|
11
|
+
attribute :price, BigDecimal
|
12
|
+
attribute :amount, BigDecimal
|
13
|
+
attribute :type, Integer
|
14
|
+
|
15
|
+
# Makes raw open order hash into OpenOrder object
|
16
|
+
#
|
17
|
+
# @return [OpenOrder]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
def parse
|
22
|
+
OpenOrder.new(
|
23
|
+
id: id,
|
24
|
+
price: {value: price, currency: "USD"},
|
25
|
+
amount: {value: amount, currency: "BTC"},
|
26
|
+
bid: type == 0
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bitbot
|
2
|
+
module Trader
|
3
|
+
module Providers
|
4
|
+
class Bitstamp
|
5
|
+
# POST request to /open_orders/
|
6
|
+
#
|
7
|
+
# @see https://www.bitstamp.net/api/
|
8
|
+
#
|
9
|
+
class OpenOrdersRequest < Request
|
10
|
+
# Fetches user's open orders
|
11
|
+
#
|
12
|
+
# @return [Array<OpenOrder>]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
def call
|
17
|
+
client.post("open_orders").map { |raw_order|
|
18
|
+
OpenOrderParser.new(raw_order).parse
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "virtus"
|
2
|
+
|
3
|
+
module Bitbot
|
4
|
+
module Trader
|
5
|
+
module Providers
|
6
|
+
# Provider for MtGox API (version 2)
|
7
|
+
#
|
8
|
+
# @see https://en.bitcoin.it/wiki/MtGox/API/HTTP/v2
|
9
|
+
#
|
10
|
+
class MtGox < Provider
|
11
|
+
# Initializes MtGox provider
|
12
|
+
#
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options [String] :key
|
15
|
+
# @option options [String] :secret
|
16
|
+
#
|
17
|
+
# @param [HttpClient] client
|
18
|
+
#
|
19
|
+
# @return [undefined]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
#
|
23
|
+
def initialize(options, client = HttpClient.build(options))
|
24
|
+
@client = client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Bitbot
|
2
|
+
module Trader
|
3
|
+
module Providers
|
4
|
+
class MtGox
|
5
|
+
# Parses raw open orders
|
6
|
+
#
|
7
|
+
class AccountInfoParser
|
8
|
+
include Virtus.model
|
9
|
+
|
10
|
+
attribute :Trade_Fee, Float
|
11
|
+
attribute :Wallets, Hash
|
12
|
+
|
13
|
+
# Makes raw account info hash into Account object
|
14
|
+
#
|
15
|
+
# @return [Account]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
def parse
|
20
|
+
parsed = attributes
|
21
|
+
|
22
|
+
Account.new(
|
23
|
+
fee: parsed[:Trade_Fee],
|
24
|
+
wallets: self.class.parse_wallets(parsed[:Wallets])
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Parses raw wallets
|
29
|
+
#
|
30
|
+
# @param [Hash<String, Hash>] raw_wallets
|
31
|
+
#
|
32
|
+
# @return [Array<Hash>]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
#
|
36
|
+
def self.parse_wallets(raw_wallets)
|
37
|
+
raw_wallets.map { |_key, raw_wallet|
|
38
|
+
ValueWithCurrencyCoercer.call(raw_wallet["Balance"])
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Bitbot
|
2
|
+
module Trader
|
3
|
+
module Providers
|
4
|
+
class MtGox
|
5
|
+
# POST request to /money/info
|
6
|
+
#
|
7
|
+
# @see https://en.bitcoin.it/wiki/MtGox/API/HTTP/v1
|
8
|
+
#
|
9
|
+
class AccountInfoRequest < Request
|
10
|
+
# Fetches user's account info
|
11
|
+
#
|
12
|
+
# @return [Account]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
def call
|
17
|
+
raw_account = client.post("money/info")["data"]
|
18
|
+
AccountInfoParser.new(raw_account).parse
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Bitbot
|
5
|
+
module Trader
|
6
|
+
module Providers
|
7
|
+
class MtGox
|
8
|
+
# MtGox specific http client
|
9
|
+
#
|
10
|
+
class HttpClient
|
11
|
+
HOST = "https://data.mtgox.com/api/2/"
|
12
|
+
|
13
|
+
# Builds a new HttpClient object
|
14
|
+
#
|
15
|
+
# @param [Hash] options
|
16
|
+
# @option options [String] :username
|
17
|
+
# @option options [String] :password
|
18
|
+
#
|
19
|
+
# @return [HttpClient]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
#
|
23
|
+
def self.build(options)
|
24
|
+
key = options.fetch(:key)
|
25
|
+
secret = options.fetch(:secret)
|
26
|
+
|
27
|
+
connection = make_connection(key, secret)
|
28
|
+
new(connection)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates a new faraday connection object
|
32
|
+
#
|
33
|
+
# @param [String] key
|
34
|
+
# @param [String] secret
|
35
|
+
#
|
36
|
+
# @return [Faraday::Connection]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
def self.make_connection(key, secret)
|
41
|
+
Faraday.new(url: HOST) do |faraday|
|
42
|
+
faraday.request :url_encoded
|
43
|
+
faraday.request :hmac, key: key, secret: secret
|
44
|
+
faraday.adapter :net_http
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return connection
|
49
|
+
#
|
50
|
+
# @return [Object]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
attr_reader :connection
|
55
|
+
|
56
|
+
# Return nonce generator
|
57
|
+
#
|
58
|
+
# @return [Object]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
attr_reader :nonce_generator
|
62
|
+
|
63
|
+
# Initializes HttpClient object
|
64
|
+
#
|
65
|
+
# @param [#post] connection
|
66
|
+
# @param [#generate] nonce_generator
|
67
|
+
# must return a uniq number
|
68
|
+
#
|
69
|
+
# @return [undefined]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
#
|
73
|
+
def initialize(connection, nonce_generator = Utils::NonceGenerator)
|
74
|
+
@connection = connection
|
75
|
+
@nonce_generator = nonce_generator
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sends post request to given path
|
79
|
+
#
|
80
|
+
# @param [String] path
|
81
|
+
# e.g. "money/orders"
|
82
|
+
# @param [Hash] options
|
83
|
+
# post request parameters
|
84
|
+
#
|
85
|
+
# @return [Hash]
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
#
|
89
|
+
def post(path, options = {})
|
90
|
+
options = options.merge(nonce: @nonce_generator.generate)
|
91
|
+
result = @connection.post(path, options).body
|
92
|
+
JSON.parse(result)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|