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
@@ -1,114 +1,164 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe BitexBot::SellOpeningFlow do
4
- before(:each) do
5
- Bitex.api_key = "valid_key"
6
- end
7
- let(:store){ BitexBot::Store.create }
4
+ before(:each) { Bitex.api_key = 'valid_key' }
5
+
6
+ let(:store) { BitexBot::Store.create }
8
7
 
9
8
  it { should validate_presence_of :status }
10
9
  it { should validate_presence_of :price }
11
10
  it { should validate_presence_of :value_to_use }
12
11
  it { should validate_presence_of :order_id }
13
- it { should(validate_inclusion_of(:status)
14
- .in_array(BitexBot::SellOpeningFlow.statuses)) }
12
+ it { should(validate_inclusion_of(:status).in_array(BitexBot::SellOpeningFlow.statuses)) }
15
13
 
16
- describe "when creating a selling flow" do
17
- it "sells 2 bitcoin" do
14
+ describe 'when creating a selling flow' do
15
+ it 'sells 2 bitcoin' do
18
16
  stub_bitex_orders
19
17
  BitexBot::Settings.stub(time_to_live: 3,
20
18
  selling: double(quantity_to_sell_per_order: 2, profit: 0))
21
19
 
22
20
  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
23
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
24
-
21
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
22
+ store)
23
+
24
+ flow.order_id.should == 12345
25
25
  flow.value_to_use.should == 2
26
26
  flow.price.should >= flow.suggested_closing_price
27
- flow.price.should == "20.15037593984962".to_d
27
+ flow.price.truncate(14).should == '20.15037593984962'.to_d
28
28
  flow.suggested_closing_price.should == 20
29
- flow.order_id.should == 12345
30
29
  end
31
30
 
32
- it "sells 4 bitcoin" do
31
+ let(:order_id) { 12_345 }
32
+ let(:usd_price) { '25.18_796_992_481_203'.to_d }
33
+ let(:suggested_closing_price) { 25.to_d }
34
+ let(:amount_to_sell) { 4.to_d }
35
+ let(:btc_balance) { 100_000.to_d }
36
+ let(:maker_fee) { 0.5.to_d }
37
+ let(:taker_fee) { 0.25.to_d }
38
+ let(:orderbook) { bitstamp_api_wrapper_order_book.asks }
39
+ let(:transactions) { bitstamp_api_wrapper_transactions_stub }
40
+
41
+ it 'sells 4 bitcoin' do
42
+ BitexBot::Settings.stub(
43
+ time_to_live: 3,
44
+ selling: double(quantity_to_sell_per_order: amount_to_sell, profit: 0)
45
+ )
33
46
  stub_bitex_orders
34
- BitexBot::Settings.stub(time_to_live: 3,
35
- selling: double(quantity_to_sell_per_order: 4, profit: 0))
36
47
 
37
- flow = BitexBot::SellOpeningFlow.create_for_market(1000,
38
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
39
-
40
- flow.value_to_use.should == 4
48
+ flow =
49
+ BitexBot::SellOpeningFlow.create_for_market(
50
+ btc_balance,
51
+ orderbook,
52
+ transactions,
53
+ maker_fee,
54
+ taker_fee,
55
+ store
56
+ )
57
+
58
+ flow.order_id.should eq order_id
59
+ flow.value_to_use.should eq amount_to_sell
41
60
  flow.price.should >= flow.suggested_closing_price
42
- flow.price.round(14).should == "25.18796992481203".to_d
43
- flow.suggested_closing_price.should == 25
44
- flow.order_id.should == 12345
61
+ flow.price.should eq usd_price
62
+ flow.suggested_closing_price.should eq suggested_closing_price
63
+ end
64
+
65
+ let(:other_fx_rate) { 10.to_d }
66
+
67
+ it 'sells 4 bitcoin' do
68
+ BitexBot::Settings.stub(
69
+ fx_rate: other_fx_rate,
70
+ time_to_live: 3,
71
+ selling: double(quantity_to_sell_per_order: amount_to_sell, profit: 0)
72
+ )
73
+ stub_bitex_orders
74
+
75
+ flow =
76
+ BitexBot::SellOpeningFlow.create_for_market(
77
+ btc_balance,
78
+ orderbook,
79
+ transactions,
80
+ maker_fee,
81
+ taker_fee,
82
+ store
83
+ )
84
+
85
+ flow.order_id.should eq order_id
86
+ flow.value_to_use.should eq amount_to_sell
87
+ flow.price.should >= flow.suggested_closing_price * other_fx_rate
88
+ flow.price.truncate(13).should eq usd_price * other_fx_rate
89
+ flow.suggested_closing_price.should eq suggested_closing_price
45
90
  end
46
-
47
- it "raises the price to charge on bitex to take a profit" do
91
+
92
+ it 'raises the price to charge on bitex to take a profit' do
48
93
  stub_bitex_orders
49
94
  BitexBot::Settings.stub(time_to_live: 3,
50
- selling: double(quantity_to_sell_per_order: 4, profit: 50))
95
+ selling: double(quantity_to_sell_per_order: 4, profit: 50.to_d))
51
96
 
52
97
  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
53
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
54
-
98
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
99
+ store)
100
+
101
+ flow.order_id.should == 12345
55
102
  flow.value_to_use.should == 4
56
103
  flow.price.should >= flow.suggested_closing_price
57
- flow.price.round(14).should == "37.78195488721804".to_d
104
+ flow.price.round(14).should == '37.78195488721804'.to_d
58
105
  flow.suggested_closing_price.should == 25
59
- flow.order_id.should == 12345
60
106
  end
61
-
62
- it "fails when there is a problem placing the ask on bitex" do
63
- Bitex::Ask.stub(:create!) do
64
- raise StandardError.new("Cannot Create")
65
- end
66
107
 
108
+ it 'fails when there is a problem placing the ask on bitex' do
109
+ Bitex::Ask.stub(:create!) { raise StandardError.new('Cannot Create') }
67
110
  BitexBot::Settings.stub(time_to_live: 3,
68
111
  selling: double(quantity_to_sell_per_order: 4, profit: 50))
69
112
 
70
113
  expect do
71
114
  flow = BitexBot::SellOpeningFlow.create_for_market(100000,
72
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
115
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
116
+ store)
117
+
73
118
  flow.should be_nil
74
119
  BitexBot::SellOpeningFlow.count.should == 0
75
120
  end.to raise_exception(BitexBot::CannotCreateFlow)
76
121
  end
77
-
78
- it "fails when there are not enough USD to re-buy in the other exchange" do
122
+
123
+ it 'fails when there are not enough USD to re-buy in the other exchange' do
79
124
  stub_bitex_orders
80
125
  BitexBot::Settings.stub(time_to_live: 3,
81
126
  selling: double(quantity_to_sell_per_order: 4, profit: 50))
82
127
 
83
128
  expect do
84
129
  flow = BitexBot::SellOpeningFlow.create_for_market(1,
85
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
130
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
131
+ store)
132
+
86
133
  flow.should be_nil
87
134
  BitexBot::SellOpeningFlow.count.should == 0
88
135
  end.to raise_exception(BitexBot::CannotCreateFlow)
89
136
  end
90
137
 
91
- it "Prioritizes profit from store" do
92
- store = BitexBot::Store.new(selling_profit: 0.5)
138
+ it 'Prioritizes profit from store' do
93
139
  stub_bitex_orders
94
140
  BitexBot::Settings.stub(time_to_live: 3,
95
141
  selling: double(quantity_to_sell_per_order: 2, profit: 0))
96
142
 
143
+ store = BitexBot::Store.new(selling_profit: 0.5)
97
144
  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
98
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
99
-
100
- flow.price.round(14).should == "20.25112781954887".to_d
145
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
146
+ store)
147
+
148
+ flow.price.round(14).should == '20.25112781954887'.to_d
101
149
  end
102
150
  end
103
-
104
- describe "when fetching open positions" do
105
- let(:flow){ create(:sell_opening_flow) }
151
+
152
+ describe 'when fetching open positions' do
153
+ let(:flow) { create(:sell_opening_flow) }
106
154
 
107
155
  it 'only gets sells' do
108
- flow.order_id.should == 12345
109
156
  stub_bitex_transactions
157
+
158
+ flow.order_id.should == 12345
110
159
  expect do
111
160
  all = BitexBot::SellOpeningFlow.sync_open_positions
161
+
112
162
  all.size.should == 1
113
163
  all.first.tap do |o|
114
164
  o.price.should == 300.0
@@ -117,50 +167,69 @@ describe BitexBot::SellOpeningFlow do
117
167
  o.transaction_id.should == 12345678
118
168
  o.opening_flow.should == flow
119
169
  end
120
- end.to change{ BitexBot::OpenSell.count }.by(1)
170
+ end.to change { BitexBot::OpenSell.count }.by(1)
121
171
  end
122
-
172
+
123
173
  it 'does not register the same buy twice' do
124
- flow.order_id.should == 12345
125
174
  stub_bitex_transactions
175
+
176
+ flow.order_id.should == 12345
126
177
  BitexBot::SellOpeningFlow.sync_open_positions
127
178
  BitexBot::OpenSell.count.should == 1
179
+
128
180
  Timecop.travel 1.second.from_now
129
181
  stub_bitex_transactions(build(:bitex_sell, id: 23456))
182
+
130
183
  expect do
131
184
  news = BitexBot::SellOpeningFlow.sync_open_positions
132
185
  news.first.transaction_id.should == 23456
133
- end.to change{ BitexBot::OpenSell.count }.by(1)
186
+ end.to change { BitexBot::OpenSell.count }.by(1)
134
187
  end
135
-
136
- it 'does not register litecoin buys' do
188
+
189
+ it 'does not register buys from another order book' do
190
+ Bitex::Trade.stub(all: [build(:bitex_sell, id: 23456, order_book: :btc_ars)])
191
+
137
192
  flow.order_id.should == 12345
138
- Bitex::Trade.stub(all: [build(:bitex_sell, id: 23456, specie: :ltc)])
139
193
  expect do
140
194
  BitexBot::SellOpeningFlow.sync_open_positions.should be_empty
141
- end.not_to change{ BitexBot::OpenSell.count }
195
+ end.not_to change { BitexBot::OpenSell.count }
142
196
  BitexBot::OpenSell.count.should == 0
143
197
  end
144
-
198
+
145
199
  it 'does not register buys from unknown bids' do
146
200
  stub_bitex_transactions
201
+
147
202
  expect do
148
203
  BitexBot::SellOpeningFlow.sync_open_positions.should be_empty
149
- end.not_to change{ BitexBot::OpenSell.count }
204
+ end.not_to change { BitexBot::OpenSell.count }
150
205
  end
151
206
  end
152
-
207
+
153
208
  it 'cancels the associated bitex bid' do
154
209
  stub_bitex_orders
155
210
  BitexBot::Settings.stub(time_to_live: 3,
156
211
  selling: double(quantity_to_sell_per_order: 4, profit: 50))
157
212
 
158
213
  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
159
- bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25, store)
160
-
214
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
215
+ store)
216
+
161
217
  flow.finalise!
162
218
  flow.should be_settling
163
219
  flow.finalise!
164
220
  flow.should be_finalised
165
221
  end
222
+
223
+ it 'order has expected order book' do
224
+ stub_bitex_orders
225
+ BitexBot::Settings.stub(time_to_live: 3,
226
+ selling: double(quantity_to_sell_per_order: 2, profit: 0))
227
+
228
+ flow = subject.class.create_for_market(1000,
229
+ bitstamp_api_wrapper_order_book.asks, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
230
+ store)
231
+
232
+ order = subject.class.order_class.find(flow.order_id)
233
+ order.order_book.should eq BitexBot::Settings.maker_settings.order_book
234
+ end
166
235
  end
data/spec/spec_helper.rb CHANGED
@@ -1,50 +1,53 @@
1
1
  require 'bundler/setup'
2
2
  Bundler.setup
3
3
 
4
- require "bitex_bot/settings"
4
+ require 'bitex_bot/settings'
5
5
  BitexBot::Settings.load_test
6
- require 'bitex_bot'
7
- require 'factory_girl'
6
+
7
+ require 'byebug'
8
8
  require 'database_cleaner'
9
+ require 'factory_bot'
9
10
  require 'shoulda/matchers'
10
11
  require 'timecop'
11
12
  require 'webmock/rspec'
12
- FactoryGirl.find_definitions
13
13
 
14
- Dir[File.dirname(__FILE__) + '/support/*.rb'].each {|file| require file }
14
+ require 'bitex_bot'
15
+ FactoryBot.find_definitions
16
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |file| require file }
15
17
 
16
18
  # Automatically do rake db:test:prepare
17
19
  ActiveRecord::Migration.maintain_test_schema!
18
20
 
19
- # Transactional fixtures do not work with Selenium tests, because Capybara
20
- # uses a separate server thread, which the transactions would be hidden
21
- # from. We hence use DatabaseCleaner to truncate our test database.
21
+ # Transactional fixtures do not work with Selenium tests, because Capybara uses a separate server thread, which the transactions
22
+ # would be hidden from. We hence use DatabaseCleaner to truncate our test database.
22
23
  DatabaseCleaner.strategy = :truncation
23
24
 
24
25
  RSpec.configure do |config|
25
- config.include(FactoryGirl::Syntax::Methods)
26
+ config.include(FactoryBot::Syntax::Methods)
26
27
  config.include(Shoulda::Matchers::ActiveModel)
27
28
  config.include(Shoulda::Matchers::ActiveRecord)
28
29
  config.mock_with :rspec do |mocks|
29
30
  mocks.yield_receiver_to_any_instance_implementation_blocks = true
30
- mocks.syntax = [:expect, :should]
31
+ mocks.syntax = %i[expect should]
31
32
  end
32
33
  config.expect_with :rspec do |c|
33
- c.syntax = [:expect, :should]
34
+ c.syntax = %i[expect should]
34
35
  end
35
-
36
+
36
37
  config.before(:all) do
37
38
  BitexBot::Robot.logger = Logger.new('/dev/null')
38
- BitexBot::Robot.test_mode = true
39
+ end
40
+
41
+ config.before(:each) do
42
+ BitexBot::Robot.stub(:sleep_for)
39
43
  end
40
44
 
41
45
  config.after(:each) do
42
- DatabaseCleaner.clean # Truncate the database
46
+ DatabaseCleaner.clean
43
47
  Timecop.return
44
48
  end
45
49
 
46
- config.order = "random"
50
+ config.order = 'random'
47
51
  end
48
52
 
49
53
  I18n.enforce_available_locales = false
50
-
@@ -1,8 +1,8 @@
1
1
  module BitexStubs
2
- mattr_accessor(:bids){ {} }
3
- mattr_accessor(:asks){ {} }
4
- mattr_accessor(:active_bids){ {} }
5
- mattr_accessor(:active_asks){ {} }
2
+ mattr_accessor(:bids) { {} }
3
+ mattr_accessor(:asks) { {} }
4
+ mattr_accessor(:active_bids) { {} }
5
+ mattr_accessor(:active_asks) { {} }
6
6
 
7
7
  def stub_bitex_orders
8
8
  Bitex::Order.stub(:all) do
@@ -17,15 +17,15 @@ module BitexStubs
17
17
  BitexStubs.asks[id]
18
18
  end
19
19
 
20
- Bitex::Bid.stub(:create!) do |specie, to_spend, price|
20
+ Bitex::Bid.stub(:create!) do |order_book, to_spend, price|
21
21
  bid = Bitex::Bid.new
22
22
  bid.id = 12345
23
23
  bid.created_at = Time.now
24
24
  bid.price = price
25
25
  bid.amount = to_spend
26
26
  bid.remaining_amount = to_spend
27
- bid.status = :executing
28
- bid.specie = specie
27
+ bid.status = :executing
28
+ bid.order_book = order_book
29
29
  bid.stub(:cancel!) do
30
30
  bid.status = :cancelled
31
31
  BitexStubs.active_bids.delete(bid.id)
@@ -36,15 +36,15 @@ module BitexStubs
36
36
  bid
37
37
  end
38
38
 
39
- Bitex::Ask.stub(:create!) do |specie, to_sell, price|
39
+ Bitex::Ask.stub(:create!) do |order_book, to_sell, price|
40
40
  ask = Bitex::Ask.new
41
41
  ask.id = 12345
42
42
  ask.created_at = Time.now
43
43
  ask.price = price
44
44
  ask.quantity = to_sell
45
45
  ask.remaining_quantity = to_sell
46
- ask.status = :executing
47
- ask.specie = specie
46
+ ask.status = :executing
47
+ ask.order_book = order_book
48
48
  ask.stub(:cancel!) do
49
49
  ask.status = :cancelled
50
50
  BitexStubs.active_asks.delete(ask.id)
@@ -55,11 +55,10 @@ module BitexStubs
55
55
  ask
56
56
  end
57
57
  end
58
-
58
+
59
59
  def stub_bitex_transactions(*extra_transactions)
60
- Bitex::Trade.stub(all: extra_transactions + [
61
- build(:bitex_buy), build(:bitex_sell)
62
- ])
60
+ Bitex::Trade.stub(all: extra_transactions + [build(:bitex_buy), build(:bitex_sell)])
63
61
  end
64
62
  end
63
+
65
64
  RSpec.configuration.include BitexStubs
@@ -0,0 +1,35 @@
1
+ module BitstampApiWrapperStubs
2
+ def stub_bitstamp_api_wrapper_order_book
3
+ BitstampApiWrapper.stub(order_book: bitstamp_api_wrapper_order_book)
4
+ end
5
+
6
+ def bitstamp_api_wrapper_order_book
7
+ ApiWrapper::OrderBook.new(
8
+ Time.now.to_i,
9
+ [['30', '3'], ['25', '2'], ['20', '1.5'], ['15', '4'], ['10', '5']].map do |price, quantity|
10
+ ApiWrapper::OrderSummary.new(price.to_d, quantity.to_d)
11
+ end,
12
+ [['10', '2'], ['15', '3'], ['20', '1.5'], ['25', '3'], ['30', '3']].map do |price, quantity|
13
+ ApiWrapper::OrderSummary.new(price.to_d, quantity.to_d)
14
+ end
15
+ )
16
+ end
17
+
18
+ def stub_bitstamp_api_wrapper_balance(usd = nil, coin = nil, fee = nil)
19
+ BitstampApiWrapper.stub(:balance) do
20
+ ApiWrapper::BalanceSummary.new(
21
+ ApiWrapper::Balance.new((coin || 10).to_d, 0.to_d, (coin || 10).to_d),
22
+ ApiWrapper::Balance.new((usd || 100).to_d, 0.to_d, (usd || 100).to_d),
23
+ 0.5.to_d
24
+ )
25
+ end
26
+ end
27
+
28
+ def bitstamp_api_wrapper_transactions_stub(price = 30.to_d, amount = 1.to_d)
29
+ transactions = 5.times.collect do |i|
30
+ ApiWrapper::Transaction.new(i, price, amount, (i+1).seconds.ago.to_i)
31
+ end
32
+ end
33
+ end
34
+
35
+ RSpec.configuration.include BitstampApiWrapperStubs
@@ -0,0 +1,91 @@
1
+ module BitstampStubs
2
+ def stub_bitstamp_order_book
3
+ Bitstamp.stub(:order_book) do
4
+ {
5
+ 'timestamp' => Time.now.to_i.to_s,
6
+ 'bids' => [['30', '3'], ['25', '2'], ['20', '1.5'], ['15', '4'], ['10', '5']],
7
+ 'asks' => [['10', '2'], ['15', '3'], ['20', '1.5'], ['25', '3'], ['30', '3']]
8
+ }
9
+ end
10
+ end
11
+
12
+ # [<Bitstamp::Transactions @date="1380648951", @tid=14, @price="1.9", @amount="1.1">]
13
+ def stub_bitstamp_transactions(price = 0.2, amount = 1 )
14
+ Bitstamp.stub(:transactions) do
15
+ 5.times.collect do |i|
16
+ double(tid: i, date: (i+1).seconds.ago.to_i.to_s, price: price.to_s, amount: amount.to_s)
17
+ end
18
+ end
19
+ end
20
+
21
+ # TODO It's only used into robot_spec buy/sell_closing_flow
22
+ def stub_bitstamp_empty_user_transactions
23
+ Bitstamp.stub(user_transactions: double(all: []))
24
+ end
25
+
26
+ # TODO It's only used into robot_spec buy/sell_closing_flow
27
+ # Takes all active orders and mockes them as executed in a single transaction.
28
+ # If a ratio is provided then each order is only partially executed and added
29
+ # as a transaction and the order itself is kept in the order list.
30
+ def stub_bitstamp_orders_into_transactions(options = {})
31
+ ratio = options[:ratio] || 1
32
+ orders = Bitstamp.orders.all
33
+ transactions = orders.collect do |o|
34
+ usd = o.amount * o.price
35
+ usd, btc = o.type == 0 ? [-usd, o.amount] : [usd, -o.amount]
36
+ double(usd: (usd * ratio).to_s, btc: (btc * ratio).to_s,
37
+ btc_usd: o.price.to_s, order_id: o.id, fee: '0.5', type: 2,
38
+ datetime: DateTime.now.to_s)
39
+ end
40
+ Bitstamp.stub(user_transactions: double(all: transactions))
41
+
42
+ return unless ratio == 1
43
+ stub_bitstamp_sell
44
+ stub_bitstamp_buy
45
+ end
46
+
47
+ # TODO It's only used into robot_spec buy/sell_closing_flow_spec
48
+ def stub_bitstamp_sell(remote_id = nil, orders = [])
49
+ ensure_bitstamp_orders_stub
50
+ Bitstamp.orders.stub(all: orders)
51
+ Bitstamp.orders.stub(:sell) do |args|
52
+ remote_id = Bitstamp.orders.all.size + 1 if remote_id.nil?
53
+ ask = double(amount: args[:amount], price: args[:price], type: 1, id: remote_id,
54
+ datetime: DateTime.now.to_s)
55
+ ask.stub(:cancel!) do
56
+ orders = Bitstamp.orders.all.reject { |o| o.id.to_s == ask.id.to_s && o.type == 1 }
57
+ stub_bitstamp_sell(remote_id + 1, orders)
58
+ end
59
+ stub_bitstamp_sell(remote_id + 1, Bitstamp.orders.all + [ask])
60
+ ask
61
+ end
62
+ end
63
+
64
+ # TODO It's only used into robot_spec buy/sell_closing_flow_spec
65
+ def stub_bitstamp_buy(remote_id = nil, orders = [])
66
+ ensure_bitstamp_orders_stub
67
+ Bitstamp.orders.stub(all: orders)
68
+ Bitstamp.orders.stub(:buy) do |args|
69
+ remote_id = Bitstamp.orders.all.size + 1 if remote_id.nil?
70
+ bid = double(amount: args[:amount], price: args[:price], type: 0, id: remote_id,
71
+ datetime: DateTime.now.to_s)
72
+ bid.stub(:cancel!) do
73
+ orders = Bitstamp.orders.all.reject { |o| o.id.to_s == bid.id.to_s && o.type == 0 }
74
+ stub_bitstamp_buy(remote_id + 1, orders)
75
+ end
76
+ stub_bitstamp_buy(remote_id + 1, Bitstamp.orders.all + [bid])
77
+ bid
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ # TODO It's only used into robot_spec buy/sell_closing_flow_spec
84
+ def ensure_bitstamp_orders_stub
85
+ Bitstamp.orders
86
+ rescue Exception => e
87
+ Bitstamp.stub(orders: double)
88
+ end
89
+ end
90
+
91
+ RSpec.configuration.include BitstampStubs