coindcx-client 0.1.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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +55 -0
  3. data/.github/workflows/release.yml +138 -0
  4. data/.rubocop.yml +56 -0
  5. data/AGENT.md +352 -0
  6. data/README.md +224 -0
  7. data/bin/console +59 -0
  8. data/docs/README.md +29 -0
  9. data/docs/coindcx_docs_gaps.md +3 -0
  10. data/docs/core.md +179 -0
  11. data/docs/rails_integration.md +151 -0
  12. data/docs/standalone_bot.md +159 -0
  13. data/lib/coindcx/auth/signer.rb +48 -0
  14. data/lib/coindcx/client.rb +44 -0
  15. data/lib/coindcx/configuration.rb +108 -0
  16. data/lib/coindcx/contracts/channel_name.rb +23 -0
  17. data/lib/coindcx/contracts/identifiers.rb +36 -0
  18. data/lib/coindcx/contracts/order_request.rb +120 -0
  19. data/lib/coindcx/contracts/socket_backend.rb +19 -0
  20. data/lib/coindcx/contracts/wallet_transfer_request.rb +46 -0
  21. data/lib/coindcx/errors/base_error.rb +54 -0
  22. data/lib/coindcx/logging/null_logger.rb +12 -0
  23. data/lib/coindcx/logging/structured_logger.rb +17 -0
  24. data/lib/coindcx/models/balance.rb +8 -0
  25. data/lib/coindcx/models/base_model.rb +31 -0
  26. data/lib/coindcx/models/instrument.rb +8 -0
  27. data/lib/coindcx/models/market.rb +8 -0
  28. data/lib/coindcx/models/order.rb +8 -0
  29. data/lib/coindcx/models/trade.rb +8 -0
  30. data/lib/coindcx/rest/base_resource.rb +35 -0
  31. data/lib/coindcx/rest/funding/facade.rb +18 -0
  32. data/lib/coindcx/rest/funding/orders.rb +46 -0
  33. data/lib/coindcx/rest/futures/facade.rb +29 -0
  34. data/lib/coindcx/rest/futures/market_data.rb +71 -0
  35. data/lib/coindcx/rest/futures/orders.rb +47 -0
  36. data/lib/coindcx/rest/futures/positions.rb +93 -0
  37. data/lib/coindcx/rest/futures/wallets.rb +44 -0
  38. data/lib/coindcx/rest/margin/facade.rb +17 -0
  39. data/lib/coindcx/rest/margin/orders.rb +57 -0
  40. data/lib/coindcx/rest/public/facade.rb +17 -0
  41. data/lib/coindcx/rest/public/market_data.rb +52 -0
  42. data/lib/coindcx/rest/spot/facade.rb +17 -0
  43. data/lib/coindcx/rest/spot/orders.rb +67 -0
  44. data/lib/coindcx/rest/transfers/facade.rb +17 -0
  45. data/lib/coindcx/rest/transfers/wallets.rb +40 -0
  46. data/lib/coindcx/rest/user/accounts.rb +17 -0
  47. data/lib/coindcx/rest/user/facade.rb +17 -0
  48. data/lib/coindcx/transport/circuit_breaker.rb +65 -0
  49. data/lib/coindcx/transport/http_client.rb +290 -0
  50. data/lib/coindcx/transport/rate_limit_registry.rb +65 -0
  51. data/lib/coindcx/transport/request_policy.rb +152 -0
  52. data/lib/coindcx/transport/response_normalizer.rb +40 -0
  53. data/lib/coindcx/transport/retry_policy.rb +79 -0
  54. data/lib/coindcx/utils/payload.rb +51 -0
  55. data/lib/coindcx/version.rb +5 -0
  56. data/lib/coindcx/ws/connection_manager.rb +423 -0
  57. data/lib/coindcx/ws/connection_state.rb +75 -0
  58. data/lib/coindcx/ws/parsers/order_book_snapshot.rb +42 -0
  59. data/lib/coindcx/ws/private_channels.rb +38 -0
  60. data/lib/coindcx/ws/public_channels.rb +92 -0
  61. data/lib/coindcx/ws/socket_io_client.rb +89 -0
  62. data/lib/coindcx/ws/socket_io_simple_backend.rb +63 -0
  63. data/lib/coindcx/ws/subscription_registry.rb +80 -0
  64. data/lib/coindcx/ws/uri_ruby3_compat.rb +13 -0
  65. data/lib/coindcx.rb +63 -0
  66. data/spec/auth_signer_spec.rb +22 -0
  67. data/spec/client_spec.rb +19 -0
  68. data/spec/contracts/order_request_spec.rb +136 -0
  69. data/spec/contracts/wallet_transfer_request_spec.rb +45 -0
  70. data/spec/models/base_model_spec.rb +18 -0
  71. data/spec/rest/funding/orders_spec.rb +43 -0
  72. data/spec/rest/futures/market_data_spec.rb +49 -0
  73. data/spec/rest/futures/orders_spec.rb +107 -0
  74. data/spec/rest/futures/positions_spec.rb +57 -0
  75. data/spec/rest/futures/wallets_spec.rb +44 -0
  76. data/spec/rest/margin/orders_spec.rb +87 -0
  77. data/spec/rest/public/market_data_spec.rb +31 -0
  78. data/spec/rest/spot/orders_spec.rb +152 -0
  79. data/spec/rest/transfers/wallets_spec.rb +33 -0
  80. data/spec/rest/user/accounts_spec.rb +21 -0
  81. data/spec/spec_helper.rb +11 -0
  82. data/spec/transport/http_client_spec.rb +232 -0
  83. data/spec/transport/rate_limit_registry_spec.rb +28 -0
  84. data/spec/transport/request_policy_spec.rb +67 -0
  85. data/spec/transport/response_normalizer_spec.rb +63 -0
  86. data/spec/ws/connection_manager_spec.rb +339 -0
  87. data/spec/ws/order_book_snapshot_spec.rb +25 -0
  88. data/spec/ws/private_channels_spec.rb +28 -0
  89. data/spec/ws/public_channels_spec.rb +89 -0
  90. data/spec/ws/socket_io_client_spec.rb +229 -0
  91. data/spec/ws/socket_io_simple_backend_spec.rb +41 -0
  92. data/spec/ws/uri_ruby3_compat_spec.rb +12 -0
  93. metadata +164 -0
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Futures::Orders do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ end
13
+
14
+ describe "#create" do
15
+ it "validates futures order payloads and applies a create-order bucket" do
16
+ resource.create(order: { side: "sell", quantity: 2, client_order_id: "futures-123" })
17
+
18
+ expect(http_client).to have_received(:post).with(
19
+ "/exchange/v1/derivatives/futures/orders/create",
20
+ body: { order: { side: "sell", quantity: 2, client_order_id: "futures-123" } },
21
+ auth: true,
22
+ base: :api,
23
+ bucket: :futures_create_order
24
+ )
25
+ end
26
+
27
+ it "accepts total_quantity for bot-style market orders" do
28
+ resource.create(
29
+ order: {
30
+ side: "buy",
31
+ pair: "B-SOL_USDT",
32
+ total_quantity: "0.05",
33
+ order_type: "market_order",
34
+ client_order_id: "futures-124",
35
+ leverage: 2
36
+ }
37
+ )
38
+
39
+ expect(http_client).to have_received(:post).with(
40
+ "/exchange/v1/derivatives/futures/orders/create",
41
+ body: {
42
+ order: {
43
+ side: "buy",
44
+ pair: "B-SOL_USDT",
45
+ total_quantity: "0.05",
46
+ order_type: "market_order",
47
+ client_order_id: "futures-124",
48
+ leverage: 2
49
+ }
50
+ },
51
+ auth: true,
52
+ base: :api,
53
+ bucket: :futures_create_order
54
+ )
55
+ end
56
+
57
+ it "rejects an invalid side" do
58
+ expect do
59
+ resource.create(order: { side: "wait", quantity: 2 })
60
+ end.to raise_error(CoinDCX::Errors::ValidationError, /side/)
61
+ end
62
+
63
+ it "rejects a non-positive quantity" do
64
+ expect do
65
+ resource.create(order: { side: "buy", quantity: 0 })
66
+ end.to raise_error(CoinDCX::Errors::ValidationError, /quantity/)
67
+ end
68
+ end
69
+
70
+ describe "authenticated routing" do
71
+ it "routes futures order operations through authenticated transport calls" do
72
+ resource.list
73
+ resource.list_trades
74
+ resource.cancel(id: "1")
75
+ resource.edit(id: "1")
76
+
77
+ expect(http_client).to have_received(:post).with(
78
+ "/exchange/v1/derivatives/futures/orders",
79
+ body: {},
80
+ auth: true,
81
+ base: :api,
82
+ bucket: :futures_list_orders
83
+ )
84
+ expect(http_client).to have_received(:post).with(
85
+ "/exchange/v1/derivatives/futures/orders/cancel",
86
+ body: { id: "1" },
87
+ auth: true,
88
+ base: :api,
89
+ bucket: :futures_cancel_order
90
+ )
91
+ expect(http_client).to have_received(:post).with(
92
+ "/exchange/v1/derivatives/futures/trades",
93
+ body: {},
94
+ auth: true,
95
+ base: :api,
96
+ bucket: :futures_trades
97
+ )
98
+ expect(http_client).to have_received(:post).with(
99
+ "/exchange/v1/derivatives/futures/orders/edit",
100
+ body: { id: "1" },
101
+ auth: true,
102
+ base: :api,
103
+ bucket: :futures_edit_order
104
+ )
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Futures::Positions do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ allow(http_client).to receive(:get).and_return({})
13
+ end
14
+
15
+ it 'routes futures position operations through authenticated transport calls' do
16
+ resource.list
17
+ resource.update_leverage(id: '1')
18
+ resource.add_margin(id: '1')
19
+ resource.remove_margin(id: '1')
20
+ resource.cancel_all_open_orders(id: '1')
21
+ resource.cancel_all_open_orders_for_position(id: '1')
22
+ resource.exit_position(id: '1')
23
+ resource.create_take_profit_stop_loss(id: '1')
24
+ resource.list_transactions
25
+ resource.fetch_cross_margin_details
26
+ resource.update_margin_type(id: '1')
27
+
28
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions', body: {}, auth: true, base: :api,
29
+ bucket: :futures_positions_list)
30
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/update_leverage', body: { id: '1' },
31
+ auth: true, base: :api, bucket: :futures_positions_update_leverage)
32
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/add_margin', body: { id: '1' },
33
+ auth: true, base: :api, bucket: :futures_positions_add_margin)
34
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/remove_margin', body: { id: '1' },
35
+ auth: true, base: :api, bucket: :futures_positions_remove_margin)
36
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/cancel_all_open_orders',
37
+ body: { id: '1' }, auth: true, base: :api, bucket: :futures_positions_cancel_all_open_orders)
38
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/cancel_all_open_orders_for_position',
39
+ body: { id: '1' }, auth: true, base: :api, bucket: :futures_positions_cancel_all_open_orders_for_position)
40
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/exit', body: { id: '1' }, auth: true,
41
+ base: :api, bucket: :futures_positions_exit)
42
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/create_tpsl', body: { id: '1' },
43
+ auth: true, base: :api, bucket: :futures_positions_create_tpsl)
44
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/transactions', body: {}, auth: true,
45
+ base: :api, bucket: :futures_positions_transactions)
46
+ expect(http_client).to have_received(:get).with(
47
+ '/exchange/v1/derivatives/futures/positions/cross_margin_details',
48
+ params: {},
49
+ body: {},
50
+ auth: true,
51
+ base: :api,
52
+ bucket: :futures_positions_cross_margin_details
53
+ )
54
+ expect(http_client).to have_received(:post).with('/exchange/v1/derivatives/futures/positions/margin_type', body: { id: '1' },
55
+ auth: true, base: :api, bucket: :futures_positions_margin_type)
56
+ end
57
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Futures::Wallets do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ allow(http_client).to receive(:get).and_return({})
13
+ end
14
+
15
+ it 'routes futures wallet operations through authenticated transport calls' do
16
+ resource.transfer(transfer_type: 'withdraw', amount: 1, currency_short_name: 'USDT')
17
+ resource.fetch_details
18
+ resource.list_transactions
19
+
20
+ expect(http_client).to have_received(:post).with(
21
+ '/exchange/v1/derivatives/futures/wallets/transfer',
22
+ body: { transfer_type: 'withdraw', amount: 1, currency_short_name: 'USDT', timestamp: nil },
23
+ auth: true,
24
+ base: :api,
25
+ bucket: :futures_wallet_transfer
26
+ )
27
+ expect(http_client).to have_received(:get).with(
28
+ '/exchange/v1/derivatives/futures/wallets',
29
+ params: {},
30
+ body: {},
31
+ auth: true,
32
+ base: :api,
33
+ bucket: :futures_wallet_details
34
+ )
35
+ expect(http_client).to have_received(:get).with(
36
+ '/exchange/v1/derivatives/futures/wallets/transactions',
37
+ params: { page: 1, size: 1000 },
38
+ body: {},
39
+ auth: true,
40
+ base: :api,
41
+ bucket: :futures_wallet_transactions
42
+ )
43
+ end
44
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Margin::Orders do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ end
13
+
14
+ it 'routes margin operations through authenticated transport calls' do
15
+ resource.create(side: 'buy', order_type: 'market_order', market: 'SNTBTC', quantity: 1, client_order_id: 'margin-1')
16
+ resource.list
17
+ resource.fetch(id: '1')
18
+ resource.cancel(id: '1')
19
+ resource.exit_order(id: '1')
20
+ resource.edit_target(id: '1')
21
+ resource.edit_stop_loss(id: '1')
22
+ resource.edit_trailing_stop_loss(id: '1')
23
+ resource.edit_target_order_price(id: '1')
24
+ resource.add_margin(id: '1')
25
+ resource.remove_margin(id: '1')
26
+
27
+ expect(http_client).to have_received(:post).with(
28
+ '/exchange/v1/margin/create',
29
+ body: { side: 'buy', order_type: 'market_order', market: 'SNTBTC', quantity: 1, client_order_id: 'margin-1' },
30
+ auth: true,
31
+ base: :api,
32
+ bucket: :margin_create_order
33
+ )
34
+ expect(http_client).to have_received(:post).with('/exchange/v1/margin/fetch_orders', body: {}, auth: true, base: :api, bucket: :margin_list_orders)
35
+ expect(http_client).to have_received(:post).with('/exchange/v1/margin/order', body: { id: '1' }, auth: true, base: :api, bucket: :margin_fetch_order)
36
+ expect(http_client).to have_received(:post).with('/exchange/v1/margin/cancel', body: { id: '1' }, auth: true, base: :api, bucket: :margin_cancel_order)
37
+ expect(http_client).to have_received(:post).with('/exchange/v1/margin/exit', body: { id: '1' }, auth: true, base: :api, bucket: :margin_exit_order)
38
+ expect(http_client).to have_received(:post).with(
39
+ '/exchange/v1/margin/edit_target',
40
+ body: { id: '1' },
41
+ auth: true,
42
+ base: :api,
43
+ bucket: :margin_edit_target
44
+ )
45
+ expect(http_client).to have_received(:post).with('/exchange/v1/margin/edit_sl', body: { id: '1' }, auth: true, base: :api, bucket: :margin_edit_stop_loss)
46
+ expect(http_client).to have_received(:post).with(
47
+ '/exchange/v1/margin/edit_trailing_sl',
48
+ body: { id: '1' },
49
+ auth: true,
50
+ base: :api,
51
+ bucket: :margin_edit_trailing_stop_loss
52
+ )
53
+ expect(http_client).to have_received(:post).with(
54
+ '/exchange/v1/margin/edit_price_of_target_order',
55
+ body: { id: '1' },
56
+ auth: true,
57
+ base: :api,
58
+ bucket: :margin_edit_target_order_price
59
+ )
60
+ expect(http_client).to have_received(:post).with('/exchange/v1/margin/add_margin', body: { id: '1' }, auth: true, base: :api, bucket: :margin_add_margin)
61
+ expect(http_client).to have_received(:post).with(
62
+ '/exchange/v1/margin/remove_margin',
63
+ body: { id: '1' },
64
+ auth: true,
65
+ base: :api,
66
+ bucket: :margin_remove_margin
67
+ )
68
+ end
69
+
70
+ describe '#create' do
71
+ context 'when side is invalid' do
72
+ it 'raises a validation error' do
73
+ expect do
74
+ resource.create(side: 'hold', order_type: 'market_order', market: 'SNTBTC', quantity: 1)
75
+ end.to raise_error(CoinDCX::Errors::ValidationError, /side/)
76
+ end
77
+ end
78
+
79
+ context 'when quantity is not positive' do
80
+ it 'raises a validation error' do
81
+ expect do
82
+ resource.create(side: 'buy', order_type: 'market_order', market: 'SNTBTC', quantity: 0)
83
+ end.to raise_error(CoinDCX::Errors::ValidationError, /quantity/)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Public::MarketData do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ it 'requests public market endpoints through the transport' do
11
+ allow(http_client).to receive(:get).and_return([])
12
+
13
+ resource.list_tickers
14
+ resource.list_markets
15
+ resource.list_market_details
16
+ resource.list_trades(pair: 'B-BTC_USDT', limit: 10)
17
+ resource.fetch_order_book(pair: 'B-BTC_USDT')
18
+ resource.list_candles(pair: 'B-BTC_USDT', interval: '1m', start_time: 1, end_time: 2, limit: 3)
19
+
20
+ expect(http_client).to have_received(:get).with('/exchange/ticker', params: {}, body: {}, auth: false, base: :api, bucket: :public_ticker)
21
+ expect(http_client).to have_received(:get).with('/exchange/v1/markets', params: {}, body: {}, auth: false, base: :api, bucket: :public_market_data)
22
+ expect(http_client).to have_received(:get).with('/exchange/v1/markets_details', params: {}, body: {}, auth: false, base: :api, bucket: :public_market_data)
23
+ expect(http_client).to have_received(:get).with('/market_data/trade_history', params: { pair: 'B-BTC_USDT', limit: 10 }, body: {},
24
+ auth: false, base: :public, bucket: :public_trades)
25
+ expect(http_client).to have_received(:get).with('/market_data/orderbook', params: { pair: 'B-BTC_USDT' }, body: {}, auth: false, base: :public,
26
+ bucket: :public_order_book)
27
+ expect(http_client).to have_received(:get).with('/market_data/candles',
28
+ params: { pair: 'B-BTC_USDT', interval: '1m', startTime: 1, endTime: 2, limit: 3 }, body: {},
29
+ auth: false, base: :public, bucket: :public_candles)
30
+ end
31
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Spot::Orders do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ end
13
+
14
+ describe "#create" do
15
+ it "validates spot order payloads before sending them" do
16
+ resource.create(
17
+ side: "buy",
18
+ order_type: "limit_order",
19
+ market: "SNTBTC",
20
+ price_per_unit: "0.03244",
21
+ total_quantity: 400,
22
+ client_order_id: "client-123"
23
+ )
24
+
25
+ expect(http_client).to have_received(:post).with(
26
+ "/exchange/v1/orders/create",
27
+ body: {
28
+ side: "buy",
29
+ order_type: "limit_order",
30
+ market: "SNTBTC",
31
+ price_per_unit: "0.03244",
32
+ total_quantity: 400,
33
+ client_order_id: "client-123"
34
+ },
35
+ auth: true,
36
+ base: :api,
37
+ bucket: :spot_create_order
38
+ )
39
+ end
40
+
41
+ it "rejects an invalid side" do
42
+ expect do
43
+ resource.create(side: "hold", order_type: "limit_order", market: "SNTBTC", total_quantity: 1)
44
+ end.to raise_error(CoinDCX::Errors::ValidationError, /side/)
45
+ end
46
+
47
+ it "rejects a non-positive quantity" do
48
+ expect do
49
+ resource.create(side: "buy", order_type: "limit_order", market: "SNTBTC", total_quantity: 0)
50
+ end.to raise_error(CoinDCX::Errors::ValidationError, /total_quantity/)
51
+ end
52
+ end
53
+
54
+ describe "authenticated routing" do
55
+ it "routes the remaining spot order operations through authenticated transport calls" do
56
+ resource.create_many(
57
+ orders: [{ side: "buy", order_type: "limit_order", market: "SNTBTC", total_quantity: 1, price_per_unit: "0.03244", client_order_id: "client-124" }]
58
+ )
59
+ resource.fetch_status(id: "1")
60
+ resource.fetch_statuses(ids: ["1"])
61
+ resource.list_active
62
+ resource.count_active
63
+ resource.list_trade_history
64
+ resource.cancel(id: "1")
65
+ resource.cancel_many(ids: ["1"])
66
+ resource.cancel_all
67
+ resource.edit_price(id: "1")
68
+
69
+ expect(http_client).to have_received(:post).with(
70
+ "/exchange/v1/orders/create_multiple",
71
+ body: {
72
+ orders: [
73
+ {
74
+ side: "buy",
75
+ order_type: "limit_order",
76
+ market: "SNTBTC",
77
+ total_quantity: 1,
78
+ price_per_unit: "0.03244",
79
+ client_order_id: "client-124"
80
+ }
81
+ ]
82
+ },
83
+ auth: true,
84
+ base: :api,
85
+ bucket: :spot_create_order_multiple
86
+ )
87
+ expect(http_client).to have_received(:post).with(
88
+ "/exchange/v1/orders/status",
89
+ body: { id: "1" },
90
+ auth: true,
91
+ base: :api,
92
+ bucket: :spot_order_status
93
+ )
94
+ expect(http_client).to have_received(:post).with(
95
+ "/exchange/v1/orders/status_multiple",
96
+ body: { ids: ["1"] },
97
+ auth: true,
98
+ base: :api,
99
+ bucket: :spot_order_status_multiple
100
+ )
101
+ expect(http_client).to have_received(:post).with(
102
+ "/exchange/v1/orders/active_orders",
103
+ body: {},
104
+ auth: true,
105
+ base: :api,
106
+ bucket: :spot_active_order
107
+ )
108
+ expect(http_client).to have_received(:post).with(
109
+ "/exchange/v1/orders/active_orders_count",
110
+ body: {},
111
+ auth: true,
112
+ base: :api,
113
+ bucket: :spot_active_order_count
114
+ )
115
+ expect(http_client).to have_received(:post).with(
116
+ "/exchange/v1/orders/trade_history",
117
+ body: {},
118
+ auth: true,
119
+ base: :api,
120
+ bucket: :spot_trade_history
121
+ )
122
+ expect(http_client).to have_received(:post).with(
123
+ "/exchange/v1/orders/cancel",
124
+ body: { id: "1" },
125
+ auth: true,
126
+ base: :api,
127
+ bucket: :spot_cancel_order
128
+ )
129
+ expect(http_client).to have_received(:post).with(
130
+ "/exchange/v1/orders/cancel_by_ids",
131
+ body: { ids: ["1"] },
132
+ auth: true,
133
+ base: :api,
134
+ bucket: :spot_cancel_multiple_by_id
135
+ )
136
+ expect(http_client).to have_received(:post).with(
137
+ "/exchange/v1/orders/cancel_all",
138
+ body: {},
139
+ auth: true,
140
+ base: :api,
141
+ bucket: :spot_cancel_all
142
+ )
143
+ expect(http_client).to have_received(:post).with(
144
+ "/exchange/v1/orders/edit",
145
+ body: { id: "1" },
146
+ auth: true,
147
+ base: :api,
148
+ bucket: :spot_edit_price
149
+ )
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::Transfers::Wallets do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ end
13
+
14
+ it 'routes transfer operations through authenticated transport calls' do
15
+ resource.transfer(source_wallet_type: 'spot', destination_wallet_type: 'futures', currency_short_name: 'USDT', amount: 1)
16
+ resource.sub_account_transfer(from_account_id: 'A', to_account_id: 'B', currency_short_name: 'USDT', amount: 1)
17
+
18
+ expect(http_client).to have_received(:post).with(
19
+ '/exchange/v1/wallets/transfer',
20
+ body: { source_wallet_type: 'spot', destination_wallet_type: 'futures', currency_short_name: 'USDT', amount: 1, timestamp: nil },
21
+ auth: true,
22
+ base: :api,
23
+ bucket: :wallets_transfer
24
+ )
25
+ expect(http_client).to have_received(:post).with(
26
+ '/exchange/v1/wallets/sub_account_transfer',
27
+ body: { from_account_id: 'A', to_account_id: 'B', currency_short_name: 'USDT', amount: 1, timestamp: nil },
28
+ auth: true,
29
+ base: :api,
30
+ bucket: :wallets_sub_account_transfer
31
+ )
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe CoinDCX::REST::User::Accounts do
6
+ subject(:resource) { described_class.new(http_client: http_client) }
7
+
8
+ let(:http_client) { instance_double(CoinDCX::Transport::HttpClient) }
9
+
10
+ before do
11
+ allow(http_client).to receive(:post).and_return({})
12
+ end
13
+
14
+ it 'routes user account operations through authenticated transport calls' do
15
+ resource.list_balances
16
+ resource.fetch_info
17
+
18
+ expect(http_client).to have_received(:post).with('/exchange/v1/users/balances', body: {}, auth: true, base: :api, bucket: :user_balances)
19
+ expect(http_client).to have_received(:post).with('/exchange/v1/users/info', body: {}, auth: true, base: :api, bucket: :user_info)
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov"
4
+
5
+ SimpleCov.start do
6
+ minimum_coverage 90
7
+ add_filter "/spec/"
8
+ end
9
+
10
+ require "rspec"
11
+ require "coindcx"