bitex_bot 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bitex_bot.rb +1 -13
- data/lib/bitex_bot/database.rb +24 -12
- data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +3 -3
- data/lib/bitex_bot/models/buy_closing_flow.rb +5 -1
- data/lib/bitex_bot/models/buy_opening_flow.rb +4 -0
- data/lib/bitex_bot/models/closing_flow.rb +4 -4
- data/lib/bitex_bot/models/opening_flow.rb +7 -7
- data/lib/bitex_bot/models/sell_closing_flow.rb +5 -1
- data/lib/bitex_bot/models/sell_opening_flow.rb +4 -0
- data/lib/bitex_bot/robot.rb +40 -41
- data/lib/bitex_bot/settings.rb +6 -2
- data/lib/bitex_bot/version.rb +1 -1
- data/settings.rb.sample +5 -2
- data/spec/bitex_bot/settings_spec.rb +18 -13
- data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +12 -12
- data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +12 -12
- data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +12 -12
- data/spec/models/buy_closing_flow_spec.rb +8 -8
- data/spec/models/buy_opening_flow_spec.rb +1 -1
- data/spec/models/robot_spec.rb +111 -100
- data/spec/models/sell_closing_flow_spec.rb +8 -8
- data/spec/models/sell_opening_flow_spec.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ec3db346eb39cba7b7760969b32205f778bdf89
|
4
|
+
data.tar.gz: 4117298e216bc463273d303ea82b0d4f549958af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b73d5b0ea727152b76ab15052ca7501fa5beae556d6946ddb5d2e0f26e1d76a93d86210283488a57e44c04c2b4ed36fe993a297debc39bb3234e657a90a570dd
|
7
|
+
data.tar.gz: 2958fc14136fb77f4b578bda495d27050a6bc14886f78fc784fda7dc2ab00703bbbe5fb55860214a5c0ec21374335163da2aa760c0dfbe9f755d10c30ce4aca1
|
data/lib/bitex_bot.rb
CHANGED
@@ -24,9 +24,7 @@ require 'bitex_bot/models/closing_flow.rb'
|
|
24
24
|
Dir[File.dirname(__FILE__) + '/bitex_bot/models/*.rb'].each { |file| require file }
|
25
25
|
require 'bitex_bot/robot'
|
26
26
|
|
27
|
-
# #
|
28
27
|
# Get version and bitex-bot as user-agent
|
29
|
-
#
|
30
28
|
module BitexBot
|
31
29
|
def self.user_agent
|
32
30
|
"Bitexbot/#{VERSION} (https://github.com/bitex-la/bitex-bot)"
|
@@ -34,20 +32,14 @@ module BitexBot
|
|
34
32
|
end
|
35
33
|
|
36
34
|
module Bitex
|
37
|
-
# #
|
38
35
|
# Set bitex-bot user-agent on request.
|
39
|
-
#
|
40
36
|
module WithUserAgent
|
41
37
|
def grab_curl
|
42
|
-
super.tap
|
43
|
-
curl.headers['User-Agent'] = BitexBot.user_agent
|
44
|
-
end
|
38
|
+
super.tap { |curl| curl.headers['User-Agent'] = BitexBot.user_agent }
|
45
39
|
end
|
46
40
|
end
|
47
41
|
|
48
|
-
##
|
49
42
|
# Mixing to include request behaviour and set user-agent.
|
50
|
-
#
|
51
43
|
class Api
|
52
44
|
class << self
|
53
45
|
prepend WithUserAgent
|
@@ -56,18 +48,14 @@ module Bitex
|
|
56
48
|
end
|
57
49
|
|
58
50
|
module RestClient
|
59
|
-
# #
|
60
51
|
# On Itbit and Bitstamp, the mechanism to set bitex-bot user-agent are different.
|
61
|
-
#
|
62
52
|
module WithUserAgent
|
63
53
|
def default_headers
|
64
54
|
super.merge(user_agent: BitexBot.user_agent)
|
65
55
|
end
|
66
56
|
end
|
67
57
|
|
68
|
-
##
|
69
58
|
# Mixing to include request behaviour and set user-agent.
|
70
|
-
#
|
71
59
|
class Request
|
72
60
|
prepend WithUserAgent
|
73
61
|
end
|
data/lib/bitex_bot/database.rb
CHANGED
@@ -53,7 +53,7 @@ module BitexBot
|
|
53
53
|
t.decimal :quantity, precision: 30, scale: 15
|
54
54
|
t.decimal :amount, precision: 30, scale: 15
|
55
55
|
t.boolean :done, null: false, default: false
|
56
|
-
t.decimal :
|
56
|
+
t.decimal :crypto_profit, precision: 30, scale: 15
|
57
57
|
t.decimal :fiat_profit, precision: 30, scale: 15
|
58
58
|
t.decimal :fx_rate, precision: 20, scale: 8
|
59
59
|
t.timestamps null: true
|
@@ -64,7 +64,7 @@ module BitexBot
|
|
64
64
|
t.decimal :quantity, precision: 30, scale: 15
|
65
65
|
t.decimal :amount, precision: 30, scale: 15
|
66
66
|
t.boolean :done, null: false, default: false
|
67
|
-
t.decimal :
|
67
|
+
t.decimal :crypto_profit, precision: 30, scale: 15
|
68
68
|
t.decimal :fiat_profit, precision: 30, scale: 15
|
69
69
|
t.decimal :fx_rate, precision: 20, scale: 8
|
70
70
|
t.timestamps null: true
|
@@ -91,20 +91,32 @@ module BitexBot
|
|
91
91
|
|
92
92
|
unless ActiveRecord::Base.connection.table_exists?('stores')
|
93
93
|
create_table :stores, force: true do |t|
|
94
|
+
t.decimal :maker_crypto, precision: 20, scale: 8
|
95
|
+
t.decimal :maker_crypto_stop, precision: 20, scale: 8
|
96
|
+
t.decimal :maker_crypto_warning, precision: 20, scale: 8
|
97
|
+
t.decimal :maker_fiat, precision: 20, scale: 8
|
98
|
+
t.decimal :maker_fiat_stop, precision: 20, scale: 8
|
99
|
+
t.decimal :maker_fiat_warning, precision: 20, scale: 8
|
100
|
+
|
101
|
+
t.decimal :taker_crypto, precision: 20, scale: 8
|
102
|
+
t.decimal :taker_crypto_stop, precision: 20, scale: 8
|
103
|
+
t.decimal :taker_crypto_warning, precision: 20, scale: 8
|
94
104
|
t.decimal :taker_fiat, precision: 20, scale: 8
|
95
|
-
t.decimal :
|
105
|
+
t.decimal :taker_fiat_stop, precision: 20, scale: 8
|
106
|
+
t.decimal :taker_fiat_warning, precision: 20, scale: 8
|
107
|
+
|
108
|
+
t.decimal :buying_amount_to_spend_per_order, precision: 20, scale: 8
|
109
|
+
t.decimal :buying_fx_rate, precision: 20, scale: 8
|
110
|
+
t.decimal :buying_profit, precision: 20, scale: 8
|
111
|
+
|
112
|
+
t.decimal :selling_quantity_to_sell_per_order, precision: 20, scale: 8
|
113
|
+
t.decimal :selling_fx_rate, precision: 20, scale: 8
|
114
|
+
t.decimal :selling_profit, precision: 20, scale: 8
|
115
|
+
|
96
116
|
t.boolean :hold, default: false
|
97
117
|
t.text :log
|
98
|
-
t.decimal :fiat_stop, precision: 20, scale: 8
|
99
|
-
t.decimal :fiat_warning, precision: 20, scale: 8
|
100
|
-
t.decimal :btc_stop, precision: 20, scale: 8
|
101
|
-
t.decimal :btc_warning, precision: 20, scale: 8
|
102
118
|
t.datetime :last_warning
|
103
|
-
|
104
|
-
t.decimal :selling_profit, precision: 20, scale: 8
|
105
|
-
t.decimal :buying_amount_to_spend_per_order, precision: 20, scale: 8
|
106
|
-
t.decimal :selling_quantity_to_sell_per_order, precision: 20, scale: 8
|
107
|
-
t.decimal :fx_rate, precision: 20, scale: 8
|
119
|
+
|
108
120
|
t.timestamps null: true
|
109
121
|
end
|
110
122
|
end
|
@@ -10,11 +10,15 @@ module BitexBot
|
|
10
10
|
OpenBuy
|
11
11
|
end
|
12
12
|
|
13
|
+
def fx_rate
|
14
|
+
Settings.buying_fx_rate
|
15
|
+
end
|
16
|
+
|
13
17
|
private
|
14
18
|
|
15
19
|
# create_or_cancel! hookers
|
16
20
|
# The coins we actually bought minus the coins we were supposed to re-buy
|
17
|
-
def
|
21
|
+
def estimate_crypto_profit
|
18
22
|
quantity - close_positions.sum(:quantity)
|
19
23
|
end
|
20
24
|
|
@@ -46,7 +46,7 @@ module BitexBot
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def positions_balance_amount
|
49
|
-
close_positions.sum(:amount) *
|
49
|
+
close_positions.sum(:amount) * fx_rate
|
50
50
|
end
|
51
51
|
|
52
52
|
private
|
@@ -87,7 +87,7 @@ module BitexBot
|
|
87
87
|
end
|
88
88
|
|
89
89
|
# This use hooks methods, these must be defined in the subclass:
|
90
|
-
#
|
90
|
+
# estimate_crypto_profit
|
91
91
|
# amount_positions_balance
|
92
92
|
# next_price_and_quantity
|
93
93
|
def create_next_position!
|
@@ -95,8 +95,8 @@ module BitexBot
|
|
95
95
|
if Robot.taker.enough_order_size?(next_quantity, next_price)
|
96
96
|
create_order_and_close_position(next_quantity, next_price)
|
97
97
|
else
|
98
|
-
update!(
|
99
|
-
Robot.logger.info("Closing: Finished #{self.class.name} ##{id} earned $#{fiat_profit} and #{
|
98
|
+
update!(crypto_profit: estimate_crypto_profit, fiat_profit: estimate_fiat_profit, fx_rate: fx_rate, done: true)
|
99
|
+
Robot.logger.info("Closing: Finished #{self.class.name} ##{id} earned $#{fiat_profit} and #{crypto_profit} BTC.")
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -36,7 +36,7 @@ module BitexBot
|
|
36
36
|
raise CannotCreateFlow, "Needed #{remote_value} but you only have #{remote_balance}" unless
|
37
37
|
enough_remote_funds?(remote_balance, remote_value)
|
38
38
|
|
39
|
-
bitex_price = maker_price(remote_value) *
|
39
|
+
bitex_price = maker_price(remote_value) * fx_rate
|
40
40
|
order = create_order!(bitex_price)
|
41
41
|
raise CannotCreateFlow, "You need to have #{value_to_use} on bitex to place this #{order_class.name}." unless
|
42
42
|
enough_funds?(order)
|
@@ -94,7 +94,7 @@ module BitexBot
|
|
94
94
|
def self.sync_open_positions
|
95
95
|
threshold = open_position_class.order('created_at DESC').first.try(:created_at)
|
96
96
|
Bitex::Trade.all.map do |transaction|
|
97
|
-
next
|
97
|
+
next unless sought_transaction?(threshold, transaction)
|
98
98
|
|
99
99
|
flow = find_by_order_id(transaction_order_id(transaction))
|
100
100
|
next unless flow.present?
|
@@ -122,16 +122,16 @@ module BitexBot
|
|
122
122
|
# This use hooks methods, these must be defined in the subclass:
|
123
123
|
# #transaction_class
|
124
124
|
def self.sought_transaction?(threshold, transaction)
|
125
|
-
|
126
|
-
active_transaction?(transaction, threshold)
|
127
|
-
open_position?(transaction)
|
128
|
-
|
125
|
+
transaction.is_a?(transaction_class) &&
|
126
|
+
!active_transaction?(transaction, threshold) &&
|
127
|
+
!open_position?(transaction) &&
|
128
|
+
expected_order_book?(transaction)
|
129
129
|
end
|
130
130
|
# end: sync_open_positions helpers
|
131
131
|
|
132
132
|
# sought_transaction helpers
|
133
133
|
def self.active_transaction?(transaction, threshold)
|
134
|
-
threshold && transaction.created_at < (threshold - 30.minutes)
|
134
|
+
threshold.present? && transaction.created_at < (threshold - 30.minutes)
|
135
135
|
end
|
136
136
|
|
137
137
|
def self.open_position?(transaction)
|
@@ -10,6 +10,10 @@ module BitexBot
|
|
10
10
|
OpenSell
|
11
11
|
end
|
12
12
|
|
13
|
+
def fx_rate
|
14
|
+
Settings.selling_fx_rate
|
15
|
+
end
|
16
|
+
|
13
17
|
private
|
14
18
|
|
15
19
|
# create_or_cancel! helpers
|
@@ -19,7 +23,7 @@ module BitexBot
|
|
19
23
|
end
|
20
24
|
|
21
25
|
# The coins we actually bought minus the coins we were supposed to re-buy.
|
22
|
-
def
|
26
|
+
def estimate_crypto_profit
|
23
27
|
close_positions.sum(:quantity) - quantity
|
24
28
|
end
|
25
29
|
|
data/lib/bitex_bot/robot.rb
CHANGED
@@ -63,13 +63,13 @@ module BitexBot
|
|
63
63
|
def_delegator self, :log
|
64
64
|
|
65
65
|
def self.with_cooldown
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
yield.tap do
|
67
|
+
self.current_cooldowns += 1
|
68
|
+
sleep_for(0.1)
|
69
|
+
end
|
70
70
|
end
|
71
71
|
|
72
|
-
|
72
|
+
private_class_method
|
73
73
|
|
74
74
|
def self.start_robot
|
75
75
|
setup
|
@@ -77,8 +77,6 @@ module BitexBot
|
|
77
77
|
new
|
78
78
|
end
|
79
79
|
|
80
|
-
# end: private class methods
|
81
|
-
|
82
80
|
# rubocop:disable Metrics/AbcSize
|
83
81
|
def trade!
|
84
82
|
sync_opening_flows if active_opening_flows?
|
@@ -106,11 +104,11 @@ module BitexBot
|
|
106
104
|
# rubocop:enable Metrics/AbcSize
|
107
105
|
|
108
106
|
def active_closing_flows?
|
109
|
-
[BuyClosingFlow
|
107
|
+
[BuyClosingFlow, SellClosingFlow].map(&:active).any?(&:exists?)
|
110
108
|
end
|
111
109
|
|
112
110
|
def active_opening_flows?
|
113
|
-
[BuyOpeningFlow
|
111
|
+
[BuyOpeningFlow, SellOpeningFlow].map(&:active).any?(&:exists?)
|
114
112
|
end
|
115
113
|
|
116
114
|
# The trader has a Store
|
@@ -158,7 +156,7 @@ module BitexBot
|
|
158
156
|
end
|
159
157
|
|
160
158
|
def open_positions?
|
161
|
-
[OpenBuy
|
159
|
+
[OpenBuy, OpenSell].map(&:open).any?(&:exists?)
|
162
160
|
end
|
163
161
|
|
164
162
|
def sync_closing_flows
|
@@ -179,14 +177,16 @@ module BitexBot
|
|
179
177
|
recent_buying, recent_selling = recent_operations
|
180
178
|
return log(:debug, 'Not placing new orders, recent ones exist.') if recent_buying && recent_selling
|
181
179
|
|
182
|
-
taker_balance = with_cooldown { Robot.taker.balance }
|
183
180
|
profile = Bitex::Profile.get
|
184
|
-
|
181
|
+
taker_balance = with_cooldown { Robot.taker.balance }
|
182
|
+
sync_log_and_store(taker_balance, profile)
|
185
183
|
|
186
|
-
|
187
|
-
|
188
|
-
return log(:debug, "Not placing new orders, #{Settings.quote} target not met") if
|
189
|
-
return log(:debug,
|
184
|
+
check_balance_warning if expired_last_warning?
|
185
|
+
|
186
|
+
return log(:debug, "Not placing new orders, #{Settings.quote} target not met") if alert?(:maker, :fiat, :stop)
|
187
|
+
return log(:debug, "Not placing new orders, #{Settings.base} target not met") if alert?(:maker, :crypto, :stop)
|
188
|
+
return log(:debug, 'Not placing new orders, USD target not met') if alert?(:taker, :fiat, :stop)
|
189
|
+
return log(:debug, "Not placing new orders, #{Settings.quote} target not met") if alert?(:taker, :crypto, :stop)
|
190
190
|
|
191
191
|
order_book = with_cooldown { Robot.taker.order_book }
|
192
192
|
transactions = with_cooldown { Robot.taker.transactions }
|
@@ -203,38 +203,37 @@ module BitexBot
|
|
203
203
|
end
|
204
204
|
end
|
205
205
|
|
206
|
-
def
|
207
|
-
total_fiat = maker_balance[:"#{Settings.quote}_balance"] + taker_balance.usd.total * Settings.fx_rate
|
208
|
-
total_btc = maker_balance[:btc_balance] + taker_balance.btc.total
|
209
|
-
|
210
|
-
[total_fiat, total_btc]
|
211
|
-
end
|
212
|
-
|
213
|
-
def sync_log(taker_balance)
|
206
|
+
def sync_log_and_store(taker_balance, maker_balance)
|
214
207
|
file = Settings.log.try(:file)
|
215
208
|
last_log = `tail -c 61440 #{file}` if file.present?
|
216
|
-
|
209
|
+
|
210
|
+
store.update(
|
211
|
+
maker_fiat: maker_balance[:"#{Settings.quote}_balance"], maker_crypto: maker_balance[:"#{Settings.base}_balance"],
|
212
|
+
taker_fiat: taker_balance.fiat.total, taker_crypto: taker_balance.crypto.total,
|
213
|
+
log: last_log
|
214
|
+
)
|
217
215
|
end
|
218
216
|
|
219
217
|
def expired_last_warning?
|
220
218
|
store.last_warning.nil? || store.last_warning < 30.minutes.ago
|
221
219
|
end
|
222
220
|
|
223
|
-
|
224
|
-
|
225
|
-
notify_balance_warning(
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
store.send("#{currency}_warning").present? && total <= store.send("#{currency}_warning")
|
221
|
+
# rubocop:disable Metrics/AbcSize
|
222
|
+
def check_balance_warning
|
223
|
+
notify_balance_warning(Settings.base, store.maker_crypto, store.maker_crypto_warning) if alert?(:maker, :crypto, :warning)
|
224
|
+
notify_balance_warning(Settings.quote, store.maker_fiat, store.maker_fiat_warning) if alert?(:maker, :fiat, :warning)
|
225
|
+
notify_balance_warning(Settings.base, store.taker_crypto, store.taker_crypto_warning) if alert?(:taker, :crypto, :warning)
|
226
|
+
notify_balance_warning(:usd, store.taker_fiat, store.taker_fiat_warning) if alert?(:taker, :fiat, :warning)
|
230
227
|
end
|
228
|
+
# rubocop:enable Metrics/AbcSize
|
231
229
|
|
232
|
-
def
|
233
|
-
store.send("#{currency}
|
230
|
+
def alert?(market, currency, flag)
|
231
|
+
return unless store.send("#{market}_#{currency}_#{flag}").present?
|
232
|
+
store.send("#{market}_#{currency}") <= store.send("#{market}_#{currency}_#{flag}")
|
234
233
|
end
|
235
234
|
|
236
|
-
def notify_balance_warning(currency,
|
237
|
-
notify("#{currency.upcase} balance is too low, it's #{
|
235
|
+
def notify_balance_warning(currency, amount, warning_amount)
|
236
|
+
notify("#{currency.upcase} balance is too low, it's #{amount}, make it #{warning_amount} to stop this warning.")
|
238
237
|
store.update(last_warning: Time.now)
|
239
238
|
end
|
240
239
|
|
@@ -242,9 +241,9 @@ module BitexBot
|
|
242
241
|
log(:error, message)
|
243
242
|
return unless Settings.mailer.present?
|
244
243
|
|
245
|
-
|
246
|
-
|
247
|
-
|
244
|
+
new_mail(subj, message).tap do |mail|
|
245
|
+
mail.delivery_method(Settings.mailer.delivery_method.to_sym, Settings.mailer.options.to_hash)
|
246
|
+
end.deliver!
|
248
247
|
end
|
249
248
|
|
250
249
|
def new_mail(subj, message)
|
@@ -257,11 +256,11 @@ module BitexBot
|
|
257
256
|
end
|
258
257
|
|
259
258
|
def create_buy_opening_flow(balance, order_book, transactions, profile)
|
260
|
-
BuyOpeningFlow.create_for_market(balance.
|
259
|
+
BuyOpeningFlow.create_for_market(balance.crypto.available, order_book.bids, transactions, profile[:fee], balance.fee, store)
|
261
260
|
end
|
262
261
|
|
263
262
|
def create_sell_opening_flow(balance, order_book, transactions, profile)
|
264
|
-
SellOpeningFlow.create_for_market(balance.
|
263
|
+
SellOpeningFlow.create_for_market(balance.fiat.available, order_book.asks, transactions, profile[:fee], balance.fee, store)
|
265
264
|
end
|
266
265
|
# rubocop:enable Metrics/ClassLength
|
267
266
|
end
|
data/lib/bitex_bot/settings.rb
CHANGED
@@ -29,8 +29,12 @@ module BitexBot
|
|
29
29
|
load_settings(path)
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
33
|
-
Store.first.try(:
|
32
|
+
def buying_fx_rate
|
33
|
+
Store.first.try(:buying_fx_rate) || buying_foreign_exchange_rate
|
34
|
+
end
|
35
|
+
|
36
|
+
def selling_fx_rate
|
37
|
+
Store.first.try(:selling_fx_rate) || selling_foreign_exchange_rate
|
34
38
|
end
|
35
39
|
|
36
40
|
def base
|
data/lib/bitex_bot/version.rb
CHANGED
data/settings.rb.sample
CHANGED
@@ -36,8 +36,11 @@ buying amount_to_spend_per_order: 10.0.to_d, profit: 0.5.to_d
|
|
36
36
|
# places an ask on Bitex charging a higher price.
|
37
37
|
selling quantity_to_sell_per_order: 0.1.to_d, profit: 0.5.to_d
|
38
38
|
|
39
|
-
# Quote fx rate for
|
40
|
-
|
39
|
+
# Quote fx rate for selling for order book
|
40
|
+
selling_foreign_exchange_rate 1.to_d
|
41
|
+
|
42
|
+
# Quote fx rate for buying for order book
|
43
|
+
buying_foreign_exchange_rate 1.to_d
|
41
44
|
|
42
45
|
# This is your maker, at the moment only will operates with bitex
|
43
46
|
# api_key: it's passed in to the bitex gem: https://github.com/bitex-la/bitex-ruby.
|
@@ -8,7 +8,8 @@ describe BitexBot::Settings do
|
|
8
8
|
time_to_live: 20,
|
9
9
|
buying: { amount_to_spend_per_order: 10, profit: 0.5 },
|
10
10
|
selling: { quantity_to_sell_per_order: 0.1, profit: 0.5 },
|
11
|
-
|
11
|
+
buying_foreign_exchange_rate: 1,
|
12
|
+
selling_foreign_exchange_rate: 1,
|
12
13
|
|
13
14
|
maker: { bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false } },
|
14
15
|
# By default Bitstamp is taker market.
|
@@ -31,22 +32,26 @@ describe BitexBot::Settings do
|
|
31
32
|
)
|
32
33
|
end
|
33
34
|
|
34
|
-
context '
|
35
|
-
context 'when Store isn´t loaded' do
|
36
|
-
it
|
37
|
-
|
38
|
-
end
|
35
|
+
context 'fx rate' do
|
36
|
+
context 'when Store isn´t loaded, by default' do
|
37
|
+
it { described_class.buying_fx_rate.should eq(1) }
|
38
|
+
it { described_class.selling_fx_rate.should eq(1) }
|
39
39
|
end
|
40
40
|
|
41
|
-
context 'when Store is loaded' do
|
42
|
-
before(:each)
|
43
|
-
BitexBot::Store.stub(first: BitexBot::Store.new )
|
44
|
-
BitexBot::Store.any_instance.stub(fx_rate: fx_rate)
|
45
|
-
end
|
41
|
+
context 'when Store is loaded, take rate from' do
|
42
|
+
before(:each) { BitexBot::Store.stub(first: BitexBot::Store.new) }
|
46
43
|
let(:fx_rate) { rand(10) }
|
47
44
|
|
48
|
-
|
49
|
-
|
45
|
+
context 'buying' do
|
46
|
+
before(:each) { BitexBot::Store.any_instance.stub(buying_fx_rate: fx_rate) }
|
47
|
+
|
48
|
+
it { described_class.buying_fx_rate.should eq(fx_rate) }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'selling' do
|
52
|
+
before(:each) { BitexBot::Store.any_instance.stub(selling_fx_rate: fx_rate) }
|
53
|
+
|
54
|
+
it { described_class.selling_fx_rate.should eq(fx_rate) }
|
50
55
|
end
|
51
56
|
end
|
52
57
|
end
|
@@ -44,18 +44,18 @@ describe BitstampApiWrapper do
|
|
44
44
|
|
45
45
|
balance = api_wrapper.balance
|
46
46
|
balance.should be_a(ApiWrapper::BalanceSummary)
|
47
|
-
balance.
|
48
|
-
balance.
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
47
|
+
balance.crypto.should be_a(ApiWrapper::Balance)
|
48
|
+
balance.fiat.should be_a(ApiWrapper::Balance)
|
49
|
+
|
50
|
+
crypto = balance.crypto
|
51
|
+
crypto.total.should be_a(BigDecimal)
|
52
|
+
crypto.reserved.should be_a(BigDecimal)
|
53
|
+
crypto.available.should be_a(BigDecimal)
|
54
|
+
|
55
|
+
fiat = balance.fiat
|
56
|
+
fiat.total.should be_a(BigDecimal)
|
57
|
+
fiat.reserved.should be_a(BigDecimal)
|
58
|
+
fiat.available.should be_a(BigDecimal)
|
59
59
|
|
60
60
|
balance.fee.should be_a(BigDecimal)
|
61
61
|
end
|
@@ -53,18 +53,18 @@ describe ItbitApiWrapper do
|
|
53
53
|
|
54
54
|
balance = api_wrapper.balance
|
55
55
|
balance.should be_a(ApiWrapper::BalanceSummary)
|
56
|
-
balance.
|
57
|
-
balance.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
56
|
+
balance.crypto.should be_a(ApiWrapper::Balance)
|
57
|
+
balance.fiat.should be_a(ApiWrapper::Balance)
|
58
|
+
|
59
|
+
crypto = balance.crypto
|
60
|
+
crypto.total.should be_a(BigDecimal)
|
61
|
+
crypto.reserved.should be_a(BigDecimal)
|
62
|
+
crypto.available.should be_a(BigDecimal)
|
63
|
+
|
64
|
+
fiat = balance.fiat
|
65
|
+
fiat.total.should be_a(BigDecimal)
|
66
|
+
fiat.reserved.should be_a(BigDecimal)
|
67
|
+
fiat.available.should be_a(BigDecimal)
|
68
68
|
|
69
69
|
balance.fee.should be_a(BigDecimal)
|
70
70
|
end
|
@@ -77,18 +77,18 @@ describe KrakenApiWrapper do
|
|
77
77
|
|
78
78
|
balance = api_wrapper.balance
|
79
79
|
balance.should be_a(ApiWrapper::BalanceSummary)
|
80
|
-
balance.
|
81
|
-
balance.
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
80
|
+
balance.crypto.should be_a(ApiWrapper::Balance)
|
81
|
+
balance.fiat.should be_a(ApiWrapper::Balance)
|
82
|
+
|
83
|
+
crypto = balance.crypto
|
84
|
+
crypto.total.should be_a(BigDecimal)
|
85
|
+
crypto.reserved.should be_a(BigDecimal)
|
86
|
+
crypto.available.should be_a(BigDecimal)
|
87
|
+
|
88
|
+
fiat = balance.fiat
|
89
|
+
fiat.total.should be_a(BigDecimal)
|
90
|
+
fiat.reserved.should be_a(BigDecimal)
|
91
|
+
fiat.available.should be_a(BigDecimal)
|
92
92
|
|
93
93
|
balance.fee.should be_a(BigDecimal)
|
94
94
|
end
|
@@ -26,7 +26,7 @@ describe BitexBot::BuyClosingFlow do
|
|
26
26
|
flow.desired_price.should == 310
|
27
27
|
flow.quantity.should == 2
|
28
28
|
flow.amount.should == 600
|
29
|
-
flow.
|
29
|
+
flow.crypto_profit.should be_nil
|
30
30
|
flow.fiat_profit.should be_nil
|
31
31
|
|
32
32
|
close = flow.close_positions.first
|
@@ -51,7 +51,7 @@ describe BitexBot::BuyClosingFlow do
|
|
51
51
|
flow.desired_price.round(10).should == '310.4_975_124_378'.to_d
|
52
52
|
flow.quantity.should == 2.01
|
53
53
|
flow.amount.should == 604
|
54
|
-
flow.
|
54
|
+
flow.crypto_profit.should be_nil
|
55
55
|
flow.fiat_profit.should be_nil
|
56
56
|
|
57
57
|
close.order_id.should == '1'
|
@@ -92,7 +92,7 @@ describe BitexBot::BuyClosingFlow do
|
|
92
92
|
close.quantity.should == 2.01
|
93
93
|
|
94
94
|
flow.should be_done
|
95
|
-
flow.
|
95
|
+
flow.crypto_profit.should be_zero
|
96
96
|
flow.fiat_profit.should == 20.105
|
97
97
|
end
|
98
98
|
|
@@ -111,7 +111,7 @@ describe BitexBot::BuyClosingFlow do
|
|
111
111
|
|
112
112
|
it 'syncs the executed orders, calculates profit with other fx rate' do
|
113
113
|
flow.should be_done
|
114
|
-
flow.
|
114
|
+
flow.crypto_profit.should be_zero
|
115
115
|
flow.fiat_profit.should eq positions_balance_amount
|
116
116
|
end
|
117
117
|
end
|
@@ -161,7 +161,7 @@ describe BitexBot::BuyClosingFlow do
|
|
161
161
|
close.quantity.should == 1.005
|
162
162
|
end
|
163
163
|
flow.should be_done
|
164
|
-
flow.
|
164
|
+
flow.crypto_profit.should be_zero
|
165
165
|
flow.fiat_profit.should == 20.07_485
|
166
166
|
end
|
167
167
|
|
@@ -176,7 +176,7 @@ describe BitexBot::BuyClosingFlow do
|
|
176
176
|
end.not_to change { BitexBot::CloseBuy.count }
|
177
177
|
|
178
178
|
flow.should be_done
|
179
|
-
flow.
|
179
|
+
flow.crypto_profit.should == 0.00_201
|
180
180
|
flow.fiat_profit.should == 19.480_895
|
181
181
|
end
|
182
182
|
|
@@ -195,7 +195,7 @@ describe BitexBot::BuyClosingFlow do
|
|
195
195
|
|
196
196
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
197
197
|
flow.reload.should be_done
|
198
|
-
flow.
|
198
|
+
flow.crypto_profit.should be_zero
|
199
199
|
flow.fiat_profit.should == -34.165
|
200
200
|
end
|
201
201
|
end
|
@@ -216,7 +216,7 @@ describe BitexBot::BuyClosingFlow do
|
|
216
216
|
flow.open_positions.should == [open]
|
217
217
|
flow.desired_price.should == 310
|
218
218
|
flow.quantity.should == 2
|
219
|
-
flow.
|
219
|
+
flow.crypto_profit.should be_nil
|
220
220
|
flow.fiat_profit.should be_nil
|
221
221
|
flow.close_positions.should be_empty
|
222
222
|
end
|
@@ -66,7 +66,7 @@ describe BitexBot::BuyOpeningFlow do
|
|
66
66
|
|
67
67
|
it 'spends 100 usd with other fx_rate' do
|
68
68
|
BitexBot::Settings.stub(
|
69
|
-
|
69
|
+
buying_foreign_exchange_rate: other_fx_rate,
|
70
70
|
time_to_live: 3,
|
71
71
|
buying: double(amount_to_spend_per_order: amount_to_spend, profit: 0)
|
72
72
|
)
|
data/spec/models/robot_spec.rb
CHANGED
@@ -50,11 +50,12 @@ describe BitexBot::Robot do
|
|
50
50
|
stub_bitex_orders
|
51
51
|
stub_bitstamp_api_wrapper_order_book
|
52
52
|
bot.trade!
|
53
|
+
|
53
54
|
stub_bitex_transactions
|
54
55
|
buying = BitexBot::BuyOpeningFlow.last
|
55
56
|
selling = BitexBot::SellOpeningFlow.last
|
56
57
|
|
57
|
-
Timecop.travel
|
58
|
+
Timecop.travel(10.minutes.from_now)
|
58
59
|
bot.trade!
|
59
60
|
|
60
61
|
buying.reload.should be_settling
|
@@ -68,39 +69,36 @@ describe BitexBot::Robot do
|
|
68
69
|
it 'creates alternating opening flows' do
|
69
70
|
Bitex::Trade.stub(all: [])
|
70
71
|
bot.trade!
|
71
|
-
BitexBot::BuyOpeningFlow.active.count.should
|
72
|
-
|
72
|
+
BitexBot::BuyOpeningFlow.active.count.should eq 1
|
73
|
+
|
74
|
+
Timecop.travel(2.seconds.from_now)
|
73
75
|
bot.trade!
|
74
|
-
BitexBot::BuyOpeningFlow.active.count.should
|
75
|
-
|
76
|
+
BitexBot::BuyOpeningFlow.active.count.should eq 1
|
77
|
+
|
78
|
+
Timecop.travel(5.seconds.from_now)
|
76
79
|
bot.trade!
|
77
|
-
BitexBot::BuyOpeningFlow.active.count.should
|
80
|
+
BitexBot::BuyOpeningFlow.active.count.should eq 2
|
78
81
|
|
79
|
-
# When transactions appear, all opening flows
|
80
|
-
#
|
81
|
-
# We stub our finder to make it so all orders
|
82
|
-
# have been successfully cancelled.
|
82
|
+
# When transactions appear, all opening flows should get old and die.
|
83
|
+
# We stub our finder to make it so all orders have been successfully cancelled.
|
83
84
|
stub_bitex_transactions
|
84
85
|
|
85
86
|
Timecop.travel 5.seconds.from_now
|
86
|
-
bot.trade!
|
87
|
-
|
88
|
-
|
87
|
+
2.times { bot.trade! }
|
88
|
+
BitexBot::BuyOpeningFlow.active.count.should eq 1
|
89
|
+
|
89
90
|
Timecop.travel 5.seconds.from_now
|
90
91
|
bot.trade!
|
91
|
-
BitexBot::BuyOpeningFlow.active.count.should
|
92
|
+
BitexBot::BuyOpeningFlow.active.count.should eq 0
|
92
93
|
end
|
93
94
|
|
94
95
|
it 'does not place new opening flows until all closing flows are done' do
|
95
96
|
bot.trade!
|
96
97
|
stub_bitex_transactions
|
97
|
-
expect
|
98
|
-
bot.trade!
|
99
|
-
end.to change { BitexBot::BuyClosingFlow.count }.by(1)
|
98
|
+
expect { bot.trade! }.to change { BitexBot::BuyClosingFlow.count }.by(1)
|
100
99
|
|
101
100
|
Timecop.travel 15.seconds.from_now
|
102
|
-
bot.trade!
|
103
|
-
bot.trade!
|
101
|
+
2.times { bot.trade! }
|
104
102
|
bot.should be_active_closing_flows
|
105
103
|
bot.should_not be_active_opening_flows
|
106
104
|
|
@@ -111,97 +109,110 @@ describe BitexBot::Robot do
|
|
111
109
|
end.to change { BitexBot::BuyOpeningFlow.count }.by(1)
|
112
110
|
end
|
113
111
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
112
|
+
context 'stops trading when' do
|
113
|
+
before(:each) do
|
114
|
+
Bitex::Profile.stub(get: {
|
115
|
+
fee: 0.5,
|
116
|
+
usd_balance: 10.00,
|
117
|
+
usd_reserved: 10.00,
|
118
|
+
usd_available: 10.00,
|
119
|
+
btc_balance: 10.00,
|
120
|
+
btc_reserved: 10.00,
|
121
|
+
btc_available: 10.00
|
122
|
+
})
|
123
|
+
|
124
|
+
stub_bitstamp_api_wrapper_balance(10, 10, 0.05)
|
125
|
+
end
|
126
|
+
|
127
|
+
after(:each) { expect { bot.trade! }.not_to change { BitexBot::BuyOpeningFlow.count } }
|
128
|
+
|
129
|
+
let(:other_bot) { described_class.new }
|
130
|
+
|
131
|
+
it 'does not place new opening flows when ordered to hold' do
|
132
|
+
other_bot.store.update(hold: true)
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'maker' do
|
136
|
+
it 'crypto stop is reached' do
|
137
|
+
other_bot.store.update(maker_crypto_stop: 30)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'fiat stop is reached' do
|
141
|
+
other_bot.store.update(maker_fiat_stop: 30)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'taker' do
|
146
|
+
it 'crypto stop is reached' do
|
147
|
+
other_bot.store.update(taker_crypto_stop: 30)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'fiat stop is reached' do
|
151
|
+
other_bot.store.update(taker_fiat_stop: 30)
|
152
|
+
end
|
153
|
+
end
|
139
154
|
end
|
140
155
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
156
|
+
context 'warns every 30 minutes when' do
|
157
|
+
before(:each) do
|
158
|
+
Bitex::Profile.stub(get: {
|
159
|
+
fee: 0.5,
|
160
|
+
usd_balance: 10.00,
|
161
|
+
usd_reserved: 1.00,
|
162
|
+
usd_available: 9.00,
|
163
|
+
btc_balance: 10.00,
|
164
|
+
btc_reserved: 1.00,
|
165
|
+
btc_available: 9.00
|
166
|
+
})
|
167
|
+
Bitex::Trade.stub(all: [])
|
168
|
+
stub_bitstamp_api_wrapper_balance(100, 100)
|
169
|
+
other_bot.store.update(maker_crypto_warning: 0, taker_crypto_warning: 0, maker_fiat_warning: 0, taker_fiat_warning: 0)
|
170
|
+
end
|
171
|
+
|
172
|
+
after(:each) do
|
173
|
+
expect { bot.trade! }.to change { Mail::TestMailer.deliveries.count }.by(1)
|
174
|
+
|
175
|
+
Timecop.travel 1.minute.from_now
|
176
|
+
stub_bitstamp_order_book # Re-stub so order book does not get old
|
177
|
+
expect { bot.trade! }.not_to change { Mail::TestMailer.deliveries.count }
|
178
|
+
|
179
|
+
Timecop.travel 31.minutes.from_now
|
180
|
+
stub_bitstamp_order_book # Re-stub so order book does not get old
|
181
|
+
expect { bot.trade! }.to change { Mail::TestMailer.deliveries.count }.by(1)
|
182
|
+
end
|
183
|
+
|
184
|
+
let(:other_bot) { described_class.new }
|
185
|
+
|
186
|
+
context 'maker' do
|
187
|
+
it 'crypto warning is reached' do
|
188
|
+
other_bot.store.update(maker_crypto_warning: 100)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'fiat warning is reached' do
|
192
|
+
other_bot.store.update(maker_fiat_warning: 100)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'taker' do
|
197
|
+
it 'crypto warning is reached' do
|
198
|
+
other_bot.store.update(taker_crypto_warning: 100)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'fiat warning is reached' do
|
202
|
+
other_bot.store.update(taker_fiat_warning: 100)
|
203
|
+
end
|
204
|
+
end
|
148
205
|
end
|
149
206
|
|
150
|
-
it '
|
151
|
-
Bitex::Trade.stub(all: [])
|
152
|
-
other_bot = BitexBot::Robot.new
|
153
|
-
other_bot.store.fiat_warning = 11000
|
154
|
-
other_bot.store.save!
|
155
|
-
expect do
|
156
|
-
bot.trade!
|
157
|
-
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
158
|
-
Timecop.travel 1.minute.from_now
|
159
|
-
stub_bitstamp_order_book # Re-stub so order book does not get old
|
160
|
-
expect do
|
161
|
-
bot.trade!
|
162
|
-
end.not_to change { Mail::TestMailer.deliveries.count }
|
163
|
-
Timecop.travel 31.minutes.from_now
|
164
|
-
stub_bitstamp_order_book # Re-stub so order book does not get old
|
165
|
-
expect do
|
166
|
-
bot.trade!
|
167
|
-
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
168
|
-
end
|
169
|
-
|
170
|
-
it 'warns every 30 minutes when btc warn is reached' do
|
171
|
-
Bitex::Trade.stub(all: [])
|
172
|
-
other_bot = BitexBot::Robot.new
|
173
|
-
other_bot.store.btc_warning = 30
|
174
|
-
other_bot.store.save!
|
175
|
-
|
176
|
-
expect do
|
177
|
-
bot.trade!
|
178
|
-
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
179
|
-
|
180
|
-
Timecop.travel 1.minute.from_now
|
181
|
-
stub_bitstamp_order_book # Re-stub so order book does not get old
|
182
|
-
expect do
|
183
|
-
bot.trade!
|
184
|
-
end.not_to change { Mail::TestMailer.deliveries.count }
|
185
|
-
|
186
|
-
Timecop.travel 31.minutes.from_now
|
187
|
-
stub_bitstamp_order_book # Re-stub so order book does not get old
|
188
|
-
|
189
|
-
expect do
|
190
|
-
bot.trade!
|
191
|
-
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'updates taker_fiat and taker_btc' do
|
207
|
+
it 'updates taker_fiat and taker_crypto' do
|
195
208
|
bot.trade!
|
196
209
|
bot.store.taker_fiat.should_not be_nil
|
197
|
-
bot.store.
|
210
|
+
bot.store.taker_crypto.should_not be_nil
|
198
211
|
end
|
199
212
|
|
200
213
|
it 'notifies exceptions and sleeps' do
|
201
214
|
BitstampApiWrapper.stub(:balance) { raise StandardError.new('oh moova') }
|
202
215
|
|
203
|
-
expect
|
204
|
-
bot.trade!
|
205
|
-
end.to change { Mail::TestMailer.deliveries.count }.by(1)
|
216
|
+
expect { bot.trade! }.to change { Mail::TestMailer.deliveries.count }.by(1)
|
206
217
|
end
|
207
218
|
end
|
@@ -26,7 +26,7 @@ describe BitexBot::SellClosingFlow do
|
|
26
26
|
flow.desired_price.should == 290
|
27
27
|
flow.quantity.should == 2
|
28
28
|
flow.amount.should == 600
|
29
|
-
flow.
|
29
|
+
flow.crypto_profit.should be_nil
|
30
30
|
flow.fiat_profit.should be_nil
|
31
31
|
|
32
32
|
close = flow.close_positions.first
|
@@ -51,7 +51,7 @@ describe BitexBot::SellClosingFlow do
|
|
51
51
|
flow.desired_price.round(10).should == '290.4975124378'.to_d
|
52
52
|
flow.quantity.should == 2.01
|
53
53
|
flow.amount.should == 604
|
54
|
-
flow.
|
54
|
+
flow.crypto_profit.should be_nil
|
55
55
|
flow.fiat_profit.should be_nil
|
56
56
|
|
57
57
|
close.order_id.should == '1'
|
@@ -75,7 +75,7 @@ describe BitexBot::SellClosingFlow do
|
|
75
75
|
flow.open_positions.should == [open]
|
76
76
|
flow.desired_price.should == 290
|
77
77
|
flow.quantity.should == 2
|
78
|
-
flow.
|
78
|
+
flow.crypto_profit.should be_nil
|
79
79
|
flow.fiat_profit.should be_nil
|
80
80
|
flow.close_positions.should be_empty
|
81
81
|
end
|
@@ -133,7 +133,7 @@ describe BitexBot::SellClosingFlow do
|
|
133
133
|
close.quantity.should == 2.01
|
134
134
|
|
135
135
|
flow.should be_done
|
136
|
-
flow.
|
136
|
+
flow.crypto_profit.should == 0
|
137
137
|
flow.fiat_profit.should == '20.095'.to_d
|
138
138
|
end
|
139
139
|
|
@@ -152,7 +152,7 @@ describe BitexBot::SellClosingFlow do
|
|
152
152
|
|
153
153
|
it 'syncs the executed orders, calculates profit with other fx rate' do
|
154
154
|
flow.should be_done
|
155
|
-
flow.
|
155
|
+
flow.crypto_profit.should be_zero
|
156
156
|
flow.fiat_profit.should eq positions_balance_amount
|
157
157
|
end
|
158
158
|
end
|
@@ -203,7 +203,7 @@ describe BitexBot::SellClosingFlow do
|
|
203
203
|
close.quantity.should == '1.0049'.to_d
|
204
204
|
end
|
205
205
|
flow.should be_done
|
206
|
-
flow.
|
206
|
+
flow.crypto_profit.should == '-0.0001'.to_d
|
207
207
|
flow.fiat_profit.should == '20.093903'.to_d
|
208
208
|
end
|
209
209
|
|
@@ -224,7 +224,7 @@ describe BitexBot::SellClosingFlow do
|
|
224
224
|
end.not_to change{ BitexBot::CloseSell.count }
|
225
225
|
|
226
226
|
flow.should be_done
|
227
|
-
flow.
|
227
|
+
flow.crypto_profit.should == '-0.0224895'.to_d
|
228
228
|
flow.fiat_profit.should == '20.66566825'.to_d
|
229
229
|
end
|
230
230
|
|
@@ -243,7 +243,7 @@ describe BitexBot::SellClosingFlow do
|
|
243
243
|
|
244
244
|
flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
|
245
245
|
flow.reload.should be_done
|
246
|
-
flow.
|
246
|
+
flow.crypto_profit.should == '-0.1709'.to_d
|
247
247
|
flow.fiat_profit.should == '20.08575'.to_d
|
248
248
|
|
249
249
|
close = flow.close_positions.last
|
@@ -66,7 +66,7 @@ describe BitexBot::SellOpeningFlow do
|
|
66
66
|
|
67
67
|
it 'sells 4 bitcoin' do
|
68
68
|
BitexBot::Settings.stub(
|
69
|
-
|
69
|
+
selling_foreign_exchange_rate: other_fx_rate,
|
70
70
|
time_to_live: 3,
|
71
71
|
selling: double(quantity_to_sell_per_order: amount_to_sell, profit: 0)
|
72
72
|
)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitex_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nubis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-07-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|