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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +63 -0
  3. data/.rubocop.yml +33 -0
  4. data/Gemfile +1 -1
  5. data/Rakefile +1 -1
  6. data/bin/bitex_bot +1 -1
  7. data/bitex_bot.gemspec +34 -34
  8. data/lib/bitex_bot/database.rb +67 -67
  9. data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +142 -0
  10. data/lib/bitex_bot/models/api_wrappers/bitstamp/bitstamp_api_wrapper.rb +137 -0
  11. data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +116 -0
  12. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +111 -0
  13. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +117 -0
  14. data/lib/bitex_bot/models/buy_closing_flow.rb +23 -16
  15. data/lib/bitex_bot/models/buy_opening_flow.rb +48 -54
  16. data/lib/bitex_bot/models/close_buy.rb +2 -2
  17. data/lib/bitex_bot/models/closing_flow.rb +98 -79
  18. data/lib/bitex_bot/models/open_buy.rb +11 -10
  19. data/lib/bitex_bot/models/open_sell.rb +11 -10
  20. data/lib/bitex_bot/models/opening_flow.rb +157 -99
  21. data/lib/bitex_bot/models/order_book_simulator.rb +62 -67
  22. data/lib/bitex_bot/models/sell_closing_flow.rb +25 -20
  23. data/lib/bitex_bot/models/sell_opening_flow.rb +47 -54
  24. data/lib/bitex_bot/models/store.rb +3 -1
  25. data/lib/bitex_bot/robot.rb +203 -176
  26. data/lib/bitex_bot/settings.rb +71 -12
  27. data/lib/bitex_bot/version.rb +1 -1
  28. data/lib/bitex_bot.rb +40 -16
  29. data/settings.rb.sample +43 -66
  30. data/spec/bitex_bot/settings_spec.rb +87 -15
  31. data/spec/factories/bitex_buy.rb +3 -3
  32. data/spec/factories/bitex_sell.rb +3 -3
  33. data/spec/factories/buy_opening_flow.rb +1 -1
  34. data/spec/factories/open_buy.rb +12 -10
  35. data/spec/factories/open_sell.rb +12 -10
  36. data/spec/factories/sell_opening_flow.rb +1 -1
  37. data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +200 -0
  38. data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +176 -0
  39. data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +209 -0
  40. data/spec/models/bitex_api_spec.rb +1 -1
  41. data/spec/models/buy_closing_flow_spec.rb +140 -71
  42. data/spec/models/buy_opening_flow_spec.rb +126 -56
  43. data/spec/models/order_book_simulator_spec.rb +10 -10
  44. data/spec/models/robot_spec.rb +61 -47
  45. data/spec/models/sell_closing_flow_spec.rb +130 -62
  46. data/spec/models/sell_opening_flow_spec.rb +129 -60
  47. data/spec/spec_helper.rb +19 -16
  48. data/spec/support/bitex_stubs.rb +13 -14
  49. data/spec/support/bitstamp/bitstamp_api_wrapper_stubs.rb +35 -0
  50. data/spec/support/bitstamp/bitstamp_stubs.rb +91 -0
  51. metadata +60 -42
  52. data/lib/bitex_bot/models/bitfinex_api_wrapper.rb +0 -118
  53. data/lib/bitex_bot/models/bitstamp_api_wrapper.rb +0 -82
  54. data/lib/bitex_bot/models/itbit_api_wrapper.rb +0 -68
  55. data/lib/bitex_bot/models/kraken_api_wrapper.rb +0 -188
  56. data/spec/models/bitfinex_api_wrapper_spec.rb +0 -17
  57. data/spec/models/bitstamp_api_wrapper_spec.rb +0 -15
  58. data/spec/models/itbit_api_wrapper_spec.rb +0 -15
  59. 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 10240000 bytes, 10 oldest
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
- # order, if the time to live is too long the price is not going to be
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
- # Use sandbox environments instead of live environments
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 bitex bid.
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
- # if it's too large you're taking a higher risk if whatever happens and you
26
- # cannot re sell it afterwards. Also, higher amounts result in your bitex
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 bitex, 0.5 means 0.5%.
31
- # After calculating the price at which bitcoins can be sold on bitstamp, the
32
- # robot deduces your profit from that price, and places a bid on bitex paying
33
- # a lower price.
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 bitex and buying on bitstamp.
24
+ # Settings for selling on maker and buying on taker.
37
25
  # quantity_to_sell_per_order:
38
- # Quantity to sell on each initial bitex Ask.
39
- # It should be at least 10 USD worth of BTC at current market prices, a bit
40
- # more just to be safe. Otherwise Bitex will reject your orders for being too
41
- # small.
42
- # If it's too large then you're taking a risk if whatever happens and you
43
- # If it's too small then the robot is pointless, and if it's too large you're
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 bitex, 0.5 means 0.5%.
49
- # After calculating the price at which bitcoins can be bought on bitstamp,
50
- # the robot deduces your profit from that price and places an ask on bitex
51
- # charging a higher price.
52
- selling quantity_to_sell_per_order: 0.1, profit: 0.5
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 bitex api key, it's passed in to the
55
- # bitex gem: https://github.com/bitex-la/bitex-ruby
56
- bitex 'your_bitex_api_key_which_should_be_kept_safe'
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 passed in to the bitstamp gem:
59
- # see https://github.com/kojnapp/bitstamp for more info.
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 itbit gem:
65
- # see https://github.com/bitex-la/itbit for more info.
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 bitfinex gem:
72
- # see https://github.com/bitfinexcom/bitfinex-api-rb for more info.
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
- # see https://github.com/shideneyu/kraken_client for more info.
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
- # key and everything under it.
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: "your_smtp_server_address.com",
73
+ address: 'your_smtp_server_address.com',
97
74
  port: 587,
98
- authentication: "plain",
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
- expect(BitexBot::Settings.to_hash).to eq({
7
- :bitex => "your_bitex_api_key_which_should_be_kept_safe",
8
- :bitfinex => {:api_key=>"your_api_key", :api_secret=>"your_api_secret"},
9
- :bitstamp => {:api_key=>"YOUR_API_KEY", :secret=>"YOUR_API_SECRET", :client_id=>"YOUR_BITSTAMP_USERNAME"},
10
- :buying => {:amount_to_spend_per_order=>10.0, :profit=>0.5},
11
- :database => {:adapter=>:sqlite3, :database=>"bitex_bot.db"},
12
- :itbit => {:client_key=>"the-client-key", :secret=>"the-secret", :user_id=>"the-user-id", :default_wallet_id=>"wallet-000"},
13
- :kraken => {:api_key=>"your_api_key", :api_secret=>"your_api_secret"},
14
- :log => {:file=>"bitex_bot.log", :level=>:info},
15
- :mailer => {:from=>"robot@example.com", :to=>"you@example.com", :delivery_method=>:smtp, :options=>{:address=>"your_smtp_server_address.com", :port=>587, :authentication=>"plain", :enable_starttls_auto=>true, :user_name=>"your_user_name", :password=>"your_smtp_password"}},
16
- :sandbox => false,
17
- :selling => {:quantity_to_sell_per_order=>0.1, :profit=>0.5},
18
- :taker => "bitstamp",
19
- :time_to_live => 20
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
@@ -1,8 +1,8 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  factory :bitex_buy, class: Bitex::Buy do
3
3
  id 12345678
4
- created_at{ Time.now }
5
- specie :btc
4
+ created_at { Time.now }
5
+ order_book :btc_usd
6
6
  quantity 2.0
7
7
  amount 600.0
8
8
  fee 0.05
@@ -1,8 +1,8 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  factory :bitex_sell, class: Bitex::Sell do
3
3
  id 12345678
4
- created_at{ Time.now }
5
- specie :btc
4
+ created_at { Time.now }
5
+ order_book :btc_usd
6
6
  quantity 2.0
7
7
  amount 600.0
8
8
  fee 0.05
@@ -1,4 +1,4 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  factory :buy_opening_flow, class: BitexBot::BuyOpeningFlow do
3
3
  price 300.0
4
4
  value_to_use 600.0
@@ -1,17 +1,19 @@
1
- FactoryGirl.define do
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
@@ -1,17 +1,19 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  factory :open_sell, class: BitexBot::OpenSell do
3
- price 300.0
4
- amount 600.0
5
- quantity 2.0
3
+ association :opening_flow, factory: :sell_opening_flow
4
+
6
5
  transaction_id 12345678
7
- association :opening_flow, factory: :sell_opening_flow
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
- price 400.0
12
- amount 4.0
13
- quantity 0.01
12
+ association :opening_flow, factory: :other_sell_opening_flow
13
+
14
14
  transaction_id 23456789
15
- association :opening_flow, factory: :other_sell_opening_flow
15
+ price 400
16
+ amount 4
17
+ quantity 0.01
16
18
  end
17
19
  end
@@ -1,4 +1,4 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  factory :sell_opening_flow, class: BitexBot::SellOpeningFlow do
3
3
  price 300.0
4
4
  value_to_use 2.0
@@ -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