-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathib.kt
388 lines (323 loc) · 13.8 KB
/
ib.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
package ib
import bon.*
import bon.http.*
// API to IB through TWS.
//
// For most REST API calls - parameters could be specified in query or JSON body.
//
// There's no way to distinguish stock from ETF contract, so "stock" type used for both.
// That's because IB uses "STK" type for both.
//
// For configs see `Config`, many config options could be overriden as shell variables.
//
abstract class IB {
// inspect ---------------------------------------------------------------------------------------
abstract fun inspect_stock(
symbol: String // MSFT
): StockInspection
private fun expose_inspect_stock(server: Server) =
server.get("/api/v1/inspect_stock") { request ->
this.inspect_stock(
request.get_string("symbol")
)
}
data class StockInspection(
val stocks: Map<Int, GroupedStocks> // id -> GroupedStocks
)
data class GroupedStocks(
val currency: String,
val names: List<String>,
val primary_exchange: String,
val exchanges: List<String>,
// exchange -> price
val stock_prices: Dict<String, ErrorneousS<SnapshotPrice>>,
val largest_option_chains_desc: Dict<String, OptionChain>,
val option_chain_contracts_count: Dict<String, ErrorneousS<Int>>,
val option_chain_prices: Dict<String, StockOptionPricesInfo>
)
data class StockOptionPricesInfo(
val total_count: Int,
val success_count: Int,
val errors_count: Dict<String, Int>,
val price_types: Dict<String, Int>
)
// get_portfolio ---------------------------------------------------------------------------------
abstract fun get_portfolio(): List<Portfolio>
private fun expose_get_portfolio(server: Server) =
server.get("/api/v1/portfolio") { request ->
this.get_portfolio()
}
data class Portfolio(
val account_id: String,
val stocks: List<PortfolioPosition<PortfolioStockContract>>,
val stock_options: List<PortfolioPosition<PortfolioOptionContract>>,
val cash_in_usd: Double
)
data class PortfolioPosition<Contract> (
val position: Int,
val average_cost: Double, // IB somehow approximately calculates it, could be
// quite different from the actual average cost.
val contract: Contract
)
data class PortfolioStockContract (
val symbol: String,
val exchange: String?, // IB dosn't always provide it
val currency: String,
val id: Int // IB id for contract
)
data class PortfolioOptionContract(
val symbol: String,
val right: String, // "put" or "call"'
val expiration: String, // 2020-08-21
val strike: Double, // 120
val exchange: String?, // IB dosn't always provide it
val currency: String,
val id: Int, // IB id for contract
val multiplier: Int // Usually 100
)
// get_stock_contract ----------------------------------------------------------------------------
abstract fun get_stock_contract(
symbol: String, // MSFT
exchange: String, // SMART
currency: String // USD
): StockContract
private fun expose_get_stock_contract(server: Server) =
server.get("/api/v1/stock_contract") { request ->
this.get_stock_contract(
request.get_string("symbol"),
request.get_string("exchange"),
request.get_string("currency")
)
}
data class StockContract(
val symbol: String,
val name: String,
val exchange: String,
val primary_exchange: String,
val currency: String,
val id: Int
) {
val type = ContractType.stock
}
// get_stock_contracts ---------------------------------------------------------------------------
// Get all stock contracts on all exchanges
abstract fun get_stock_contracts(
symbol: String // MSFT
): List<StockContract>
private fun expose_get_stock_contracts(server: Server) =
server.get("/api/v1/stock_contracts") { request ->
this.get_stock_contracts(
request.get_string("symbol")
)
}
// get_stock_price -------------------------------------------------------------------------------
abstract fun get_stock_price(
symbol: String, // MSFT
exchange: String, // SMART
currency: String, // USD
data_type: MarketDataType? // optional, realtime by default
): SnapshotPrice
private fun expose_get_stock_price(server: Server) =
server.get("/api/v1/stock_price") { request ->
this.get_stock_price(
request.get_string("symbol"),
request.get_string("exchange"),
request.get_string("currency"),
request.get_data_type_optional("data_type")
)
}
data class SnapshotPrice(
val last_price: Double?, // Some prices could be unavailable
val close_price: Double?,
val ask_price: Double?,
val bid_price: Double?,
val approximate_price: Double, // Always available, see `approximate_price` function.
val data_type: MarketDataType // IB code for market data type, realtime, delayed etc.
)
// Not all prices always available in TWS API, calculating the best guess for the price
fun approximate_price(
last_price: Double?, close_price: Double?, ask_price: Double?, bid_price: Double?
): Double? = when {
bid_price != null && ask_price != null -> (ask_price + bid_price) / 2
last_price != null -> last_price
close_price != null -> close_price
else -> null
}
// get_stock_price -------------------------------------------------------------------------------
abstract fun get_stock_price_by_id(
id: Int, // IB contract id
exchange: String, // SMART, exchange used just to be sure the proper contract will be found
currency: String, // USD, currency used just to be sure the proper contract will be found
data_type: MarketDataType? // optional, realtime by default
): SnapshotPrice
private fun expose_get_stock_price_by_id(server: Server) =
server.get("/api/v1/stock_price_by_id") { request ->
this.get_stock_price_by_id(
request.get_integer("id"),
request.get_string("exchange"),
request.get_string("currency"),
request.get_data_type_optional("data_type")
)
}
// get_stock_option_chains -----------------------------------------------------------------------
abstract fun get_stock_option_contract(
symbol: String, // MSFT
currency: String, // USD
option_exchange: String, // SMART
right: String, // call
expiration: String, // 2020-01-01
strike: Double // 300.0
): OptionContract
private fun expose_get_stock_option_contract(server: Server) =
server.get("/api/v1/stock_option_contract") { request ->
this.get_stock_option_contract(
request.get_string("symbol"),
request.get_string("currency"),
request.get_string("option_exchange"),
request.get_string("right"),
request.get_string("expiration"),
request.get_double("strike")
)
}
// get_stock_option_chains -----------------------------------------------------------------------
abstract fun get_stock_option_chains(
symbol: String, // IB contract id
exchange: String, // SMART
currency: String // USD
): OptionChains
private fun expose_get_stock_option_chains(server: Server) =
server.get("/api/v1/stock_option_chains") { request ->
this.get_stock_option_chains(
request.get_string("symbol"),
request.get_string("exchange"),
request.get_string("currency")
)
}
data class OptionChains(
val largest_desc: List<OptionChain>, // Some exchanges contain multiple option chains, in such case for
// every exchange the largest chain is selected.
// Largest by size of expirations and strikes.
// Example AIR:DTB:EUR
val all: List<OptionChain> // Full list of chains.
)
data class OptionChain(
val option_exchange: String, // Could be different from stock exchange
val expirations_asc: List<String>, // Sorted
val strikes_asc: List<Double>, // Sorted
val multiplier: Int // Usually 100
)
// get_stock_option_chain --------------------------------------------------------------------
abstract fun get_stock_option_chain_contracts(
symbol: String, // MSFT
option_exchange: String, // AMEX, different from the stock exchange
currency: String // USD
): OptionContracts
private fun expose_stock_option_chain_contracts(server: Server) =
server.get("/api/v1/stock_option_chain_contracts") { request ->
this.get_stock_option_chain_contracts(
request.get_string("symbol"),
request.get_string("option_exchange"),
request.get_string("currency")
)
}
data class OptionContracts(
val multiplier: Int, // Usually 100
val contracts_asc_by_right_expiration_strike: List<OptionContract>
)
data class OptionContract(
val id: Int,
val expiration: String, // 2020-08-21
val strike: Double, // 120
val right: String // "put" or "call"
)
// get_stock_option_chain --------------------------------------------------------------------
abstract fun get_stock_option_chain_contracts_by_expiration(
symbol: String, // MSFT
expiration: String, // 2020-01-01
option_exchange: String, // AMEX, different from the stock exchange
currency: String // USD
): List<OptionContract>
private fun expose_get_stock_option_chain_contracts_by_expiration(server: Server) =
server.get("/api/v1/stock_option_chain_contracts_by_expiration") { request ->
this.get_stock_option_chain_contracts_by_expiration(
request.get_string("symbol"),
request.get_string("expiration"),
request.get_string("option_exchange"),
request.get_string("currency")
)
}
// get_stock_option_price ------------------------------------------------------------------------
abstract fun get_stock_option_price(
symbol: String, // MSFT
right: String, // "put" or "call"'
expiration: String, // 2020-08-21
strike: Double, // 120
option_exchange: String, // AMEX, different from the stock exchange
currency: String, // USD
data_type: MarketDataType? // optional, realtime by default
): SnapshotPrice
private fun expose_get_stock_option_price(server: Server) =
server.get("/api/v1/stock_option_price") { request ->
this.get_stock_option_price(
request.get_string("symbol"),
request.get_string("right"),
request.get_string("expiration"),
request.get_double("strike"),
request.get_string("option_exchange"),
request.get_string("currency"),
request.get_data_type_optional("data_type")
)
}
// get_stock_option_price_by_id ------------------------------------------------------------
// When searching by id - the currency is ignored by IB
abstract fun get_stock_option_price_by_id(
id: Int, // Contract id 426933553
option_exchange: String, // AMEX, different from the stock exchange
currency: String, // USD
data_type: MarketDataType? // optional, realtime by default
): SnapshotPrice
private fun expose_get_stock_option_price_by_id(server: Server) =
server.get("/api/v1/stock_option_price_by_id") { request ->
this.get_stock_option_price_by_id(
request.get_integer("id"),
request.get_string("option_exchange"),
request.get_string("currency"),
request.get_data_type_optional("data_type")
)
}
// Minor types -----------------------------------------------------------------------------------
enum class ContractType { stock, option }
enum class MarketDataType(val code: Int) {
realtime (com.ib.client.MarketDataType.REALTIME),
frozen (com.ib.client.MarketDataType.FROZEN),
delayed (com.ib.client.MarketDataType.DELAYED),
delayed_frozen (com.ib.client.MarketDataType.DELAYED_FROZEN);
companion object {
fun value_of(code: Int): MarketDataType =
values().find { it.code == code } ?: throw Exception("Unknown MarketDataType '$code'")
fun value_of(name: String): MarketDataType =
values().find { it.to_string() == name } ?: throw Exception("Unknown MarketDataType '$name'")
}
}
// REST API --------------------------------------------------------------------------------------
fun expose_on(server: Server) {
expose_inspect_stock(server)
expose_get_portfolio(server)
expose_get_stock_contract(server)
expose_get_stock_contracts(server)
expose_get_stock_price(server)
expose_get_stock_price_by_id(server)
expose_get_stock_option_contract(server)
expose_get_stock_option_chains(server)
expose_stock_option_chain_contracts(server)
expose_get_stock_option_chain_contracts_by_expiration(server)
expose_get_stock_option_price(server)
expose_get_stock_option_price_by_id(server)
server.add_call_alias("/api/v1/call")
}
}
// Helpers -----------------------------------------------------------------------------------------
fun Request.get_data_type(key: String) = IB.MarketDataType.value_of(this.get_string(key))
fun Request.get_data_type_optional(key: String) = this.get_string_optional(key) {
IB.MarketDataType.value_of(it)
}