bitex_bot 0.3.7 → 0.4.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 +4 -4
- data/.circleci/config.yml +63 -0
- data/.rubocop.yml +33 -0
- data/Gemfile +1 -1
- data/Rakefile +1 -1
- data/bin/bitex_bot +1 -1
- data/bitex_bot.gemspec +34 -34
- data/lib/bitex_bot/database.rb +67 -67
- data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +142 -0
- data/lib/bitex_bot/models/api_wrappers/bitstamp/bitstamp_api_wrapper.rb +137 -0
- data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +116 -0
- data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +111 -0
- data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +117 -0
- data/lib/bitex_bot/models/buy_closing_flow.rb +23 -16
- data/lib/bitex_bot/models/buy_opening_flow.rb +48 -54
- data/lib/bitex_bot/models/close_buy.rb +2 -2
- data/lib/bitex_bot/models/closing_flow.rb +98 -79
- data/lib/bitex_bot/models/open_buy.rb +11 -10
- data/lib/bitex_bot/models/open_sell.rb +11 -10
- data/lib/bitex_bot/models/opening_flow.rb +157 -99
- data/lib/bitex_bot/models/order_book_simulator.rb +62 -67
- data/lib/bitex_bot/models/sell_closing_flow.rb +25 -20
- data/lib/bitex_bot/models/sell_opening_flow.rb +47 -54
- data/lib/bitex_bot/models/store.rb +3 -1
- data/lib/bitex_bot/robot.rb +203 -176
- data/lib/bitex_bot/settings.rb +71 -12
- data/lib/bitex_bot/version.rb +1 -1
- data/lib/bitex_bot.rb +40 -16
- data/settings.rb.sample +43 -66
- data/spec/bitex_bot/settings_spec.rb +87 -15
- data/spec/factories/bitex_buy.rb +3 -3
- data/spec/factories/bitex_sell.rb +3 -3
- data/spec/factories/buy_opening_flow.rb +1 -1
- data/spec/factories/open_buy.rb +12 -10
- data/spec/factories/open_sell.rb +12 -10
- data/spec/factories/sell_opening_flow.rb +1 -1
- data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +200 -0
- data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +176 -0
- data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +209 -0
- data/spec/models/bitex_api_spec.rb +1 -1
- data/spec/models/buy_closing_flow_spec.rb +140 -71
- data/spec/models/buy_opening_flow_spec.rb +126 -56
- data/spec/models/order_book_simulator_spec.rb +10 -10
- data/spec/models/robot_spec.rb +61 -47
- data/spec/models/sell_closing_flow_spec.rb +130 -62
- data/spec/models/sell_opening_flow_spec.rb +129 -60
- data/spec/spec_helper.rb +19 -16
- data/spec/support/bitex_stubs.rb +13 -14
- data/spec/support/bitstamp/bitstamp_api_wrapper_stubs.rb +35 -0
- data/spec/support/bitstamp/bitstamp_stubs.rb +91 -0
- metadata +60 -42
- data/lib/bitex_bot/models/bitfinex_api_wrapper.rb +0 -118
- data/lib/bitex_bot/models/bitstamp_api_wrapper.rb +0 -82
- data/lib/bitex_bot/models/itbit_api_wrapper.rb +0 -68
- data/lib/bitex_bot/models/kraken_api_wrapper.rb +0 -188
- data/spec/models/bitfinex_api_wrapper_spec.rb +0 -17
- data/spec/models/bitstamp_api_wrapper_spec.rb +0 -15
- data/spec/models/itbit_api_wrapper_spec.rb +0 -15
- data/spec/support/bitstamp_stubs.rb +0 -110
data/settings.rb.sample
CHANGED
@@ -1,82 +1,61 @@
|
|
1
|
-
# File to write log to. The log is rotated every
|
2
|
-
# logs are kept around.
|
1
|
+
# File to write log to. The log is rotated every 10.240.000 bytes, 10 oldest logs are kept around.
|
3
2
|
# If you want to log to stdout you can set 'file' to nil or remove the key.
|
4
|
-
# How much information to show in the log, valid values are debug, info, error
|
3
|
+
# How much information to show in the log, valid values are: :debug, :info, :error
|
5
4
|
log file: 'bitex_bot.log', level: :info
|
6
5
|
|
7
6
|
# Seconds to keep an order alive before recalculating the price and replacing it.
|
8
|
-
# Given the way in which the robot tries to find the safest price to place an
|
9
|
-
#
|
10
|
-
# competitive. About 20 seconds is a good number here.
|
7
|
+
# Given the way in which the robot tries to find the safest price to place an order, if the time to live is too long the price is
|
8
|
+
# not going to be competitive. About 20 seconds is a good number here.
|
11
9
|
time_to_live 20
|
12
10
|
|
13
|
-
#
|
14
|
-
# only available for bitex and itbit
|
15
|
-
sandbox false
|
16
|
-
|
17
|
-
# Which market to use for taking (we're always makers on bitex)
|
18
|
-
# 'itbit', 'bitstamp', 'bitfinex' or 'kraken'
|
19
|
-
taker 'bitstamp'
|
20
|
-
|
21
|
-
# Settings for buying on bitex and selling on bitstamp.
|
11
|
+
# Settings for buying on maker and selling on taker.
|
22
12
|
# amount_to_spend_per_order:
|
23
|
-
# Dollars to spend on each initial
|
13
|
+
# Dollars to spend on each initial maker bid.
|
24
14
|
# Should at least be 10.0 which is the current minimum order size on Bitex.
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# bid price not being competitive.
|
15
|
+
# If it's too large you're taking a higher risk if whatever happens and you cannot re sell it afterwards. Also, higher amounts
|
16
|
+
# result in your Bitex bid price not being competitive.
|
28
17
|
# A number between 10.0 and 1000.0 is reccommended.
|
29
18
|
# profit:
|
30
|
-
# Your profit when buying on
|
31
|
-
# After calculating the price at which bitcoins can be sold on
|
32
|
-
#
|
33
|
-
|
34
|
-
buying amount_to_spend_per_order: 10.0, profit: 0.5
|
19
|
+
# Your profit when buying on Bitex, 0.5 means 0.5%.
|
20
|
+
# After calculating the price at which bitcoins can be sold on taker, the robot deduces your profit from that price, and places
|
21
|
+
# a bid on Bitex paying a lower price.
|
22
|
+
buying amount_to_spend_per_order: 10.0.to_d, profit: 0.5.to_d
|
35
23
|
|
36
|
-
# Settings for selling on
|
24
|
+
# Settings for selling on maker and buying on taker.
|
37
25
|
# quantity_to_sell_per_order:
|
38
|
-
# Quantity to sell on each initial
|
39
|
-
# It should be at least 10 USD worth of BTC at current market prices, a bit
|
40
|
-
#
|
41
|
-
# small
|
42
|
-
#
|
43
|
-
#
|
44
|
-
# taking a higher risk if whatever happens ad you cannot re buy afterwards.
|
45
|
-
# Also, higher amounts result in your bitex bid price not being competitive.
|
26
|
+
# Quantity to sell on each initial maker ask.
|
27
|
+
# It should be at least 10 USD worth of BTC at current market prices, a bit more just to be safe. Otherwise Bitex will reject
|
28
|
+
# your orders for being too small.
|
29
|
+
# If it's too small then the robot is pointless, and if it's too large you're taking a higher risk if whatever happens ad you
|
30
|
+
# cannot re buy afterwards.
|
31
|
+
# Also, higher amounts result in your Bitex bid price not being competitive.
|
46
32
|
# A number between 0.05 and 2.0 is recommended.
|
47
33
|
# profit:
|
48
|
-
# Your profit when selling on
|
49
|
-
# After calculating the price at which bitcoins can be bought on
|
50
|
-
#
|
51
|
-
|
52
|
-
|
34
|
+
# Your profit when selling on Bitex, 0.5 means 0.5%.
|
35
|
+
# After calculating the price at which bitcoins can be bought on taker, the robot deduces your profit from that price and
|
36
|
+
# places an ask on Bitex charging a higher price.
|
37
|
+
selling quantity_to_sell_per_order: 0.1.to_d, profit: 0.5.to_d
|
38
|
+
|
39
|
+
# Quote fx rate for maker order book
|
40
|
+
foreign_exchange_rate 1.to_d
|
53
41
|
|
54
|
-
# This is your
|
55
|
-
# bitex gem: https://github.com/bitex-la/bitex-ruby
|
56
|
-
|
42
|
+
# This is your maker, at the moment only will operates with bitex
|
43
|
+
# api_key: it's passed in to the bitex gem: https://github.com/bitex-la/bitex-ruby.
|
44
|
+
# sandbox: Use sandbox environments instead of live environments.
|
45
|
+
# order_book: order book with which you will operate.
|
46
|
+
maker bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false }
|
57
47
|
|
58
|
-
# These are
|
59
|
-
#
|
60
|
-
bitstamp api_key: 'YOUR_API_KEY',
|
61
|
-
secret: 'YOUR_API_SECRET',
|
62
|
-
client_id: 'YOUR_BITSTAMP_USERNAME'
|
48
|
+
# These are the configurations we need for the markets currently supported.
|
49
|
+
# Turn on and off the chosen Bitstamp, Itbit or Kraken market with comments.
|
63
50
|
|
64
|
-
# These are passed in to the
|
65
|
-
|
66
|
-
itbit client_key: 'the-client-key',
|
67
|
-
secret: 'the-secret',
|
68
|
-
user_id: 'the-user-id',
|
69
|
-
default_wallet_id: 'wallet-000'
|
51
|
+
# These are passed in to the bitstamp gem: see https://github.com/kojnapp/bitstamp for more info.
|
52
|
+
taker bitstamp: { api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME' }
|
70
53
|
|
71
|
-
# These are passed in to the
|
72
|
-
#
|
73
|
-
bitfinex api_key: 'your_api_key',
|
74
|
-
api_secret: 'your_api_secret'
|
54
|
+
# These are passed in to the itbit gem: see https://github.com/bitex-la/itbit for more info.
|
55
|
+
# taker itbit: { client_key: 'client-key', secret: 'secret', user_id: 'user-id', default_wallet_id: 'wallet-000', sandbox: false }
|
75
56
|
|
76
|
-
# These are passed in to the kraken gem:
|
77
|
-
#
|
78
|
-
kraken api_key: 'your_api_key',
|
79
|
-
api_secret: 'your_api_secret'
|
57
|
+
# These are passed in to the kraken gem: see https://github.com/shideneyu/kraken_client for more info.
|
58
|
+
# taker kraken: { api_key: 'your_api_key', api_secret: 'your_api_secret' }
|
80
59
|
|
81
60
|
# Settings for the ActiveRecord Database to use.
|
82
61
|
# sqlite is just fine. Check this link for more options:
|
@@ -84,18 +63,16 @@ kraken api_key: 'your_api_key',
|
|
84
63
|
database adapter: :sqlite3, database: 'bitex_bot.db'
|
85
64
|
|
86
65
|
# The robot sends you emails whenever a problem occurs.
|
87
|
-
# If you do not want to receive emails just remove this 'mailer'
|
88
|
-
#
|
89
|
-
# It uses https://github.com/mikel/mail under the hood, so method
|
90
|
-
# is any valid delivery_method for teh mail gem.
|
66
|
+
# If you do not want to receive emails just remove this 'mailer' key and everything under it.
|
67
|
+
# It uses https://github.com/mikel/mail under the hood, so method is any valid delivery_method for teh mail gem.
|
91
68
|
# Options is the options hash passed in to delivery_method.
|
92
69
|
mailer from: 'robot@example.com',
|
93
70
|
to: 'you@example.com',
|
94
71
|
delivery_method: :smtp,
|
95
72
|
options: {
|
96
|
-
address:
|
73
|
+
address: 'your_smtp_server_address.com',
|
97
74
|
port: 587,
|
98
|
-
authentication:
|
75
|
+
authentication: 'plain',
|
99
76
|
enable_starttls_auto: true,
|
100
77
|
user_name: 'your_user_name',
|
101
78
|
password: 'your_smtp_password'
|
@@ -3,21 +3,93 @@ require 'spec_helper'
|
|
3
3
|
describe BitexBot::Settings do
|
4
4
|
describe '#to_hash' do
|
5
5
|
it 'returns a symbolized hash' do
|
6
|
-
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
|
13
|
-
:
|
14
|
-
|
15
|
-
:
|
16
|
-
|
17
|
-
:
|
18
|
-
:
|
19
|
-
|
20
|
-
|
6
|
+
described_class.to_hash.should eq(
|
7
|
+
log: { file: 'bitex_bot.log', level: :info },
|
8
|
+
time_to_live: 20,
|
9
|
+
buying: { amount_to_spend_per_order: 10, profit: 0.5 },
|
10
|
+
selling: { quantity_to_sell_per_order: 0.1, profit: 0.5 },
|
11
|
+
foreign_exchange_rate: 1,
|
12
|
+
|
13
|
+
maker: { bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false } },
|
14
|
+
# By default Bitstamp is taker market.
|
15
|
+
taker: { bitstamp: { api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME' } },
|
16
|
+
|
17
|
+
database: { adapter: :sqlite3, database: 'bitex_bot.db' },
|
18
|
+
mailer: {
|
19
|
+
from: 'robot@example.com',
|
20
|
+
to: 'you@example.com',
|
21
|
+
delivery_method: :smtp,
|
22
|
+
options: {
|
23
|
+
address: 'your_smtp_server_address.com',
|
24
|
+
port: 587,
|
25
|
+
authentication: 'plain',
|
26
|
+
enable_starttls_auto: true,
|
27
|
+
user_name: 'your_user_name',
|
28
|
+
password: 'your_smtp_password'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'fx_rate' do
|
35
|
+
context 'when Store isn´t loaded' do
|
36
|
+
it 'by default' do
|
37
|
+
described_class.fx_rate.should eq(1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when Store is loaded' do
|
42
|
+
before(:each) do
|
43
|
+
BitexBot::Store.stub(first: BitexBot::Store.new )
|
44
|
+
BitexBot::Store.any_instance.stub(fx_rate: fx_rate)
|
45
|
+
end
|
46
|
+
let(:fx_rate) { rand(10) }
|
47
|
+
|
48
|
+
it 'take rate from it' do
|
49
|
+
described_class.fx_rate.should eq(fx_rate)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'maker' do
|
55
|
+
{
|
56
|
+
bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false }
|
57
|
+
}.each do |market, market_settings|
|
58
|
+
before(:each) { described_class.stub(taker: BitexBot::SettingsClass.new(taker_hash)) }
|
59
|
+
|
60
|
+
let(:taker_hash) { { market => market_settings } }
|
61
|
+
|
62
|
+
context "for #{market}" do
|
63
|
+
it { described_class.taker.to_hash.should eq(taker_hash) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'taker' do
|
69
|
+
{
|
70
|
+
bitstamp: { api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME' },
|
71
|
+
itbit: { client_key: 'client-key', secret: 'secret', user_id: 'user-id', default_wallet_id: 'wallet-000', sandbox: false },
|
72
|
+
kraken: { api_key: 'your_api_key', api_secret: 'your_api_secret' }
|
73
|
+
}.each do |market, market_settings|
|
74
|
+
before(:each) { described_class.stub(taker: BitexBot::SettingsClass.new(taker_hash)) }
|
75
|
+
|
76
|
+
let(:taker_hash) { { market => market_settings } }
|
77
|
+
let(:taker_class) { "#{market.capitalize}ApiWrapper".constantize }
|
78
|
+
|
79
|
+
context "for #{market}" do
|
80
|
+
it { described_class.taker.to_hash.should eq(taker_hash) }
|
81
|
+
it { described_class.taker_class.should eq(taker_class) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'currencies by default' do
|
87
|
+
let(:order_book) { described_class.maker.bitex.order_book.to_s }
|
88
|
+
let(:base) { order_book.split('_')[0] }
|
89
|
+
let(:quote) { order_book.split('_')[1] }
|
90
|
+
|
91
|
+
it { described_class.base.should eq(base) }
|
92
|
+
it { described_class.quote.should eq(quote) }
|
21
93
|
end
|
22
94
|
end
|
23
95
|
end
|
data/spec/factories/bitex_buy.rb
CHANGED
data/spec/factories/open_buy.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
|
1
|
+
FactoryBot.define do
|
2
2
|
factory :open_buy, class: BitexBot::OpenBuy do
|
3
|
-
price 300.0
|
4
|
-
amount 600.0
|
5
|
-
quantity 2.0
|
6
|
-
transaction_id 12345678
|
7
3
|
association :opening_flow, factory: :buy_opening_flow
|
4
|
+
|
5
|
+
transaction_id 12345678
|
6
|
+
price 300
|
7
|
+
amount 600
|
8
|
+
quantity 2
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
factory :tiny_open_buy, class: BitexBot::OpenBuy do
|
11
|
-
price 400.0
|
12
|
-
amount 4.0
|
13
|
-
quantity 0.01
|
14
|
-
transaction_id 23456789
|
15
12
|
association :opening_flow, factory: :other_buy_opening_flow
|
13
|
+
|
14
|
+
transaction_id 23456789
|
15
|
+
price 400
|
16
|
+
amount 4
|
17
|
+
quantity 0.01
|
16
18
|
end
|
17
19
|
end
|
data/spec/factories/open_sell.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
|
1
|
+
FactoryBot.define do
|
2
2
|
factory :open_sell, class: BitexBot::OpenSell do
|
3
|
-
|
4
|
-
|
5
|
-
quantity 2.0
|
3
|
+
association :opening_flow, factory: :sell_opening_flow
|
4
|
+
|
6
5
|
transaction_id 12345678
|
7
|
-
|
6
|
+
price 300
|
7
|
+
amount 600
|
8
|
+
quantity 2
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
factory :tiny_open_sell, class: BitexBot::OpenSell do
|
11
|
-
|
12
|
-
|
13
|
-
quantity 0.01
|
12
|
+
association :opening_flow, factory: :other_sell_opening_flow
|
13
|
+
|
14
14
|
transaction_id 23456789
|
15
|
-
|
15
|
+
price 400
|
16
|
+
amount 4
|
17
|
+
quantity 0.01
|
16
18
|
end
|
17
19
|
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BitstampApiWrapper do
|
4
|
+
let(:api_wrapper) { described_class }
|
5
|
+
let(:taker_settings) do
|
6
|
+
BitexBot::SettingsClass.new(
|
7
|
+
bitstamp: {
|
8
|
+
api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME'
|
9
|
+
}
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
BitexBot::Settings.stub(taker: taker_settings)
|
15
|
+
BitexBot::Robot.setup
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'Sends User-Agent header' do
|
19
|
+
url = 'https://www.bitstamp.net/api/v2/balance/btcusd/'
|
20
|
+
stub_stuff = stub_request(:post, url).with(headers: { 'User-Agent': BitexBot.user_agent })
|
21
|
+
|
22
|
+
# we don't care about the response
|
23
|
+
api_wrapper.balance rescue nil
|
24
|
+
|
25
|
+
expect(stub_stuff).to have_been_requested
|
26
|
+
end
|
27
|
+
|
28
|
+
def stub_balance(balance: '0.5', reserved: '1.5', available: '2.0', fee: '0.2')
|
29
|
+
Bitstamp.stub(:balance) do
|
30
|
+
{
|
31
|
+
'btc_balance' => balance,
|
32
|
+
'btc_reserved' => reserved,
|
33
|
+
'btc_available' => available,
|
34
|
+
'usd_balance' => balance,
|
35
|
+
'usd_reserved' => reserved,
|
36
|
+
'usd_available' => balance,
|
37
|
+
'fee' => fee
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it '#balance' do
|
43
|
+
stub_balance
|
44
|
+
|
45
|
+
balance = api_wrapper.balance
|
46
|
+
balance.should be_a(ApiWrapper::BalanceSummary)
|
47
|
+
balance.btc.should be_a(ApiWrapper::Balance)
|
48
|
+
balance.usd.should be_a(ApiWrapper::Balance)
|
49
|
+
|
50
|
+
btc = balance.btc
|
51
|
+
btc.total.should be_a(BigDecimal)
|
52
|
+
btc.reserved.should be_a(BigDecimal)
|
53
|
+
btc.available.should be_a(BigDecimal)
|
54
|
+
|
55
|
+
usd = balance.usd
|
56
|
+
usd.total.should be_a(BigDecimal)
|
57
|
+
usd.reserved.should be_a(BigDecimal)
|
58
|
+
usd.available.should be_a(BigDecimal)
|
59
|
+
|
60
|
+
balance.fee.should be_a(BigDecimal)
|
61
|
+
end
|
62
|
+
|
63
|
+
it '#cancel' do
|
64
|
+
stub_orders
|
65
|
+
|
66
|
+
expect(api_wrapper.orders.sample).to respond_to(:cancel!)
|
67
|
+
end
|
68
|
+
|
69
|
+
def stub_order_book(count: 3, price: 1.5, amount: 2.5)
|
70
|
+
Bitstamp.stub(:order_book) do
|
71
|
+
{
|
72
|
+
'timestamp' => Time.now.to_i.to_s,
|
73
|
+
'bids' => count.times.map { |i| [(price + i).to_s, (amount + i).to_s] },
|
74
|
+
'asks' => count.times.map { |i| [(price + i).to_s, (amount + i).to_s] }
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it '#order_book' do
|
80
|
+
stub_order_book
|
81
|
+
|
82
|
+
order_book = api_wrapper.order_book
|
83
|
+
order_book.should be_a(ApiWrapper::OrderBook)
|
84
|
+
order_book.bids.all? { |bid| bid.should be_a(ApiWrapper::OrderSummary) }
|
85
|
+
order_book.asks.all? { |ask| ask.should be_a(ApiWrapper::OrderSummary) }
|
86
|
+
order_book.timestamp.should be_a(Integer)
|
87
|
+
|
88
|
+
bid = order_book.bids.sample
|
89
|
+
bid.price.should be_a(BigDecimal)
|
90
|
+
bid.quantity.should be_a(BigDecimal)
|
91
|
+
|
92
|
+
ask = order_book.asks.sample
|
93
|
+
ask.price.should be_a(BigDecimal)
|
94
|
+
ask.quantity.should be_a(BigDecimal)
|
95
|
+
end
|
96
|
+
|
97
|
+
# [<Bitstamp::Order @id=76, @type=0, @price='1.1', @amount='1.0', @datetime='2013-09-26 23:15:04'>]
|
98
|
+
def stub_orders(count: 1, price: 1.5, amount: 2.5)
|
99
|
+
Bitstamp.orders.stub(:all) do
|
100
|
+
count.times.map do |i|
|
101
|
+
Bitstamp::Order.new(
|
102
|
+
id: i,
|
103
|
+
type: (i % 2),
|
104
|
+
price: (price + 1).to_s,
|
105
|
+
amount: (amount + i).to_s,
|
106
|
+
datetime: 1.seconds.ago.strftime('%Y-%m-%d %H:%m:%S')
|
107
|
+
)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it '#orders' do
|
113
|
+
stub_orders
|
114
|
+
|
115
|
+
api_wrapper.orders.all? { |o| o.should be_a(ApiWrapper::Order) }
|
116
|
+
|
117
|
+
order = api_wrapper.orders.sample
|
118
|
+
order.id.should be_a(String)
|
119
|
+
order.type.should be_a(Symbol)
|
120
|
+
order.price.should be_a(BigDecimal)
|
121
|
+
order.amount.should be_a(BigDecimal)
|
122
|
+
order.timestamp.should be_a(Integer)
|
123
|
+
|
124
|
+
expect(order).to respond_to(:cancel!)
|
125
|
+
end
|
126
|
+
|
127
|
+
context '#place_order' do
|
128
|
+
it 'raises OrderNotFound error on bitstamp errors' do
|
129
|
+
Bitstamp.orders.stub(:buy) do
|
130
|
+
raise OrderNotFound
|
131
|
+
end
|
132
|
+
|
133
|
+
expect { api_wrapper.place_order(:buy, 10, 100) }.to raise_exception(OrderNotFound)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# [<Bitstamp::Transactions @tid=14, @price='1.9', @amount='1.1', @date='1380648951'>]
|
138
|
+
def stub_transactions(count: 1, price: 1.5, amount: 2.5)
|
139
|
+
Bitstamp.stub(:transactions) do
|
140
|
+
count.times.map do |i|
|
141
|
+
double(
|
142
|
+
tid: i,
|
143
|
+
date: 1.seconds.ago.to_i,
|
144
|
+
price: (price + i).to_s,
|
145
|
+
amount: (amount + i).to_s
|
146
|
+
)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it '#transactions' do
|
152
|
+
stub_transactions
|
153
|
+
|
154
|
+
api_wrapper.transactions.all? { |o| o.should be_a(ApiWrapper::Transaction) }
|
155
|
+
|
156
|
+
transaction = api_wrapper.transactions.sample
|
157
|
+
transaction.id.should be_a(Integer)
|
158
|
+
transaction.price.should be_a(BigDecimal)
|
159
|
+
transaction.amount.should be_a(BigDecimal)
|
160
|
+
transaction.timestamp.should be_a(Integer)
|
161
|
+
end
|
162
|
+
|
163
|
+
# [<Bitstamp::UserTransaction @id=76, @order_id=14, @type=1, @usd='0.00', @btc='-3.078', @btc_usd='0.00', @fee='0.00', @datetime='2013-09-26 13:46:59'>]
|
164
|
+
def stub_user_transactions(count: 1, usd: 1.5, btc: 2.5, btc_usd: 3.5, fee: 0.05)
|
165
|
+
Bitstamp.user_transactions.stub(:all) do
|
166
|
+
count.times.map do |i|
|
167
|
+
double(
|
168
|
+
id: i,
|
169
|
+
order_id: i,
|
170
|
+
type: (i % 2),
|
171
|
+
usd: (usd + i).to_s,
|
172
|
+
btc: (btc + i).to_s,
|
173
|
+
btc_usd: (btc_usd + i).to_s,
|
174
|
+
fee: fee.to_s,
|
175
|
+
datetime: 1.seconds.ago.strftime('%Y-%m-%d %H:%m:%S')
|
176
|
+
)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it '#user_transaction' do
|
182
|
+
stub_user_transactions
|
183
|
+
BitstampApiWrapper.user_transactions.all? { |ut| ut.should be_a(ApiWrapper::UserTransaction) }
|
184
|
+
|
185
|
+
user_transaction = BitstampApiWrapper.user_transactions.sample
|
186
|
+
user_transaction.usd.should be_a(BigDecimal)
|
187
|
+
user_transaction.btc.should be_a(BigDecimal)
|
188
|
+
user_transaction.btc_usd.should be_a(BigDecimal)
|
189
|
+
user_transaction.order_id.should be_a(Integer)
|
190
|
+
user_transaction.fee.should be_a(BigDecimal)
|
191
|
+
user_transaction.type.should be_a(Integer)
|
192
|
+
user_transaction.timestamp.should be_a(Integer)
|
193
|
+
end
|
194
|
+
|
195
|
+
it '#find_lost' do
|
196
|
+
stub_orders
|
197
|
+
|
198
|
+
api_wrapper.orders.all? { |o| api_wrapper.find_lost(o.type, o.price, o.amount).present? }
|
199
|
+
end
|
200
|
+
end
|