@@ -23,13 +23,8 @@ import okhttp3.OkHttpClient
23
23
import okhttp3.Request
24
24
import okhttp3.WebSocket
25
25
import okhttp3.WebSocketListener
26
- import org.roboquant.common.Asset
27
- import org.roboquant.common.AssetType
28
- import org.roboquant.common.Logging
29
- import org.roboquant.feeds.Event
30
- import org.roboquant.feeds.LiveFeed
31
- import org.roboquant.feeds.PriceAction
32
- import org.roboquant.feeds.PriceQuote
26
+ import org.roboquant.common.*
27
+ import org.roboquant.feeds.*
33
28
import java.time.Instant
34
29
import java.util.concurrent.TimeUnit
35
30
import kotlin.collections.set
@@ -39,24 +34,26 @@ private val logger = Logging.getLogger(TiingoLiveFeed::class)
39
34
40
35
private class Handler (private val feed : TiingoLiveFeed ) : WebSocketListener() {
41
36
42
- val assets = mutableMapOf<String , Asset >()
43
-
44
37
private fun handleIEX (data : JsonArray ) {
45
38
val type = data[0 ].asString
39
+ val symbol = data[3 ].asString.uppercase()
40
+ val asset = Config .getOrPutAsset(symbol) { Asset .forexPair(symbol) }
41
+ val time = Instant .ofEpochMilli(0 ).plusNanos(data[2 ].asLong)
42
+
46
43
if (type == " Q" ) {
47
- val symbol = data[3 ].asString.uppercase()
48
- val asset = assets.getOrPut(symbol) { Asset (symbol) }
49
44
val quote = PriceQuote (asset, data[7 ].asDouble, data[8 ].asDouble, data[5 ].asDouble, data[4 ].asDouble)
50
- val time = Instant .ofEpochMilli(0 ).plusNanos(data[2 ].asLong)
51
45
feed.deliver(quote, time)
46
+ } else if (type == " T" ) {
47
+ val trade = TradePrice (asset, data[9 ].asDouble, data[10 ].asDouble)
48
+ feed.deliver(trade, time)
52
49
}
53
50
}
54
51
55
52
private fun handleFX (data : JsonArray ) {
56
53
val type = data[0 ].asString
57
54
if (type == " Q" ) {
58
55
val symbol = data[1 ].asString.uppercase()
59
- val asset = assets.getOrPut (symbol) { Asset .forexPair(symbol) }
56
+ val asset = Config .getOrPutAsset (symbol) { Asset .forexPair(symbol) }
60
57
val quote = PriceQuote (asset, data[7 ].asDouble, data[6 ].asDouble, data[4 ].asDouble, data[3 ].asDouble)
61
58
val time = Instant .parse(data[2 ].asString)
62
59
feed.deliver(quote, time)
@@ -65,12 +62,15 @@ private class Handler(private val feed: TiingoLiveFeed) : WebSocketListener() {
65
62
66
63
private fun handleCrypto (data : JsonArray ) {
67
64
val type = data[0 ].asString
65
+ val symbol = data[1 ].asString.uppercase()
66
+ val asset = Config .getOrPutAsset(symbol) { Asset (symbol, AssetType .CRYPTO , exchange = Exchange .CRYPTO ) }
67
+ val time = Instant .parse(data[2 ].asString)
68
68
if (type == " Q" ) {
69
- val symbol = data[1 ].asString.uppercase()
70
- val asset = assets.getOrPut(symbol) { Asset (symbol, AssetType .CRYPTO ) }
71
69
val quote = PriceQuote (asset, data[8 ].asDouble, data[7 ].asDouble, data[5 ].asDouble, data[4 ].asDouble)
72
- val time = Instant .parse(data[2 ].asString)
73
70
feed.deliver(quote, time)
71
+ } else if (type == " T" ) {
72
+ val trade = TradePrice (asset, data[5 ].asDouble, data[4 ].asDouble)
73
+ feed.deliver(trade, time)
74
74
}
75
75
}
76
76
@@ -99,19 +99,21 @@ private class Handler(private val feed: TiingoLiveFeed) : WebSocketListener() {
99
99
}
100
100
101
101
/* *
102
- * Tiingo live feed
102
+ * Retrieve real-time data from Tiingo. It has support for US stocks, Forex and Crypto.
103
103
*
104
- * This feed uses web-sockets for low letency and has nanosecond resolution
104
+ * This feed uses the Tiingo websocket API for low letency and has nanosecond time resolution
105
105
*/
106
106
class TiingoLiveFeed private constructor(
107
107
private val type : String ,
108
- private val thresholdLevel : Int ,
108
+ private val thresholdLevel : Int = 5 ,
109
109
configure : TiingoConfig .() -> Unit = {}
110
110
) : LiveFeed() {
111
111
112
112
private val config = TiingoConfig ()
113
113
114
114
init {
115
+ val types = setOf (" iex" , " crypto" , " fx" )
116
+ require(type in types) { " invalid type $type , allowed types are $types " }
115
117
config.configure()
116
118
require(config.key.isNotBlank()) { " no valid key found" }
117
119
}
@@ -178,13 +180,12 @@ class TiingoLiveFeed private constructor(
178
180
}
179
181
180
182
/* *
181
- * Subscribe to quotes for provided [symbols].
183
+ * Subscribe to real-time trades/ quotes for provided [symbols].
182
184
*
183
- * If no symbols are provided, all available data is subscribed to. That is a lot of data and can impact
184
- * your monthly quato quickly.
185
+ * If no symbols are provided, all available market data is subscribed to. Be aware that this a lot of data.
185
186
*
186
- * For crypto and some forex it is challenging to derive the underlying currency from just the symbol name,
187
- * so it is better to use [subscribeAssets] instead.
187
+ * For crypto and some forex it is challenging to derive the underlying [Asset] from just its symbol name.
188
+ * You can use [Config.registerAsset] to register assets upfront
188
189
*/
189
190
fun subscribe (vararg symbols : String ) {
190
191
@@ -206,22 +207,5 @@ class TiingoLiveFeed private constructor(
206
207
207
208
}
208
209
209
- /* *
210
- * Subscribe to quotes for the provided [assets].
211
- *
212
- * If no symbols are provided, all available data is subscribed to. That is a lot of data and can impact
213
- * your monthly quato quickly.
214
- */
215
- fun subscribeAssets (vararg assets : Asset ) {
216
- val tickers = mutableListOf<String >()
217
- for (asset in assets) {
218
- val tiingoTicker = asset.symbol.replace(" [^a-zA-Z]+" .toRegex(), " " )
219
- this .handler.assets[tiingoTicker] = asset
220
- tickers.add(tiingoTicker)
221
- }
222
-
223
- @Suppress(" SpreadOperator" )
224
- subscribe(* tickers.toTypedArray())
225
- }
226
210
227
211
}
0 commit comments