schwab_rb 0.2.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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/.copilotignore +4 -0
  3. data/.rspec +2 -0
  4. data/.rspec_status +292 -0
  5. data/.rubocop.yml +41 -0
  6. data/.rubocop_todo.yml +105 -0
  7. data/CHANGELOG.md +28 -0
  8. data/LICENSE.txt +23 -0
  9. data/README.md +271 -0
  10. data/Rakefile +12 -0
  11. data/doc/notes/data_objects_analysis.md +223 -0
  12. data/doc/notes/data_objects_refactoring_plan.md +82 -0
  13. data/examples/fetch_account_numbers.rb +49 -0
  14. data/examples/fetch_user_preferences.rb +49 -0
  15. data/lib/schwab_rb/account.rb +9 -0
  16. data/lib/schwab_rb/auth/auth_context.rb +23 -0
  17. data/lib/schwab_rb/auth/init_client_easy.rb +45 -0
  18. data/lib/schwab_rb/auth/init_client_login.rb +201 -0
  19. data/lib/schwab_rb/auth/init_client_token_file.rb +30 -0
  20. data/lib/schwab_rb/auth/login_flow_server.rb +55 -0
  21. data/lib/schwab_rb/auth/token.rb +24 -0
  22. data/lib/schwab_rb/auth/token_manager.rb +105 -0
  23. data/lib/schwab_rb/clients/async_client.rb +122 -0
  24. data/lib/schwab_rb/clients/base_client.rb +887 -0
  25. data/lib/schwab_rb/clients/client.rb +97 -0
  26. data/lib/schwab_rb/configuration.rb +39 -0
  27. data/lib/schwab_rb/constants.rb +7 -0
  28. data/lib/schwab_rb/data_objects/account.rb +281 -0
  29. data/lib/schwab_rb/data_objects/account_numbers.rb +68 -0
  30. data/lib/schwab_rb/data_objects/instrument.rb +156 -0
  31. data/lib/schwab_rb/data_objects/market_hours.rb +275 -0
  32. data/lib/schwab_rb/data_objects/option.rb +147 -0
  33. data/lib/schwab_rb/data_objects/option_chain.rb +95 -0
  34. data/lib/schwab_rb/data_objects/option_expiration_chain.rb +134 -0
  35. data/lib/schwab_rb/data_objects/order.rb +186 -0
  36. data/lib/schwab_rb/data_objects/order_leg.rb +68 -0
  37. data/lib/schwab_rb/data_objects/order_preview.rb +237 -0
  38. data/lib/schwab_rb/data_objects/position.rb +100 -0
  39. data/lib/schwab_rb/data_objects/price_history.rb +187 -0
  40. data/lib/schwab_rb/data_objects/quote.rb +276 -0
  41. data/lib/schwab_rb/data_objects/transaction.rb +132 -0
  42. data/lib/schwab_rb/data_objects/user_preferences.rb +129 -0
  43. data/lib/schwab_rb/market_hours.rb +13 -0
  44. data/lib/schwab_rb/movers.rb +35 -0
  45. data/lib/schwab_rb/option.rb +64 -0
  46. data/lib/schwab_rb/orders/builder.rb +202 -0
  47. data/lib/schwab_rb/orders/destination.rb +19 -0
  48. data/lib/schwab_rb/orders/duration.rb +9 -0
  49. data/lib/schwab_rb/orders/equity_instructions.rb +10 -0
  50. data/lib/schwab_rb/orders/errors.rb +5 -0
  51. data/lib/schwab_rb/orders/instruments.rb +35 -0
  52. data/lib/schwab_rb/orders/option_instructions.rb +10 -0
  53. data/lib/schwab_rb/orders/order.rb +77 -0
  54. data/lib/schwab_rb/orders/price_link_basis.rb +15 -0
  55. data/lib/schwab_rb/orders/price_link_type.rb +9 -0
  56. data/lib/schwab_rb/orders/session.rb +14 -0
  57. data/lib/schwab_rb/orders/special_instruction.rb +10 -0
  58. data/lib/schwab_rb/orders/stop_price_link_basis.rb +15 -0
  59. data/lib/schwab_rb/orders/stop_price_link_type.rb +9 -0
  60. data/lib/schwab_rb/orders/stop_type.rb +11 -0
  61. data/lib/schwab_rb/orders/tax_lot_method.rb +13 -0
  62. data/lib/schwab_rb/price_history.rb +55 -0
  63. data/lib/schwab_rb/quote.rb +13 -0
  64. data/lib/schwab_rb/transaction.rb +23 -0
  65. data/lib/schwab_rb/utils/enum_enforcer.rb +73 -0
  66. data/lib/schwab_rb/utils/logger.rb +70 -0
  67. data/lib/schwab_rb/utils/redactor.rb +104 -0
  68. data/lib/schwab_rb/version.rb +5 -0
  69. data/lib/schwab_rb.rb +48 -0
  70. data/sig/schwab_rb.rbs +4 -0
  71. metadata +289 -0
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'order_leg'
4
+
5
+ module SchwabRb
6
+ module DataObjects
7
+ class ExecutionLeg
8
+ attr_reader :leg_id, :quantity, :mismarked_quantity, :price, :time, :instrument_id
9
+
10
+ class << self
11
+ def build(data)
12
+ new(
13
+ leg_id: data[:legId],
14
+ quantity: data[:quantity],
15
+ mismarked_quantity: data[:mismarkedQuantity],
16
+ price: data[:price],
17
+ time: data[:time],
18
+ instrument_id: data[:instrumentId]
19
+ )
20
+ end
21
+ end
22
+
23
+ def initialize(leg_id:, quantity:, mismarked_quantity:, price:, time:, instrument_id:)
24
+ @leg_id = leg_id
25
+ @quantity = quantity
26
+ @mismarked_quantity = mismarked_quantity
27
+ @price = price
28
+ @time = time
29
+ @instrument_id = instrument_id
30
+ end
31
+
32
+ def to_h
33
+ {
34
+ legId: @leg_id,
35
+ quantity: @quantity,
36
+ mismarkedQuantity: @mismarked_quantity,
37
+ price: @price,
38
+ time: @time,
39
+ instrumentId: @instrument_id
40
+ }
41
+ end
42
+ end
43
+
44
+ class OrderActivity
45
+ attr_reader :activity_type, :activity_id, :execution_type, :quantity, :order_remaining_quantity, :execution_legs
46
+
47
+ class << self
48
+ def build(data)
49
+ new(
50
+ activity_type: data[:activityType],
51
+ activity_id: data[:activityId],
52
+ execution_type: data[:executionType],
53
+ quantity: data[:quantity],
54
+ order_remaining_quantity: data[:orderRemainingQuantity],
55
+ execution_legs: data.fetch(:executionLegs, []).map { |leg| ExecutionLeg.build(leg) }
56
+ )
57
+ end
58
+ end
59
+
60
+ def initialize(activity_type:, activity_id:, execution_type:, quantity:, order_remaining_quantity:, execution_legs:)
61
+ @activity_type = activity_type
62
+ @activity_id = activity_id
63
+ @execution_type = execution_type
64
+ @quantity = quantity
65
+ @order_remaining_quantity = order_remaining_quantity
66
+ @execution_legs = execution_legs
67
+ end
68
+
69
+ def to_h
70
+ {
71
+ activityType: @activity_type,
72
+ activityId: @activity_id,
73
+ executionType: @execution_type,
74
+ quantity: @quantity,
75
+ orderRemainingQuantity: @order_remaining_quantity,
76
+ executionLegs: @execution_legs.map(&:to_h)
77
+ }
78
+ end
79
+ end
80
+
81
+ class Order
82
+ attr_reader :duration, :order_type, :complex_order_strategy_type, :quantity,
83
+ :filled_quantity, :remaining_quantity, :price, :order_leg_collection, :order_strategy_type, :order_id, :status, :entered_time, :close_time, :order_activity_collection
84
+
85
+ class << self
86
+ def build(data)
87
+ # Parse datetime strings into DateTime objects
88
+ entered_time = parse_datetime(data[:enteredTime])
89
+ close_time = parse_datetime(data[:closeTime])
90
+
91
+ new(
92
+ duration: data[:duration],
93
+ order_type: data[:orderType],
94
+ complex_order_strategy_type: data[:complexOrderStrategyType],
95
+ quantity: data[:quantity],
96
+ filled_quantity: data[:filledQuantity],
97
+ remaining_quantity: data[:remainingQuantity],
98
+ price: data[:price],
99
+ order_leg_collection: data.fetch(:orderLegCollection, []).map { |leg| OrderLeg.build(leg) },
100
+ order_strategy_type: data[:orderStrategyType],
101
+ order_id: data[:orderId],
102
+ status: data[:status],
103
+ entered_time: entered_time,
104
+ close_time: close_time,
105
+ order_activity_collection: data.fetch(:orderActivityCollection, []).map do |activity|
106
+ OrderActivity.build(activity)
107
+ end
108
+ )
109
+ end
110
+
111
+ private
112
+
113
+ def parse_datetime(datetime_str)
114
+ return nil if datetime_str.nil? || datetime_str.empty?
115
+
116
+ begin
117
+ DateTime.parse(datetime_str)
118
+ rescue ArgumentError
119
+ nil
120
+ end
121
+ end
122
+ end
123
+
124
+ def initialize(
125
+ duration:, order_type:, complex_order_strategy_type:, quantity:, filled_quantity:,
126
+ remaining_quantity:, price:, order_strategy_type:, order_id:, status:, entered_time:, close_time:, order_leg_collection: [], order_activity_collection: []
127
+ )
128
+ @duration = duration
129
+ @order_type = order_type
130
+ @complex_order_strategy_type = complex_order_strategy_type
131
+ @quantity = quantity
132
+ @filled_quantity = filled_quantity
133
+ @remaining_quantity = remaining_quantity
134
+ @price = price
135
+ @order_leg_collection = order_leg_collection
136
+ @order_strategy_type = order_strategy_type
137
+ @order_id = order_id
138
+ @status = status
139
+ @entered_time = entered_time
140
+ @close_time = close_time
141
+ @order_activity_collection = order_activity_collection
142
+ end
143
+
144
+ def symbols
145
+ order_leg_collection.map(&:symbol)
146
+ end
147
+
148
+ def strategy
149
+ if %w[VERTICAL CUSTOM].include?(complex_order_strategy_type) && order_leg_collection.all?(&:call?)
150
+ 'CALL_SPREAD'
151
+ elsif %w[VERTICAL CUSTOM].include?(complex_order_strategy_type) && order_leg_collection.all?(&:put?)
152
+ 'PUT_SPREAD'
153
+ else
154
+ order_strategy_type
155
+ end
156
+ end
157
+
158
+ def close?
159
+ order_leg_collection.all? { |leg| leg.position_effect == 'CLOSING' }
160
+ end
161
+
162
+ def open?
163
+ order_leg_collection.all? { |leg| leg.position_effect == 'OPENING' }
164
+ end
165
+
166
+ def to_h
167
+ {
168
+ duration: @duration,
169
+ orderType: @order_type,
170
+ complexOrderStrategyType: @complex_order_strategy_type,
171
+ quantity: @quantity,
172
+ filledQuantity: @filled_quantity,
173
+ remainingQuantity: @remaining_quantity,
174
+ price: @price,
175
+ orderLegCollection: @order_leg_collection.map(&:to_h),
176
+ orderStrategyType: @order_strategy_type,
177
+ orderId: @order_id,
178
+ status: @status,
179
+ enteredTime: @entered_time&.iso8601,
180
+ closeTime: @close_time&.iso8601,
181
+ orderActivityCollection: @order_activity_collection.map(&:to_h)
182
+ }
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'instrument'
4
+
5
+ module SchwabRb
6
+ module DataObjects
7
+ class OrderLeg
8
+ attr_reader :leg_id, :order_leg_type, :quantity, :instrument, :instruction, :position_effect
9
+
10
+ class << self
11
+ def build(data)
12
+ new(
13
+ leg_id: data[:legId],
14
+ order_leg_type: data[:orderLegType],
15
+ quantity: data[:quantity],
16
+ instrument: Instrument.build(data[:instrument]),
17
+ instruction: data.fetch(:instruction, nil),
18
+ position_effect: data[:positionEffect]
19
+ )
20
+ end
21
+ end
22
+
23
+ def initialize(leg_id:, order_leg_type:, quantity:, instrument:, instruction:, position_effect:)
24
+ @leg_id = leg_id
25
+ @order_leg_type = order_leg_type
26
+ @quantity = quantity
27
+ @instrument = instrument
28
+ @instruction = instruction
29
+ @position_effect = position_effect
30
+ end
31
+
32
+ def call?
33
+ put_call == 'CALL'
34
+ end
35
+
36
+ def close?
37
+ position_effect == 'CLOSING'
38
+ end
39
+
40
+ def open?
41
+ position_effect == 'OPENING'
42
+ end
43
+
44
+ def put?
45
+ put_call == 'PUT'
46
+ end
47
+
48
+ def put_call
49
+ instrument.put_call
50
+ end
51
+
52
+ def symbol
53
+ instrument.symbol
54
+ end
55
+
56
+ def to_h
57
+ {
58
+ legId: @leg_id,
59
+ orderLegType: @order_leg_type,
60
+ quantity: @quantity,
61
+ instrument: @instrument.to_h,
62
+ instruction: @instruction,
63
+ positionEffect: @position_effect
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'order_leg'
4
+
5
+ module SchwabRb
6
+ module DataObjects
7
+ class OrderPreview
8
+ attr_reader :order_id, :order_value, :order_strategy, :order_balance, :order_validation_result, :projected_commission
9
+
10
+ class << self
11
+ def build(data)
12
+ new(data)
13
+ end
14
+ end
15
+
16
+ def initialize(attrs)
17
+ @order_id = attrs[:orderId]
18
+ @order_value = attrs[:orderValue]
19
+ @order_strategy = attrs[:orderStrategy] ? OrderStrategy.new(attrs[:orderStrategy]) : nil
20
+ @order_balance = attrs[:orderBalance] ? OrderBalance.new(attrs[:orderBalance]) : nil
21
+ @order_validation_result = attrs[:orderValidationResult] ? OrderValidationResult.new(attrs[:orderValidationResult]) : nil
22
+ @projected_commission = attrs[:projectedCommission] ? CommissionAndFee.new(attrs[:projectedCommission]) : nil
23
+ end
24
+
25
+ def status
26
+ @order_strategy&.status
27
+ end
28
+
29
+ def price
30
+ @order_strategy&.price
31
+ end
32
+
33
+ def quantity
34
+ @order_strategy&.quantity
35
+ end
36
+
37
+ def accepted?
38
+ status == 'ACCEPTED'
39
+ end
40
+
41
+ def commission
42
+ return 0.0 unless @projected_commission
43
+
44
+ @projected_commission.commission.to_f
45
+ end
46
+
47
+ def fees
48
+ return 0.0 unless @projected_commission
49
+
50
+ @projected_commission.fee.to_f
51
+ end
52
+
53
+ def to_h
54
+ {
55
+ orderId: @order_id,
56
+ orderValue: @order_value,
57
+ orderStrategy: @order_strategy&.to_h,
58
+ orderBalance: @order_balance&.to_h,
59
+ orderValidationResult: @order_validation_result&.to_h,
60
+ projectedCommission: @projected_commission&.to_h
61
+ }
62
+ end
63
+
64
+ class OrderStrategy
65
+ attr_reader :account_number, :status, :price, :quantity, :order_type, :type, :strategy_id, :order_legs
66
+
67
+ def initialize(attrs)
68
+ @account_number = attrs[:accountNumber]
69
+ @status = attrs[:status]
70
+ @price = attrs[:price]
71
+ @quantity = attrs[:quantity]
72
+ @order_type = attrs[:orderType]
73
+ @type = attrs[:type]
74
+ @strategy_id = attrs[:strategyId]
75
+ @order_legs = attrs[:orderLegs]&.map { |leg| OrderLeg.build(leg) } || []
76
+ end
77
+
78
+ def to_h
79
+ {
80
+ accountNumber: @account_number,
81
+ status: @status,
82
+ price: @price,
83
+ quantity: @quantity,
84
+ orderType: @order_type,
85
+ type: @type,
86
+ strategyId: @strategy_id,
87
+ orderLegs: @order_legs.map(&:to_h)
88
+ }
89
+ end
90
+ end
91
+
92
+ class OrderBalance
93
+ attr_reader :order_value, :projected_available_fund, :projected_buying_power, :projected_commission
94
+
95
+ def initialize(attrs)
96
+ @order_value = attrs[:orderValue]
97
+ @projected_available_fund = attrs[:projectedAvailableFund]
98
+ @projected_buying_power = attrs[:projectedBuyingPower]
99
+ @projected_commission = attrs[:projectedCommission]
100
+ end
101
+
102
+ def to_h
103
+ {
104
+ orderValue: @order_value,
105
+ projectedAvailableFund: @projected_available_fund,
106
+ projectedBuyingPower: @projected_buying_power,
107
+ projectedCommission: @projected_commission
108
+ }
109
+ end
110
+ end
111
+
112
+ class OrderValidationResult
113
+ attr_reader :is_valid, :warning_message, :rejects
114
+
115
+ def initialize(attrs)
116
+ @is_valid = attrs[:isValid]
117
+ @warning_message = attrs[:warningMessage]
118
+ @rejects = attrs[:rejects]&.map { |reject| Reject.new(reject) } || []
119
+ end
120
+
121
+ def to_h
122
+ {
123
+ isValid: @is_valid,
124
+ warningMessage: @warning_message,
125
+ rejects: @rejects.map(&:to_h)
126
+ }
127
+ end
128
+
129
+ class Reject
130
+ attr_reader :reject_code, :reject_message
131
+
132
+ def initialize(attrs)
133
+ @reject_code = attrs[:rejectCode]
134
+ @reject_message = attrs[:rejectMessage]
135
+ end
136
+
137
+ def to_h
138
+ {
139
+ rejectCode: @reject_code,
140
+ rejectMessage: @reject_message
141
+ }
142
+ end
143
+ end
144
+ end
145
+
146
+ class CommissionAndFee
147
+ attr_reader :commission_data, :fee_data
148
+
149
+ def initialize(attrs)
150
+ # Handle the flattened structure (from API)
151
+ if attrs[:commissions]
152
+ @commission_data = attrs[:commissions]
153
+ @fee_data = attrs[:fees] || []
154
+ @true_commission_data = []
155
+ @direct_commission = attrs[:commission]
156
+ @direct_fee = attrs[:fee]
157
+ @direct_true_commission = attrs[:trueCommission]
158
+ # Handle the nested structure (from test data)
159
+ else
160
+ @commission_data = attrs.dig(:commission, :commissionLegs) || []
161
+ @fee_data = attrs.dig(:fee, :feeLegs) || []
162
+ @true_commission_data = attrs.dig(:trueCommission, :commissionLegs) || []
163
+ @direct_commission = nil
164
+ @direct_fee = nil
165
+ @direct_true_commission = nil
166
+ end
167
+ end
168
+
169
+ def commission_total
170
+ calculate_total_from_legs(@commission_data, 'COMMISSION')
171
+ end
172
+
173
+ def commission
174
+ @direct_commission || sprintf("%.2f", commission_total)
175
+ end
176
+
177
+ def fee_total
178
+ calculate_total_from_legs(@fee_data, ['OPT_REG_FEE', 'INDEX_OPTION_FEE'])
179
+ end
180
+
181
+ def fee
182
+ @direct_fee || sprintf("%.2f", fee_total)
183
+ end
184
+
185
+ def true_commission
186
+ if @direct_true_commission
187
+ @direct_true_commission
188
+ else
189
+ # For nested structure, calculate from true commission legs
190
+ if @true_commission_data.any?
191
+ true_commission_total = calculate_total_from_legs(@true_commission_data, 'COMMISSION')
192
+ sprintf("%.2f", true_commission_total * 2)
193
+ else
194
+ sprintf("%.2f", commission_total * 2)
195
+ end
196
+ end
197
+ end
198
+
199
+ def commissions
200
+ @commission_data
201
+ end
202
+
203
+ def fees
204
+ @fee_data
205
+ end
206
+
207
+ def to_h
208
+ {
209
+ commission: commission,
210
+ fee: fee,
211
+ trueCommission: true_commission,
212
+ commissions: commissions,
213
+ fees: fees
214
+ }
215
+ end
216
+
217
+ private
218
+
219
+ def calculate_total_from_legs(legs, types)
220
+ total = 0.0
221
+ types = [types] unless types.is_a?(Array)
222
+
223
+ legs.each do |leg|
224
+ values = leg[:commissionValues] || leg[:feeValues] || []
225
+ values.each do |value_item|
226
+ if types.include?(value_item[:type])
227
+ total += (value_item[:value] || 0.0)
228
+ end
229
+ end
230
+ end
231
+
232
+ total.round(2)
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'instrument'
4
+
5
+ module SchwabRb
6
+ module DataObjects
7
+ class Position
8
+ attr_reader :short_quantity, :average_price, :current_day_profit_loss, :current_day_profit_loss_percentage,
9
+ :long_quantity, :settled_long_quantity, :settled_short_quantity, :instrument, :market_value,
10
+ :maintenance_requirement, :average_long_price, :average_short_price, :long_open_profit_loss, :short_open_profit_loss, :current_day_cost, :strike, :delta, :mark
11
+
12
+ class << self
13
+ def build(data)
14
+ new(
15
+ short_quantity: data[:shortQuantity],
16
+ average_price: data[:averagePrice],
17
+ current_day_profit_loss: data[:currentDayProfitLoss],
18
+ current_day_profit_loss_percentage: data[:currentDayProfitLossPercentage],
19
+ long_quantity: data[:longQuantity],
20
+ settled_long_quantity: data[:settledLongQuantity],
21
+ settled_short_quantity: data[:settledShortQuantity],
22
+ instrument: Instrument.build(data[:instrument]),
23
+ market_value: data[:marketValue],
24
+ maintenance_requirement: data[:maintenanceRequirement],
25
+ average_long_price: data[:averageLongPrice],
26
+ average_short_price: data[:averageShortPrice],
27
+ long_open_profit_loss: data[:longOpenProfitLoss],
28
+ short_open_profit_loss: data[:shortOpenProfitLoss],
29
+ current_day_cost: data[:currentDayCost]
30
+ )
31
+ end
32
+ end
33
+
34
+ def initialize(
35
+ short_quantity: nil, average_price: nil, current_day_profit_loss: nil, current_day_profit_loss_percentage: nil,
36
+ long_quantity: nil, settled_long_quantity: nil, settled_short_quantity: nil, instrument: nil, market_value: nil, maintenance_requirement: nil, average_long_price: nil, average_short_price: nil, long_open_profit_loss: nil, short_open_profit_loss: nil, current_day_cost: nil, strike: nil, delta: nil, mark: nil
37
+ )
38
+ @short_quantity = short_quantity
39
+ @average_price = average_price
40
+ @current_day_profit_loss = current_day_profit_loss
41
+ @current_day_profit_loss_percentage = current_day_profit_loss_percentage
42
+ @long_quantity = long_quantity
43
+ @settled_long_quantity = settled_long_quantity
44
+ @settled_short_quantity = settled_short_quantity
45
+ @instrument = instrument
46
+ @market_value = market_value
47
+ @maintenance_requirement = maintenance_requirement
48
+ @average_long_price = average_long_price
49
+ @average_short_price = average_short_price
50
+ @long_open_profit_loss = long_open_profit_loss
51
+ @short_open_profit_loss = short_open_profit_loss
52
+ @current_day_cost = current_day_cost
53
+ @strike = strike
54
+ @delta = delta
55
+ @mark = mark
56
+ end
57
+
58
+ def symbol
59
+ instrument&.symbol
60
+ end
61
+
62
+ def underlying_symbol
63
+ instrument.underlying_symbol
64
+ end
65
+
66
+ def long?
67
+ long_quantity.positive?
68
+ end
69
+
70
+ def short?
71
+ short_quantity.positive?
72
+ end
73
+
74
+ def long_short
75
+ if long?
76
+ 'LONG'
77
+ elsif short?
78
+ 'SHORT'
79
+ else
80
+ 'NONE'
81
+ end
82
+ end
83
+
84
+ def put_call
85
+ instrument.put_call
86
+ end
87
+
88
+ def to_h
89
+ {
90
+ symbol: symbol,
91
+ underlying_symbol: underlying_symbol,
92
+ average_price: average_price,
93
+ market_value: market_value,
94
+ put_call: put_call,
95
+ long_short: long_short
96
+ }
97
+ end
98
+ end
99
+ end
100
+ end