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