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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '027380d0b5310a8656d1e4ea0357617f1c750c1a'
4
- data.tar.gz: 66974805bb9b7b130181e737f688512d8f234d17
3
+ metadata.gz: 1ec3db346eb39cba7b7760969b32205f778bdf89
4
+ data.tar.gz: 4117298e216bc463273d303ea82b0d4f549958af
5
5
  SHA512:
6
- metadata.gz: d70e171eb1bae487ab0866d441c7c7ac3711d129976a4bf0351d1a1848834dbb9b4f2cb9227af7d2f73df7a3aa200052dc48028b8a8b0c0fa753d775ba21070a
7
- data.tar.gz: 4bc5ae223b7ddde9b07b8397ce2c5fcbc25b89b289084b57bc357448c8d6c822e301a0814f657ccf89a9d12a57f6d56d5cabdd1d2d88f7393a36b00e18c1480b
6
+ metadata.gz: b73d5b0ea727152b76ab15052ca7501fa5beae556d6946ddb5d2e0f26e1d76a93d86210283488a57e44c04c2b4ed36fe993a297debc39bb3234e657a90a570dd
7
+ data.tar.gz: 2958fc14136fb77f4b578bda495d27050a6bc14886f78fc784fda7dc2ab00703bbbe5fb55860214a5c0ec21374335163da2aa760c0dfbe9f755d10c30ce4aca1
@@ -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 do |curl|
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
@@ -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 :btc_profit, precision: 30, scale: 15
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 :btc_profit, precision: 30, scale: 15
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 :taker_btc, precision: 20, scale: 8
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
- t.decimal :buying_profit, precision: 20, scale: 8
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
@@ -38,9 +38,9 @@ class ApiWrapper
38
38
  )
39
39
 
40
40
  BalanceSummary = Struct.new(
41
- :btc, # Balance
42
- :usd, # Balance
43
- :fee # Decimal
41
+ :crypto, # Balance
42
+ :fiat, # Balance
43
+ :fee # Decimal
44
44
  )
45
45
 
46
46
  Balance = Struct.new(
@@ -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 estimate_btc_profit
21
+ def estimate_crypto_profit
18
22
  quantity - close_positions.sum(:quantity)
19
23
  end
20
24
 
@@ -73,5 +73,9 @@ module BitexBot
73
73
  store.buying_amount_to_spend_per_order || Settings.buying.amount_to_spend_per_order
74
74
  end
75
75
  # end: create_for_market helpers
76
+
77
+ def self.fx_rate
78
+ Settings.buying_fx_rate
79
+ end
76
80
  end
77
81
  end
@@ -46,7 +46,7 @@ module BitexBot
46
46
  end
47
47
 
48
48
  def positions_balance_amount
49
- close_positions.sum(:amount) * Settings.fx_rate
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
- # estimate_btc_profit
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!(btc_profit: estimate_btc_profit, fiat_profit: estimate_fiat_profit, fx_rate: Settings.fx_rate, done: true)
99
- Robot.logger.info("Closing: Finished #{self.class.name} ##{id} earned $#{fiat_profit} and #{btc_profit} BTC.")
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) * Settings.fx_rate
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 if sought_transaction?(threshold, transaction)
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
- !transaction.is_a?(transaction_class) ||
126
- active_transaction?(transaction, threshold) ||
127
- open_position?(transaction) ||
128
- !expected_order_book?(transaction)
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 estimate_btc_profit
26
+ def estimate_crypto_profit
23
27
  close_positions.sum(:quantity) - quantity
24
28
  end
25
29
 
@@ -72,5 +72,9 @@ module BitexBot
72
72
  store.selling_quantity_to_sell_per_order || Settings.selling.quantity_to_sell_per_order
73
73
  end
74
74
  # end: create_for_market helpers
75
+
76
+ def self.fx_rate
77
+ Settings.selling_fx_rate
78
+ end
75
79
  end
76
80
  end
@@ -63,13 +63,13 @@ module BitexBot
63
63
  def_delegator self, :log
64
64
 
65
65
  def self.with_cooldown
66
- result = yield
67
- self.current_cooldowns += 1
68
- sleep_for(0.1)
69
- result
66
+ yield.tap do
67
+ self.current_cooldowns += 1
68
+ sleep_for(0.1)
69
+ end
70
70
  end
71
71
 
72
- # private class methods
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.active, SellClosingFlow.active].any?(&:exists?)
107
+ [BuyClosingFlow, SellClosingFlow].map(&:active).any?(&:exists?)
110
108
  end
111
109
 
112
110
  def active_opening_flows?
113
- [BuyOpeningFlow.active, SellOpeningFlow.active].any?(&:exists?)
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.open, OpenSell.open].any?(&:exists?)
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
- total_fiat, total_btc = balances(taker_balance, profile)
181
+ taker_balance = with_cooldown { Robot.taker.balance }
182
+ sync_log_and_store(taker_balance, profile)
185
183
 
186
- sync_log(taker_balance)
187
- check_balance_warning(total_fiat, total_btc) if expired_last_warning?
188
- return log(:debug, "Not placing new orders, #{Settings.quote} target not met") if target_met?(:fiat, total_fiat)
189
- return log(:debug, 'Not placing new orders, BTC target not met') if target_met?(:btc, total_btc)
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 balances(taker_balance, maker_balance)
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
- store.update(taker_fiat: taker_balance.usd.total * Settings.fx_rate, taker_btc: taker_balance.btc.total, log: last_log)
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
- def check_balance_warning(total_fiat, total_btc)
224
- notify_balance_warning(Settings.quote, total_fiat, store.fiat_warning) if balance_warning_notify?(:fiat, total_fiat)
225
- notify_balance_warning(:btc, total_btc, store.btc_warning) if balance_warning_notify?(:btc, total_btc)
226
- end
227
-
228
- def balance_warning_notify?(currency, total)
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 target_met?(currency, total)
233
- store.send("#{currency}_stop").present? && total <= store.send("#{currency}_stop")
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, total, warning_amount)
237
- notify("#{currency.upcase} balance is too low, it's #{total}, make it #{warning_amount} to stop this warning.")
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
- mail = new_mail(subj, message)
246
- mail.delivery_method(Settings.mailer.delivery_method.to_sym, Settings.mailer.options.to_hash)
247
- mail.deliver!
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.btc.available, order_book.bids, transactions, profile[:fee], balance.fee, store)
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.usd.available, order_book.asks, transactions, profile[:fee], balance.fee, store)
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
@@ -29,8 +29,12 @@ module BitexBot
29
29
  load_settings(path)
30
30
  end
31
31
 
32
- def fx_rate
33
- Store.first.try(:fx_rate) || foreign_exchange_rate
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
@@ -1,3 +1,3 @@
1
1
  module BitexBot
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'.freeze
3
3
  end
@@ -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 maker order book
40
- foreign_exchange_rate 1.to_d
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
- foreign_exchange_rate: 1,
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 'fx_rate' do
35
- context 'when Store isn´t loaded' do
36
- it 'by default' do
37
- described_class.fx_rate.should eq(1)
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) do
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
- it 'take rate from it' do
49
- described_class.fx_rate.should eq(fx_rate)
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.btc.should be_a(ApiWrapper::Balance)
48
- balance.usd.should be_a(ApiWrapper::Balance)
49
-
50
- btc = balance.btc
51
- btc.total.should be_a(BigDecimal)
52
- btc.reserved.should be_a(BigDecimal)
53
- btc.available.should be_a(BigDecimal)
54
-
55
- usd = balance.usd
56
- usd.total.should be_a(BigDecimal)
57
- usd.reserved.should be_a(BigDecimal)
58
- usd.available.should be_a(BigDecimal)
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.btc.should be_a(ApiWrapper::Balance)
57
- balance.usd.should be_a(ApiWrapper::Balance)
58
-
59
- btc = balance.btc
60
- btc.total.should be_a(BigDecimal)
61
- btc.reserved.should be_a(BigDecimal)
62
- btc.available.should be_a(BigDecimal)
63
-
64
- usd = balance.usd
65
- usd.total.should be_a(BigDecimal)
66
- usd.reserved.should be_a(BigDecimal)
67
- usd.available.should be_a(BigDecimal)
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.btc.should be_a(ApiWrapper::Balance)
81
- balance.usd.should be_a(ApiWrapper::Balance)
82
-
83
- btc = balance.btc
84
- btc.total.should be_a(BigDecimal)
85
- btc.reserved.should be_a(BigDecimal)
86
- btc.available.should be_a(BigDecimal)
87
-
88
- usd = balance.usd
89
- usd.total.should be_a(BigDecimal)
90
- usd.reserved.should be_a(BigDecimal)
91
- usd.available.should be_a(BigDecimal)
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.btc_profit.should be_nil
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.btc_profit.should be_nil
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.btc_profit.should be_zero
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.btc_profit.should be_zero
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.btc_profit.should be_zero
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.btc_profit.should == 0.00_201
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.btc_profit.should be_zero
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.btc_profit.should be_nil
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
- fx_rate: other_fx_rate,
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
  )
@@ -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 10.minutes.from_now
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 == 1
72
- Timecop.travel 2.seconds.from_now
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 == 1
75
- Timecop.travel 5.seconds.from_now
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 == 2
80
+ BitexBot::BuyOpeningFlow.active.count.should eq 2
78
81
 
79
- # When transactions appear, all opening flows
80
- # should get old and die.
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
- bot.trade!
88
- BitexBot::BuyOpeningFlow.active.count.should == 1
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 == 0
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 do
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
- it 'does not place new opening flows when ordered to hold' do
115
- other_bot = BitexBot::Robot.new
116
- other_bot.store.hold = true
117
- other_bot.store.save!
118
- expect do
119
- bot.trade!
120
- end.not_to change { BitexBot::BuyOpeningFlow.count }
121
- end
122
-
123
- it 'stops trading when fiat stop is reached' do
124
- other_bot = BitexBot::Robot.new
125
- other_bot.store.btc_stop = 30
126
- other_bot.store.save!
127
- expect do
128
- bot.trade!
129
- end.not_to change { BitexBot::BuyOpeningFlow.count }
130
- end
131
-
132
- it 'stops trading when usd stop is reached' do
133
- other_bot = BitexBot::Robot.new
134
- other_bot.store.btc_stop = 30
135
- other_bot.store.save!
136
- expect do
137
- bot.trade!
138
- end.not_to change { BitexBot::BuyOpeningFlow.count }
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
- it 'stops trading when btc stop is reached' do
142
- other_bot = BitexBot::Robot.new
143
- other_bot.store.fiat_stop = 11000
144
- other_bot.store.save!
145
- expect do
146
- bot.trade!
147
- end.not_to change { BitexBot::BuyOpeningFlow.count }
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 'warns every 30 minutes when usd warn is reached' do
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.taker_btc.should_not be_nil
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 do
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.btc_profit.should be_nil
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.btc_profit.should be_nil
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.btc_profit.should be_nil
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.btc_profit.should == 0
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.btc_profit.should be_zero
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.btc_profit.should == '-0.0001'.to_d
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.btc_profit.should == '-0.0224895'.to_d
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.btc_profit.should == '-0.1709'.to_d
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
- fx_rate: other_fx_rate,
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.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-06-23 00:00:00.000000000 Z
12
+ date: 2018-07-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord