using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; /* This describes what goes across the wire to the unix side. Other * programs get data from other sources, and possibly even different * application frameworks, but they will all share this. * * I used all 64 bit integers to match the destination assumptions. Realtick * never gives me 64 bit values, so perhaps I could have used less. But this * makes the format somewhat generic and less likely to change in the future. * * Exchange codes are all copied directly from the data provider. * The listener has a CSV file that it uses to translate from those codes * to things that we understand. Exchange codes can only be up to 7 bytes * (plus a terminating null). They will be truncated if they are longer. * * CurrentTime is the time when the event is reported. If the data feed is * running behind, this will help us know. This is espeically helpful when * creating candles. We want to start a new candle at a specific time. If * the datafeed is a little behind, we wait a little to create the candles. * This can be null if we don't know the current time. * * Note that the DateTime format specifies a timezone. We expect that to * be correct. That sounds silly, but some data feeds assume that that * client is in the Eastern time zone. Even when they report in a format * like this, they will report incorrectly if the timezone is different. */ namespace TradeIdeas.MarketDataProxy.DataFormats { public class WireL1 { public static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public static long ToTimeT(DateTime? dt) { // Interesting. Converting the unix epoch to local time does not appear to work. It was off by an // hour. And just subtracting without converting did not work. It appears that the subtraction // operation ignored the timezone (or "kind") of the two parameters. if (dt == null) return 0; // Note: Teletrader gives us time in UTC. The code below should be smart enough to detect that. // As long as Teletrader marks the time as UTC, the conversion below should be a no-op. TimeSpan diff = TimeZoneInfo.ConvertTimeToUtc((DateTime)dt) - UnixEpoch; return (long)Math.Round(diff.TotalSeconds); } private static readonly byte[] EMPTY_EXCHANGE = { 0, 0, 0, 0, 0, 0, 0, 0 }; public static void WriteExchange(BinaryWriter dest, String Exchange) { byte[] asBytes; if (Exchange == null) // We allow this case to make the main program simpler. This has the same result // as setting Exchange to "", but tgus is slightly more effecient. asBytes = EMPTY_EXCHANGE; else { asBytes = Encoding.UTF8.GetBytes(Exchange); int origSize = asBytes.Length; if (origSize > 7) // Truncate origSize = 7; Array.Resize(ref asBytes, 8); for (int i = origSize; i < 8; i++) // Pad with 0s. Always add at least one for the terminating null. asBytes[i] = 0; } dest.Write(asBytes); } public DateTime? CurrentTime; public double BidPrice; public long BidSize; // Shares public String BidExchange; public double AskPrice; public long AskSize; // Shares public String AskExchange; public override string ToString() { StringBuilder WireL1 = new StringBuilder(); WireL1.Append("CurrentTime:"); WireL1.Append(CurrentTime); WireL1.Append("\nBidPrice:"); WireL1.Append(BidPrice); WireL1.Append("\nBidSize:"); WireL1.Append(BidSize); WireL1.Append("\nBidExchange:"); WireL1.Append(BidExchange); WireL1.Append("\nAskPrice:"); WireL1.Append(AskPrice); WireL1.Append("\nAskSize:"); WireL1.Append(AskSize); WireL1.Append("\nAskExchange:"); WireL1.Append(AskExchange); //StringBuilder WireTOS = new StringBuilder(); //WireTOS.Append("CurrentTime:"); //WireTOS.Append(WireTos.CurrentTime); return WireL1.ToString(); } public byte[] Export() { byte[] result = new byte[56]; BinaryWriter stream = new BinaryWriter(new MemoryStream(result)); stream.Write(ToTimeT(CurrentTime)); stream.Write(BidPrice); stream.Write(BidSize); WriteExchange(stream, BidExchange); stream.Write(AskPrice); stream.Write(AskSize); WriteExchange(stream, AskExchange); return result; } // These are the commands to send to the server with data exported from above. public static readonly byte[] DataCommand = Encoding.ASCII.GetBytes("WireL1Data"); public static readonly byte[] InvalidCommand = Encoding.ASCII.GetBytes("WireL1Invalid"); public Dictionary MakeMessage(string symbol) { return new Dictionary { { WireSimple.Symbol, Encoding.ASCII.GetBytes(symbol) }, { WireSimple.Command, DataCommand }, { WireSimple.Body, Export() } }; } public static Dictionary MakeInvalidMessage(string symbol) { return new Dictionary { { WireSimple.Symbol, Encoding.ASCII.GetBytes(symbol) }, { WireSimple.Command, InvalidCommand } }; } } public class WireTos { public DateTime? CurrentTime; public double Price; public DateTime? Time; public long Size; public String Exchange; public double Last; // In some places this is called "Today's Close". public long Volume; public double Open, High, Low; public bool FormT, UpdatesLast, NewPrint; public override string ToString() { StringBuilder WireTOS = new StringBuilder(); WireTOS.Append("CurrentTime:"); WireTOS.Append(CurrentTime); WireTOS.Append("\nPrice:"); WireTOS.Append(Price); WireTOS.Append("\nTime:"); WireTOS.Append(Time); WireTOS.Append("\nSize:"); WireTOS.Append(Size); WireTOS.Append("\nExchange:"); WireTOS.Append(Exchange); WireTOS.Append("\nLast:"); WireTOS.Append(Last); WireTOS.Append("\nOpen:"); WireTOS.Append(Open); WireTOS.Append("\nHigh:"); WireTOS.Append(High); WireTOS.Append("\nLow:"); WireTOS.Append(Low); WireTOS.Append("\nUpdatesLast:"); WireTOS.Append(UpdatesLast); WireTOS.Append("\nFormT:"); WireTOS.Append(FormT); WireTOS.Append("\nNewPrint:"); WireTOS.Append(NewPrint); return WireTOS.ToString(); } public byte[] Export() { byte[] result = new byte[83]; BinaryWriter stream = new BinaryWriter(new MemoryStream(result)); stream.Write(WireL1.ToTimeT(CurrentTime)); stream.Write(Price); stream.Write(WireL1.ToTimeT(Time)); stream.Write(Size); WireL1.WriteExchange(stream, Exchange); stream.Write(Last); stream.Write(Volume); stream.Write(Open); stream.Write(High); stream.Write(Low); stream.Write(FormT); stream.Write(UpdatesLast); stream.Write(NewPrint); return result; } // These are the commands to send to the server with data exported from above. public static readonly byte[] DataCommand = Encoding.ASCII.GetBytes("WireTosData"); public static readonly byte[] InvalidCommand = Encoding.ASCII.GetBytes("WireTosInvalid"); public Dictionary MakeMessage(string symbol) { return new Dictionary { { WireSimple.Symbol, Encoding.ASCII.GetBytes(symbol) }, { WireSimple.Command, DataCommand }, { WireSimple.Body, Export() } }; } public static Dictionary MakeInvalidMessage(string symbol) { return new Dictionary { { WireSimple.Symbol, Encoding.ASCII.GetBytes(symbol) }, { WireSimple.Command, InvalidCommand } }; } } public class WireSimple { // These each use a single int64, rather than a record. public static readonly byte[] ImbalanceCommand = Encoding.ASCII.GetBytes("WireImbalanceData"); public static readonly byte[] PutVolumeCommand = Encoding.ASCII.GetBytes("WirePutVolumeData"); public static readonly byte[] CallVolumeCommand = Encoding.ASCII.GetBytes("WireCallVolumeData"); // These each use a single double, rather than a record. public static readonly byte[] NyseBidCommand = Encoding.ASCII.GetBytes("WireNyseBidData"); public static readonly byte[] NyseAskCommand = Encoding.ASCII.GetBytes("WireNyseAskData"); // These are the key values for proxy messages. public static readonly string Command = "command"; public static readonly string Body = "body"; public static readonly string Symbol = "symbol"; private static Dictionary MakeMessage(string symbol, byte[] command, byte[] body) { return new Dictionary { { Symbol, Encoding.ASCII.GetBytes(symbol) }, { Command, command }, { Body, body } }; } private static Dictionary MakeMessage(string symbol, byte[] command, long data) { byte[] body = new byte[8]; BinaryWriter stream = new BinaryWriter(new MemoryStream(body)); stream.Write(data); return MakeMessage(symbol, command, body); } private static Dictionary MakeMessage(string symbol, byte[] command, double data) { byte[] body = new byte[8]; BinaryWriter stream = new BinaryWriter(new MemoryStream(body)); stream.Write(data); return MakeMessage(symbol, command, body); } // These all use 0 for the value to say invalid. public static Dictionary MakeImbalanceMessage(string symbol, long value) { return MakeMessage(Symbol, ImbalanceCommand, value); } public static Dictionary MakePutVolumeMessage(string symbol, long value) { return MakeMessage(Symbol, PutVolumeCommand, value); } public static Dictionary MakeCallVolumeMessage(string symbol, long value) { return MakeMessage(Symbol, CallVolumeCommand, value); } public static Dictionary MakeNyseBidMessage(string symbol, double value) { return MakeMessage(Symbol, NyseBidCommand, value); } public static Dictionary MakeNyseAskMessage(string symbol, double value) { return MakeMessage(Symbol, NyseAskCommand, value); } } }