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