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
data/spec/models/robot_spec.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe BitexBot::Robot do
|
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
|
+
|
4
12
|
before(:each) do
|
13
|
+
BitexBot::Settings.stub(taker: taker_settings)
|
14
|
+
BitexBot::Robot.setup
|
5
15
|
BitexBot::Settings.stub(
|
6
16
|
time_to_live: 10,
|
7
|
-
buying: double(
|
8
|
-
|
9
|
-
profit: 0),
|
10
|
-
selling: double(
|
11
|
-
quantity_to_sell_per_order: 1,
|
12
|
-
profit: 0),
|
17
|
+
buying: double(amount_to_spend_per_order: 50, profit: 0),
|
18
|
+
selling: double(quantity_to_sell_per_order: 1, profit: 0),
|
13
19
|
mailer: double(
|
14
20
|
from: 'test@test.com',
|
15
21
|
to: 'test@test.com',
|
@@ -17,7 +23,6 @@ describe BitexBot::Robot do
|
|
17
23
|
options: {}
|
18
24
|
)
|
19
25
|
)
|
20
|
-
Bitex.api_key = "valid_key"
|
21
26
|
Bitex::Profile.stub(get: {
|
22
27
|
fee: 0.5,
|
23
28
|
usd_balance: 10000.00, # Total USD balance
|
@@ -28,20 +33,22 @@ describe BitexBot::Robot do
|
|
28
33
|
btc_available: 15.00000000, # BTC available for trading
|
29
34
|
ltc_balance: 250.00000000, # Total LTC balance
|
30
35
|
ltc_reserved: 100.00000000, # LTC reserved in open orders
|
31
|
-
ltc_available: 150.00000000
|
36
|
+
ltc_available: 150.00000000 # LTC available for trading
|
32
37
|
})
|
33
38
|
stub_bitex_orders
|
34
39
|
stub_bitstamp_sell
|
35
40
|
stub_bitstamp_buy
|
36
|
-
|
37
|
-
|
41
|
+
stub_bitstamp_api_wrapper_balance
|
42
|
+
stub_bitstamp_api_wrapper_order_book
|
38
43
|
stub_bitstamp_transactions
|
39
|
-
|
44
|
+
stub_bitstamp_empty_user_transactions
|
40
45
|
end
|
41
|
-
|
46
|
+
|
47
|
+
let(:bot) { BitexBot::Robot.new }
|
42
48
|
|
43
49
|
it 'Starts out by creating opening flows that timeout' do
|
44
50
|
stub_bitex_orders
|
51
|
+
stub_bitstamp_api_wrapper_order_book
|
45
52
|
bot.trade!
|
46
53
|
stub_bitex_transactions
|
47
54
|
buying = BitexBot::BuyOpeningFlow.last
|
@@ -49,7 +56,7 @@ describe BitexBot::Robot do
|
|
49
56
|
|
50
57
|
Timecop.travel 10.minutes.from_now
|
51
58
|
bot.trade!
|
52
|
-
|
59
|
+
|
53
60
|
buying.reload.should be_settling
|
54
61
|
selling.reload.should be_settling
|
55
62
|
|
@@ -57,7 +64,7 @@ describe BitexBot::Robot do
|
|
57
64
|
buying.reload.should be_finalised
|
58
65
|
selling.reload.should be_finalised
|
59
66
|
end
|
60
|
-
|
67
|
+
|
61
68
|
it 'creates alternating opening flows' do
|
62
69
|
Bitex::Trade.stub(all: [])
|
63
70
|
bot.trade!
|
@@ -68,7 +75,7 @@ describe BitexBot::Robot do
|
|
68
75
|
Timecop.travel 5.seconds.from_now
|
69
76
|
bot.trade!
|
70
77
|
BitexBot::BuyOpeningFlow.active.count.should == 2
|
71
|
-
|
78
|
+
|
72
79
|
# When transactions appear, all opening flows
|
73
80
|
# should get old and die.
|
74
81
|
# We stub our finder to make it so all orders
|
@@ -89,7 +96,7 @@ describe BitexBot::Robot do
|
|
89
96
|
stub_bitex_transactions
|
90
97
|
expect do
|
91
98
|
bot.trade!
|
92
|
-
end.to change{ BitexBot::BuyClosingFlow.count }.by(1)
|
99
|
+
end.to change { BitexBot::BuyClosingFlow.count }.by(1)
|
93
100
|
|
94
101
|
Timecop.travel 15.seconds.from_now
|
95
102
|
bot.trade!
|
@@ -101,25 +108,25 @@ describe BitexBot::Robot do
|
|
101
108
|
expect do
|
102
109
|
bot.trade!
|
103
110
|
bot.should_not be_active_closing_flows
|
104
|
-
end.to change{ BitexBot::BuyOpeningFlow.count }.by(1)
|
111
|
+
end.to change { BitexBot::BuyOpeningFlow.count }.by(1)
|
105
112
|
end
|
106
|
-
|
113
|
+
|
107
114
|
it 'does not place new opening flows when ordered to hold' do
|
108
115
|
other_bot = BitexBot::Robot.new
|
109
116
|
other_bot.store.hold = true
|
110
117
|
other_bot.store.save!
|
111
118
|
expect do
|
112
119
|
bot.trade!
|
113
|
-
end.not_to change{ BitexBot::BuyOpeningFlow.count }
|
120
|
+
end.not_to change { BitexBot::BuyOpeningFlow.count }
|
114
121
|
end
|
115
122
|
|
116
|
-
it 'stops trading when
|
123
|
+
it 'stops trading when fiat stop is reached' do
|
117
124
|
other_bot = BitexBot::Robot.new
|
118
|
-
other_bot.store.
|
125
|
+
other_bot.store.btc_stop = 30
|
119
126
|
other_bot.store.save!
|
120
127
|
expect do
|
121
128
|
bot.trade!
|
122
|
-
end.not_to change{ BitexBot::BuyOpeningFlow.count }
|
129
|
+
end.not_to change { BitexBot::BuyOpeningFlow.count }
|
123
130
|
end
|
124
131
|
|
125
132
|
it 'stops trading when usd stop is reached' do
|
@@ -128,27 +135,36 @@ describe BitexBot::Robot do
|
|
128
135
|
other_bot.store.save!
|
129
136
|
expect do
|
130
137
|
bot.trade!
|
131
|
-
end.not_to change{ BitexBot::BuyOpeningFlow.count }
|
138
|
+
end.not_to change { BitexBot::BuyOpeningFlow.count }
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'stops trading when btc stop is reached' do
|
142
|
+
other_bot = BitexBot::Robot.new
|
143
|
+
other_bot.store.fiat_stop = 11000
|
144
|
+
other_bot.store.save!
|
145
|
+
expect do
|
146
|
+
bot.trade!
|
147
|
+
end.not_to change { BitexBot::BuyOpeningFlow.count }
|
132
148
|
end
|
133
149
|
|
134
150
|
it 'warns every 30 minutes when usd warn is reached' do
|
135
151
|
Bitex::Trade.stub(all: [])
|
136
152
|
other_bot = BitexBot::Robot.new
|
137
|
-
other_bot.store.
|
153
|
+
other_bot.store.fiat_warning = 11000
|
138
154
|
other_bot.store.save!
|
139
155
|
expect do
|
140
156
|
bot.trade!
|
141
|
-
end.to change{ Mail::TestMailer.deliveries.count }.by(1)
|
157
|
+
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
142
158
|
Timecop.travel 1.minute.from_now
|
143
|
-
stub_bitstamp_order_book # Re-stub so
|
159
|
+
stub_bitstamp_order_book # Re-stub so order book does not get old
|
144
160
|
expect do
|
145
161
|
bot.trade!
|
146
|
-
end.not_to change{ Mail::TestMailer.deliveries.count }
|
162
|
+
end.not_to change { Mail::TestMailer.deliveries.count }
|
147
163
|
Timecop.travel 31.minutes.from_now
|
148
|
-
stub_bitstamp_order_book # Re-stub so
|
164
|
+
stub_bitstamp_order_book # Re-stub so order book does not get old
|
149
165
|
expect do
|
150
166
|
bot.trade!
|
151
|
-
end.to change{ Mail::TestMailer.deliveries.count }.by(1)
|
167
|
+
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
152
168
|
end
|
153
169
|
|
154
170
|
it 'warns every 30 minutes when btc warn is reached' do
|
@@ -156,38 +172,36 @@ describe BitexBot::Robot do
|
|
156
172
|
other_bot = BitexBot::Robot.new
|
157
173
|
other_bot.store.btc_warning = 30
|
158
174
|
other_bot.store.save!
|
175
|
+
|
159
176
|
expect do
|
160
177
|
bot.trade!
|
161
|
-
end.to change{ Mail::TestMailer.deliveries.count }.by(1)
|
178
|
+
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
179
|
+
|
162
180
|
Timecop.travel 1.minute.from_now
|
163
|
-
stub_bitstamp_order_book # Re-stub so
|
181
|
+
stub_bitstamp_order_book # Re-stub so order book does not get old
|
164
182
|
expect do
|
165
183
|
bot.trade!
|
166
|
-
end.not_to change{ Mail::TestMailer.deliveries.count }
|
184
|
+
end.not_to change { Mail::TestMailer.deliveries.count }
|
185
|
+
|
167
186
|
Timecop.travel 31.minutes.from_now
|
168
|
-
stub_bitstamp_order_book # Re-stub so
|
187
|
+
stub_bitstamp_order_book # Re-stub so order book does not get old
|
188
|
+
|
169
189
|
expect do
|
170
190
|
bot.trade!
|
171
|
-
end.to change{ Mail::TestMailer.deliveries.count }.by(1)
|
191
|
+
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
172
192
|
end
|
173
|
-
|
174
|
-
it 'updates
|
193
|
+
|
194
|
+
it 'updates taker_fiat and taker_btc' do
|
175
195
|
bot.trade!
|
176
|
-
bot.store.
|
196
|
+
bot.store.taker_fiat.should_not be_nil
|
177
197
|
bot.store.taker_btc.should_not be_nil
|
178
198
|
end
|
179
|
-
|
199
|
+
|
180
200
|
it 'notifies exceptions and sleeps' do
|
181
|
-
|
182
|
-
|
183
|
-
end
|
201
|
+
BitstampApiWrapper.stub(:balance) { raise StandardError.new('oh moova') }
|
202
|
+
|
184
203
|
expect do
|
185
204
|
bot.trade!
|
186
|
-
end.to change{ Mail::TestMailer.deliveries.count }.by(1)
|
187
|
-
end
|
188
|
-
|
189
|
-
it 'knows how to setup sandbox mode for both gems' do
|
190
|
-
pending
|
191
|
-
fail
|
205
|
+
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
192
206
|
end
|
193
207
|
end
|
@@ -1,101 +1,166 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe BitexBot::SellClosingFlow 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_buy
|
6
19
|
open = create :open_sell
|
7
|
-
|
20
|
+
BitexBot::SellClosingFlow.close_open_positions
|
21
|
+
flow = BitexBot::SellClosingFlow.last
|
22
|
+
|
8
23
|
open.reload.closing_flow.should == flow
|
24
|
+
|
9
25
|
flow.open_positions.should == [open]
|
10
26
|
flow.desired_price.should == 290
|
11
27
|
flow.quantity.should == 2
|
12
28
|
flow.amount.should == 600
|
13
29
|
flow.btc_profit.should be_nil
|
14
|
-
flow.
|
30
|
+
flow.fiat_profit.should be_nil
|
31
|
+
|
15
32
|
close = flow.close_positions.first
|
16
33
|
close.order_id.should == '1'
|
17
34
|
close.amount.should be_nil
|
18
35
|
close.quantity.should be_nil
|
19
36
|
end
|
20
|
-
|
21
|
-
it
|
37
|
+
|
38
|
+
it 'closes an aggregate of several open positions' do
|
22
39
|
stub_bitstamp_buy
|
23
40
|
open_one = create :tiny_open_sell
|
24
41
|
open_two = create :open_sell
|
25
|
-
|
42
|
+
BitexBot::SellClosingFlow.close_open_positions
|
43
|
+
flow = BitexBot::SellClosingFlow.last
|
44
|
+
|
26
45
|
close = flow.close_positions.first
|
46
|
+
|
27
47
|
open_one.reload.closing_flow.should == flow
|
28
48
|
open_two.reload.closing_flow.should == flow
|
49
|
+
|
29
50
|
flow.open_positions.should == [open_one, open_two]
|
30
|
-
flow.desired_price.round(
|
51
|
+
flow.desired_price.round(10).should == '290.4975124378'.to_d
|
31
52
|
flow.quantity.should == 2.01
|
32
53
|
flow.amount.should == 604
|
33
54
|
flow.btc_profit.should be_nil
|
34
|
-
flow.
|
55
|
+
flow.fiat_profit.should be_nil
|
56
|
+
|
35
57
|
close.order_id.should == '1'
|
36
58
|
close.amount.should be_nil
|
37
59
|
close.quantity.should be_nil
|
38
60
|
end
|
39
61
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
62
|
+
describe 'when there are errors placing the closing order' do
|
63
|
+
it 'keeps trying to place a closed position on bitstamp errors' do
|
64
|
+
BitstampApiWrapper.stub(send_order: nil)
|
65
|
+
BitstampApiWrapper.stub(find_lost: nil)
|
66
|
+
|
67
|
+
open = create :open_sell
|
68
|
+
expect do
|
69
|
+
flow = BitexBot::SellClosingFlow.close_open_positions
|
70
|
+
end.to raise_exception(OrderNotFound)
|
71
|
+
flow = BitexBot::SellClosingFlow.last
|
72
|
+
|
73
|
+
open.reload.closing_flow.should == flow
|
74
|
+
|
75
|
+
flow.open_positions.should == [open]
|
76
|
+
flow.desired_price.should == 290
|
77
|
+
flow.quantity.should == 2
|
78
|
+
flow.btc_profit.should be_nil
|
79
|
+
flow.fiat_profit.should be_nil
|
80
|
+
flow.close_positions.should be_empty
|
44
81
|
end
|
45
|
-
open = create :open_sell
|
46
|
-
flow = BitexBot::SellClosingFlow.close_open_positions
|
47
|
-
open.reload.closing_flow.should == flow
|
48
|
-
flow.open_positions.should == [open]
|
49
|
-
flow.desired_price.should == 290
|
50
|
-
flow.quantity.should == 2
|
51
|
-
flow.btc_profit.should be_nil
|
52
|
-
flow.usd_profit.should be_nil
|
53
|
-
flow.close_positions.should be_empty
|
54
82
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
83
|
+
it 'retries until it finds the lost order' do
|
84
|
+
BitstampApiWrapper.stub(send_order: nil)
|
85
|
+
BitstampApiWrapper.stub(:orders) do
|
86
|
+
[BitstampApiWrapper::Order.new(1, :buy, 290, 2, 1.minute.ago.to_i)]
|
87
|
+
end
|
88
|
+
|
89
|
+
open = create(:open_sell)
|
90
|
+
BitexBot::SellClosingFlow.close_open_positions
|
91
|
+
flow = BitexBot::SellClosingFlow.last
|
92
|
+
|
93
|
+
flow.close_positions.should_not be_empty
|
94
|
+
flow.close_positions.first do |position|
|
95
|
+
position.id.should eq 1234
|
96
|
+
position.type.should eq 1
|
97
|
+
position.amount.should eq 1000
|
98
|
+
position.price.should eq 2000
|
99
|
+
end
|
100
|
+
end
|
62
101
|
end
|
63
|
-
|
64
|
-
it
|
102
|
+
|
103
|
+
it 'does not try to close if the amount is too low' do
|
65
104
|
open = create :tiny_open_sell
|
66
105
|
expect do
|
67
106
|
BitexBot::SellClosingFlow.close_open_positions.should be_nil
|
68
107
|
end.not_to change{ BitexBot::SellClosingFlow.count }
|
69
108
|
end
|
70
|
-
|
71
|
-
it
|
109
|
+
|
110
|
+
it 'does not try to close if there are no open positions' do
|
72
111
|
expect do
|
73
112
|
BitexBot::SellClosingFlow.close_open_positions.should be_nil
|
74
113
|
end.not_to change{ BitexBot::SellClosingFlow.count }
|
75
114
|
end
|
76
|
-
|
77
|
-
describe
|
115
|
+
|
116
|
+
describe 'when syncinc executed orders' do
|
78
117
|
before(:each) do
|
79
118
|
stub_bitstamp_buy
|
80
|
-
|
119
|
+
stub_bitstamp_empty_user_transactions
|
81
120
|
create :tiny_open_sell
|
82
121
|
create :open_sell
|
83
122
|
end
|
84
|
-
|
85
|
-
it
|
86
|
-
|
123
|
+
|
124
|
+
it 'syncs the executed orders, calculates profit' do
|
125
|
+
BitexBot::SellClosingFlow.close_open_positions
|
126
|
+
flow = BitexBot::SellClosingFlow.last
|
87
127
|
stub_bitstamp_orders_into_transactions
|
128
|
+
|
88
129
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
130
|
+
|
89
131
|
close = flow.close_positions.last
|
90
|
-
close.amount.should ==
|
132
|
+
close.amount.should == '583.905'.to_d
|
91
133
|
close.quantity.should == 2.01
|
134
|
+
|
92
135
|
flow.should be_done
|
93
136
|
flow.btc_profit.should == 0
|
94
|
-
flow.
|
137
|
+
flow.fiat_profit.should == '20.095'.to_d
|
95
138
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
139
|
+
|
140
|
+
context 'with other fx rate and closed open positions' do
|
141
|
+
let(:fx_rate) { 10.to_d }
|
142
|
+
let(:flow) { subject.class.last }
|
143
|
+
let(:positions_balance_amount) { flow.open_positions.sum(:amount) - flow.positions_balance_amount }
|
144
|
+
|
145
|
+
before(:each) do
|
146
|
+
BitexBot::Settings.stub(fx_rate: fx_rate)
|
147
|
+
subject.class.close_open_positions
|
148
|
+
|
149
|
+
stub_bitstamp_orders_into_transactions
|
150
|
+
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'syncs the executed orders, calculates profit with other fx rate' do
|
154
|
+
flow.should be_done
|
155
|
+
flow.btc_profit.should be_zero
|
156
|
+
flow.fiat_profit.should eq positions_balance_amount
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'retries closing at a higher price every minute' do
|
161
|
+
BitexBot::SellClosingFlow.close_open_positions
|
162
|
+
flow = BitexBot::SellClosingFlow.last
|
163
|
+
|
99
164
|
expect do
|
100
165
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
101
166
|
end.not_to change{ BitexBot::CloseSell.count }
|
@@ -115,36 +180,38 @@ describe BitexBot::SellClosingFlow do
|
|
115
180
|
end.not_to change{ BitexBot::CloseSell.count }
|
116
181
|
Bitstamp.orders.all.size.should == 0
|
117
182
|
flow.should_not be_done
|
118
|
-
|
183
|
+
|
119
184
|
# Next time we try to sync_closed_positions the flow
|
120
185
|
# detects the previous close_buy was cancelled correctly so
|
121
186
|
# it syncs it's total amounts and tries to place a new one.
|
122
187
|
expect do
|
123
188
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
124
189
|
end.to change{ BitexBot::CloseSell.count }.by(1)
|
190
|
+
|
125
191
|
flow.close_positions.first.tap do |close|
|
126
|
-
close.amount.should ==
|
192
|
+
close.amount.should == '291.9525'.to_d
|
127
193
|
close.quantity.should == 1.005
|
128
194
|
end
|
129
195
|
|
130
196
|
# The second ask is executed completely so we can wrap it up and consider
|
131
197
|
# this closing flow done.
|
132
198
|
stub_bitstamp_orders_into_transactions
|
199
|
+
|
133
200
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
134
201
|
flow.close_positions.last.tap do |close|
|
135
|
-
close.amount.should ==
|
202
|
+
close.amount.should == '291.953597'.to_d
|
136
203
|
close.quantity.should == '1.0049'.to_d
|
137
204
|
end
|
138
205
|
flow.should be_done
|
139
206
|
flow.btc_profit.should == '-0.0001'.to_d
|
140
|
-
flow.
|
141
|
-
|
207
|
+
flow.fiat_profit.should == '20.093903'.to_d
|
142
208
|
end
|
143
|
-
|
144
|
-
it "does not retry for an amount less than minimum_for_closing" do
|
145
|
-
flow = BitexBot::SellClosingFlow.close_open_positions
|
146
209
|
|
147
|
-
|
210
|
+
it 'does not retry for an amount less than minimum_for_closing' do
|
211
|
+
BitexBot::SellClosingFlow.close_open_positions
|
212
|
+
flow = BitexBot::SellClosingFlow.last
|
213
|
+
|
214
|
+
20.times do
|
148
215
|
Timecop.travel 60.seconds.from_now
|
149
216
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
150
217
|
end
|
@@ -158,14 +225,16 @@ describe BitexBot::SellClosingFlow do
|
|
158
225
|
|
159
226
|
flow.should be_done
|
160
227
|
flow.btc_profit.should == '-0.0224895'.to_d
|
161
|
-
flow.
|
228
|
+
flow.fiat_profit.should == '20.66566825'.to_d
|
162
229
|
end
|
163
|
-
|
164
|
-
it
|
230
|
+
|
231
|
+
it 'can lose BTC if price had to be raised dramatically' do
|
165
232
|
# This flow is forced to spend the original USD amount paying more than
|
166
233
|
# expected, thus regaining less BTC than what was sold on bitex.
|
167
|
-
|
168
|
-
|
234
|
+
BitexBot::SellClosingFlow.close_open_positions
|
235
|
+
flow = BitexBot::SellClosingFlow.last
|
236
|
+
|
237
|
+
60.times do
|
169
238
|
Timecop.travel 60.seconds.from_now
|
170
239
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
171
240
|
end
|
@@ -173,13 +242,12 @@ describe BitexBot::SellClosingFlow do
|
|
173
242
|
stub_bitstamp_orders_into_transactions
|
174
243
|
|
175
244
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
176
|
-
|
177
245
|
flow.reload.should be_done
|
178
|
-
flow.btc_profit.should ==
|
179
|
-
flow.
|
246
|
+
flow.btc_profit.should == '-0.1709'.to_d
|
247
|
+
flow.fiat_profit.should == '20.08575'.to_d
|
248
|
+
|
180
249
|
close = flow.close_positions.last
|
181
|
-
(close.amount / close.quantity)
|
182
|
-
.should == '317.5'.to_d
|
250
|
+
(close.amount / close.quantity).should == '317.5'.to_d
|
183
251
|
end
|
184
252
|
end
|
185
253
|
end
|