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