bitex_bot 0.6.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -3
- data/Gemfile +3 -1
- data/bitex_bot.gemspec +5 -2
- data/lib/bitex_bot/database.rb +2 -2
- data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +47 -35
- data/lib/bitex_bot/models/api_wrappers/bitex/bitex_api_wrapper.rb +178 -0
- data/lib/bitex_bot/models/api_wrappers/bitstamp/bitstamp_api_wrapper.rb +62 -45
- data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +52 -28
- data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +61 -28
- data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +12 -6
- data/lib/bitex_bot/models/buy_closing_flow.rb +3 -2
- data/lib/bitex_bot/models/buy_opening_flow.rb +31 -6
- data/lib/bitex_bot/models/closing_flow.rb +37 -22
- data/lib/bitex_bot/models/open_buy.rb +1 -3
- data/lib/bitex_bot/models/open_sell.rb +1 -3
- data/lib/bitex_bot/models/opening_flow.rb +42 -28
- data/lib/bitex_bot/models/order_book_simulator.rb +14 -13
- data/lib/bitex_bot/models/sell_closing_flow.rb +3 -2
- data/lib/bitex_bot/models/sell_opening_flow.rb +29 -4
- data/lib/bitex_bot/robot.rb +28 -43
- data/lib/bitex_bot/settings.rb +2 -0
- data/lib/bitex_bot/version.rb +1 -1
- data/settings.rb.sample +23 -5
- data/spec/bitex_bot/settings_spec.rb +13 -6
- data/spec/factories/bitex_ask.rb +14 -0
- data/spec/factories/bitex_bid.rb +14 -0
- data/spec/factories/bitex_buy.rb +7 -7
- data/spec/factories/bitex_sell.rb +7 -7
- data/spec/factories/buy_opening_flow.rb +10 -10
- data/spec/factories/open_buy.rb +8 -8
- data/spec/factories/open_sell.rb +8 -8
- data/spec/factories/sell_opening_flow.rb +10 -10
- data/spec/fixtures/bitstamp/balance.yml +63 -0
- data/spec/fixtures/bitstamp/order_book.yml +60 -0
- data/spec/fixtures/bitstamp/orders/all.yml +62 -0
- data/spec/fixtures/bitstamp/orders/failure_sell.yml +60 -0
- data/spec/fixtures/bitstamp/orders/successful_buy.yml +62 -0
- data/spec/fixtures/bitstamp/transactions.yml +244 -0
- data/spec/fixtures/bitstamp/user_transactions.yml +223 -0
- data/spec/models/api_wrappers/bitex_api_wrapper_spec.rb +147 -0
- data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +134 -140
- data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +9 -3
- data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +142 -73
- data/spec/models/bitex_api_spec.rb +4 -4
- data/spec/models/buy_closing_flow_spec.rb +19 -24
- data/spec/models/buy_opening_flow_spec.rb +102 -83
- data/spec/models/order_book_simulator_spec.rb +5 -0
- data/spec/models/robot_spec.rb +7 -4
- data/spec/models/sell_closing_flow_spec.rb +21 -25
- data/spec/models/sell_opening_flow_spec.rb +100 -80
- data/spec/spec_helper.rb +3 -1
- data/spec/support/bitex_stubs.rb +80 -40
- data/spec/support/bitstamp/bitstamp_api_wrapper_stubs.rb +2 -2
- data/spec/support/bitstamp/bitstamp_stubs.rb +3 -3
- data/spec/support/vcr.rb +8 -0
- data/spec/support/webmock.rb +8 -0
- metadata +77 -10
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe KrakenApiWrapper do
|
4
|
-
let(:api_wrapper) {
|
4
|
+
let(:api_wrapper) { BitexBot::Robot.taker }
|
5
5
|
let(:api_client) { api_wrapper.client }
|
6
6
|
let(:taker_settings) do
|
7
7
|
BitexBot::SettingsClass.new(
|
8
8
|
kraken: {
|
9
|
-
api_key: 'your_api_key',
|
9
|
+
api_key: 'your_api_key',
|
10
|
+
api_secret: 'your_api_secret',
|
11
|
+
order_book: 'xbtusd'
|
10
12
|
}
|
11
13
|
)
|
12
14
|
end
|
@@ -16,63 +18,118 @@ describe KrakenApiWrapper do
|
|
16
18
|
BitexBot::Robot.setup
|
17
19
|
end
|
18
20
|
|
19
|
-
def
|
20
|
-
|
21
|
+
def stub_request_helper(method:, path: '', status: 200, result: {}, error: [], header_params: {})
|
22
|
+
stub_request(method, "https://api.kraken.com/0#{path}")
|
23
|
+
.with(headers: { 'User-Agent': BitexBot.user_agent }.merge(header_params))
|
24
|
+
.to_return(
|
25
|
+
status: status,
|
26
|
+
body: { error: error, result: result }.to_json,
|
27
|
+
headers: { 'Content-Type': 'application/json' }
|
28
|
+
)
|
21
29
|
end
|
22
30
|
|
23
|
-
def
|
24
|
-
|
31
|
+
def stub_assets
|
32
|
+
stub_request_helper(
|
33
|
+
method: :get,
|
34
|
+
path: '/public/AssetPairs',
|
35
|
+
result: {
|
36
|
+
XXBTZUSD: {
|
37
|
+
altname: 'XBTUSD',
|
38
|
+
aclass_base: 'currency',
|
39
|
+
base: 'XXBT',
|
40
|
+
aclass_quote: 'currency',
|
41
|
+
quote: 'ZUSD',
|
42
|
+
lot: 'unit',
|
43
|
+
pair_decimals: 1,
|
44
|
+
lot_decimals: 8,
|
45
|
+
lot_multiplier: 1,
|
46
|
+
leverage_buy: [2, 3, 4, 5],
|
47
|
+
leverage_sell: [2, 3, 4, 5],
|
48
|
+
fees: [
|
49
|
+
[0, 0.26],
|
50
|
+
[50_000, 0.24],
|
51
|
+
[100_000, 0.22],
|
52
|
+
[250_000, 0.2],
|
53
|
+
[500_000, 0.18],
|
54
|
+
[1_000_000, 0.16],
|
55
|
+
[2_500_000, 0.14],
|
56
|
+
[5_000_000, 0.12],
|
57
|
+
[10_000_000, 0.1]
|
58
|
+
],
|
59
|
+
fees_maker: [
|
60
|
+
[0, 0.16],
|
61
|
+
[50_000, 0.14],
|
62
|
+
[100_000, 0.12],
|
63
|
+
[250_000, 0.1],
|
64
|
+
[500_000, 0.08],
|
65
|
+
[1_000_000, 0.06],
|
66
|
+
[2_500_000, 0.04],
|
67
|
+
[5_000_000, 0.02],
|
68
|
+
[10_000_000, 0]
|
69
|
+
],
|
70
|
+
fee_volume_currency: 'ZUSD',
|
71
|
+
margin_call: 80,
|
72
|
+
margin_stop: 40
|
73
|
+
}
|
74
|
+
}
|
75
|
+
)
|
25
76
|
end
|
26
77
|
|
27
78
|
it 'Sends User-Agent header' do
|
28
|
-
|
29
|
-
stub_stuff =
|
79
|
+
stub_assets
|
80
|
+
stub_stuff = stub_order_book
|
30
81
|
|
31
82
|
# We don't care about the response
|
32
|
-
|
83
|
+
api_wrapper.order_book
|
33
84
|
|
34
|
-
|
85
|
+
stub_stuff.should have_been_requested
|
35
86
|
end
|
36
87
|
|
37
88
|
def stub_balance
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
89
|
+
stub_request_helper(
|
90
|
+
method: :post,
|
91
|
+
path: '/private/Balance',
|
92
|
+
header_params: { 'Api-Key': api_wrapper.api_key },
|
93
|
+
result: { XXBT: '1433.0939', ZUSD: '1230.0233', ETH: '99.7497224800' }
|
94
|
+
)
|
42
95
|
end
|
43
96
|
|
44
97
|
def stub_trade_volume
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
98
|
+
stub_request_helper(
|
99
|
+
method: :post,
|
100
|
+
path: '/private/TradeVolume',
|
101
|
+
header_params: { 'Api-Key': api_wrapper.api_key },
|
102
|
+
result: {
|
103
|
+
currency: 'ZUSD',
|
104
|
+
volume: '3878.8703',
|
105
|
+
fees: {
|
106
|
+
XXBTZUSD: {
|
107
|
+
fee: '0.2600',
|
108
|
+
minfee: '0.1000',
|
109
|
+
maxfee: '0.2600',
|
110
|
+
nextfee: '0.2400',
|
111
|
+
nextvolume: '10000.0000',
|
112
|
+
tiervolume: '0.0000'
|
56
113
|
}
|
57
114
|
},
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
115
|
+
fees_maker: {
|
116
|
+
XETHZEUR: {
|
117
|
+
fee: '0.1600',
|
118
|
+
minfee: '0.0000',
|
119
|
+
maxfee: '0.1600',
|
120
|
+
nextfee: '0.1400',
|
121
|
+
nextvolume: '10000.0000',
|
122
|
+
tiervolume: '0.0000'
|
66
123
|
}
|
67
124
|
}
|
68
|
-
}
|
69
|
-
|
125
|
+
}
|
126
|
+
)
|
70
127
|
end
|
71
128
|
|
72
129
|
it '#balance' do
|
73
|
-
|
74
|
-
stub_orders
|
130
|
+
stub_assets
|
75
131
|
stub_balance
|
132
|
+
stub_orders
|
76
133
|
stub_trade_volume
|
77
134
|
|
78
135
|
balance = api_wrapper.balance
|
@@ -94,25 +151,26 @@ describe KrakenApiWrapper do
|
|
94
151
|
end
|
95
152
|
|
96
153
|
it '#cancel' do
|
97
|
-
stub_private_client
|
98
154
|
stub_orders
|
99
155
|
|
100
|
-
|
156
|
+
api_wrapper.orders.sample.should respond_to(:cancel!)
|
101
157
|
end
|
102
158
|
|
103
159
|
def stub_order_book(count: 3, price: 1.5, amount: 2.5)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
160
|
+
stub_request_helper(
|
161
|
+
method: :get,
|
162
|
+
path: '/public/Depth?pair=XBTUSD',
|
163
|
+
result: {
|
164
|
+
XXBTZUSD: {
|
165
|
+
bids: count.times.map { |i| [(price + i).to_d, (amount + i).to_d, 1.seconds.ago.to_i.to_s] },
|
166
|
+
asks: count.times.map { |i| [(price + i).to_d, (amount + i).to_d, 1.seconds.ago.to_i.to_s] }
|
109
167
|
}
|
110
|
-
}
|
111
|
-
|
168
|
+
}
|
169
|
+
)
|
112
170
|
end
|
113
171
|
|
114
172
|
it '#order_book' do
|
115
|
-
|
173
|
+
stub_assets
|
116
174
|
stub_order_book
|
117
175
|
|
118
176
|
order_book = api_wrapper.order_book
|
@@ -131,34 +189,36 @@ describe KrakenApiWrapper do
|
|
131
189
|
end
|
132
190
|
|
133
191
|
def stub_orders
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
192
|
+
stub_request_helper(
|
193
|
+
method: :post,
|
194
|
+
path: '/private/OpenOrders',
|
195
|
+
header_params: { 'Api-Key': api_wrapper.api_key },
|
196
|
+
result: {
|
197
|
+
open: {
|
198
|
+
'O5TDV2-WDYB2-6OGJRD': {
|
199
|
+
refid: nil, userref: nil, status: 'open', opentm: 1_440_292_821.839, starttm: 0, expiretm: 0,
|
200
|
+
descr: {
|
201
|
+
pair: 'ETHEUR', type: 'buy', ordertype: 'limit', price: '1.19000', price2: '0',
|
202
|
+
leverage: 'none', order: 'buy 1204.00000000 ETHEUR @ limit 1.19000'
|
142
203
|
},
|
143
|
-
|
144
|
-
|
204
|
+
vol: '1204.00000000', vol_exec: '0.00000000', cost: '0.00000', fee: '0.00000',
|
205
|
+
price: '0.00008', misc: '', oflags: 'fciq'
|
145
206
|
},
|
146
|
-
'OGAEYL-LVSPL-BYGGRR'
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
207
|
+
'OGAEYL-LVSPL-BYGGRR': {
|
208
|
+
refid: nil, userref: nil, status: 'open', opentm: 1_440_254_004.621, starttm: 0, expiretm: 0,
|
209
|
+
descr: {
|
210
|
+
pair: 'ETHEUR', type: 'sell', ordertype: 'limit', price: '1.29000', price2: '0',
|
211
|
+
leverage: 'none', order: 'sell 99.74972000 ETHEUR @ limit 1.29000'
|
151
212
|
},
|
152
|
-
|
153
|
-
|
213
|
+
vol: '99.74972000', vol_exec: '0.00000000', cost: '0.00000', fee: '0.00000',
|
214
|
+
price: '0.00009', misc: '', oflags: 'fciq'
|
154
215
|
}
|
155
216
|
}
|
156
|
-
}
|
157
|
-
|
217
|
+
}
|
218
|
+
)
|
158
219
|
end
|
159
220
|
|
160
221
|
it '#orders' do
|
161
|
-
stub_private_client
|
162
222
|
stub_orders
|
163
223
|
|
164
224
|
api_wrapper.orders.all? { |o| o.should be_a(ApiWrapper::Order) }
|
@@ -172,18 +232,20 @@ describe KrakenApiWrapper do
|
|
172
232
|
end
|
173
233
|
|
174
234
|
def stub_transactions(count: 1, price: 1.5, amount: 2.5)
|
175
|
-
|
176
|
-
|
235
|
+
stub_request_helper(
|
236
|
+
method: :get,
|
237
|
+
path: '/public/Trades?pair=XBTUSD',
|
238
|
+
result: {
|
177
239
|
XXBTZUSD: [
|
178
240
|
['202.51626', '0.01440000', 1_440_277_319.1_922, 'b', 'l', ''],
|
179
241
|
['202.54000', '0.10000000', 1_440_277_322.8_993, 'b', 'l', '']
|
180
242
|
]
|
181
243
|
}
|
182
|
-
|
244
|
+
)
|
183
245
|
end
|
184
246
|
|
185
247
|
it '#transactions' do
|
186
|
-
|
248
|
+
stub_assets
|
187
249
|
stub_transactions
|
188
250
|
|
189
251
|
api_wrapper.transactions.all? { |o| o.should be_a(ApiWrapper::Transaction) }
|
@@ -201,9 +263,16 @@ describe KrakenApiWrapper do
|
|
201
263
|
end
|
202
264
|
|
203
265
|
it '#find_lost' do
|
204
|
-
stub_private_client
|
205
266
|
stub_orders
|
206
267
|
|
207
|
-
|
268
|
+
api_wrapper.orders.all? { |o| api_wrapper.find_lost(o.type, o.price, o.amount).present? }
|
269
|
+
end
|
270
|
+
|
271
|
+
it '#currency_pair' do
|
272
|
+
stub_assets
|
273
|
+
BitexBot::Settings.taker.kraken.order_book.should eq taker_settings.kraken.order_book
|
274
|
+
|
275
|
+
api_wrapper.currency_pair.should be_a(HashWithIndifferentAccess)
|
276
|
+
api_wrapper.currency_pair.keys.should include(*%w[altname base quote name])
|
208
277
|
end
|
209
278
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'BitexApi' do
|
4
|
-
before(:each)
|
5
|
-
|
6
|
-
end
|
4
|
+
before(:each) { BitexBot::Robot.setup }
|
5
|
+
let(:maker) { BitexBot::Robot.maker }
|
7
6
|
|
8
7
|
it 'Sends User-Agent header' do
|
9
8
|
stub_request(:get, 'https://bitex.la/api-v1/rest/private/profile?api_key=your_bitex_api_key_which_should_be_kept_safe')
|
10
9
|
.with(headers: { 'User-Agent': BitexBot.user_agent })
|
11
|
-
|
10
|
+
|
11
|
+
maker.profile rescue nil # we don't care about the response
|
12
12
|
end
|
13
13
|
end
|
@@ -4,7 +4,10 @@ describe BitexBot::BuyClosingFlow do
|
|
4
4
|
let(:taker_settings) do
|
5
5
|
BitexBot::SettingsClass.new(
|
6
6
|
bitstamp: {
|
7
|
-
api_key: 'YOUR_API_KEY',
|
7
|
+
api_key: 'YOUR_API_KEY',
|
8
|
+
secret: 'YOUR_API_SECRET',
|
9
|
+
client_id: 'YOUR_BITSTAMP_USERNAME',
|
10
|
+
order_book: 'btcusd'
|
8
11
|
}
|
9
12
|
)
|
10
13
|
end
|
@@ -85,7 +88,7 @@ describe BitexBot::BuyClosingFlow do
|
|
85
88
|
flow = BitexBot::BuyClosingFlow.last
|
86
89
|
stub_bitstamp_orders_into_transactions
|
87
90
|
|
88
|
-
flow.sync_closed_positions
|
91
|
+
flow.sync_closed_positions
|
89
92
|
|
90
93
|
close = flow.close_positions.last
|
91
94
|
close.amount.should == 624.105
|
@@ -106,7 +109,7 @@ describe BitexBot::BuyClosingFlow do
|
|
106
109
|
subject.class.close_open_positions
|
107
110
|
|
108
111
|
stub_bitstamp_orders_into_transactions
|
109
|
-
flow.sync_closed_positions
|
112
|
+
flow.sync_closed_positions
|
110
113
|
end
|
111
114
|
|
112
115
|
it 'syncs the executed orders, calculates profit with other fx rate' do
|
@@ -120,13 +123,11 @@ describe BitexBot::BuyClosingFlow do
|
|
120
123
|
BitexBot::BuyClosingFlow.close_open_positions
|
121
124
|
flow = BitexBot::BuyClosingFlow.last
|
122
125
|
|
123
|
-
expect
|
124
|
-
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
125
|
-
end.not_to change { BitexBot::CloseBuy.count }
|
126
|
+
expect { flow.sync_closed_positions }.not_to change { BitexBot::CloseBuy.count }
|
126
127
|
flow.should_not be_done
|
127
128
|
|
128
129
|
# Immediately calling sync again does not try to cancel the ask.
|
129
|
-
flow.sync_closed_positions
|
130
|
+
flow.sync_closed_positions
|
130
131
|
Bitstamp.orders.all.size.should == 1
|
131
132
|
|
132
133
|
# Partially executes order, and 61 seconds after that
|
@@ -134,18 +135,14 @@ describe BitexBot::BuyClosingFlow do
|
|
134
135
|
stub_bitstamp_orders_into_transactions(ratio: 0.5)
|
135
136
|
Timecop.travel 61.seconds.from_now
|
136
137
|
Bitstamp.orders.all.size.should == 1
|
137
|
-
expect
|
138
|
-
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
139
|
-
end.not_to change { BitexBot::CloseBuy.count }
|
138
|
+
expect { flow.sync_closed_positions }.not_to change { BitexBot::CloseBuy.count }
|
140
139
|
Bitstamp.orders.all.size.should be_zero
|
141
140
|
flow.should_not be_done
|
142
141
|
|
143
142
|
# Next time we try to sync_closed_positions the flow
|
144
143
|
# detects the previous close_buy was cancelled correctly so
|
145
144
|
# it syncs it's total amounts and tries to place a new one.
|
146
|
-
expect
|
147
|
-
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
148
|
-
end.to change { BitexBot::CloseBuy.count }.by(1)
|
145
|
+
expect { flow.sync_closed_positions }.to change { BitexBot::CloseBuy.count }.by(1)
|
149
146
|
|
150
147
|
flow.close_positions.first.tap do |close|
|
151
148
|
close.amount.should == 312.0_525
|
@@ -155,7 +152,7 @@ describe BitexBot::BuyClosingFlow do
|
|
155
152
|
# The second ask is executed completely so we can wrap it up and consider
|
156
153
|
# this closing flow done.
|
157
154
|
stub_bitstamp_orders_into_transactions
|
158
|
-
flow.sync_closed_positions
|
155
|
+
flow.sync_closed_positions
|
159
156
|
flow.close_positions.last.tap do |close|
|
160
157
|
close.amount.should == 312.02_235
|
161
158
|
close.quantity.should == 1.005
|
@@ -171,9 +168,7 @@ describe BitexBot::BuyClosingFlow do
|
|
171
168
|
stub_bitstamp_orders_into_transactions(ratio: 0.999)
|
172
169
|
Bitstamp.orders.all.first.cancel!
|
173
170
|
|
174
|
-
expect
|
175
|
-
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
176
|
-
end.not_to change { BitexBot::CloseBuy.count }
|
171
|
+
expect { flow.sync_closed_positions }.not_to change { BitexBot::CloseBuy.count }
|
177
172
|
|
178
173
|
flow.should be_done
|
179
174
|
flow.crypto_profit.should == 0.00_201
|
@@ -188,12 +183,12 @@ describe BitexBot::BuyClosingFlow do
|
|
188
183
|
|
189
184
|
60.times do
|
190
185
|
Timecop.travel 60.seconds.from_now
|
191
|
-
flow.sync_closed_positions
|
186
|
+
flow.sync_closed_positions
|
192
187
|
end
|
193
188
|
|
194
189
|
stub_bitstamp_orders_into_transactions
|
195
190
|
|
196
|
-
flow.sync_closed_positions
|
191
|
+
flow.sync_closed_positions
|
197
192
|
flow.reload.should be_done
|
198
193
|
flow.crypto_profit.should be_zero
|
199
194
|
flow.fiat_profit.should == -34.165
|
@@ -202,8 +197,8 @@ describe BitexBot::BuyClosingFlow do
|
|
202
197
|
|
203
198
|
describe 'when there are errors placing the closing order' do
|
204
199
|
it 'keeps trying to place a closed position on bitstamp errors' do
|
205
|
-
BitstampApiWrapper.stub(send_order: nil)
|
206
|
-
BitstampApiWrapper.stub(find_lost: nil)
|
200
|
+
BitstampApiWrapper.any_instance.stub(send_order: nil)
|
201
|
+
BitstampApiWrapper.any_instance.stub(find_lost: nil)
|
207
202
|
|
208
203
|
open = create :open_buy
|
209
204
|
expect do
|
@@ -222,9 +217,9 @@ describe BitexBot::BuyClosingFlow do
|
|
222
217
|
end
|
223
218
|
|
224
219
|
it 'retries until it finds the lost order' do
|
225
|
-
BitstampApiWrapper.stub(send_order: nil)
|
226
|
-
BitstampApiWrapper.stub(:orders) do
|
227
|
-
[
|
220
|
+
BitstampApiWrapper.any_instance.stub(send_order: nil)
|
221
|
+
BitstampApiWrapper.any_instance.stub(:orders) do
|
222
|
+
[ApiWrapper::Order.new(1, :sell, 310, 2.5, 1.minute.ago.to_i)]
|
228
223
|
end
|
229
224
|
|
230
225
|
open = create(:open_buy)
|