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