bitex_bot 0.4.0 → 0.5.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/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
|