ig_markets 0.19 → 0.20

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,20 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # Contains details on an account update received via the streaming API. Used by {Streaming::Subscription#on_data}.
4
+ class AccountUpdate < Model
5
+ attribute :account_id
6
+ attribute :available_cash, Float
7
+ attribute :available_to_deal, Float
8
+ attribute :deposit, Float
9
+ attribute :equity, Float
10
+ attribute :equity_used, Float
11
+ attribute :funds, Float
12
+ attribute :margin, Float
13
+ attribute :margin_lr, Float
14
+ attribute :margin_nlr, Float
15
+ attribute :pnl, Float
16
+ attribute :pnl_lr, Float
17
+ attribute :pnl_nlr, Float
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # Contains details on a chart tick update received via the streaming API. Used by {Streaming::Subscription#on_data}.
4
+ class ChartTickUpdate < Model
5
+ attribute :bid, Float
6
+ attribute :day_high, Float
7
+ attribute :day_low, Float
8
+ attribute :day_net_chg_mid, Float
9
+ attribute :day_open_mid, Float
10
+ attribute :day_perc_chg_mid, Float
11
+ attribute :epic, String, regex: Regex::EPIC
12
+ attribute :ltp, Float
13
+ attribute :ltv, Float
14
+ attribute :ofr, Float
15
+ attribute :ttv, Float
16
+ attribute :utm, Time, format: '%Q'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # Contains details on a consolidated chart data update received via the streaming API. Used by
4
+ # {Streaming::Subscription#on_data}.
5
+ class ConsolidatedChartDataUpdate < Model
6
+ attribute :bid_close, Float
7
+ attribute :bid_high, Float
8
+ attribute :bid_low, Float
9
+ attribute :bid_open, Float
10
+ attribute :cons_end, Boolean
11
+ attribute :cons_tick_count, Fixnum
12
+ attribute :day_high, Float
13
+ attribute :day_low, Float
14
+ attribute :day_net_chg_mid, Float
15
+ attribute :day_open_mid, Float
16
+ attribute :day_perc_chg_mid, Float
17
+ attribute :epic, String, regex: Regex::EPIC
18
+ attribute :ltp_close, Float
19
+ attribute :ltp_high, Float
20
+ attribute :ltp_low, Float
21
+ attribute :ltp_open, Float
22
+ attribute :ltv, Float
23
+ attribute :ofr_close, Float
24
+ attribute :ofr_high, Float
25
+ attribute :ofr_low, Float
26
+ attribute :ofr_open, Float
27
+ attribute :scale, Symbol, allowed_values: [:one_second, :one_minute, :five_minutes, :one_hour]
28
+ attribute :ttv, Float
29
+ attribute :utm, Time, format: '%Q'
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,86 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # This class manages a set of streaming subscriptions for a changing set of EPICs. The set of EPICs to stream data
4
+ # for is set by calls to {#epics=}, and when streaming market data becomes available it is passed to all registered
5
+ # {#on_data} callbacks.
6
+ #
7
+ # This class can be used standalone, but its primary function is as a helper for the {AccountState} class.
8
+ class MarketSubscriptionManager
9
+ # Initializes this market subscription manager with the specified dealing platform.
10
+ #
11
+ # @param [DealingPlatform] dealing_platform The dealing platform.
12
+ def initialize(dealing_platform)
13
+ @dealing_platform = dealing_platform
14
+
15
+ @subscriptions = {}
16
+ @on_data_callbacks = []
17
+ end
18
+
19
+ # Removes all market subscriptions and data callbacks.
20
+ def clear
21
+ @dealing_platform.streaming.remove_subscriptions @subscriptions.values
22
+
23
+ @subscriptions = {}
24
+ @on_data_callbacks = []
25
+ end
26
+
27
+ # Adds the passed block to the list of callbacks that will be run when a market that is subscribed to receives new
28
+ # data. The block will be called on a worker thread and so the code that is run by the block must be thread-safe.
29
+ # The arguments passed to the block are `|data, merged_data|`, and both arguments will be instances of
30
+ # {MarketUpdate}.
31
+ #
32
+ # @param [Proc] callback The callback that is to be run.
33
+ def on_data(&callback)
34
+ @on_data_callbacks << callback
35
+ end
36
+
37
+ # Sets the EPICs that this market subscription manager should be subscribed to and notifying updates for through
38
+ # {#on_data}.
39
+ #
40
+ # @param [Array<String>] epics
41
+ def epics=(epics)
42
+ epics = Array(epics).uniq
43
+
44
+ create_missing_subscriptions epics
45
+ remove_unused_subscriptions epics
46
+ end
47
+
48
+ private
49
+
50
+ def create_missing_subscriptions(epics)
51
+ new_subscriptions = []
52
+
53
+ epics.each do |epic|
54
+ next if @subscriptions.key? epic
55
+
56
+ subscription = @dealing_platform.streaming.build_markets_subscription epic
57
+ subscription.on_data(&method(:run_callbacks))
58
+
59
+ @subscriptions[epic] = subscription
60
+
61
+ new_subscriptions << subscription
62
+ end
63
+
64
+ @dealing_platform.streaming.start_subscriptions new_subscriptions, snapshot: true
65
+ end
66
+
67
+ def remove_unused_subscriptions(epics)
68
+ old_subscriptions = []
69
+
70
+ @subscriptions.keys.each do |epic|
71
+ next if epics.include? epic
72
+
73
+ old_subscriptions << @subscriptions.delete(epic)
74
+ end
75
+
76
+ @dealing_platform.streaming.remove_subscriptions old_subscriptions
77
+ end
78
+
79
+ def run_callbacks(data, merged_data)
80
+ @on_data_callbacks.each do |callback|
81
+ callback.call data, merged_data
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,21 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # Contains details on a market update received via the streaming API. Used by {Streaming::Subscription#on_data}.
4
+ class MarketUpdate < Model
5
+ attribute :bid, Float
6
+ attribute :change, Float
7
+ attribute :change_pct, Float
8
+ attribute :epic, String, regex: Regex::EPIC
9
+ attribute :high, Float
10
+ attribute :low, Float
11
+ attribute :market_delay, Boolean
12
+ attribute :market_state, Symbol, allowed_values: [:closed, :offline, :tradeable, :edit, :auction,
13
+ :auction_no_edit, :suspended]
14
+ attribute :mid_open, Float
15
+ attribute :odds, Float
16
+ attribute :offer, Float
17
+ attribute :strike_price, Float
18
+ attribute :update_time
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # Contains details on a position update received via the streaming API. Used by {Streaming::Subscription#on_data}.
4
+ class PositionUpdate < Model
5
+ attribute :account_id
6
+ attribute :channel
7
+ attribute :deal_id
8
+ attribute :deal_id_origin
9
+ attribute :deal_reference
10
+ attribute :deal_status, Symbol, allowed_values: [:accepted, :rejected]
11
+ attribute :direction, Symbol, allowed_values: [:buy, :sell]
12
+ attribute :epic, String, regex: Regex::EPIC
13
+ attribute :expiry, Date, nil_if: %w(- DFB), format: ['%d-%b-%y', '%b-%y']
14
+ attribute :guaranteed_stop, Boolean
15
+ attribute :level, Float
16
+ attribute :limit_level, Float
17
+ attribute :size, Float
18
+ attribute :status, Symbol, allowed_values: [:deleted, :open, :updated]
19
+ attribute :stop_level, Float
20
+ attribute :timestamp, Time, format: '%FT%T.%L'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,122 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # This class manages a single Lightstreamer subscription used to handle streaming data. Subscriptions should always
4
+ # be managed using the methods provided by {DealingPlatform::StreamingMethods}. Data can be consumed by registering
5
+ # an asynchronous data callback using {#on_data}.
6
+ class Subscription
7
+ # The underlying Lightstreamer subscription instance being managed by this class.
8
+ #
9
+ # @return [Lightstreamer::Subscription]
10
+ attr_reader :lightstreamer_subscription
11
+
12
+ # Initializes this subscription with the specified dealing platform and Lightstreamer subscription.
13
+ #
14
+ # @param [DealingPlatform] dealing_platform
15
+ # @param [Lightstreamer::Subscription] lightstreamer_subscription
16
+ #
17
+ # @private
18
+ def initialize(dealing_platform, lightstreamer_subscription)
19
+ @dealing_platform = dealing_platform
20
+
21
+ @lightstreamer_subscription = lightstreamer_subscription
22
+ @lightstreamer_subscription.on_data(&method(:on_raw_data))
23
+
24
+ @on_data_callbacks = []
25
+ end
26
+
27
+ # If this subscription was started with the `silent: true` option then this method can be called to unsilence the
28
+ # subscription and start receiving its data.
29
+ def unsilence
30
+ lightstreamer_subscription.unsilence
31
+ end
32
+
33
+ # Adds the passed block to the list of callbacks that will be run when this subscription receives new data. The
34
+ # block will be called on a worker thread and so the code that is run by the block must be thread-safe. The
35
+ # arguments passed to the block are `|data, merged_data|`, and the data will be an instance of {AccountUpdate},
36
+ # {MarketUpdate}, {DealConfirmation}, {PositionUpdate}, {WorkingOrderUpdate}, {ConsolidatedChartDataUpdate} or
37
+ # {ChartTickUpdate}, depending on what was subscribed to. The `merged_data` argument will be `nil` for deal
38
+ # confirmations, position updates, working order updates, and chart tick updates.
39
+ #
40
+ # @param [Proc] callback The callback that is to be run.
41
+ def on_data(&callback)
42
+ @on_data_callbacks << callback
43
+ end
44
+
45
+ private
46
+
47
+ ACCOUNT_DATA_REGEX = /^ACCOUNT:(.*)$/
48
+ MARKET_DATA_REGEX = /^MARKET:(.*)$/
49
+ TRADE_DATA_REGEX = /^TRADE:(.*)$/
50
+ CHART_TICK_DATA_REGEX = /^CHART:(.*):TICK$/
51
+ CONSOLIDATED_CHART_DATA_REGEX = /^CHART:(.*):(SECOND|1MINUTE|5MINUTE|HOUR)$/
52
+
53
+ def on_raw_data(subscription, item_name, item_data, new_data)
54
+ on_account_data subscription, item_name, item_data, new_data if item_name =~ ACCOUNT_DATA_REGEX
55
+ on_market_data subscription, item_name, item_data, new_data if item_name =~ MARKET_DATA_REGEX
56
+ on_trade_data subscription, item_name, item_data, new_data if item_name =~ TRADE_DATA_REGEX
57
+ on_chart_tick_data subscription, item_name, item_data, new_data if item_name =~ CHART_TICK_DATA_REGEX
58
+
59
+ if item_name =~ CONSOLIDATED_CHART_DATA_REGEX
60
+ on_consolidated_chart_data subscription, item_name, item_data, new_data
61
+ end
62
+ end
63
+
64
+ def on_account_data(_subscription, item_name, item_data, new_data)
65
+ item_data = @dealing_platform.instantiate_models AccountUpdate, item_data
66
+ new_data = @dealing_platform.instantiate_models AccountUpdate, new_data
67
+
68
+ item_data.account_id = item_name.match(ACCOUNT_DATA_REGEX).captures.first
69
+ new_data.account_id = item_data.account_id
70
+
71
+ run_callbacks new_data, item_data
72
+ end
73
+
74
+ def on_market_data(_subscription, item_name, item_data, new_data)
75
+ item_data = @dealing_platform.instantiate_models MarketUpdate, item_data
76
+ new_data = @dealing_platform.instantiate_models MarketUpdate, new_data
77
+
78
+ item_data.epic = item_name.match(MARKET_DATA_REGEX).captures.first
79
+ new_data.epic = item_data.epic
80
+
81
+ run_callbacks new_data, item_data
82
+ end
83
+
84
+ def on_trade_data(_subscription, item_name, _item_data, new_data)
85
+ account_id = item_name.match(TRADE_DATA_REGEX).captures.first
86
+
87
+ { confirms: DealConfirmation, opu: PositionUpdate, wou: WorkingOrderUpdate }.each do |key, model_class|
88
+ next unless new_data[key]
89
+
90
+ data = @dealing_platform.instantiate_models_from_json model_class, new_data[key]
91
+ data.account_id = account_id
92
+
93
+ run_callbacks data
94
+ end
95
+ end
96
+
97
+ def on_chart_tick_data(_subscription, item_name, _item_data, new_data)
98
+ new_data = @dealing_platform.instantiate_models ChartTickUpdate, new_data
99
+
100
+ new_data.epic = item_name.match(CHART_TICK_DATA_REGEX).captures.first
101
+
102
+ run_callbacks new_data
103
+ end
104
+
105
+ def on_consolidated_chart_data(_subscription, item_name, item_data, new_data)
106
+ item_data = @dealing_platform.instantiate_models ConsolidatedChartDataUpdate, item_data
107
+ new_data = @dealing_platform.instantiate_models ConsolidatedChartDataUpdate, new_data
108
+
109
+ captures = item_name.match(CONSOLIDATED_CHART_DATA_REGEX).captures
110
+ item_data.epic = new_data.epic = captures[0]
111
+ item_data.scale = new_data.scale = { 'SECOND' => :one_second, '1MINUTE' => :one_minute,
112
+ '5MINUTE' => :five_minutes, 'HOUR' => :one_hour }.fetch captures[1]
113
+
114
+ run_callbacks new_data, item_data
115
+ end
116
+
117
+ def run_callbacks(data, merged_data = nil)
118
+ @on_data_callbacks.each { |block| block.call data, merged_data }
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,23 @@
1
+ module IGMarkets
2
+ module Streaming
3
+ # Contains details on a working order update received via the streaming API. Used by
4
+ # {Streaming::Subscription#on_data}.
5
+ class WorkingOrderUpdate < Model
6
+ attribute :account_id
7
+ attribute :channel
8
+ attribute :deal_id
9
+ attribute :deal_reference
10
+ attribute :deal_status, Symbol, allowed_values: [:accepted, :rejected]
11
+ attribute :direction, Symbol, allowed_values: [:buy, :sell]
12
+ attribute :epic, String, regex: Regex::EPIC
13
+ attribute :expiry, Date, nil_if: %w(- DFB), format: ['%d-%b-%y', '%b-%y']
14
+ attribute :guaranteed_stop, Boolean
15
+ attribute :level, Float
16
+ attribute :limit_distance, Fixnum
17
+ attribute :size, Float
18
+ attribute :status, Symbol, allowed_values: [:deleted, :open, :updated]
19
+ attribute :stop_distance, Fixnum
20
+ attribute :timestamp, Time, format: '%FT%T.%L'
21
+ end
22
+ end
23
+ end
@@ -1,4 +1,4 @@
1
1
  module IGMarkets
2
2
  # The version of this gem.
3
- VERSION = '0.19'.freeze
3
+ VERSION = '0.20'.freeze
4
4
  end
@@ -10,7 +10,7 @@ module IGMarkets
10
10
 
11
11
  # Returns the markets for this watchlist.
12
12
  #
13
- # @return [Array<Market>]
13
+ # @return [Array<MarketOverview>]
14
14
  def markets
15
15
  result = @dealing_platform.session.get("watchlists/#{id}").fetch :markets
16
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ig_markets
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.19'
4
+ version: '0.20'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Viney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-05 00:00:00.000000000 Z
11
+ date: 2016-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: curses
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: excon
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +58,14 @@ dependencies:
44
58
  requirements:
45
59
  - - '='
46
60
  - !ruby/object:Gem::Version
47
- version: '0.10'
61
+ version: '0.12'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - '='
53
67
  - !ruby/object:Gem::Version
54
- version: '0.10'
68
+ version: '0.12'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: pry
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -254,6 +268,7 @@ files:
254
268
  - lib/ig_markets/cli/commands/transactions_command.rb
255
269
  - lib/ig_markets/cli/commands/watchlists_command.rb
256
270
  - lib/ig_markets/cli/config_file.rb
271
+ - lib/ig_markets/cli/curses_window.rb
257
272
  - lib/ig_markets/cli/main.rb
258
273
  - lib/ig_markets/cli/tables/accounts_table.rb
259
274
  - lib/ig_markets/cli/tables/activities_table.rb
@@ -276,6 +291,7 @@ files:
276
291
  - lib/ig_markets/dealing_platform/market_methods.rb
277
292
  - lib/ig_markets/dealing_platform/position_methods.rb
278
293
  - lib/ig_markets/dealing_platform/sprint_market_position_methods.rb
294
+ - lib/ig_markets/dealing_platform/streaming_methods.rb
279
295
  - lib/ig_markets/dealing_platform/watchlist_methods.rb
280
296
  - lib/ig_markets/dealing_platform/working_order_methods.rb
281
297
  - lib/ig_markets/errors.rb
@@ -295,6 +311,15 @@ files:
295
311
  - lib/ig_markets/response_parser.rb
296
312
  - lib/ig_markets/session.rb
297
313
  - lib/ig_markets/sprint_market_position.rb
314
+ - lib/ig_markets/streaming/account_state.rb
315
+ - lib/ig_markets/streaming/account_update.rb
316
+ - lib/ig_markets/streaming/chart_tick_update.rb
317
+ - lib/ig_markets/streaming/consolidated_chart_data_update.rb
318
+ - lib/ig_markets/streaming/market_subscription_manager.rb
319
+ - lib/ig_markets/streaming/market_update.rb
320
+ - lib/ig_markets/streaming/position_update.rb
321
+ - lib/ig_markets/streaming/subscription.rb
322
+ - lib/ig_markets/streaming/working_order_update.rb
298
323
  - lib/ig_markets/transaction.rb
299
324
  - lib/ig_markets/version.rb
300
325
  - lib/ig_markets/watchlist.rb