bitex_bot 0.6.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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