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
@@ -1,102 +1,128 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe BitexBot::BuyClosingFlow do
|
4
|
-
|
4
|
+
let(:taker_settings) do
|
5
|
+
BitexBot::SettingsClass.new(
|
6
|
+
bitstamp: {
|
7
|
+
api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME'
|
8
|
+
}
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
BitexBot::Settings.stub(taker: taker_settings)
|
14
|
+
BitexBot::Robot.setup
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'closes a single open position completely' do
|
5
18
|
stub_bitstamp_sell
|
6
19
|
open = create :open_buy
|
7
|
-
|
20
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
21
|
+
flow = BitexBot::BuyClosingFlow.last
|
22
|
+
|
8
23
|
open.reload.closing_flow.should == flow
|
24
|
+
|
9
25
|
flow.open_positions.should == [open]
|
10
26
|
flow.desired_price.should == 310
|
11
27
|
flow.quantity.should == 2
|
28
|
+
flow.amount.should == 600
|
12
29
|
flow.btc_profit.should be_nil
|
13
|
-
flow.
|
30
|
+
flow.fiat_profit.should be_nil
|
31
|
+
|
14
32
|
close = flow.close_positions.first
|
15
33
|
close.order_id.should == '1'
|
16
34
|
close.amount.should be_nil
|
17
35
|
close.quantity.should be_nil
|
18
36
|
end
|
19
|
-
|
20
|
-
it
|
37
|
+
|
38
|
+
it 'closes an aggregate of several open positions' do
|
21
39
|
stub_bitstamp_sell
|
22
40
|
open_one = create :tiny_open_buy
|
23
41
|
open_two = create :open_buy
|
24
|
-
|
42
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
43
|
+
flow = BitexBot::BuyClosingFlow.last
|
44
|
+
|
25
45
|
close = flow.close_positions.first
|
46
|
+
|
26
47
|
open_one.reload.closing_flow.should == flow
|
27
48
|
open_two.reload.closing_flow.should == flow
|
49
|
+
|
28
50
|
flow.open_positions.should == [open_one, open_two]
|
29
|
-
flow.desired_price.round(
|
51
|
+
flow.desired_price.round(10).should == '310.4_975_124_378'.to_d
|
30
52
|
flow.quantity.should == 2.01
|
53
|
+
flow.amount.should == 604
|
31
54
|
flow.btc_profit.should be_nil
|
32
|
-
flow.
|
33
|
-
close.order_id.should == '1'
|
34
|
-
close.amount.should be_nil
|
35
|
-
close.quantity.should be_nil
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'keeps trying to place a closed position on bitstamp errors' do
|
39
|
-
stub_bitstamp_sell
|
40
|
-
Bitstamp.orders.stub(:sell) do
|
41
|
-
double(id: nil, error: {price: ['Ensure it is good']})
|
42
|
-
end
|
43
|
-
open = create :open_buy
|
44
|
-
flow = BitexBot::BuyClosingFlow.close_open_positions
|
45
|
-
open.reload.closing_flow.should == flow
|
46
|
-
flow.open_positions.should == [open]
|
47
|
-
flow.desired_price.should == 310
|
48
|
-
flow.quantity.should == 2
|
49
|
-
flow.btc_profit.should be_nil
|
50
|
-
flow.usd_profit.should be_nil
|
51
|
-
flow.close_positions.should be_empty
|
55
|
+
flow.fiat_profit.should be_nil
|
52
56
|
|
53
|
-
stub_bitstamp_user_transactions
|
54
|
-
stub_bitstamp_sell
|
55
|
-
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
56
|
-
close = flow.close_positions.first
|
57
57
|
close.order_id.should == '1'
|
58
58
|
close.amount.should be_nil
|
59
59
|
close.quantity.should be_nil
|
60
60
|
end
|
61
|
-
|
62
|
-
it
|
61
|
+
|
62
|
+
it 'does not try to close if the amount is too low' do
|
63
63
|
open = create :tiny_open_buy
|
64
64
|
expect do
|
65
65
|
BitexBot::BuyClosingFlow.close_open_positions.should be_nil
|
66
|
-
end.not_to change{ BitexBot::BuyClosingFlow.count }
|
66
|
+
end.not_to change { BitexBot::BuyClosingFlow.count }
|
67
67
|
end
|
68
|
-
|
69
|
-
it
|
68
|
+
|
69
|
+
it 'does not try to close if there are no open positions' do
|
70
70
|
expect do
|
71
71
|
BitexBot::BuyClosingFlow.close_open_positions.should be_nil
|
72
|
-
end.not_to change{ BitexBot::BuyClosingFlow.count }
|
72
|
+
end.not_to change { BitexBot::BuyClosingFlow.count }
|
73
73
|
end
|
74
|
-
|
75
|
-
describe
|
74
|
+
|
75
|
+
describe 'when syncinc executed orders' do
|
76
76
|
before(:each) do
|
77
77
|
stub_bitstamp_sell
|
78
|
-
|
78
|
+
stub_bitstamp_empty_user_transactions
|
79
79
|
create :tiny_open_buy
|
80
80
|
create :open_buy
|
81
81
|
end
|
82
|
-
|
83
|
-
it
|
84
|
-
|
82
|
+
|
83
|
+
it 'syncs the executed orders, calculates profit' do
|
84
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
85
|
+
flow = BitexBot::BuyClosingFlow.last
|
85
86
|
stub_bitstamp_orders_into_transactions
|
87
|
+
|
86
88
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
89
|
+
|
87
90
|
close = flow.close_positions.last
|
88
|
-
close.amount.should ==
|
91
|
+
close.amount.should == 624.105
|
89
92
|
close.quantity.should == 2.01
|
93
|
+
|
90
94
|
flow.should be_done
|
91
|
-
flow.btc_profit.should
|
92
|
-
flow.
|
95
|
+
flow.btc_profit.should be_zero
|
96
|
+
flow.fiat_profit.should == 20.105
|
93
97
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
98
|
+
|
99
|
+
context 'with other fx rate and closed open positions' do
|
100
|
+
let(:fx_rate) { 10.to_d }
|
101
|
+
let(:flow) { subject.class.last }
|
102
|
+
let(:positions_balance_amount) { flow.positions_balance_amount - flow.open_positions.sum(:amount) }
|
103
|
+
|
104
|
+
before(:each) do
|
105
|
+
BitexBot::Settings.stub(fx_rate: fx_rate)
|
106
|
+
subject.class.close_open_positions
|
107
|
+
|
108
|
+
stub_bitstamp_orders_into_transactions
|
109
|
+
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'syncs the executed orders, calculates profit with other fx rate' do
|
113
|
+
flow.should be_done
|
114
|
+
flow.btc_profit.should be_zero
|
115
|
+
flow.fiat_profit.should eq positions_balance_amount
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'retries closing at a lower price every minute' do
|
120
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
121
|
+
flow = BitexBot::BuyClosingFlow.last
|
122
|
+
|
97
123
|
expect do
|
98
124
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
99
|
-
end.not_to change{ BitexBot::CloseBuy.count }
|
125
|
+
end.not_to change { BitexBot::CloseBuy.count }
|
100
126
|
flow.should_not be_done
|
101
127
|
|
102
128
|
# Immediately calling sync again does not try to cancel the ask.
|
@@ -110,18 +136,19 @@ describe BitexBot::BuyClosingFlow do
|
|
110
136
|
Bitstamp.orders.all.size.should == 1
|
111
137
|
expect do
|
112
138
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
113
|
-
end.not_to change{ BitexBot::CloseBuy.count }
|
114
|
-
Bitstamp.orders.all.size.should
|
139
|
+
end.not_to change { BitexBot::CloseBuy.count }
|
140
|
+
Bitstamp.orders.all.size.should be_zero
|
115
141
|
flow.should_not be_done
|
116
|
-
|
142
|
+
|
117
143
|
# Next time we try to sync_closed_positions the flow
|
118
144
|
# detects the previous close_buy was cancelled correctly so
|
119
145
|
# it syncs it's total amounts and tries to place a new one.
|
120
146
|
expect do
|
121
147
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
122
|
-
end.to change{ BitexBot::CloseBuy.count }.by(1)
|
148
|
+
end.to change { BitexBot::CloseBuy.count }.by(1)
|
149
|
+
|
123
150
|
flow.close_positions.first.tap do |close|
|
124
|
-
close.amount.should ==
|
151
|
+
close.amount.should == 312.0_525
|
125
152
|
close.quantity.should == 1.005
|
126
153
|
end
|
127
154
|
|
@@ -130,34 +157,36 @@ describe BitexBot::BuyClosingFlow do
|
|
130
157
|
stub_bitstamp_orders_into_transactions
|
131
158
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
132
159
|
flow.close_positions.last.tap do |close|
|
133
|
-
close.amount.should ==
|
160
|
+
close.amount.should == 312.02_235
|
134
161
|
close.quantity.should == 1.005
|
135
162
|
end
|
136
163
|
flow.should be_done
|
137
|
-
flow.btc_profit.should
|
138
|
-
flow.
|
164
|
+
flow.btc_profit.should be_zero
|
165
|
+
flow.fiat_profit.should == 20.07_485
|
139
166
|
end
|
140
|
-
|
141
|
-
it "does not retry for an amount less than minimum_for_closing" do
|
142
|
-
flow = BitexBot::BuyClosingFlow.close_open_positions
|
143
167
|
|
168
|
+
it 'does not retry for an amount less than minimum_for_closing' do
|
169
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
170
|
+
flow = BitexBot::BuyClosingFlow.last
|
144
171
|
stub_bitstamp_orders_into_transactions(ratio: 0.999)
|
145
172
|
Bitstamp.orders.all.first.cancel!
|
146
173
|
|
147
174
|
expect do
|
148
175
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
149
|
-
end.not_to change{ BitexBot::CloseBuy.count }
|
176
|
+
end.not_to change { BitexBot::CloseBuy.count }
|
150
177
|
|
151
178
|
flow.should be_done
|
152
|
-
flow.btc_profit.should == 0.
|
153
|
-
flow.
|
179
|
+
flow.btc_profit.should == 0.00_201
|
180
|
+
flow.fiat_profit.should == 19.480_895
|
154
181
|
end
|
155
|
-
|
156
|
-
it
|
182
|
+
|
183
|
+
it 'can lose USD if price had to be dropped dramatically' do
|
157
184
|
# This flow is forced to sell the original BTC quantity for less, thus regaining
|
158
185
|
# less USD than what was spent on bitex.
|
159
|
-
|
160
|
-
|
186
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
187
|
+
flow = BitexBot::BuyClosingFlow.last
|
188
|
+
|
189
|
+
60.times do
|
161
190
|
Timecop.travel 60.seconds.from_now
|
162
191
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
163
192
|
end
|
@@ -165,10 +194,50 @@ describe BitexBot::BuyClosingFlow do
|
|
165
194
|
stub_bitstamp_orders_into_transactions
|
166
195
|
|
167
196
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
168
|
-
|
169
197
|
flow.reload.should be_done
|
170
|
-
flow.btc_profit.should
|
171
|
-
flow.
|
198
|
+
flow.btc_profit.should be_zero
|
199
|
+
flow.fiat_profit.should == -34.165
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe 'when there are errors placing the closing order' do
|
204
|
+
it 'keeps trying to place a closed position on bitstamp errors' do
|
205
|
+
BitstampApiWrapper.stub(send_order: nil)
|
206
|
+
BitstampApiWrapper.stub(find_lost: nil)
|
207
|
+
|
208
|
+
open = create :open_buy
|
209
|
+
expect do
|
210
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
211
|
+
end.to raise_exception(OrderNotFound)
|
212
|
+
flow = BitexBot::BuyClosingFlow.last
|
213
|
+
|
214
|
+
open.reload.closing_flow.should == flow
|
215
|
+
|
216
|
+
flow.open_positions.should == [open]
|
217
|
+
flow.desired_price.should == 310
|
218
|
+
flow.quantity.should == 2
|
219
|
+
flow.btc_profit.should be_nil
|
220
|
+
flow.fiat_profit.should be_nil
|
221
|
+
flow.close_positions.should be_empty
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'retries until it finds the lost order' do
|
225
|
+
BitstampApiWrapper.stub(send_order: nil)
|
226
|
+
BitstampApiWrapper.stub(:orders) do
|
227
|
+
[BitstampApiWrapper::Order.new(1, :sell, 310, 2.5, 1.minute.ago.to_i)]
|
228
|
+
end
|
229
|
+
|
230
|
+
open = create(:open_buy)
|
231
|
+
BitexBot::BuyClosingFlow.close_open_positions
|
232
|
+
flow = BitexBot::BuyClosingFlow.last
|
233
|
+
|
234
|
+
flow.close_positions.should_not be_empty
|
235
|
+
flow.close_positions.first do |position|
|
236
|
+
position.id.should eq 1234
|
237
|
+
position.type.should eq 1
|
238
|
+
position.amount.should eq 1000
|
239
|
+
position.price.should eq 2000
|
240
|
+
end
|
172
241
|
end
|
173
242
|
end
|
174
243
|
end
|
@@ -1,91 +1,141 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe BitexBot::BuyOpeningFlow do
|
4
|
-
before(:each)
|
5
|
-
|
6
|
-
|
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::BuyOpeningFlow.statuses)) }
|
12
|
+
it { should(validate_inclusion_of(:status).in_array(BitexBot::BuyOpeningFlow.statuses)) }
|
15
13
|
|
16
|
-
describe
|
17
|
-
it
|
14
|
+
describe 'when creating a buying flow' do
|
15
|
+
it 'spends 50 usd' do
|
18
16
|
stub_bitex_orders
|
19
17
|
BitexBot::Settings.stub(time_to_live: 3,
|
20
18
|
buying: double(amount_to_spend_per_order: 50, profit: 0))
|
21
19
|
|
22
20
|
flow = BitexBot::BuyOpeningFlow.create_for_market(100,
|
23
|
-
|
24
|
-
|
21
|
+
bitstamp_api_wrapper_order_book.bids, 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 == 50
|
26
26
|
flow.price.should <= flow.suggested_closing_price
|
27
|
-
flow.price.round(14).should ==
|
27
|
+
flow.price.round(14).should == '19.85074626865672'.to_d
|
28
28
|
flow.suggested_closing_price.should == 20
|
29
|
-
flow.order_id.should == 12345
|
30
29
|
end
|
31
30
|
|
32
|
-
|
31
|
+
let(:order_id) { 12_345 }
|
32
|
+
let(:usd_price) { '14.888_059_701_492'.to_d }
|
33
|
+
let(:suggested_closing_price) { 15.to_d }
|
34
|
+
let(:amount_to_spend) { 100.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(:order_book) { bitstamp_api_wrapper_order_book.bids }
|
39
|
+
let(:transactions) { bitstamp_api_wrapper_transactions_stub }
|
40
|
+
|
41
|
+
it 'spends 100 usd' do
|
42
|
+
BitexBot::Settings.stub(
|
43
|
+
time_to_live: 3,
|
44
|
+
buying: double(amount_to_spend_per_order: amount_to_spend, profit: 0)
|
45
|
+
)
|
33
46
|
stub_bitex_orders
|
34
|
-
BitexBot::Settings.stub(time_to_live: 3,
|
35
|
-
buying: double(amount_to_spend_per_order: 100, profit: 0))
|
36
47
|
|
37
|
-
flow =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
48
|
+
flow =
|
49
|
+
BitexBot::BuyOpeningFlow.create_for_market(
|
50
|
+
btc_balance,
|
51
|
+
order_book,
|
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_spend
|
60
|
+
flow.price.should.should <= flow.suggested_closing_price
|
61
|
+
flow.price.truncate(12).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 'spends 100 usd with other fx_rate' do
|
68
|
+
BitexBot::Settings.stub(
|
69
|
+
fx_rate: other_fx_rate,
|
70
|
+
time_to_live: 3,
|
71
|
+
buying: double(amount_to_spend_per_order: amount_to_spend, profit: 0)
|
72
|
+
)
|
73
|
+
stub_bitex_orders
|
74
|
+
|
75
|
+
flow =
|
76
|
+
BitexBot::BuyOpeningFlow.create_for_market(
|
77
|
+
btc_balance,
|
78
|
+
order_book,
|
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_spend
|
87
|
+
flow.price.should <= flow.suggested_closing_price * other_fx_rate
|
88
|
+
flow.price.truncate(11).should eq usd_price * other_fx_rate
|
89
|
+
flow.suggested_closing_price.should eq suggested_closing_price
|
44
90
|
end
|
45
|
-
|
46
|
-
it
|
91
|
+
|
92
|
+
it 'lowers the price to pay on bitex to take a profit' do
|
47
93
|
stub_bitex_orders
|
48
94
|
BitexBot::Settings.stub(time_to_live: 3,
|
49
|
-
buying: double(amount_to_spend_per_order: 100, profit: 50))
|
95
|
+
buying: double(amount_to_spend_per_order: 100, profit: 50.to_d))
|
50
96
|
|
51
97
|
flow = BitexBot::BuyOpeningFlow.create_for_market(100000,
|
52
|
-
|
98
|
+
bitstamp_api_wrapper_order_book.bids, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
|
99
|
+
store)
|
100
|
+
|
101
|
+
flow.order_id.should == 12345
|
53
102
|
flow.value_to_use.should == 100
|
54
103
|
flow.price.should <= flow.suggested_closing_price
|
55
|
-
flow.price.
|
104
|
+
flow.price.should == '7.44402985074627'.to_d
|
56
105
|
flow.suggested_closing_price.should == 15
|
57
|
-
flow.order_id.should == 12345
|
58
106
|
end
|
59
|
-
|
60
|
-
it
|
61
|
-
Bitex::Bid.stub(:create!)
|
62
|
-
raise StandardError.new("Cannot Create")
|
63
|
-
end
|
107
|
+
|
108
|
+
it 'fails when there is a problem placing the bid on bitex' do
|
109
|
+
Bitex::Bid.stub(:create!) { raise StandardError.new('Cannot Create') }
|
64
110
|
|
65
111
|
BitexBot::Settings.stub(time_to_live: 3,
|
66
112
|
buying: double(amount_to_spend_per_order: 100, profit: 0))
|
67
113
|
|
68
114
|
expect do
|
69
115
|
flow = BitexBot::BuyOpeningFlow.create_for_market(100000,
|
70
|
-
|
116
|
+
bitstamp_api_wrapper_order_book.bids, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
|
117
|
+
store)
|
118
|
+
|
71
119
|
flow.should be_nil
|
72
120
|
BitexBot::BuyOpeningFlow.count.should == 0
|
73
121
|
end.to raise_exception(BitexBot::CannotCreateFlow)
|
74
122
|
end
|
75
|
-
|
76
|
-
it
|
123
|
+
|
124
|
+
it 'fails when there are not enough bitcoin to sell in the other exchange' do
|
77
125
|
stub_bitex_orders
|
78
126
|
BitexBot::Settings.stub(time_to_live: 3,
|
79
127
|
buying: double(amount_to_spend_per_order: 100, profit: 0))
|
80
128
|
|
81
129
|
expect do
|
82
130
|
flow = BitexBot::BuyOpeningFlow.create_for_market(1,
|
83
|
-
|
131
|
+
bitstamp_api_wrapper_order_book.bids, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
|
132
|
+
store)
|
133
|
+
|
84
134
|
flow.should be_nil
|
85
135
|
BitexBot::BuyOpeningFlow.count.should == 0
|
86
136
|
end.to raise_exception(BitexBot::CannotCreateFlow)
|
87
137
|
end
|
88
|
-
|
138
|
+
|
89
139
|
it 'prioritizes profit from store' do
|
90
140
|
store = BitexBot::Store.new(buying_profit: 0.5)
|
91
141
|
stub_bitex_orders
|
@@ -93,20 +143,23 @@ describe BitexBot::BuyOpeningFlow do
|
|
93
143
|
buying: double(amount_to_spend_per_order: 50, profit: 0))
|
94
144
|
|
95
145
|
flow = BitexBot::BuyOpeningFlow.create_for_market(100,
|
96
|
-
|
97
|
-
|
98
|
-
|
146
|
+
bitstamp_api_wrapper_order_book.bids, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
|
147
|
+
store)
|
148
|
+
|
149
|
+
flow.price.round(13).should == '19.7514925373134'.to_d
|
99
150
|
end
|
100
151
|
end
|
101
|
-
|
102
|
-
describe
|
103
|
-
let(:flow){ create(:buy_opening_flow) }
|
152
|
+
|
153
|
+
describe 'when fetching open positions' do
|
154
|
+
let(:flow) { create(:buy_opening_flow) }
|
104
155
|
|
105
156
|
it 'only gets buys' do
|
106
157
|
flow.order_id.should == 12345
|
107
158
|
stub_bitex_transactions
|
159
|
+
|
108
160
|
expect do
|
109
161
|
all = BitexBot::BuyOpeningFlow.sync_open_positions
|
162
|
+
|
110
163
|
all.size.should == 1
|
111
164
|
all.first.tap do |o|
|
112
165
|
o.price == 300.0
|
@@ -115,9 +168,9 @@ describe BitexBot::BuyOpeningFlow do
|
|
115
168
|
o.transaction_id.should == 12345678
|
116
169
|
o.opening_flow.should == flow
|
117
170
|
end
|
118
|
-
end.to change{ BitexBot::OpenBuy.count }.by(1)
|
171
|
+
end.to change { BitexBot::OpenBuy.count }.by(1)
|
119
172
|
end
|
120
|
-
|
173
|
+
|
121
174
|
it 'does not register the same buy twice' do
|
122
175
|
flow.order_id.should == 12345
|
123
176
|
stub_bitex_transactions
|
@@ -125,40 +178,57 @@ describe BitexBot::BuyOpeningFlow do
|
|
125
178
|
BitexBot::OpenBuy.count.should == 1
|
126
179
|
Timecop.travel 1.second.from_now
|
127
180
|
stub_bitex_transactions(build(:bitex_buy, id: 23456))
|
181
|
+
|
128
182
|
expect do
|
129
183
|
news = BitexBot::BuyOpeningFlow.sync_open_positions
|
130
184
|
news.first.transaction_id.should == 23456
|
131
|
-
end.to change{ BitexBot::OpenBuy.count }.by(1)
|
185
|
+
end.to change { BitexBot::OpenBuy.count }.by(1)
|
132
186
|
end
|
133
|
-
|
134
|
-
it 'does not register
|
187
|
+
|
188
|
+
it 'does not register buys from another order book' do
|
135
189
|
flow.order_id.should == 12345
|
136
|
-
Bitex::Trade.stub(all: [build(:bitex_buy, id: 23456,
|
190
|
+
Bitex::Trade.stub(all: [build(:bitex_buy, id: 23456, order_book: :btc_ars)])
|
191
|
+
|
137
192
|
expect do
|
138
193
|
BitexBot::BuyOpeningFlow.sync_open_positions.should be_empty
|
139
|
-
end.not_to change{ BitexBot::OpenBuy.count }
|
194
|
+
end.not_to change { BitexBot::OpenBuy.count }
|
140
195
|
BitexBot::OpenBuy.count.should == 0
|
141
196
|
end
|
142
|
-
|
197
|
+
|
143
198
|
it 'does not register buys from unknown bids' do
|
144
199
|
stub_bitex_transactions
|
200
|
+
|
145
201
|
expect do
|
146
202
|
BitexBot::BuyOpeningFlow.sync_open_positions.should be_empty
|
147
|
-
end.not_to change{ BitexBot::OpenBuy.count }
|
203
|
+
end.not_to change { BitexBot::OpenBuy.count }
|
148
204
|
end
|
149
205
|
end
|
150
|
-
|
206
|
+
|
151
207
|
it 'cancels the associated bitex bid' do
|
152
208
|
stub_bitex_orders
|
153
209
|
BitexBot::Settings.stub(time_to_live: 3,
|
154
210
|
buying: double(amount_to_spend_per_order: 50, profit: 0))
|
155
211
|
|
156
212
|
flow = BitexBot::BuyOpeningFlow.create_for_market(100,
|
157
|
-
|
158
|
-
|
213
|
+
bitstamp_api_wrapper_order_book.bids, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
|
214
|
+
store)
|
215
|
+
|
159
216
|
flow.finalise!
|
160
217
|
flow.should be_settling
|
161
218
|
flow.finalise!
|
162
219
|
flow.should be_finalised
|
163
220
|
end
|
221
|
+
|
222
|
+
it 'order has expected order book' do
|
223
|
+
stub_bitex_orders
|
224
|
+
BitexBot::Settings.stub(time_to_live: 3,
|
225
|
+
buying: double(amount_to_spend_per_order: 50, profit: 0))
|
226
|
+
|
227
|
+
flow = subject.class.create_for_market(100,
|
228
|
+
bitstamp_api_wrapper_order_book.bids, bitstamp_api_wrapper_transactions_stub, 0.5, 0.25,
|
229
|
+
store)
|
230
|
+
|
231
|
+
order = subject.class.order_class.find(flow.order_id)
|
232
|
+
order.order_book.should eq BitexBot::Settings.maker_settings.order_book
|
233
|
+
end
|
164
234
|
end
|
@@ -3,8 +3,8 @@ require 'spec_helper'
|
|
3
3
|
describe BitexBot::OrderBookSimulator do
|
4
4
|
describe 'when buying on bitex to sell somewhere else' do
|
5
5
|
def simulate(volatility, amount)
|
6
|
-
BitexBot::OrderBookSimulator.run(volatility,
|
7
|
-
|
6
|
+
BitexBot::OrderBookSimulator.run(volatility, bitstamp_api_wrapper_transactions_stub,
|
7
|
+
bitstamp_api_wrapper_order_book.bids, amount, nil)
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'gets the safest price' do
|
@@ -14,15 +14,15 @@ describe BitexBot::OrderBookSimulator do
|
|
14
14
|
it 'adjusts for medium volatility' do
|
15
15
|
simulate(3, 20).should == 25
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
it 'adjusts for high volatility' do
|
19
19
|
simulate(6, 20).should == 20
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
it 'big orders dig deep' do
|
23
23
|
simulate(0, 180).should == 15
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
it 'big orders with high volatility' do
|
27
27
|
simulate(6, 100).should == 10
|
28
28
|
end
|
@@ -34,8 +34,8 @@ describe BitexBot::OrderBookSimulator do
|
|
34
34
|
|
35
35
|
describe 'when selling on bitex to buy somewhere else' do
|
36
36
|
def simulate(volatility, quantity)
|
37
|
-
BitexBot::OrderBookSimulator.run(volatility,
|
38
|
-
|
37
|
+
BitexBot::OrderBookSimulator.run(volatility, bitstamp_api_wrapper_transactions_stub,
|
38
|
+
bitstamp_api_wrapper_order_book.asks, nil, quantity)
|
39
39
|
end
|
40
40
|
|
41
41
|
it 'gets the safest price' do
|
@@ -45,15 +45,15 @@ describe BitexBot::OrderBookSimulator do
|
|
45
45
|
it 'adjusts for medium volatility' do
|
46
46
|
simulate(3, 2).should == 15
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
it 'adjusts for high volatility' do
|
50
50
|
simulate(6, 2).should == 25
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
it 'big orders dig deep' do
|
54
54
|
simulate(0, 8).should == 25
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
it 'big orders with high volatility dig deep' do
|
58
58
|
simulate(6, 6).should == 30
|
59
59
|
end
|