ib-api 10.33.1
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.
- checksums.yaml +7 -0
- data/.gitignore +52 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CLAUDE.md +131 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +120 -0
- data/Guardfile +24 -0
- data/LICENSE +674 -0
- data/LLM_GUIDE.md +388 -0
- data/README.md +114 -0
- data/Rakefile +11 -0
- data/VERSION +1 -0
- data/api.gemspec +50 -0
- data/bin/console +96 -0
- data/bin/console.yml +3 -0
- data/bin/setup +8 -0
- data/bin/simple +91 -0
- data/changelog.md +32 -0
- data/conditions/ib/execution_condition.rb +31 -0
- data/conditions/ib/margin_condition.rb +28 -0
- data/conditions/ib/order_condition.rb +29 -0
- data/conditions/ib/percent_change_condition.rb +34 -0
- data/conditions/ib/price_condition.rb +44 -0
- data/conditions/ib/time_condition.rb +42 -0
- data/conditions/ib/volume_condition.rb +36 -0
- data/lib/class_extensions.rb +167 -0
- data/lib/ib/base.rb +109 -0
- data/lib/ib/base_properties.rb +178 -0
- data/lib/ib/connection.rb +573 -0
- data/lib/ib/constants.rb +402 -0
- data/lib/ib/contract.rb +30 -0
- data/lib/ib/errors.rb +52 -0
- data/lib/ib/messages/abstract_message.rb +68 -0
- data/lib/ib/messages/incoming/abstract_message.rb +116 -0
- data/lib/ib/messages/incoming/abstract_tick.rb +25 -0
- data/lib/ib/messages/incoming/account_message.rb +26 -0
- data/lib/ib/messages/incoming/alert.rb +34 -0
- data/lib/ib/messages/incoming/contract_data.rb +105 -0
- data/lib/ib/messages/incoming/contract_message.rb +13 -0
- data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
- data/lib/ib/messages/incoming/execution_data.rb +50 -0
- data/lib/ib/messages/incoming/histogram_data.rb +30 -0
- data/lib/ib/messages/incoming/historical_data.rb +65 -0
- data/lib/ib/messages/incoming/historical_data_update.rb +50 -0
- data/lib/ib/messages/incoming/managed_accounts.rb +21 -0
- data/lib/ib/messages/incoming/market_depth.rb +34 -0
- data/lib/ib/messages/incoming/market_depth_l2.rb +15 -0
- data/lib/ib/messages/incoming/next_valid_id.rb +19 -0
- data/lib/ib/messages/incoming/open_order.rb +290 -0
- data/lib/ib/messages/incoming/order_status.rb +85 -0
- data/lib/ib/messages/incoming/portfolio_value.rb +47 -0
- data/lib/ib/messages/incoming/position_data.rb +21 -0
- data/lib/ib/messages/incoming/positions_multi.rb +15 -0
- data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
- data/lib/ib/messages/incoming/receive_fa.rb +30 -0
- data/lib/ib/messages/incoming/scanner_data.rb +54 -0
- data/lib/ib/messages/incoming/tick_by_tick.rb +77 -0
- data/lib/ib/messages/incoming/tick_efp.rb +18 -0
- data/lib/ib/messages/incoming/tick_generic.rb +12 -0
- data/lib/ib/messages/incoming/tick_option.rb +60 -0
- data/lib/ib/messages/incoming/tick_price.rb +60 -0
- data/lib/ib/messages/incoming/tick_size.rb +55 -0
- data/lib/ib/messages/incoming/tick_string.rb +13 -0
- data/lib/ib/messages/incoming.rb +292 -0
- data/lib/ib/messages/outgoing/abstract_message.rb +84 -0
- data/lib/ib/messages/outgoing/bar_request_message.rb +247 -0
- data/lib/ib/messages/outgoing/new-place-order.rb +193 -0
- data/lib/ib/messages/outgoing/old-place-order.rb +147 -0
- data/lib/ib/messages/outgoing/place_order.rb +149 -0
- data/lib/ib/messages/outgoing/request_account_summary.rb +79 -0
- data/lib/ib/messages/outgoing/request_historical_data.rb +182 -0
- data/lib/ib/messages/outgoing/request_market_data.rb +102 -0
- data/lib/ib/messages/outgoing/request_market_depth.rb +57 -0
- data/lib/ib/messages/outgoing/request_real_time_bars.rb +48 -0
- data/lib/ib/messages/outgoing/request_scanner_subscription.rb +73 -0
- data/lib/ib/messages/outgoing/request_tick_by_tick_data.rb +21 -0
- data/lib/ib/messages/outgoing.rb +410 -0
- data/lib/ib/messages.rb +139 -0
- data/lib/ib/order_condition.rb +26 -0
- data/lib/ib/plugins.rb +27 -0
- data/lib/ib/prepare_data.rb +61 -0
- data/lib/ib/raw_message_parser.rb +99 -0
- data/lib/ib/socket.rb +83 -0
- data/lib/ib/support.rb +236 -0
- data/lib/ib/version.rb +6 -0
- data/lib/ib-api.rb +44 -0
- data/lib/server_versions.rb +145 -0
- data/lib/support/array_function.rb +28 -0
- data/lib/support/logging.rb +45 -0
- data/models/ib/account.rb +72 -0
- data/models/ib/account_value.rb +33 -0
- data/models/ib/bag.rb +55 -0
- data/models/ib/bar.rb +31 -0
- data/models/ib/combo_leg.rb +127 -0
- data/models/ib/contract.rb +411 -0
- data/models/ib/contract_detail.rb +118 -0
- data/models/ib/execution.rb +67 -0
- data/models/ib/forex.rb +12 -0
- data/models/ib/future.rb +64 -0
- data/models/ib/index.rb +14 -0
- data/models/ib/option.rb +149 -0
- data/models/ib/option_detail.rb +84 -0
- data/models/ib/order.rb +720 -0
- data/models/ib/order_state.rb +155 -0
- data/models/ib/portfolio_value.rb +86 -0
- data/models/ib/spread.rb +176 -0
- data/models/ib/stock.rb +25 -0
- data/models/ib/underlying.rb +32 -0
- data/plugins/ib/advanced-account.rb +442 -0
- data/plugins/ib/alerts/base-alert.rb +125 -0
- data/plugins/ib/alerts/gateway-alerts.rb +15 -0
- data/plugins/ib/alerts/order-alerts.rb +73 -0
- data/plugins/ib/auto-adjust.rb +0 -0
- data/plugins/ib/connection-tools.rb +122 -0
- data/plugins/ib/eod.rb +326 -0
- data/plugins/ib/greeks.rb +102 -0
- data/plugins/ib/managed-accounts.rb +274 -0
- data/plugins/ib/market-price.rb +150 -0
- data/plugins/ib/option-chain.rb +167 -0
- data/plugins/ib/order-flow.rb +157 -0
- data/plugins/ib/order-prototypes/abstract.rb +67 -0
- data/plugins/ib/order-prototypes/adaptive.rb +40 -0
- data/plugins/ib/order-prototypes/all-in-one.rb +46 -0
- data/plugins/ib/order-prototypes/combo.rb +46 -0
- data/plugins/ib/order-prototypes/forex.rb +40 -0
- data/plugins/ib/order-prototypes/limit.rb +193 -0
- data/plugins/ib/order-prototypes/market.rb +116 -0
- data/plugins/ib/order-prototypes/pegged.rb +169 -0
- data/plugins/ib/order-prototypes/premarket.rb +31 -0
- data/plugins/ib/order-prototypes/stop.rb +202 -0
- data/plugins/ib/order-prototypes/volatility.rb +39 -0
- data/plugins/ib/order-prototypes.rb +118 -0
- data/plugins/ib/probability-of-expiring.rb +109 -0
- data/plugins/ib/process-orders.rb +155 -0
- data/plugins/ib/roll.rb +86 -0
- data/plugins/ib/spread-prototypes/butterfly.rb +77 -0
- data/plugins/ib/spread-prototypes/calendar.rb +97 -0
- data/plugins/ib/spread-prototypes/stock-spread.rb +56 -0
- data/plugins/ib/spread-prototypes/straddle.rb +70 -0
- data/plugins/ib/spread-prototypes/strangle.rb +93 -0
- data/plugins/ib/spread-prototypes/vertical.rb +83 -0
- data/plugins/ib/spread-prototypes.rb +70 -0
- data/plugins/ib/symbols/abstract.rb +136 -0
- data/plugins/ib/symbols/bonds.rb +28 -0
- data/plugins/ib/symbols/cfd.rb +19 -0
- data/plugins/ib/symbols/combo.rb +46 -0
- data/plugins/ib/symbols/commodity.rb +17 -0
- data/plugins/ib/symbols/forex.rb +41 -0
- data/plugins/ib/symbols/futures.rb +127 -0
- data/plugins/ib/symbols/index.rb +43 -0
- data/plugins/ib/symbols/options.rb +99 -0
- data/plugins/ib/symbols/stocks.rb +44 -0
- data/plugins/ib/symbols/version.rb +5 -0
- data/plugins/ib/symbols.rb +118 -0
- data/plugins/ib/verify.rb +226 -0
- data/symbols/w20.yml +210 -0
- data/t.txt +20 -0
- data/update.md +71 -0
- metadata +327 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
|
|
3
|
+
# OrderState represents dynamic (changeable) info about a single Order,
|
|
4
|
+
# isolating these changes and making Order essentially immutable
|
|
5
|
+
class OrderState < IB::Base
|
|
6
|
+
include BaseProperties
|
|
7
|
+
|
|
8
|
+
#p column_names
|
|
9
|
+
belongs_to :order
|
|
10
|
+
|
|
11
|
+
# Properties arriving via OpenOrder message
|
|
12
|
+
prop :init_margin_after, # Float: The impact the order would have on your initial margin.
|
|
13
|
+
:maint_margin_after, # Float: The impact the order would have on your maintenance margin.
|
|
14
|
+
:equity_with_loan_after, # Float: The impact the order would have on your equity
|
|
15
|
+
:init_margin_before, :maint_margin_before, :equity_with_loan_before,
|
|
16
|
+
:init_margin_change, :maint_margin_change, :equity_with_loan_change,
|
|
17
|
+
:commission, # double: Shows the commission amount on the order.
|
|
18
|
+
:min_commission, # The possible min range of the actual order commission.
|
|
19
|
+
:max_commission, # The possible max range of the actual order commission.
|
|
20
|
+
|
|
21
|
+
:commission_currency, # String: Shows the currency of the commission.
|
|
22
|
+
:warning_text, # String: Displays a warning message if warranted.
|
|
23
|
+
|
|
24
|
+
:market_cap_price # messages#incomming#orderstae#vers. 11
|
|
25
|
+
|
|
26
|
+
# Properties arriving via OrderStatus message:
|
|
27
|
+
prop :filled, # int
|
|
28
|
+
:remaining, # int
|
|
29
|
+
[:price, :last_fill_price,], # double
|
|
30
|
+
[:average_price, :average_fill_price], # double
|
|
31
|
+
:why_held # String: comma-separated list of reasons for order to be held.
|
|
32
|
+
|
|
33
|
+
# Properties arriving in both messages:
|
|
34
|
+
prop :local_id, # int: Order id associated with client (volatile).
|
|
35
|
+
:perm_id, # int: TWS permanent id, remains the same over TWS sessions.
|
|
36
|
+
:client_id, # int: The id of the client that placed this order.
|
|
37
|
+
:parent_id, # int: The order ID of the parent (original) order, used
|
|
38
|
+
:status => :s # String: one of
|
|
39
|
+
# ApiCancelled, PreSubmitted, PendingCancel, Cancelled, Submitted, Filled,
|
|
40
|
+
# Inactive, PendingSubmit, Unknown, ApiPending,
|
|
41
|
+
#
|
|
42
|
+
# Displays the order status. Possible values include:
|
|
43
|
+
# - PendingSubmit - indicates that you have transmitted the order, but
|
|
44
|
+
# have not yet received confirmation that it has been accepted by the
|
|
45
|
+
# order destination. NOTE: This order status is NOT sent back by TWS
|
|
46
|
+
# and should be explicitly set by YOU when an order is submitted.
|
|
47
|
+
# - PendingCancel - indicates that you have sent a request to cancel
|
|
48
|
+
# the order but have not yet received cancel confirmation from the
|
|
49
|
+
# order destination. At this point, your order cancel is not confirmed.
|
|
50
|
+
# You may still receive an execution while your cancellation request
|
|
51
|
+
# is pending. NOTE: This order status is not sent back by TWS and
|
|
52
|
+
# should be explicitly set by YOU when an order is canceled.
|
|
53
|
+
# - PreSubmitted - indicates that a simulated order type has been
|
|
54
|
+
# accepted by the IB system and that this order has yet to be elected.
|
|
55
|
+
# The order is held in the IB system until the election criteria are
|
|
56
|
+
# met. At that time the order is transmitted to the order destination
|
|
57
|
+
# as specified.
|
|
58
|
+
# - Submitted - indicates that your order has been accepted at the order
|
|
59
|
+
# destination and is working.
|
|
60
|
+
# - Cancelled - indicates that the balance of your order has been
|
|
61
|
+
# confirmed canceled by the IB system. This could occur unexpectedly
|
|
62
|
+
# when IB or the destination has rejected your order.
|
|
63
|
+
# - ApiCancelled - canceled via API
|
|
64
|
+
# - Filled - indicates that the order has been completely filled.
|
|
65
|
+
# - Inactive - indicates that the order has been accepted by the system
|
|
66
|
+
# (simulated orders) or an exchange (native orders) but that currently
|
|
67
|
+
# the order is inactive due to system, exchange or other issues.
|
|
68
|
+
#
|
|
69
|
+
|
|
70
|
+
validates_format_of :status, :without => /\A\z/, :message => 'must not be empty'
|
|
71
|
+
validates_numericality_of :price, :average_price, :allow_nil => true
|
|
72
|
+
validates_numericality_of :local_id, :perm_id, :client_id, :parent_id, :filled,
|
|
73
|
+
:remaining, :only_integer => true, :allow_nil => true
|
|
74
|
+
|
|
75
|
+
def self.valid_status? the_message
|
|
76
|
+
valid_stati = %w( ApiCancelled PreSubmitted PendingCancel Cancelled Submitted Filled
|
|
77
|
+
Inactive PendingSubmit Unknown ApiPending)
|
|
78
|
+
valid_stati.include?( the_message )
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
## Testing Order state:
|
|
82
|
+
|
|
83
|
+
def new?
|
|
84
|
+
status.empty? || status == 'New'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Order is in a valid, working state on TWS side
|
|
88
|
+
def submitted?
|
|
89
|
+
status =~ /Submit/
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Order is in a valid, working state on TWS side
|
|
93
|
+
def pending?
|
|
94
|
+
submitted? || status =~ /Pending/
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Order is in invalid state
|
|
98
|
+
def inactive?
|
|
99
|
+
new? || pending? || status =~ /Cancel/
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def active?
|
|
103
|
+
!inactive? # status == 'Inactive'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def complete_fill?
|
|
107
|
+
status == 'Filled' && remaining == 0 # filled >= total_quantity # Manually corrected
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Comparison
|
|
111
|
+
def == other
|
|
112
|
+
|
|
113
|
+
super(other) ||
|
|
114
|
+
other.is_a?(self.class) &&
|
|
115
|
+
status == other.status &&
|
|
116
|
+
local_id == other.local_id &&
|
|
117
|
+
perm_id == other.perm_id &&
|
|
118
|
+
client_id == other.client_id &&
|
|
119
|
+
filled == other.filled &&
|
|
120
|
+
remaining == other.remaining &&
|
|
121
|
+
last_fill_price == other.last_fill_price &&
|
|
122
|
+
init_margin_after == other.init_margin_after &&
|
|
123
|
+
maint_margin_after == other.maint_margin_after &&
|
|
124
|
+
equity_with_loan_after == other.equity_with_loan_after &&
|
|
125
|
+
why_held == other.why_held &&
|
|
126
|
+
warning_text == other.warning_text &&
|
|
127
|
+
commission == other.commission
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def to_human
|
|
131
|
+
"<OrderState: #{status} ##{local_id}/#{perm_id} from #{client_id}" +
|
|
132
|
+
(filled ? " filled #{filled}/#{remaining}" : '') +
|
|
133
|
+
(last_fill_price ? " at #{last_fill_price}/#{average_fill_price}" : '') +
|
|
134
|
+
(init_margin_after ? " margin #{init_margin_after}/#{maint_margin_after}" : '') +
|
|
135
|
+
(equity_with_loan_after ? " equity #{equity_with_loan_after}" : '') +
|
|
136
|
+
(commission && commission > 0 ? " fee #{commission}" : "") +
|
|
137
|
+
(why_held ? " why_held #{why_held}" : '') +
|
|
138
|
+
((warning_text && warning_text != '') ? " warning #{warning_text}" : '') + ">"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
alias to_s to_human
|
|
142
|
+
=begin
|
|
143
|
+
If an Order is submitted with the :what_if-Flag set, commission and margin are returned
|
|
144
|
+
via the order_state-Object.
|
|
145
|
+
=end
|
|
146
|
+
def forcast
|
|
147
|
+
{ :init_margin => init_margin_after,
|
|
148
|
+
:maint_margin => maint_margin_after,
|
|
149
|
+
:equity_with_loan => equity_with_loan_after ,
|
|
150
|
+
:commission => commission,
|
|
151
|
+
:commission_currency=> commission_currency,
|
|
152
|
+
:warning => warning_text }
|
|
153
|
+
end
|
|
154
|
+
end # class Order
|
|
155
|
+
end # module IB
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
class PortfolioValue < IB::Base
|
|
3
|
+
include BaseProperties
|
|
4
|
+
# belongs_to :currency
|
|
5
|
+
belongs_to :account
|
|
6
|
+
belongs_to :contract
|
|
7
|
+
|
|
8
|
+
# scope :single, ->(key) { where :schluessel => key } rescue nil
|
|
9
|
+
|
|
10
|
+
prop :position,
|
|
11
|
+
:market_price,
|
|
12
|
+
:market_value,
|
|
13
|
+
:average_cost,
|
|
14
|
+
:unrealized_pnl,
|
|
15
|
+
:realized_pnl
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Order comparison
|
|
19
|
+
def == other
|
|
20
|
+
super(other) ||
|
|
21
|
+
other.is_a?(self.class) &&
|
|
22
|
+
market_price == other.market_price &&
|
|
23
|
+
average_cost == other.average_cost &&
|
|
24
|
+
position == other.position &&
|
|
25
|
+
unrealized_pnl == other.unrealized_pnl &&
|
|
26
|
+
realized_pnl == other.realized_pnl &&
|
|
27
|
+
contract == other.contract
|
|
28
|
+
end
|
|
29
|
+
def to_human
|
|
30
|
+
the_account = if account.present?
|
|
31
|
+
if account.is_a?(String)
|
|
32
|
+
account + " "
|
|
33
|
+
else
|
|
34
|
+
account.account+" "
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
""
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
"<PortfolioValue: "+
|
|
41
|
+
the_account +
|
|
42
|
+
"Pos=#{ position.to_i } @ #{market_price.to_f.round(3)};" +
|
|
43
|
+
"Value=#{market_value.to_f.round(2)};PNL=" +
|
|
44
|
+
( unrealized_pnl.to_i.zero? ? "": "#{unrealized_pnl} unrealized;") +
|
|
45
|
+
( realized_pnl.to_i.zero? ? "" : "#{realized_pnl} realized;>" ) +
|
|
46
|
+
contract.to_human
|
|
47
|
+
end
|
|
48
|
+
alias to_s to_human
|
|
49
|
+
|
|
50
|
+
def table_header
|
|
51
|
+
if block_given?
|
|
52
|
+
[ '' , yield , 'pos', 'entry', 'market', 'value', 'unrealized', 'realized' ]
|
|
53
|
+
else
|
|
54
|
+
[ '' , '', 'pos', 'entry', 'market', 'value', 'unrealized', 'realized' ]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def table_row
|
|
59
|
+
outprice= ->( item ) { { value: item.nil? ? "--" : item , alignment: :right } }
|
|
60
|
+
|
|
61
|
+
the_account = if account.present?
|
|
62
|
+
if account.is_a?(String)
|
|
63
|
+
account + " "
|
|
64
|
+
else
|
|
65
|
+
account.account+" "
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
""
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
entry = average_cost.to_f / (contract.multiplier.to_i.zero? ? 1 : contract.multiplier.to_i)
|
|
72
|
+
|
|
73
|
+
[ the_account,
|
|
74
|
+
contract.to_human[1..-2],
|
|
75
|
+
outprice[position.to_i],
|
|
76
|
+
outprice[entry.to_f.round(3)],
|
|
77
|
+
outprice[market_price.to_f.round(3)],
|
|
78
|
+
outprice[market_value.to_f.round(2)],
|
|
79
|
+
unrealized_pnl.to_i.zero? ? "": outprice[unrealized_pnl],
|
|
80
|
+
realized_pnl.to_i.zero? ? "" : outprice[realized_pnl]
|
|
81
|
+
]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
end # class
|
|
86
|
+
end # module
|
data/models/ib/spread.rb
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
class Spread < Bag
|
|
3
|
+
has_many :legs
|
|
4
|
+
|
|
5
|
+
using IB::Support
|
|
6
|
+
|
|
7
|
+
=begin
|
|
8
|
+
Parameters: front: YYYMM(DD)
|
|
9
|
+
back: {n}w, {n}d or YYYYMM(DD)
|
|
10
|
+
|
|
11
|
+
Adds (or substracts) relative (back) measures to the front month, just passes absolute YYYYMM(DD) value
|
|
12
|
+
|
|
13
|
+
front: 201809 back: 2m (-1m) --> 201811 (201808)
|
|
14
|
+
front: 20180908 back: 1w (-1w) --> 20180918 (20180902)
|
|
15
|
+
=end
|
|
16
|
+
|
|
17
|
+
def self.transform_distance front, back
|
|
18
|
+
# Check Format of back: 201809 --> > 200.000
|
|
19
|
+
# 20180989 ---> 20.000.000
|
|
20
|
+
start_date = front.to_i < 20000000 ? Date.strptime(front.to_s,"%Y%m") : Date.strptime(front.to_s,"%Y%m%d")
|
|
21
|
+
nb = if back.to_i > 200000
|
|
22
|
+
back.to_i
|
|
23
|
+
elsif back[-1] == "w" && front.to_i > 20000000
|
|
24
|
+
start_date + (back.to_i * 7) + 1 # +1 to compensate for friday's bank-holiday, target has to be verified through next_expiry
|
|
25
|
+
elsif back[-1] == "m" && front.to_i > 200000
|
|
26
|
+
start_date >> back.to_i
|
|
27
|
+
else
|
|
28
|
+
error "Wrong date #{back} required format YYYMM, YYYYMMDD ord {n}w or {n}m"
|
|
29
|
+
end
|
|
30
|
+
if nb.is_a?(Date)
|
|
31
|
+
if back[-1]=='w'
|
|
32
|
+
nb.strftime("%Y%m%d")
|
|
33
|
+
else
|
|
34
|
+
nb.strftime("%Y%m")
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
nb
|
|
38
|
+
end
|
|
39
|
+
end # def
|
|
40
|
+
|
|
41
|
+
def to_human
|
|
42
|
+
self.description
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def calculate_spread_value( array_of_portfolio_values )
|
|
46
|
+
array_of_portfolio_values.map{|x| x.send yield }.sum if block_given?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def fake_portfolio_position( array_of_portfolio_values )
|
|
50
|
+
calculate_spread_value= ->( a_o_p_v, attribute ) do
|
|
51
|
+
a_o_p_v.map{|x| x.send attribute }.sum
|
|
52
|
+
end
|
|
53
|
+
ar=array_of_portfolio_values
|
|
54
|
+
IB::PortfolioValue.new contract: self,
|
|
55
|
+
average_cost: calculate_spread_value[ar, :average_cost],
|
|
56
|
+
market_price: calculate_spread_value[ar, :market_price],
|
|
57
|
+
market_value: calculate_spread_value[ar, :market_value],
|
|
58
|
+
unrealized_pnl: calculate_spread_value[ar, :unrealized_pnl],
|
|
59
|
+
realized_pnl: calculate_spread_value[ar, :realized_pnl],
|
|
60
|
+
position: 0
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# adds a leg to any spread
|
|
66
|
+
#
|
|
67
|
+
# Parameter:
|
|
68
|
+
# contract: Will be verified. Contract.essential is added to legs-array
|
|
69
|
+
# action: :buy or :sell
|
|
70
|
+
# weight:
|
|
71
|
+
# ratio:
|
|
72
|
+
#
|
|
73
|
+
# Default: action: :buy, weight: 1
|
|
74
|
+
|
|
75
|
+
def add_leg contract, **leg_params
|
|
76
|
+
error "need a IB::Contract as first argument" unless contract.is_a? IB::Contract
|
|
77
|
+
self.legs << contract
|
|
78
|
+
error "cannot add leg if no con_id is provided" if contract.con_id.blank?
|
|
79
|
+
# weigth = 1 --> sets Combo.side to buy and overwrites the action statement
|
|
80
|
+
# leg_params[:weight] = 1 unless leg_params.key?(:weight) || leg_params.key?(:ratio)
|
|
81
|
+
leg_description = leg_params.extract!( :description )
|
|
82
|
+
leg_description = "#{leg_params[:action] || 'buy'} #{leg_params[:weight] || "1"} #{contract.to_human}" if leg_description.empty?
|
|
83
|
+
self.combo_legs << ComboLeg.new( contract.attributes.slice( :con_id, :exchange ).merge( leg_params ))
|
|
84
|
+
self.description = "#{description.nil? ? "": description + " / "} #{leg_description}" rescue "Spread: #{contract.to_human}"
|
|
85
|
+
|
|
86
|
+
self # return object to enable chaining
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# removes the contract from the spread definition
|
|
92
|
+
#
|
|
93
|
+
def remove_leg contract_or_position = nil
|
|
94
|
+
contract = if contract_or_position.is_a? (IB::Contract)
|
|
95
|
+
contract_or_position
|
|
96
|
+
elsif contract_or_position.is_a? Numeric
|
|
97
|
+
legs.at contract_or_position
|
|
98
|
+
else
|
|
99
|
+
error "Specify a contract to be removed or the position in the legs-array as parameter to remove a leg"
|
|
100
|
+
end
|
|
101
|
+
the_con_id = contract.verify.first &.con_id
|
|
102
|
+
error "Invalid Contract specified" unless the_con_id.is_a? Numeric
|
|
103
|
+
legs.delete_if { |x| x.con_id == the_con_id }
|
|
104
|
+
combo_legs.delete_if { |x| x.con_id == the_con_id }
|
|
105
|
+
self.description = description + " removed #{contract.to_human}"
|
|
106
|
+
self # make method chainable
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# essentail
|
|
110
|
+
# effectivley clones the object
|
|
111
|
+
#
|
|
112
|
+
def essential
|
|
113
|
+
the_es = self.class.new invariant_attributes
|
|
114
|
+
the_es.legs = legs.map{|y| IB::Contract.build y.invariant_attributes}
|
|
115
|
+
the_es.combo_legs = combo_legs.map{|y| IB::ComboLeg.new y.invariant_attributes }
|
|
116
|
+
the_es.description = description
|
|
117
|
+
the_es # return
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def multiplier
|
|
121
|
+
(legs.map(&:multiplier).sum/legs.size).to_i
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# provide a negative con_id
|
|
125
|
+
def con_id
|
|
126
|
+
if attributes[:con_id].present? && attributes[] < 0
|
|
127
|
+
attributes[:con_id]
|
|
128
|
+
else
|
|
129
|
+
-legs.map{ |x| x.is_a?(String) ? x.expand.con_id : x.con_id}.sum
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# def non_guaranteed= x
|
|
135
|
+
# super.merge combo_params: [ ['NonGuaranteed', x] ]
|
|
136
|
+
# end
|
|
137
|
+
#
|
|
138
|
+
#
|
|
139
|
+
# def non_guaranteed
|
|
140
|
+
# combo_params['NonGuaranteed']
|
|
141
|
+
# end
|
|
142
|
+
# optional: specify default order prarmeters for all spreads
|
|
143
|
+
# def order_requirements
|
|
144
|
+
# super.merge symbol: symbol
|
|
145
|
+
# end
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def as_table
|
|
149
|
+
t= Terminal::Table.new title: description[1..-2] ,
|
|
150
|
+
headings: table_header,
|
|
151
|
+
|
|
152
|
+
style: { border: :unicode }
|
|
153
|
+
|
|
154
|
+
t.add_row table_row
|
|
155
|
+
legs.each{ |y| t.add_row y.table_row }
|
|
156
|
+
t.render
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def self.build_from_json container
|
|
161
|
+
read_leg = ->(a) do
|
|
162
|
+
IB::ComboLeg.new :con_id => a.read_int,
|
|
163
|
+
:ratio => a.read_int,
|
|
164
|
+
:action => a.read_string,
|
|
165
|
+
:exchange => a.read_string
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
object= self.new container['Spread'].clone.read_contract
|
|
169
|
+
object.legs = container['legs'].map{|x| IB::Contract.build x.clone.read_contract}
|
|
170
|
+
object.combo_legs = container['combo_legs'].map{ |x| read_leg[ x.clone ] }
|
|
171
|
+
object.description = container['misc'].clone.read_string
|
|
172
|
+
object
|
|
173
|
+
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
data/models/ib/stock.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
class Stock < IB::Contract
|
|
3
|
+
validates_format_of :sec_type, :with => /\Astock\z/,
|
|
4
|
+
:message => "should be a Stock"
|
|
5
|
+
validates_format_of :symbol, with: /\A.*\z/,
|
|
6
|
+
message: 'should not be blank'
|
|
7
|
+
def default_attributes
|
|
8
|
+
super.merge :sec_type => :stock, currency:'USD', exchange:'SMART'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def merge **new_attributes
|
|
12
|
+
super( **{ trading_class: '', primary_exchange: '' }.merge(new_attributes) )
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_human
|
|
16
|
+
att = [ symbol,
|
|
17
|
+
currency, ( exchange == 'SMART' ? nil: exchange ),
|
|
18
|
+
(primary_exchange.present? && !primary_exchange.empty? ? primary_exchange : nil),
|
|
19
|
+
@description.present? ? " (#{@description}) " : nil,
|
|
20
|
+
].compact
|
|
21
|
+
"<Stock: " + att.join(" ") + ">"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module IB
|
|
2
|
+
# Calculated characteristics of underlying Contract (volatile)
|
|
3
|
+
class Underlying < IB::Base
|
|
4
|
+
include BaseProperties
|
|
5
|
+
|
|
6
|
+
has_one :contract
|
|
7
|
+
|
|
8
|
+
prop :con_id, # Id of the Underlying Contract
|
|
9
|
+
:delta, # double: The underlying stock or future delta.
|
|
10
|
+
:price # double: The price of the underlying.
|
|
11
|
+
|
|
12
|
+
validates_numericality_of :con_id, :delta, :price #, :allow_nil => true
|
|
13
|
+
|
|
14
|
+
def default_attributes
|
|
15
|
+
super.merge :con_id => 0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Serialize under_comp parameters
|
|
19
|
+
def serialize
|
|
20
|
+
[con_id, delta, price]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Comparison
|
|
24
|
+
def == other
|
|
25
|
+
super(other) ||
|
|
26
|
+
other.is_a?(self.class) &&
|
|
27
|
+
con_id == other.con_id && delta == other.delta && price == other.price
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end # class Underlying
|
|
31
|
+
UnderComp = Underlying
|
|
32
|
+
end
|