xtb 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.
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ class Response
6
+ def initialize(raw_response, command)
7
+ @response = parse_json(raw_response)
8
+ @command = command
9
+ end
10
+
11
+ def self.parse(raw_response, command)
12
+ new(raw_response, command).parse
13
+ end
14
+
15
+ def parse
16
+ return data if success?
17
+
18
+ raise_error
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :response, :command
24
+
25
+ def parse_json(raw_response)
26
+ JSON.parse(raw_response).deep_transform_keys { |key| key.underscore.to_sym }
27
+ end
28
+
29
+ def success?
30
+ response[:status]
31
+ end
32
+
33
+ def data
34
+ return response if command == :login
35
+
36
+ response[:return_data]
37
+ end
38
+
39
+ def error_code
40
+ response[:error_code]
41
+ end
42
+
43
+ def error_description
44
+ response[:error_descr]
45
+ end
46
+
47
+ def error_string
48
+ "(#{error_code}) #{error_description}"
49
+ end
50
+
51
+ def raise_error
52
+ case error_code
53
+ when 'BE103'
54
+ raise NotLoggedInError, error_string
55
+ when 'BE118'
56
+ raise AlreadyLoggedInError, error_string
57
+ else
58
+ raise BrokerError, error_string
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getServerTime
6
+ class ServerTime < Command
7
+ ServerTimeResponse = Data.define(:time, :time_string)
8
+
9
+ def call
10
+ ServerTimeResponse.new(**super)
11
+ end
12
+
13
+ private
14
+
15
+ def command = :getServerTime
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require_relative '../config'
5
+
6
+ module Xtb
7
+ module Http
8
+ class SslClient
9
+ class << self
10
+ def request(payload)
11
+ ssl_socket.connect
12
+ ssl_socket.puts(payload)
13
+ next_line
14
+ end
15
+
16
+ private
17
+
18
+ def next_line
19
+ line = ssl_socket.gets.chomp
20
+ return line unless line.empty?
21
+
22
+ next_line
23
+ end
24
+
25
+ def ssl_context
26
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new
27
+ end
28
+
29
+ def ssl_socket
30
+ @ssl_socket ||= OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context) do |socket|
31
+ socket.hostname = Config.https_host
32
+ socket.sync_close = true
33
+ socket
34
+ end
35
+ end
36
+
37
+ def tcp_socket
38
+ TCPSocket.new(Config.https_host, Config.https_port)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getStepRules
6
+ class StepRules < Command
7
+ StepRecord = Data.define(:from_value, :step)
8
+
9
+ def call
10
+ super.map { |record| StepRecord.new(**record) }
11
+ end
12
+
13
+ private
14
+
15
+ def command = :getStepRules
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getSymbol
6
+ class Symbol < Command
7
+ # @param symbol [String|Symbol]
8
+ def initialize(symbol)
9
+ @symbol = symbol
10
+ end
11
+
12
+ def call
13
+ Xtb::SymbolRecord.new(**super)
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :symbol
19
+
20
+ def command = :getSymbol
21
+
22
+ def arguments
23
+ {
24
+ symbol:
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getTickPrices
6
+ class TickPrices < Command
7
+ TickRecord = Data.define(:ask, :ask_volume, :bid, :bid_volume, :high, :level, :low, :spread_raw, :spread_table,
8
+ :symbol, :timestamp)
9
+
10
+ def initialize(level, symbols, timestamp)
11
+ @level = level
12
+ @symbols = symbols
13
+ @timestamp = timestamp
14
+ end
15
+
16
+ def call
17
+ super[:quotations].map { |record| TickRecord.new(**record) }
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :level, :symbols, :timestamp
23
+
24
+ def command = :getTickPrices
25
+
26
+ def arguments
27
+ {
28
+ level:,
29
+ symbols:,
30
+ timestamp:
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getTradeRecords
6
+ class TradeRecords < Command
7
+ # @param orders [Array<Integer>] list of order ids
8
+ def initialize(orders)
9
+ @orders = orders
10
+ end
11
+
12
+ def call
13
+ super.map { |record| Xtb::TradeRecord.new(**record) }
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :orders
19
+
20
+ def command = :getTradeRecords
21
+
22
+ def arguments
23
+ {
24
+ orders:
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#tradeTransaction
6
+ class TradeTransaction < Command
7
+ TradeTransactionResponse = Data.define(:order)
8
+
9
+ # @param cmd [Symbol]
10
+ # @param custom_comment [String]
11
+ # @param expiration [Integer]
12
+ # @param offset [Integer]
13
+ # @param order [Integer]
14
+ # @param price [Float]
15
+ # @param stop_loss [Float]
16
+ # @param symbol [String]
17
+ # @param take_profit [Float]
18
+ # @param type [Symbol]
19
+ # @param volume [Float]
20
+ def initialize(cmd:, custom_comment:, expiration:, offset:, order:, price:, stop_loss:, symbol:, take_profit:,
21
+ type:, volume:)
22
+ @cmd = cmd
23
+ @custom_comment = custom_comment
24
+ @expiration = expiration
25
+ @offset = offset
26
+ @order = order
27
+ @price = price
28
+ @stop_loss = stop_loss
29
+ @symbol = symbol
30
+ @take_profit = take_profit
31
+ @type = type
32
+ @volume = volume
33
+ end
34
+
35
+ def call
36
+ TradeTransactionResponse.new(**super)
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :cmd, :custom_comment, :expiration, :offset, :order, :price, :stop_loss, :symbol, :take_profit,
42
+ :type, :volume
43
+
44
+ def command = :tradeTransaction
45
+
46
+ def arguments
47
+ {
48
+ trade_trans_info: {
49
+ cmd: Xtb::OPERATIONS[cmd],
50
+ custom_comment:,
51
+ expiration:,
52
+ offset:,
53
+ order:,
54
+ price:,
55
+ sl: stop_loss,
56
+ symbol:,
57
+ tp: take_profit,
58
+ type: Xtb::TRADE_TRANSACTION_TYPES[type],
59
+ volume:
60
+ }
61
+ }
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getTrades
6
+ class Trades < Command
7
+ # @param opened_only [Boolean]
8
+ def initialize(opened_only)
9
+ @opened_only = opened_only
10
+ end
11
+
12
+ def call
13
+ super.map { |record| Xtb::TradeRecord.new(**record) }
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :opened_only
19
+
20
+ def command = :getTrades
21
+
22
+ def arguments
23
+ {
24
+ opened_only:
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getTrades
6
+ class TradesHistory < Command
7
+ # @param end_time [Time]
8
+ # @param start_time [Time]
9
+ def initialize(end_time, start_time)
10
+ @end_time = end_time
11
+ @start_time = start_time
12
+ end
13
+
14
+ def call
15
+ super.map { |record| Xtb::TradeRecord.new(**record) }
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :end_time, :start_time
21
+
22
+ def command = :getTradesHistory
23
+
24
+ def arguments
25
+ {
26
+ end: end_time,
27
+ start: start_time
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getTradingHours
6
+ class TradingHours < Command
7
+ TradingHoursRecord = Data.define(:quotes, :symbol, :trading) do
8
+ # Represents both quotes and trading times records.
9
+ TimesRecord = Data.define(:day, :from_t, :to_t)
10
+
11
+ def initialize(symbol:, quotes: TimesRecord.new, trading: TimesRecord.new)
12
+ super(quotes:, symbol:, trading:)
13
+ end
14
+ end
15
+
16
+ # @param symbols [Array<String|Symbol>]
17
+ def initialize(symbols)
18
+ @symbols = symbols
19
+ end
20
+
21
+ def call
22
+ super.map { |record| TradingHoursRecord.new(**record) }
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :symbols
28
+
29
+ def command = :getTradingHours
30
+
31
+ def arguments
32
+ {
33
+ symbols:
34
+ }
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module Http
5
+ # http://developers.xstore.pro/documentation/2.5.0#getVersion
6
+ class Version < Command
7
+ VersionResponse = Data.define(:version)
8
+
9
+ def call
10
+ VersionResponse.new(**super)
11
+ end
12
+
13
+ private
14
+
15
+ def command = :getVersion
16
+ end
17
+ end
18
+ end
data/lib/xtb/http.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'http/client'
4
+
5
+ # commands
6
+ require_relative 'http/command'
7
+ require_relative 'http/all_symbols'
8
+ require_relative 'http/calendar'
9
+ require_relative 'http/chart_last_request'
10
+ require_relative 'http/chart_range_request'
11
+ require_relative 'http/commission_def'
12
+ require_relative 'http/current_user_data'
13
+ require_relative 'http/margin_level'
14
+ require_relative 'http/margin_trade'
15
+ require_relative 'http/news'
16
+ require_relative 'http/profit_calculation'
17
+ require_relative 'http/server_time'
18
+ require_relative 'http/step_rules'
19
+ require_relative 'http/symbol'
20
+ require_relative 'http/tick_prices'
21
+ require_relative 'http/trade_records'
22
+ require_relative 'http/trades'
23
+ require_relative 'http/trades_history'
24
+ require_relative 'http/trading_hours'
25
+ require_relative 'http/version'
26
+
27
+ module Xtb
28
+ module Http # :nodoc:
29
+ end
30
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require_relative 'config'
5
+
6
+ module Xtb
7
+ # Module provides a mechanism to limit the number of requests to the broker server.
8
+ module RequestQueue
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+ private
15
+
16
+ def with_request_queue(&block)
17
+ queue << block
18
+ queue << wait_proc
19
+
20
+ result = queue.pop.call
21
+ queue.pop.call
22
+
23
+ result
24
+ end
25
+
26
+ def queue
27
+ @queue ||= Queue.new
28
+ end
29
+
30
+ def wait_proc
31
+ proc { sleep(min_request_interval) }
32
+ end
33
+
34
+ def min_request_interval
35
+ Config.min_request_interval
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module WebSocket
5
+ class Client
6
+ include RequestQueue
7
+
8
+ def initialize() end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Xtb
4
+ module WebSocket
5
+ end
6
+ end
data/lib/xtb.rb ADDED
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'xtb/version'
4
+ require_relative 'xtb/http'
5
+ require_relative 'xtb/web_socket'
6
+ require_relative 'xtb/config'
7
+
8
+ module Xtb
9
+ # Periods in minutes.
10
+ PERIODS = {
11
+ m1: 1,
12
+ m5: 5,
13
+ m15: 15,
14
+ m30: 30,
15
+ h1: 60,
16
+ h4: 240,
17
+ d1: 1440,
18
+ w1: 10_080,
19
+ mn1: 43_200
20
+ }.freeze
21
+
22
+ # Operations with XTB broker codes.
23
+ OPERATIONS = {
24
+ buy: 0,
25
+ sell: 1,
26
+ buy_limit: 2,
27
+ sell_limit: 3,
28
+ buy_stop: 4,
29
+ sell_stop: 5,
30
+ balance: 6,
31
+ credit: 7
32
+ }.freeze
33
+
34
+ # Trade transaction type with XTB broker codes.
35
+ TRADE_TRANSACTION_TYPES = {
36
+ open: 0,
37
+ pending: 1,
38
+ close: 2,
39
+ modify: 3,
40
+ delete: 4
41
+ }.freeze
42
+
43
+ SymbolRecord = Data.define(:ask, :bid, :category_name, :contract_size, :currency, :currency_pair, :currency_profit,
44
+ :description, :exemode, :expiration, :group_name, :high, :initial_margin,
45
+ :instant_max_volume, :leverage, :long_only, :lot_max, :lot_min, :lot_step, :low,
46
+ :margin_hedged, :margin_hedged_strong, :margin_maintenance, :margin_mode, :percentage,
47
+ :pips_precision, :precision, :profit_mode, :quote_id, :quote_id_cross, :short_selling,
48
+ :spread_raw, :spread_table, :starting, :step_rule_id, :stops_level, :swap_rollover3days,
49
+ :swap_enable, :swap_long, :swap_short, :swap_type, :symbol, :tick_size, :tick_value,
50
+ :time, :time_string, :trailing_enabled, :type)
51
+
52
+ TradeRecord = Data.define(:close_price, :close_time, :close_time_string, :closed, :cmd, :comment, :commission,
53
+ :custom_comment, :digits, :expiration, :expiration_string, :margin_rate, :offset,
54
+ :open_price, :open_time, :open_time_string, :order, :order2, :position, :profit, :sl,
55
+ :storage, :symbol, :timestamp, :tp, :volume)
56
+ end
data/sig/xtb.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Xtb
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xtb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - jacekmaciag
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-11-12 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: 'The XTB API (xAPI) client for Ruby provides a simple and easy to use
14
+ interface to interact with XTB API.
15
+
16
+ '
17
+ email:
18
+ - jacek.maciag@gmail.com
19
+ executables: []
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - ".rspec"
24
+ - ".rubocop.yml"
25
+ - CHANGELOG.md
26
+ - LICENSE.txt
27
+ - README.md
28
+ - Rakefile
29
+ - lib/xtb.rb
30
+ - lib/xtb/config.rb
31
+ - lib/xtb/errors.rb
32
+ - lib/xtb/http.rb
33
+ - lib/xtb/http/all_symbols.rb
34
+ - lib/xtb/http/calendar.rb
35
+ - lib/xtb/http/chart_last_request.rb
36
+ - lib/xtb/http/chart_range_request.rb
37
+ - lib/xtb/http/client.rb
38
+ - lib/xtb/http/command.rb
39
+ - lib/xtb/http/commission_def.rb
40
+ - lib/xtb/http/current_user_data.rb
41
+ - lib/xtb/http/margin_level.rb
42
+ - lib/xtb/http/margin_trade.rb
43
+ - lib/xtb/http/news.rb
44
+ - lib/xtb/http/profit_calculation.rb
45
+ - lib/xtb/http/response.rb
46
+ - lib/xtb/http/server_time.rb
47
+ - lib/xtb/http/ssl_client.rb
48
+ - lib/xtb/http/step_rules.rb
49
+ - lib/xtb/http/symbol.rb
50
+ - lib/xtb/http/tick_prices.rb
51
+ - lib/xtb/http/trade_records.rb
52
+ - lib/xtb/http/trade_transaction.rb
53
+ - lib/xtb/http/trades.rb
54
+ - lib/xtb/http/trades_history.rb
55
+ - lib/xtb/http/trading_hours.rb
56
+ - lib/xtb/http/version.rb
57
+ - lib/xtb/request_queue.rb
58
+ - lib/xtb/version.rb
59
+ - lib/xtb/web_socket.rb
60
+ - lib/xtb/web_socket/client.rb
61
+ - sig/xtb.rbs
62
+ homepage: https://github.com/jacekmaciag/xtb
63
+ licenses:
64
+ - MIT
65
+ metadata:
66
+ allowed_push_host: https://rubygems.org
67
+ homepage_uri: https://github.com/jacekmaciag/xtb
68
+ source_code_uri: https://github.com/jacekmaciag/xtb
69
+ changelog_uri: https://github.com/jacekmaciag/xtb/blob/main/CHANGELOG.md
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 3.1.0
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubygems_version: 3.5.11
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: XTB API client for Ruby.
89
+ test_files: []