bitex_bot 0.6.1 → 0.9.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -3
  3. data/Gemfile +3 -1
  4. data/bitex_bot.gemspec +5 -2
  5. data/lib/bitex_bot/database.rb +2 -2
  6. data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +47 -35
  7. data/lib/bitex_bot/models/api_wrappers/bitex/bitex_api_wrapper.rb +178 -0
  8. data/lib/bitex_bot/models/api_wrappers/bitstamp/bitstamp_api_wrapper.rb +62 -45
  9. data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +52 -28
  10. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +61 -28
  11. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +12 -6
  12. data/lib/bitex_bot/models/buy_closing_flow.rb +3 -2
  13. data/lib/bitex_bot/models/buy_opening_flow.rb +31 -6
  14. data/lib/bitex_bot/models/closing_flow.rb +37 -22
  15. data/lib/bitex_bot/models/open_buy.rb +1 -3
  16. data/lib/bitex_bot/models/open_sell.rb +1 -3
  17. data/lib/bitex_bot/models/opening_flow.rb +42 -28
  18. data/lib/bitex_bot/models/order_book_simulator.rb +14 -13
  19. data/lib/bitex_bot/models/sell_closing_flow.rb +3 -2
  20. data/lib/bitex_bot/models/sell_opening_flow.rb +29 -4
  21. data/lib/bitex_bot/robot.rb +28 -43
  22. data/lib/bitex_bot/settings.rb +2 -0
  23. data/lib/bitex_bot/version.rb +1 -1
  24. data/settings.rb.sample +23 -5
  25. data/spec/bitex_bot/settings_spec.rb +13 -6
  26. data/spec/factories/bitex_ask.rb +14 -0
  27. data/spec/factories/bitex_bid.rb +14 -0
  28. data/spec/factories/bitex_buy.rb +7 -7
  29. data/spec/factories/bitex_sell.rb +7 -7
  30. data/spec/factories/buy_opening_flow.rb +10 -10
  31. data/spec/factories/open_buy.rb +8 -8
  32. data/spec/factories/open_sell.rb +8 -8
  33. data/spec/factories/sell_opening_flow.rb +10 -10
  34. data/spec/fixtures/bitstamp/balance.yml +63 -0
  35. data/spec/fixtures/bitstamp/order_book.yml +60 -0
  36. data/spec/fixtures/bitstamp/orders/all.yml +62 -0
  37. data/spec/fixtures/bitstamp/orders/failure_sell.yml +60 -0
  38. data/spec/fixtures/bitstamp/orders/successful_buy.yml +62 -0
  39. data/spec/fixtures/bitstamp/transactions.yml +244 -0
  40. data/spec/fixtures/bitstamp/user_transactions.yml +223 -0
  41. data/spec/models/api_wrappers/bitex_api_wrapper_spec.rb +147 -0
  42. data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +134 -140
  43. data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +9 -3
  44. data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +142 -73
  45. data/spec/models/bitex_api_spec.rb +4 -4
  46. data/spec/models/buy_closing_flow_spec.rb +19 -24
  47. data/spec/models/buy_opening_flow_spec.rb +102 -83
  48. data/spec/models/order_book_simulator_spec.rb +5 -0
  49. data/spec/models/robot_spec.rb +7 -4
  50. data/spec/models/sell_closing_flow_spec.rb +21 -25
  51. data/spec/models/sell_opening_flow_spec.rb +100 -80
  52. data/spec/spec_helper.rb +3 -1
  53. data/spec/support/bitex_stubs.rb +80 -40
  54. data/spec/support/bitstamp/bitstamp_api_wrapper_stubs.rb +2 -2
  55. data/spec/support/bitstamp/bitstamp_stubs.rb +3 -3
  56. data/spec/support/vcr.rb +8 -0
  57. data/spec/support/webmock.rb +8 -0
  58. metadata +77 -10
@@ -3,6 +3,8 @@ module BitexBot
3
3
  # The OpeningFlow stage places an order on bitex, detecting and storing all transactions spawn from that order as
4
4
  # Open positions.
5
5
  class OpeningFlow < ActiveRecord::Base
6
+ extend Forwardable
7
+
6
8
  self.abstract_class = true
7
9
 
8
10
  # The updated config store as passed from the robot
@@ -29,26 +31,31 @@ module BitexBot
29
31
  # #safest_price
30
32
  # #value_to_use
31
33
  # rubocop:disable Metrics/AbcSize
32
- def self.create_for_market(remote_balance, order_book, transactions, maker_fee, taker_fee, store)
34
+ def self.create_for_market(taker_balance, taker_orders, taker_transactions, maker_fee, taker_fee, store)
33
35
  self.store = store
34
36
 
35
- remote_value, safest_price = calc_remote_value(maker_fee, taker_fee, order_book, transactions)
36
- raise CannotCreateFlow, "Needed #{remote_value} but you only have #{remote_balance}" unless
37
- enough_remote_funds?(remote_balance, remote_value)
37
+ remote_value, safest_price = calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions)
38
+ unless enough_remote_funds?(taker_balance, remote_value)
39
+ raise CannotCreateFlow,
40
+ "Needed #{remote_value} but you only have #{taker_specie_to_spend} #{taker_balance} on your taker market."
41
+ end
42
+
43
+ price = maker_price(remote_value)
38
44
 
39
- bitex_price = maker_price(remote_value)
40
- order = create_order!(bitex_price)
41
- raise CannotCreateFlow, "You need to have #{value_to_use} on bitex to place this #{order_class.name}." unless
42
- enough_funds?(order)
45
+ order = create_order!(price)
46
+ unless enough_funds?(order)
47
+ raise CannotCreateFlow,
48
+ "You need to have #{maker_specie_to_spend} #{value_per_order} on Bitex to place this #{order_class}."
49
+ end
43
50
 
44
51
  Robot.log(
45
52
  :info,
46
- "Opening: Placed #{order_class.name} ##{order.id} #{value_to_use} @ #{Settings.quote.upcase} #{bitex_price}"\
47
- " (#{remote_value})"
53
+ "Opening: Placed #{order_class} ##{order.id} #{value_per_order} @ #{Robot.maker.quote.upcase} #{price}"\
54
+ " (#{maker_specie_to_obtain} #{remote_value})"
48
55
  )
49
56
 
50
57
  create!(
51
- price: bitex_price,
58
+ price: price,
52
59
  value_to_use: value_to_use,
53
60
  suggested_closing_price: safest_price,
54
61
  status: 'executing',
@@ -60,16 +67,16 @@ module BitexBot
60
67
  # rubocop:enable Metrics/AbcSize
61
68
 
62
69
  # create_for_market helpers
63
- def self.calc_remote_value(maker_fee, taker_fee, order_book, transactions)
70
+ def self.calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions)
64
71
  value_to_use_needed = (value_to_use + maker_plus(maker_fee)) / (1 - taker_fee / 100)
65
- safest_price = safest_price(transactions, order_book, value_to_use_needed)
72
+ safest_price = safest_price(taker_transactions, taker_orders, value_to_use_needed)
66
73
  remote_value = remote_value_to_use(value_to_use_needed, safest_price)
67
74
 
68
75
  [remote_value, safest_price]
69
76
  end
70
77
 
71
- def self.create_order!(bitex_price)
72
- order_class.create!(Settings.maker_settings.order_book, value_to_use, bitex_price, true)
78
+ def self.create_order!(maker_price)
79
+ Robot.maker.send_order(order_type, maker_price, value_per_order, true)
73
80
  rescue StandardError => e
74
81
  raise CannotCreateFlow, e.message
75
82
  end
@@ -78,8 +85,8 @@ module BitexBot
78
85
  !order.reason.to_s.inquiry.not_enough_funds?
79
86
  end
80
87
 
81
- def self.enough_remote_funds?(remote_balance, remote_value)
82
- remote_balance >= remote_value
88
+ def self.enough_remote_funds?(taker_balance, remote_value)
89
+ taker_balance >= remote_value
83
90
  end
84
91
 
85
92
  def self.maker_plus(fee)
@@ -93,7 +100,7 @@ module BitexBot
93
100
  # #open_position_class
94
101
  def self.sync_open_positions
95
102
  threshold = open_position_class.order('created_at DESC').first.try(:created_at)
96
- Bitex::Trade.all.map do |transaction|
103
+ Robot.maker.transactions.map do |transaction|
97
104
  next unless sought_transaction?(threshold, transaction)
98
105
 
99
106
  flow = find_by_order_id(transaction_order_id(transaction))
@@ -104,25 +111,28 @@ module BitexBot
104
111
  end
105
112
 
106
113
  # sync_open_positions helpers
114
+ # rubocop:disable Metrics/AbcSize
107
115
  def self.create_open_position!(transaction, flow)
108
116
  Robot.log(
109
117
  :info,
110
- "Opening: #{name} ##{flow.id} was hit for #{transaction.quantity} #{Settings.base.upcase} @ #{Settings.quote.upcase}"\
111
- " #{transaction.price}"
118
+ "Opening: #{name} ##{flow.id} was hit for #{transaction.raw.quantity} #{Robot.maker.base.upcase}"\
119
+ " @ #{Robot.maker.quote.upcase} #{transaction.price}"
112
120
  )
121
+
113
122
  open_position_class.create!(
114
123
  transaction_id: transaction.id,
115
124
  price: transaction.price,
116
125
  amount: transaction.amount,
117
- quantity: transaction.quantity,
126
+ quantity: transaction.raw.quantity,
118
127
  opening_flow: flow
119
128
  )
120
129
  end
130
+ # rubocop:enable Metrics/AbcSize
121
131
 
122
132
  # This use hooks methods, these must be defined in the subclass:
123
133
  # #transaction_class
124
134
  def self.sought_transaction?(threshold, transaction)
125
- transaction.is_a?(transaction_class) &&
135
+ expected_kind_transaction?(transaction) &&
126
136
  !active_transaction?(transaction, threshold) &&
127
137
  !open_position?(transaction) &&
128
138
  expected_order_book?(transaction)
@@ -130,16 +140,20 @@ module BitexBot
130
140
  # end: sync_open_positions helpers
131
141
 
132
142
  # sought_transaction helpers
143
+ def self.expected_kind_transaction?(transaction)
144
+ transaction.raw.is_a?(transaction_class)
145
+ end
146
+
133
147
  def self.active_transaction?(transaction, threshold)
134
- threshold.present? && transaction.created_at < (threshold - 30.minutes)
148
+ threshold.present? && transaction.timestamp < (threshold - 30.minutes).to_i
135
149
  end
136
150
 
137
151
  def self.open_position?(transaction)
138
152
  open_position_class.find_by_transaction_id(transaction.id)
139
153
  end
140
154
 
141
- def self.expected_order_book?(transaction)
142
- transaction.order_book == Settings.maker_settings.order_book
155
+ def self.expected_order_book?(maker_transaction)
156
+ maker_transaction.raw.order_book.to_s == Robot.maker.base_quote
143
157
  end
144
158
  # end: sought_transaction helpers
145
159
 
@@ -157,7 +171,7 @@ module BitexBot
157
171
  end
158
172
 
159
173
  def finalise!
160
- order = self.class.order_class.find(order_id)
174
+ order = order_class.find(order_id)
161
175
  canceled_or_completed?(order) ? do_finalize : do_cancel(order)
162
176
  end
163
177
 
@@ -169,12 +183,12 @@ module BitexBot
169
183
  end
170
184
 
171
185
  def do_finalize
172
- Robot.log(:info, "Opening: #{self.class.order_class.name} ##{order_id} finalised.")
186
+ Robot.log(:info, "Opening: #{order_class} ##{order_id} finalised.")
173
187
  finalised!
174
188
  end
175
189
 
176
190
  def do_cancel(order)
177
- Robot.log(:info, "Opening: #{self.class.order_class.name} ##{order_id} canceled.")
191
+ Robot.log(:info, "Opening: #{order_class} ##{order_id} canceled.")
178
192
  order.cancel!
179
193
  settling! unless settling?
180
194
  end
@@ -17,35 +17,36 @@ module BitexBot
17
17
  # quantity.
18
18
  #
19
19
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
20
- def self.run(volatility, transactions, order_book, amount_target, quantity_target)
21
- to_skip = estimate_quantity_to_skip(volatility, transactions)
22
- Robot.log(:debug, "Skipping #{to_skip} BTC")
20
+ def self.run(volatility, taker_transactions, taker_orderbook, amount_target, quantity_target, fx_rate = 1)
21
+ to_skip = estimate_quantity_to_skip(volatility, taker_transactions)
22
+ Robot.log(:debug, "Skipping #{to_skip} #{Robot.taker.base.upcase}")
23
23
  seen = 0
24
24
 
25
- order_book.each do |order_summary|
25
+ taker_orderbook.each do |order_summary|
26
26
  price = order_summary.price
27
27
  quantity = order_summary.quantity
28
28
 
29
29
  # An order may be partially or completely skipped due to volatility.
30
30
  if to_skip.positive?
31
- [quantity, to_skip].min.tap do |dropped|
32
- to_skip -= dropped
33
- quantity -= dropped
34
- Robot.log(:debug, "Skipped #{dropped} BTC @ $#{price}")
35
- end
31
+ dropped = [quantity, to_skip].min
32
+ to_skip -= dropped
33
+ quantity -= dropped
34
+ Robot.log(:debug, "Skipped #{dropped} #{Robot.taker.base.upcase} @ #{Robot.taker.quote.upcase} #{price}")
36
35
  next if quantity.zero?
37
36
  end
38
37
 
39
38
  if quantity_target.present?
40
- return best_price('BTC', quantity_target, price) if best_price?(quantity, quantity_target, seen)
39
+ return best_price(Robot.maker.base.upcase, quantity_target, price) if best_price?(quantity, quantity_target, seen)
40
+
41
41
  seen += quantity
42
42
  elsif amount_target.present?
43
43
  amount = price * quantity
44
- return best_price('$', amount_target, price) if best_price?(amount, amount_target, seen)
44
+ return best_price(Robot.maker.quote.upcase, amount_target * fx_rate, price) if best_price?(amount, amount_target, seen)
45
+
45
46
  seen += amount
46
47
  end
47
48
  end
48
- order_book.last.price
49
+ taker_orderbook.last.price
49
50
  end
50
51
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
51
52
 
@@ -64,7 +65,7 @@ module BitexBot
64
65
  end
65
66
 
66
67
  def self.best_price(currency, target, price)
67
- price.tap { Robot.log(:debug, "Best price to get #{currency} #{target} is $#{price}") }
68
+ price.tap { Robot.log(:debug, "Best price to get #{currency} #{target} is #{Robot.taker.quote.upcase} #{price}") }
68
69
  end
69
70
 
70
71
  # end: private class methods
@@ -10,9 +10,10 @@ module BitexBot
10
10
  OpenSell
11
11
  end
12
12
 
13
- def fx_rate
13
+ def self.fx_rate
14
14
  Settings.selling_fx_rate
15
15
  end
16
+ def_delegator self, :fx_rate
16
17
 
17
18
  private
18
19
 
@@ -37,7 +38,7 @@ module BitexBot
37
38
  # end: create_or_cancel! helpers
38
39
 
39
40
  # create_order_and_close_position helpers
40
- def order_method
41
+ def order_type
41
42
  :buy
42
43
  end
43
44
  # end: create_order_and_close_position helpers
@@ -28,13 +28,13 @@ module BitexBot
28
28
  # @return [SellOpeningFlow] The newly created flow.
29
29
  # @raise [CannotCreateFlow] If there's any problem creating this flow, for example when you run out of BTC on bitex or out
30
30
  # of USD on the other exchange.
31
- def self.create_for_market(usd_balance, order_book, transactions, maker_fee, taker_fee, store)
31
+ def self.create_for_market(taker_fiat_balance, taker_asks, taker_transactions, maker_fee, taker_fee, store)
32
32
  super
33
33
  end
34
34
 
35
35
  # sync_open_positions helpers
36
36
  def self.transaction_order_id(transaction)
37
- transaction.ask_id
37
+ transaction.raw.ask_id
38
38
  end
39
39
 
40
40
  def self.open_position_class
@@ -56,6 +56,11 @@ module BitexBot
56
56
  def self.order_class
57
57
  Bitex::Ask
58
58
  end
59
+ def_delegator self, :order_class
60
+
61
+ def self.order_type
62
+ :sell
63
+ end
59
64
 
60
65
  def self.profit
61
66
  store.selling_profit || Settings.selling.profit
@@ -65,8 +70,8 @@ module BitexBot
65
70
  value_to_use_needed * safest_price
66
71
  end
67
72
 
68
- def self.safest_price(transactions, order_book, bitcoins_to_use)
69
- OrderBookSimulator.run(Settings.time_to_live, transactions, order_book, nil, bitcoins_to_use)
73
+ def self.safest_price(transactions, taker_asks, bitcoins_to_use)
74
+ OrderBookSimulator.run(Settings.time_to_live, transactions, taker_asks, nil, bitcoins_to_use, nil)
70
75
  end
71
76
 
72
77
  def self.value_to_use
@@ -77,5 +82,25 @@ module BitexBot
77
82
  def self.fx_rate
78
83
  Settings.selling_fx_rate
79
84
  end
85
+
86
+ def self.value_per_order
87
+ value_to_use
88
+ end
89
+
90
+ def self.maker_specie_to_spend
91
+ Robot.maker.base.upcase
92
+ end
93
+
94
+ def self.maker_specie_to_obtain
95
+ Robot.maker.quote.upcase
96
+ end
97
+
98
+ def self.taker_specie_to_spend
99
+ Robot.taker.quote.upcase
100
+ end
101
+
102
+ def self.taker_specie_to_obtain
103
+ Robot.taker.base.upcase
104
+ end
80
105
  end
81
106
  end
@@ -10,11 +10,11 @@ end
10
10
 
11
11
  module BitexBot
12
12
  # Documentation here!
13
- # rubocop:disable Metrics/ClassLength
14
13
  class Robot
15
14
  extend Forwardable
16
15
 
17
16
  cattr_accessor :taker
17
+ cattr_accessor :maker
18
18
 
19
19
  cattr_accessor :graceful_shutdown
20
20
  cattr_accessor :cooldown_until
@@ -33,9 +33,8 @@ module BitexBot
33
33
  end
34
34
 
35
35
  def self.setup
36
- Bitex.api_key = Settings.maker_settings.api_key
37
- Bitex.sandbox = Settings.maker_settings.sandbox
38
- self.taker = Settings.taker_class.tap { |klass| klass.setup(Settings.taker_settings) }
36
+ self.maker = Settings.maker_class.new(Settings.maker_settings)
37
+ self.taker = Settings.taker_class.new(Settings.taker_settings)
39
38
  end
40
39
 
41
40
  # Trade constantly respecting cooldown times so that we don't get banned by api clients.
@@ -68,6 +67,7 @@ module BitexBot
68
67
  sleep_for(0.1)
69
68
  end
70
69
  end
70
+ def_delegator self, :with_cooldown
71
71
 
72
72
  def self.start_robot
73
73
  setup
@@ -116,10 +116,6 @@ module BitexBot
116
116
 
117
117
  private
118
118
 
119
- def with_cooldown(&block)
120
- self.class.with_cooldown(&block)
121
- end
122
-
123
119
  def sync_opening_flows
124
120
  [SellOpeningFlow, BuyOpeningFlow].each(&:sync_open_positions)
125
121
  end
@@ -158,53 +154,45 @@ module BitexBot
158
154
  end
159
155
 
160
156
  def sync_closing_flows
161
- orders = with_cooldown { Robot.taker.orders }
162
- transactions = with_cooldown { Robot.taker.user_transactions }
163
-
164
- [BuyClosingFlow, SellClosingFlow].each do |kind|
165
- kind.active.each { |flow| flow.sync_closed_positions(orders, transactions) }
166
- end
157
+ [BuyClosingFlow, SellClosingFlow].each { |kind| kind.active.each(&:sync_closed_positions) }
167
158
  end
168
159
 
169
160
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
170
161
  def start_opening_flows_if_needed
171
- return log(:debug, 'Not placing new orders because of hold') if store.reload.hold?
172
- return log(:debug, 'Not placing new orders, closing flows.') if active_closing_flows?
162
+ return log(:debug, 'Not placing new orders, because Store is held') if store.reload.hold?
163
+ return log(:debug, 'Not placing new orders, has active closing flows.') if active_closing_flows?
173
164
  return log(:debug, 'Not placing new orders, shutting down.') if turn_off?
174
165
 
175
166
  recent_buying, recent_selling = recent_operations
176
167
  return log(:debug, 'Not placing new orders, recent ones exist.') if recent_buying && recent_selling
177
168
 
178
- profile = Bitex::Profile.get
179
- taker_balance = with_cooldown { Robot.taker.balance }
180
- sync_log_and_store(taker_balance, profile)
169
+ maker_balance = with_cooldown { maker.balance }
170
+ taker_balance = with_cooldown { taker.balance }
171
+ sync_log_and_store(maker_balance, taker_balance)
181
172
 
182
173
  check_balance_warning if expired_last_warning?
174
+ return if stop_opening_flows?
183
175
 
184
- return log(:debug, "Not placing new orders, #{Settings.quote} target not met") if alert?(:fiat, :stop)
185
- return log(:debug, "Not placing new orders, #{Settings.base} target not met") if alert?(:crypto, :stop)
176
+ order_book = with_cooldown { taker.order_book }
177
+ transactions = with_cooldown { taker.transactions }
186
178
 
187
- order_book = with_cooldown { Robot.taker.order_book }
188
- transactions = with_cooldown { Robot.taker.transactions }
189
-
190
- create_buy_opening_flow(taker_balance, order_book, transactions, profile) unless recent_buying
191
- create_sell_opening_flow(taker_balance, order_book, transactions, profile) unless recent_selling
179
+ args = [transactions, maker_balance.fee, taker_balance.fee, store]
180
+ BuyOpeningFlow.create_for_market(*[taker_balance.crypto.available, order_book.bids] + args) unless recent_buying
181
+ SellOpeningFlow.create_for_market(*[taker_balance.fiat.available, order_book.asks] + args) unless recent_selling
192
182
  end
193
183
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
194
184
 
195
185
  def recent_operations
196
- [BuyOpeningFlow, SellOpeningFlow].map do |kind|
197
- threshold = (Settings.time_to_live / 2).seconds.ago
198
- kind.active.where('created_at > ?', threshold).first
199
- end
186
+ threshold = (Settings.time_to_live / 2).seconds.ago
187
+ [BuyOpeningFlow, SellOpeningFlow].map { |kind| kind.active.where('created_at > ?', threshold).first }
200
188
  end
201
189
 
202
- def sync_log_and_store(taker_balance, maker_balance)
190
+ def sync_log_and_store(maker_balance, taker_balance)
203
191
  file = Settings.log.try(:file)
204
192
  last_log = `tail -c 61440 #{file}` if file.present?
205
193
 
206
194
  store.update(
207
- maker_fiat: maker_balance[:"#{Settings.quote}_balance"], maker_crypto: maker_balance[:"#{Settings.base}_balance"],
195
+ maker_fiat: maker_balance.fiat.total, maker_crypto: maker_balance.crypto.total,
208
196
  taker_fiat: taker_balance.fiat.total, taker_crypto: taker_balance.crypto.total,
209
197
  log: last_log
210
198
  )
@@ -214,13 +202,19 @@ module BitexBot
214
202
  store.last_warning.nil? || store.last_warning < 30.minutes.ago
215
203
  end
216
204
 
205
+ def stop_opening_flows?
206
+ (log(:debug, "Not placing new orders, #{maker.quote.upcase} target not met") if alert?(:fiat, :stop)) ||
207
+ (log(:debug, "Not placing new orders, #{maker.base.upcase} target not met") if alert?(:crypto, :stop))
208
+ end
209
+
217
210
  def check_balance_warning
218
- notify_balance_warning(Settings.base, balance(:crypto), store.crypto_warning) if alert?(:crypto, :warning)
219
- notify_balance_warning(Settings.quote, balance(:fiat), store.fiat_warning) if alert?(:fiat, :warning)
211
+ notify_balance_warning(maker.base, balance(:crypto), store.crypto_warning) if alert?(:crypto, :warning)
212
+ notify_balance_warning(maker.quote, balance(:fiat), store.fiat_warning) if alert?(:fiat, :warning)
220
213
  end
221
214
 
222
215
  def alert?(currency, flag)
223
216
  return unless store.send("#{currency}_#{flag}").present?
217
+
224
218
  balance(currency) <= store.send("#{currency}_#{flag}")
225
219
  end
226
220
 
@@ -251,14 +245,5 @@ module BitexBot
251
245
  body message
252
246
  end
253
247
  end
254
-
255
- def create_buy_opening_flow(balance, order_book, transactions, profile)
256
- BuyOpeningFlow.create_for_market(balance.crypto.available, order_book.bids, transactions, profile[:fee], balance.fee, store)
257
- end
258
-
259
- def create_sell_opening_flow(balance, order_book, transactions, profile)
260
- SellOpeningFlow.create_for_market(balance.fiat.available, order_book.asks, transactions, profile[:fee], balance.fee, store)
261
- end
262
- # rubocop:enable Metrics/ClassLength
263
248
  end
264
249
  end
@@ -1,4 +1,5 @@
1
1
  require 'hashie'
2
+ require 'fileutils'
2
3
  require 'bigdecimal'
3
4
  require 'bigdecimal/util'
4
5
 
@@ -7,6 +8,7 @@ module BitexBot
7
8
  class FileSettings < ::Hashie::Clash
8
9
  def method_missing(name, *args, &block)
9
10
  return super unless args.none? && args.size == 1
11
+
10
12
  self[name] = args.first
11
13
  end
12
14
 
@@ -1,3 +1,3 @@
1
1
  module BitexBot
2
- VERSION = '0.6.1'.freeze
2
+ VERSION = '0.9.0'.freeze
3
3
  end
data/settings.rb.sample CHANGED
@@ -46,19 +46,37 @@ buying_foreign_exchange_rate 1.to_d
46
46
  # api_key: it's passed in to the bitex gem: https://github.com/bitex-la/bitex-ruby.
47
47
  # sandbox: Use sandbox environments instead of live environments.
48
48
  # order_book: order book with which you will operate.
49
- maker bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false }
49
+ maker bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', sandbox: false, ssl_version: nil, debug: false, order_book: 'btc_usd' }
50
50
 
51
51
  # These are the configurations we need for the markets currently supported.
52
- # Turn on and off the chosen Bitstamp, Itbit or Kraken market with comments.
52
+ # Turn on and off the chosen Bitex, Bitstamp, Itbit or Kraken market with comments.
53
53
 
54
54
  # These are passed in to the bitstamp gem: see https://github.com/kojnapp/bitstamp for more info.
55
- taker bitstamp: { api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME' }
55
+ # Supported values for order_book: 'btcusd', 'btceur', 'eurusd', 'xrpusd', 'xrpeur', 'xrpbtc', 'ltcusd', 'ltceur', 'ltcbtc',
56
+ # 'ethusd', 'etheur', 'ethbtc', 'bchusd', 'bcheur', 'bchbtc'
57
+ #
58
+ taker bitstamp: { api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME', order_book: 'btcusd' }
56
59
 
57
60
  # These are passed in to the itbit gem: see https://github.com/bitex-la/itbit for more info.
58
- # taker itbit: { client_key: 'client-key', secret: 'secret', user_id: 'user-id', default_wallet_id: 'wallet-000', sandbox: false }
61
+ # Choices: 'xbtusd', 'xbtsgd', 'xbteur', https://api.itbit.com/docs#market-data-get-order-book-get.
62
+ #
63
+ # taker itbit: { client_key: 'client-key', secret: 'secret', user_id: 'user-id', default_wallet_id: 'wallet-000', sandbox: false, order_book: 'xbtusd' }
59
64
 
60
65
  # These are passed in to the kraken gem: see https://github.com/shideneyu/kraken_client for more info.
61
- # taker kraken: { api_key: 'your_api_key', api_secret: 'your_api_secret' }
66
+ # Supported values for order_book https://api.kraken.com/0/public/AssetPairs:
67
+ # 'bcheur', 'bchusd', 'bchxbt', 'dasheur', 'dashusd', 'dashxbt', 'eoseth', 'eoseur', 'eosusd', 'eosxbt', 'etceth', 'etceur',
68
+ # 'etcusd', 'etcxbt', 'ethcad', '"ethcad.d"', 'etheur', '"etheur.d"', 'ethgbp', ''ethgbp.d'', 'ethjpy', ''ethjpy.d'', 'ethusd',
69
+ # 'ethusd.d', 'ethxbt', ''ethxbt.d'', 'gnoeth', 'gnoeur', 'gnousd', 'gnoxbt', 'icneth', 'icnxbt', 'ltceur', 'ltcusd', 'ltcxbt',
70
+ # 'mlneth', 'mlnxbt', 'repeth', 'repeur', 'repusd', 'repxbt', 'usdtusd', 'xbtcad', ''xbtcad.d'', 'xbteur', ''xbteur.d'', 'xbtgbp',
71
+ # 'xbtgbp.d', 'xbtjpy', ''xbtjpy.d'', 'xbtusd', ''xbtusd.d'', 'xdgxbt', 'xlmeur', 'xlmusd', 'xlmxbt', 'xmreur', 'xmrusd',
72
+ # 'xmrxbt', 'xrpcad', 'xrpeur', 'xrpjpy', 'xrpusd', 'xrpxbt', 'zeceur', 'zecjpy', 'zecusd', 'zecxbt']
73
+ #
74
+ # taker kraken: { api_key: 'your_api_key', api_secret: 'your_api_secret', order_book: 'xbtusd' }
75
+
76
+ # These are passed in to the Bitex gem: see https://github.com/bitex-la/bitex-ruby.
77
+ # Supported values for order_book: 'btc_usd', 'bch_usd', 'bct_ars', 'bct_clp', 'bct_pyg', 'btc_uyu'
78
+ #
79
+ # taker bitex: { api_key: 'taker_api_key', sandbox: false, ssl_version: nil, debug: false, order_book: 'btc_usd' }
62
80
 
63
81
  # Settings for the ActiveRecord Database to use.
64
82
  # sqlite is just fine. Check this link for more options:
@@ -11,9 +11,16 @@ describe BitexBot::Settings do
11
11
  buying_foreign_exchange_rate: 1,
12
12
  selling_foreign_exchange_rate: 1,
13
13
 
14
- maker: { bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false } },
14
+ maker: { bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: 'btc_usd', sandbox: false, ssl_version: nil, debug: false } },
15
15
  # By default Bitstamp is taker market.
16
- taker: { bitstamp: { api_key: 'YOUR_API_KEY', secret: 'YOUR_API_SECRET', client_id: 'YOUR_BITSTAMP_USERNAME' } },
16
+ taker: {
17
+ bitstamp: {
18
+ api_key: 'YOUR_API_KEY',
19
+ secret: 'YOUR_API_SECRET',
20
+ client_id: 'YOUR_BITSTAMP_USERNAME',
21
+ order_book: 'btcusd'
22
+ }
23
+ },
17
24
 
18
25
  database: { adapter: :sqlite3, database: 'bitex_bot.db' },
19
26
  mailer: {
@@ -32,13 +39,13 @@ describe BitexBot::Settings do
32
39
  )
33
40
  end
34
41
 
35
- context 'fx rate' do
36
- context 'when Store isn´t loaded, by default' do
42
+ context 'fx rate, when Store' do
43
+ context 'isn´t loaded, by default' do
37
44
  it { described_class.buying_fx_rate.should eq(1) }
38
45
  it { described_class.selling_fx_rate.should eq(1) }
39
46
  end
40
47
 
41
- context 'when Store is loaded, take rate from' do
48
+ context 'is loaded, take rate from' do
42
49
  before(:each) { BitexBot::Store.stub(first: BitexBot::Store.new) }
43
50
  let(:fx_rate) { rand(10) }
44
51
 
@@ -58,7 +65,7 @@ describe BitexBot::Settings do
58
65
 
59
66
  context 'maker' do
60
67
  {
61
- bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', order_book: :btc_usd, sandbox: false }
68
+ bitex: { api_key: 'your_bitex_api_key_which_should_be_kept_safe', ssl_version: nil, debug: false, sandbox: false, order_book: 'btc_usd' }
62
69
  }.each do |market, market_settings|
63
70
  before(:each) { described_class.stub(taker: BitexBot::SettingsClass.new(taker_hash)) }
64
71
 
@@ -0,0 +1,14 @@
1
+ FactoryBot.define do
2
+ factory :bitex_ask, class: Bitex::Ask do
3
+ id { 12_345_678 }
4
+ created_at { Time.now }
5
+ order_book { :btc_usd }
6
+ price { 1_000.to_d }
7
+ status { :received }
8
+ reason { :not_cancelled }
9
+ issuer { 'User#1' }
10
+ quantity { 100.to_d }
11
+ remaining_quantity { 100.to_d }
12
+ produced_amount { 10.to_d }
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ FactoryBot.define do
2
+ factory :bitex_bid, class: Bitex::Bid do
3
+ id { 12_345_678 }
4
+ created_at { Time.now }
5
+ order_book { :btc_usd }
6
+ price { 1_000.to_d }
7
+ status { :received }
8
+ reason { :not_cancelled }
9
+ issuer { 'User#1' }
10
+ amount { 100.to_d }
11
+ remaining_amount { 100.to_d }
12
+ produced_quantity { 10.to_d }
13
+ end
14
+ end
@@ -1,12 +1,12 @@
1
1
  FactoryBot.define do
2
2
  factory :bitex_buy, class: Bitex::Buy do
3
- id 12345678
3
+ id { 12_345_678 }
4
4
  created_at { Time.now }
5
- order_book :btc_usd
6
- quantity 2.0
7
- amount 600.0
8
- fee 0.05
9
- price 300.0
10
- bid_id 12345
5
+ order_book { :btc_usd }
6
+ quantity { 2.0.to_d }
7
+ amount { 600.0.to_d }
8
+ fee { 0.05.to_d }
9
+ price { 300.0.to_d }
10
+ bid_id { 12_345 }
11
11
  end
12
12
  end
@@ -1,12 +1,12 @@
1
1
  FactoryBot.define do
2
2
  factory :bitex_sell, class: Bitex::Sell do
3
- id 12345678
3
+ id { 12_345_678 }
4
4
  created_at { Time.now }
5
- order_book :btc_usd
6
- quantity 2.0
7
- amount 600.0
8
- fee 0.05
9
- price 300.0
10
- ask_id 12345
5
+ order_book { :btc_usd }
6
+ quantity { 2.0.to_d }
7
+ amount { 600.0.to_d }
8
+ fee { 0.05.to_d }
9
+ price { 300.0.to_d }
10
+ ask_id { 12_345 }
11
11
  end
12
12
  end