Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

itch: Implement BATS TOP 1.2.0

Signed-off-by: Jussi Virtanen <jussi.virtanen@valotrading.com>
  • Loading branch information...
commit 78d0172609b19980a4491d0cdd0a86a50cff014d 1 parent 70ffd5e
@jvirtanen jvirtanen authored
View
1  README.md
@@ -17,6 +17,7 @@ Features
- UBS
- ITCH with the following profiles:
- BATS TCP PITCH 1.12.0
+ - BATS TOP 1.2.0
- NASDAQ OMX Nordic Equity TotalView-ITCH 1.86
- NASDAQ TotalView-ITCH 4.1
- MB Trading Quote API
View
2  doc/specifications.md
@@ -2,6 +2,7 @@ Specifications
==============
- [BATS TCP PITCH 1.12.0][PITCH 1.12.0]
+ - [BATS TOP 1.2.0][TOP 1.2.0]
- [FIX](http://fixprotocol.org/)
- [MB Trading Quote API](http://www.mbtrading.com/developersMain.aspx?page=api)
- [NASDAQ OMX Nordic Equity TotalView-ITCH 1.86][ITCH 1.86]
@@ -13,6 +14,7 @@ Specifications
[ITCH 1.86]: http://nordic.nasdaqomxtrader.com/digitalAssets/72/72740_nordic_equity_totalview-itch_1.86.pdf
[ITCH 4.1]: http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/tvitch-v4.pdf
[PITCH 1.12.0]: http://cdn.batstrading.com/resources/membership/BATS_PITCH_Specification.pdf
+[TOP 1.2.0]: http://cdn.batstrading.com/resources/membership/BATS_TOP_Specification.pdf
[BinaryFILE 1.00]: http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/binaryfile.pdf
[SoupFILE 1.00]: http://www.nasdaqtrader.com/content/technicalSupport/specifications/dataproducts/soupfile100.pdf
[SoupTCP 2.00]: http://www.nasdaqtrader.com/content/technicalsupport/specifications/dataproducts/souptcp.pdf
View
39 src/main/scala/stirling/itch/bats/top120/Constants.scala
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package stirling.itch.bats.top120
+
+/*
+ * Section 4.3
+ */
+object RejectReason {
+ val AuthenticationOrAuthorizationProblem = 'A'.toByte
+}
+
+/*
+ * Section 5.1.2
+ */
+object HaltStatus {
+ val Halted = 'H'.toByte
+ val Trading = 'T'.toByte
+}
+
+/*
+ * Section 5.1.2
+ */
+object RegSHOAction {
+ val NoPriceTestInEffect = '0'.toByte
+ val RegSHOPriceTestRestrictionInEffect = '1'.toByte
+}
View
65 src/main/scala/stirling/itch/bats/top120/MessageParser.scala
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package stirling.itch.bats.top120
+
+import java.nio.{BufferUnderflowException, ByteBuffer, ByteOrder}
+import scala.annotation.switch
+import silvertip.{GarbledMessageException, PartialMessageException}
+import stirling.itch.ByteBuffers
+
+class MessageParser extends silvertip.MessageParser[Message] {
+ def parse(buffer: ByteBuffer) = {
+ try {
+ parseMessage(buffer)
+ } catch {
+ case _: BufferUnderflowException => throw new PartialMessageException
+ }
+ }
+
+ protected def parseMessage(buffer: ByteBuffer) = {
+ if (buffer.limit == buffer.position)
+ throw new PartialMessageException
+
+ val msgType = messageType(buffer.get(buffer.position))
+ val msg = msgType.apply(ByteBuffers.slice(buffer, buffer.position, msgType.size))
+
+ msg
+ }
+
+ protected def messageType(messageType: Byte) = (messageType: @switch) match {
+ case 'C' => LogonAccepted
+ case 'J' => LogonRejected
+ case 's' => ExpandedSpin
+ case 'D' => SpinDone
+ case 'H' => ServerHeartbeat
+ case 'T' => Seconds
+ case 'M' => Milliseconds
+ case 'E' => ExpandedBidUpdate
+ case 'B' => LongBidUpdate
+ case 'b' => ShortBidUpdate
+ case 'e' => ExpandedAskUpdate
+ case 'A' => LongAskUpdate
+ case 'a' => ShortAskUpdate
+ case 'F' => ExpandedTwoSidedUpdate
+ case 'U' => LongTwoSidedUpdate
+ case 'u' => ShortTwoSidedUpdate
+ case 'f' => ExpandedTrade
+ case 'V' => LongTrade
+ case 'v' => ShortTrade
+ case 't' => TradingStatus
+ case x => throw new GarbledMessageException("Unknown message type: " + x)
+ }
+}
View
336 src/main/scala/stirling/itch/bats/top120/Messages.scala
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package stirling.itch.bats.top120
+
+import stirling.itch.ByteString
+
+sealed trait Message {
+ def payload: ByteString
+
+ def messageType = payload.byteAt(0)
+
+ override def toString = payload.toString
+}
+
+sealed trait MessageType {
+ def apply(payload: ByteString): Message
+
+ def size: Int
+}
+
+/*
+ * Section 4.2
+ */
+class LogonAccepted(val payload: ByteString) extends Message
+
+object LogonAccepted extends MessageType {
+ def apply(payload: ByteString) = new LogonAccepted(payload)
+
+ val size = 2
+}
+
+/*
+ * Section 4.3
+ */
+class LogonRejected(val payload: ByteString) extends Message {
+ def rejectReason: Byte = payload.byteAt(1)
+}
+
+object LogonRejected extends MessageType {
+ def apply(payload: ByteString) = new LogonRejected(payload)
+
+ val size = 3
+}
+
+/*
+ * Section 5.1.2
+ */
+class ExpandedSpin(val payload: ByteString) extends Message {
+ def timestamp: Long = payload.slice(1, 8).toLong
+ def symbol: ByteString = payload.slice(9, 8)
+ def bidPrice: Long = payload.slice(17, 10).toLong
+ def bidQuantity: Long = payload.slice(27, 6).toLong
+ def askPrice: Long = payload.slice(33, 10).toLong
+ def askQuantity: Long = payload.slice(43, 6).toLong
+ def lastTradeTime: Long = payload.slice(49, 8).toLong
+ def lastTradePrice: Long = payload.slice(57, 10).toLong
+ def lastTradeSize: Long = payload.slice(67, 6).toLong
+ def cumulativeVolume: Long = payload.slice(73, 9).toLong
+ def haltStatus: Byte = payload.byteAt(82)
+ def regShoAction: Byte = payload.byteAt(83)
+ def reserved1: Byte = payload.byteAt(84)
+ def reserved2: Byte = payload.byteAt(85)
+}
+
+object ExpandedSpin extends MessageType {
+ def apply(payload: ByteString) = new ExpandedSpin(payload)
+
+ val size = 87
+}
+
+/*
+ * Section 5.2
+ */
+class SpinDone(val payload: ByteString) extends Message
+
+object SpinDone extends MessageType {
+ def apply(payload: ByteString) = new SpinDone(payload)
+
+ val size = 2
+}
+
+/*
+ * Section 6.1
+ */
+class ServerHeartbeat(val payload: ByteString) extends Message
+
+object ServerHeartbeat extends MessageType {
+ def apply(payload: ByteString) = new ServerHeartbeat(payload)
+
+ val size = 2
+}
+
+/*
+ * Section 7.1
+ */
+class Seconds(val payload: ByteString) extends Message {
+ def seconds: Long = payload.slice(1, 5).toLong
+}
+
+object Seconds extends MessageType {
+ def apply(payload: ByteString) = new Seconds(payload)
+
+ val size = 7
+}
+
+/*
+ * Section 7.2
+ */
+class Milliseconds(val payload: ByteString) extends Message {
+ def milliseconds: Long = payload.slice(1, 3).toLong
+}
+
+object Milliseconds extends MessageType {
+ def apply(payload: ByteString) = new Milliseconds(payload)
+
+ val size = 5
+}
+
+/*
+ * Section 8.1.1
+ */
+class ExpandedBidUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 8)
+ def bidPrice: Long = payload.slice(9, 10).toLong
+ def bidQuantity: Long = payload.slice(19, 6).toLong
+}
+
+object ExpandedBidUpdate extends MessageType {
+ def apply(payload: ByteString) = new ExpandedBidUpdate(payload)
+
+ val size = 26
+}
+
+/*
+ * Section 8.1.2
+ */
+class LongBidUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 6)
+ def bidPrice: Long = payload.slice(7, 10).toLong
+ def bidQuantity: Long = payload.slice(17, 6).toLong
+}
+
+object LongBidUpdate extends MessageType {
+ def apply(payload: ByteString) = new LongBidUpdate(payload)
+
+ val size = 24
+}
+
+/*
+ * Section 8.1.3
+ */
+class ShortBidUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 4)
+ def bidPrice: Long = payload.slice(5, 5).toLong
+ def bidQuantity: Long = payload.slice(10, 5).toLong
+}
+
+object ShortBidUpdate extends MessageType {
+ def apply(payload: ByteString) = new ShortBidUpdate(payload)
+
+ val size = 16
+}
+
+/*
+ * Section 8.1.4
+ */
+class ExpandedAskUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 8)
+ def askPrice: Long = payload.slice(9, 10).toLong
+ def askQuantity: Long = payload.slice(19, 6).toLong
+}
+
+object ExpandedAskUpdate extends MessageType {
+ def apply(payload: ByteString) = new ExpandedAskUpdate(payload)
+
+ val size = 26
+}
+
+/*
+ * Section 8.1.5
+ */
+class LongAskUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 6)
+ def askPrice: Long = payload.slice(7, 10).toLong
+ def askQuantity: Long = payload.slice(17, 6).toLong
+}
+
+object LongAskUpdate extends MessageType {
+ def apply(payload: ByteString) = new LongAskUpdate(payload)
+
+ val size = 24
+}
+
+/*
+ * Section 8.1.6
+ */
+class ShortAskUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 4)
+ def askPrice: Long = payload.slice(5, 5).toLong
+ def askQuantity: Long = payload.slice(10, 5).toLong
+}
+
+object ShortAskUpdate extends MessageType {
+ def apply(payload: ByteString) = new ShortAskUpdate(payload)
+
+ val size = 16
+}
+
+/*
+ * Section 8.2.1
+ */
+class ExpandedTwoSidedUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 8)
+ def bidPrice: Long = payload.slice(9, 10).toLong
+ def bidQuantity: Long = payload.slice(19, 6).toLong
+ def askPrice: Long = payload.slice(25, 10).toLong
+ def askQuantity: Long = payload.slice(35, 6).toLong
+}
+
+object ExpandedTwoSidedUpdate extends MessageType {
+ def apply(payload: ByteString) = new ExpandedTwoSidedUpdate(payload)
+
+ val size = 42
+}
+
+/*
+ * Section 8.2.2
+ */
+class LongTwoSidedUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 6)
+ def bidPrice: Long = payload.slice(7, 10).toLong
+ def bidQuantity: Long = payload.slice(17, 6).toLong
+ def askPrice: Long = payload.slice(23, 10).toLong
+ def askQuantity: Long = payload.slice(33, 6).toLong
+}
+
+object LongTwoSidedUpdate extends MessageType {
+ def apply(payload: ByteString) = new LongTwoSidedUpdate(payload)
+
+ val size = 40
+}
+
+/*
+ * Section 8.2.3
+ */
+class ShortTwoSidedUpdate(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 4)
+ def bidPrice: Long = payload.slice(5, 5).toLong
+ def bidQuantity: Long = payload.slice(10, 5).toLong
+ def askPrice: Long = payload.slice(15, 5).toLong
+ def askQuantity: Long = payload.slice(20, 5).toLong
+}
+
+object ShortTwoSidedUpdate extends MessageType {
+ def apply(payload: ByteString) = new ShortTwoSidedUpdate(payload)
+
+ val size = 26
+}
+
+/*
+ * Section 9.1
+ */
+class ExpandedTrade(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 8)
+ def lastPrice: Long = payload.slice(9, 10).toLong
+ def lastQuantity: Long = payload.slice(19, 6).toLong
+ def cumulativeVolume: Long = payload.slice(25, 9).toLong
+}
+
+object ExpandedTrade extends MessageType {
+ def apply(payload: ByteString) = new ExpandedTrade(payload)
+
+ val size = 35
+}
+
+/*
+ * Section 9.2
+ */
+class LongTrade(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 6)
+ def lastPrice: Long = payload.slice(7, 10).toLong
+ def lastQuantity: Long = payload.slice(17, 6).toLong
+ def cumulativeVolume: Long = payload.slice(23, 9).toLong
+}
+
+object LongTrade extends MessageType {
+ def apply(payload: ByteString) = new LongTrade(payload)
+
+ val size = 33
+}
+
+/*
+ * Section 9.3
+ */
+class ShortTrade(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 4)
+ def lastPrice: Long = payload.slice(5, 5).toLong
+ def lastQuantity: Long = payload.slice(10, 5).toLong
+ def cumulativeVolume: Long = payload.slice(15, 7).toLong
+}
+
+object ShortTrade extends MessageType {
+ def apply(payload: ByteString) = new ShortTrade(payload)
+
+ val size = 23
+}
+
+/*
+ * Section 10.1
+ */
+class TradingStatus(val payload: ByteString) extends Message {
+ def symbol: ByteString = payload.slice(1, 8)
+ def haltStatus: Byte = payload.byteAt(9)
+ def regShoAction: Byte = payload.byteAt(10)
+ def reserved1: Byte = payload.byteAt(11)
+ def reserved2: Byte = payload.byteAt(12)
+}
+
+object TradingStatus extends MessageType {
+ def apply(payload: ByteString) = new TradingStatus(payload)
+
+ val size = 14
+}
View
20 src/test/resources/top-v120.txt
@@ -0,0 +1,20 @@
+C
+JA
+s34348112TESTA 00001234000002000000123500001000343470000000123400000100000120100T0??
+D
+H
+T34348
+M110
+EZVZZT 0000123400001100
+BZVZZT 0000123400001100
+bRIMM1312200100
+eQQQQ 0000123400001100
+AQQQQ 0000123400001100
+aQID 0394500200
+FQQQQ 00004870002402000000487100000200
+UQQQQ 00004870002402000000487100000200
+uQID 03944120000394500300
+fSPY 0001379800000100024250601
+VSPY 0001379800000100024250601
+vRIMM13122003001200400
+tQQQQ H1??
View
36 src/test/scala/stirling/itch/bats/top120/MessageParserSpec.scala
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package stirling.itch.bats.top120
+
+import stirling.itch.Spec
+import stirling.itch.io.Source
+
+class MessageParserSpec extends Spec {
+ "MessageParser" must {
+ "parse messages with read buffer overflow inside message" in {
+ val messageTypes = "CJsDHTMEBbeAaFUufVvt"
+ source(128).map(_.messageType.toChar).mkString must equal(messageTypes)
+ }
+ }
+
+ private def source(readBufferSize: Int): Source[Message] = {
+ Source.fromInputStream[Message](
+ stream = getClass.getResourceAsStream("/top-v120.txt"),
+ parser = new MessageParser,
+ readBufferSize = readBufferSize
+ )
+ }
+}
View
168 src/test/scala/stirling/itch/bats/top120/MessageSpec.scala
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package stirling.itch.bats.top120
+
+import stirling.itch.{ByteString, Spec}
+
+class MessageSpec extends Spec {
+ "Message" should {
+ "decode LogonAccepted" in {
+ val message = LogonAccepted("C\n")
+ message.messageType must equal('C')
+ }
+ "decode LogonRejected" in {
+ val message = LogonRejected("JA\n")
+ message.messageType must equal('J')
+ message.rejectReason must equal(RejectReason.AuthenticationOrAuthorizationProblem)
+ }
+ "decode ExpandedSpin" in {
+ val message = ExpandedSpin("s34348112TESTA 00001234000002000000123500001000343470000000123400000100000120100T0??\n")
+ message.messageType must equal('s')
+ message.timestamp must equal(34348112)
+ message.symbol.toString must equal("TESTA ")
+ message.bidPrice must equal(123400)
+ message.bidQuantity must equal(200)
+ message.askPrice must equal(123500)
+ message.askQuantity must equal(1000)
+ message.lastTradeTime must equal(34347000)
+ message.lastTradePrice must equal(123400)
+ message.lastTradeSize must equal(100)
+ message.cumulativeVolume must equal(120100)
+ message.haltStatus must equal(HaltStatus.Trading)
+ message.regShoAction must equal(RegSHOAction.NoPriceTestInEffect)
+ }
+ "decode SpinDone" in {
+ val message = SpinDone("D\n")
+ message.messageType must equal('D')
+ }
+ "decode ServerHeartbeat" in {
+ val message = ServerHeartbeat("H\n")
+ message.messageType must equal('H')
+ }
+ "decode Seconds" in {
+ val message = Seconds("T34348\n")
+ message.messageType must equal('T')
+ message.seconds must equal(34348)
+ }
+ "decode Milliseconds" in {
+ val message = Milliseconds("M110\n")
+ message.messageType must equal('M')
+ message.milliseconds must equal(110)
+ }
+ "decode ExpandedBidUpdate" in {
+ val message = ExpandedBidUpdate("EZVZZT 0000123400001100\n")
+ message.messageType must equal('E')
+ message.symbol.toString must equal("ZVZZT ")
+ message.bidPrice must equal(123400)
+ message.bidQuantity must equal(1100)
+ }
+ "decode LongBidUpdate" in {
+ val message = LongBidUpdate("BZVZZT 0000123400001100\n")
+ message.messageType must equal('B')
+ message.symbol.toString must equal("ZVZZT ")
+ message.bidPrice must equal(123400)
+ message.bidQuantity must equal(1100)
+ }
+ "decode ShortBidUpdate" in {
+ val message = ShortBidUpdate("bRIMM1312200100\n")
+ message.messageType must equal('b')
+ message.symbol.toString must equal("RIMM")
+ message.bidPrice must equal(13122)
+ message.bidQuantity must equal(100)
+ }
+ "decode ExpandedAskUpdate" in {
+ val message = ExpandedAskUpdate("eQQQQ 0000123400001100\n")
+ message.messageType must equal('e')
+ message.symbol.toString must equal("QQQQ ")
+ message.askPrice must equal(123400)
+ message.askQuantity must equal(1100)
+ }
+ "decode LongAskUpdate" in {
+ val message = LongAskUpdate("AQQQQ 0000123400001100\n")
+ message.messageType must equal('A')
+ message.symbol.toString must equal("QQQQ ")
+ message.askPrice must equal(123400)
+ message.askQuantity must equal(1100)
+ }
+ "decode ShortAskUpdate" in {
+ val message = ShortAskUpdate("aQID 0394500200\n")
+ message.messageType must equal('a')
+ message.symbol.toString must equal("QID ")
+ message.askPrice must equal(3945)
+ message.askQuantity must equal(200)
+ }
+ "decode ExpandedTwoSidedUpdate" in {
+ val message = ExpandedTwoSidedUpdate("FQQQQ 00004870002402000000487100000200\n")
+ message.messageType must equal('F')
+ message.symbol.toString must equal("QQQQ ")
+ message.bidPrice must equal(487000)
+ message.bidQuantity must equal(240200)
+ message.askPrice must equal(487100)
+ message.askQuantity must equal(200)
+ }
+ "decode LongTwoSidedUpdate" in {
+ val message = LongTwoSidedUpdate("UQQQQ 00004870002402000000487100000200\n")
+ message.messageType must equal('U')
+ message.symbol.toString must equal("QQQQ ")
+ message.bidPrice must equal(487000)
+ message.bidQuantity must equal(240200)
+ message.askPrice must equal(487100)
+ message.askQuantity must equal(200)
+ }
+ "decode ShortTwoSidedUpdate" in {
+ val message = ShortTwoSidedUpdate("uQID 03944120000394500300\n")
+ message.messageType must equal('u')
+ message.symbol.toString must equal("QID ")
+ message.bidPrice must equal(3944)
+ message.bidQuantity must equal(12000)
+ message.askPrice must equal(3945)
+ message.askQuantity must equal(300)
+ }
+ "decode ExpandedTrade" in {
+ val message = ExpandedTrade("fSPY 0001379800000100024250601\n")
+ message.messageType must equal('f')
+ message.symbol.toString must equal("SPY ")
+ message.lastPrice must equal(1379800)
+ message.lastQuantity must equal(100)
+ message.cumulativeVolume must equal(24250601)
+ }
+ "decode LongTrade" in {
+ val message = LongTrade("VSPY 0001379800000100024250601\n")
+ message.messageType must equal('V')
+ message.symbol.toString must equal("SPY ")
+ message.lastPrice must equal(1379800)
+ message.lastQuantity must equal(100)
+ message.cumulativeVolume must equal(24250601)
+ }
+ "decode ShortTrade" in {
+ val message = ShortTrade("vRIMM13122003001200400\n")
+ message.messageType must equal('v')
+ message.symbol.toString must equal("RIMM")
+ message.lastPrice must equal(13122)
+ message.lastQuantity must equal(300)
+ message.cumulativeVolume must equal(1200400)
+ }
+ "decode TradingStatus" in {
+ val message = TradingStatus("tQQQQ H1??\n")
+ message.messageType must equal('t')
+ message.symbol.toString must equal("QQQQ ")
+ message.haltStatus must equal(HaltStatus.Halted)
+ message.regShoAction must equal(RegSHOAction.RegSHOPriceTestRestrictionInEffect)
+ }
+ }
+
+ implicit def stringToByteString(string: String): ByteString = new ByteString(string.getBytes)
+}
Please sign in to comment.
Something went wrong with that request. Please try again.