bitex_bot 0.3.7 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|