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