using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Linq; using System.Text; using System.IO; using System.Windows.Forms; namespace TradeIdeas.TIProGUI { using StringList = List; //kinda like C++'s "typdef" using ColumnRowKey = KeyValuePair; //kinda like C++'s "typdef" public class StandardCandleData { private List _sCandle = new List(); public struct SingleCandle { // //A candle is encoded as open:high:low:close:volume public double open; public double high; public double low; public double close; public Int64 volume; } public List CandleArray { get { return _sCandle; } } // /// // /// Class Constructor // /// // /// public StandardCandleData(string encoded) { } // /// // /// A candle is encoded as open:high:low:close:volume. All five should be // /// present. (If volume is unknown, set it to 0.) If any of the values // /// are bad, this function will return false. If this returns an error, // /// candle will be in an undefined state. This will check for improperly // /// formatted data. // /// // /// string to be decoded // /// candle item to be inspected // /// public bool Decode(string encoded, ref SingleCandle candle) { int badInt = int.MinValue; // /*double.Epsilon approximates c++ numeric_limits::min(), but orders // of magnitude smaller. See the following article: // http://www.johndcook.com/blog/2010/06/08/c-math-gotchas/ // */ double badDouble = double.Epsilon; string[] currentCandle = encoded.Split(':'); if ((currentCandle.Length) != 5) { return false; } candle.open = MiscSupport.strtodDefault(currentCandle[0], badDouble); candle.high = MiscSupport.strtodDefault(currentCandle[1], badDouble); candle.low = MiscSupport.strtodDefault(currentCandle[2], badDouble); candle.close = MiscSupport.strtodDefault(currentCandle[3], badDouble); candle.volume = MiscSupport.strtollDefault(currentCandle[4], badInt); return (candle.open != badDouble) && (candle.high != badDouble) && (candle.low != badDouble) && (candle.close != badDouble) && (candle.volume != badInt); } // /// // /// A candle array is a list of candles encoded as described above, and // /// sperated by a semicolon. If candle is "defined"(bool above = true) // /// it will be added to the list of candles ("CandleArray") // /// // /// // /// public void Decode(string encoded) { _sCandle.Clear(); if (encoded == "") { return; } string[] allCandles = encoded.Split(';'); foreach (string s in allCandles) { SingleCandle current = new SingleCandle(); // //pass current candle by reference here, to the boolean function Decode if (Decode(s, ref current)) { _sCandle.Add(current); } } return; } } public class VolumeBlockCandleData { private List _vBlocks = new List(); public struct VolumeBlock { // //These items are separated by colons in the string public double high; public double low; public DateTime start; public DateTime end; } public List VolumeBlocks { get { return _vBlocks; } } // /// // /// Class constructor // /// // /// public VolumeBlockCandleData(string encoded) { } private VolumeBlock Decode(string encoded) { string[] pieces = encoded.Split(':'); VolumeBlock result = new VolumeBlock(); switch (pieces.Length) { case 4: // // This is the newer format, created by our C++ servers. result.high = MiscSupport.nfroma(pieces[0]); result.low = MiscSupport.nfroma(pieces[1]); result.start = MiscSupport.importTime(pieces[2]); result.end = MiscSupport.importTime(pieces[3]); break; case 7: // // This is the older format, created by our Delphi severs. The extra data // // was available for some development tools, but not used in any // // production work. result.high = MiscSupport.nfroma(pieces[1]); result.low = MiscSupport.nfroma(pieces[3]); result.start = MiscSupport.importTime(pieces[5]); result.end = MiscSupport.importTime(pieces[6]); break; } // // if (!(result.start && result.end)) if ((result.start == new DateTime() && result.end == new DateTime())) { // //invalid times... throw new Exception("Invalid time encountered"); } return result; } public void VolumneBlocksFromString(string s) { _vBlocks.Clear(); if (s == "") { return; } string[] pieces = s.Split(';'); foreach (string p in pieces) { _vBlocks.Add(Decode(p)); } return; } } public class TwoDArray { const string _dir = @"..\..\"; StringList _colsInOrder = new StringList(); StringList _rowsInOrder = new StringList(); private Dictionary _data = new Dictionary(); public TwoDArray() { _colsInOrder.Add(""); } /// /// Given a key of the 2-D array, returns the string value /// /// /// /// Never null. "" as the default. public string get(String column, String row) { ColumnRowKey test = new ColumnRowKey(column, row); if (_data.ContainsKey(test)) { return _data[test]; } else { return ""; } } public List RowNames { get { return _rowsInOrder; } private set { } } public List ColumnNames { get { return _colsInOrder; } private set { } } private enum Mode { mNormal, mInQuotes, mAfterQuote, mDone } /// /// This method will take in a comma/delimited string, for example: abc,cd,"1 2 3","ab" /// and breaks it up to give /// ab /// cd /// 123 /// /// input stream(from file) /// public static void readRow(StreamReader stream, StringList list) { Mode m; char ch; list.Clear(); string inProgress = ""; m = Mode.mNormal; //make sure we are still "in the file" and not finished while (stream.Peek() != -1 && (m != Mode.mDone)) { ch = (char)stream.Read(); if (stream.EndOfStream) { // This is like adding a \n to the end of every file. That makes the // logic below simpler. ch = '\n'; } if (ch == '"') { switch (m) { case Mode.mNormal: m = Mode.mInQuotes; break; case Mode.mInQuotes: m = Mode.mAfterQuote; break; case Mode.mAfterQuote: inProgress += '"'; m = Mode.mInQuotes; break; default: break; } } else if (m == Mode.mInQuotes) { inProgress += ch; } else { switch (ch) { case '\r': if (stream.Peek() != '\n') { // cr lf is a valid end of line. Otherwise cr is a normal // character. inProgress += '\r'; } m = Mode.mNormal; break; case ',': list.Add(inProgress); inProgress = ""; m = Mode.mNormal; break; case '\n': if ((list.Count != 0) || (m == Mode.mAfterQuote) || (inProgress != "")) { // A blank line has no cells. A line with just two quotes // contains a single cell containing the empty string. list.Add(inProgress); } m = Mode.mDone; break; default: inProgress += ch; m = Mode.mNormal; break; } } } } /// /// Take in entire file (may wish to append) and adding it to internal /// dictionary /// /// name of file we wish to open public void appendFromCSV(string file) { StreamReader dataIn; dataIn = null; try { string filePath = _dir + file; dataIn = new StreamReader(new FileStream(filePath, FileMode.Open, FileAccess.Read)); appendFromCSV(dataIn); } catch (FileNotFoundException) { MessageBox.Show("File " + file + " not found!"); } catch (DirectoryNotFoundException) { MessageBox.Show("Directory not found!"); } catch (IOException ex) { MessageBox.Show("IO Exception {0}" + ex.ToString()); } finally { if (dataIn != null) { dataIn.Close(); } } } public void appendFromCSV(StreamReader streamReader) { StringList firstRow = new StringList(); //Take in first line readRow(streamReader, firstRow); if (firstRow.Count <= 1) { // No columns. return; } for (int i = 1; i < firstRow.Count; i++) //counting up columns (number of commas plus one) { string colHeader = firstRow[i]; //grabbing column header if ((colHeader != "") && (!_colsInOrder.Contains(colHeader)))//make sure we don't duplicate column headers { _colsInOrder.Add(colHeader); } } while (!streamReader.EndOfStream) { StringList row = new StringList(); readRow(streamReader, row); //read in next row int end = Math.Min(row.Count, firstRow.Count); //trying to avoid out of bounds error if (end > 1) //skip a row if empty { string rowHeader = row[0]; //row header can be symbol name or any other thing if (rowHeader != "") //ignore empty rows { if (!_rowsInOrder.Contains(rowHeader)) //keeping list of all rows count = zero or one { _rowsInOrder.Add(rowHeader); //list } for (int i = 1; i < end; i++) //go thru body of that particular row { add(firstRow[i], rowHeader, row[i]); } } } } } /// /// This method is the gateway for loading a csv file /// /// name of file public void loadFromCSV(string fileName) { clear(); appendFromCSV(fileName); } /// /// This method lets you send a string of CSV instead of a file name. /// /// public void loadFromCSVString(string csv) { clear(); using (StreamReader streamReader = new StreamReader(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(csv)))) { appendFromCSV(streamReader); } } /// /// For exporting purposes. Determines whether a string /// item needs to be enclosed in quotes or not /// /// string item /// static bool needsQuotes(string value) //for exporting { for (int i = value.Length - 1; i >= 0; i--) { switch (value[i]) { case '"': case '\n': case ',': return true; } } return false; } /// /// Writes to the stream /// /// /// public static void writeRow(StreamWriter stream, StringList list) { if (list.Count == 0) { stream.Write('\n'); return; } if ((list.Count == 1) && (list[0] == "")) //empty string different than "no items" { stream.Write("\"\"\n"); return; } bool first = true; for (int i = 0; i < list.Count(); i++) { if (first) { first = false; } else { stream.Write(','); } if (needsQuotes(list[i])) //list[i] is a string element in StringList "list" { stream.Write('"'); string data = list[i]; for (int j = 0; j < data.Length; j++) { if (data[j] == '"') { stream.Write("\"\"\n"); } else { stream.Write(data[j]); } } stream.Write('"'); } else { stream.Write(list[i]); } } stream.Write("\r\n"); } /// /// Write to an output csv file /// /// name of file to create public void writeToCSV(string fileName) { string filePath = _dir + fileName; using (StreamWriter textOut = new StreamWriter(new FileStream(filePath, FileMode.Create, FileAccess.Write))) { writeToCSV(textOut); } } /// /// processes the items in the stream /// /// input stream public void writeToCSV(StreamWriter stream) { writeRow(stream, _colsInOrder); //_colsInOrder includes empty string (not rows) for (int i = 0; i < _rowsInOrder.Count; i++) //iterate over each row { StringList currentRow = new StringList(); currentRow.Add(_rowsInOrder[i]); for (int j = 0; j < _colsInOrder.Count; j++) { if (_colsInOrder[j] != "") { currentRow.Add(get(_colsInOrder[j], _rowsInOrder[i])); } } writeRow(stream, currentRow); } } /// /// This method actually populates the "Dictionary" where the /// Key is a pair of strings, and the value is a single string. /// /// /// /// public void add(string colHeader, string rowHeader, string value) { //add if (!(colHeader == "" || rowHeader == "")) { if (value == "") { // A minor optimization. _data.Remove(new ColumnRowKey(colHeader, rowHeader)); } else //creating one big object..which is key. Then mapping key to value { _data.Add(new ColumnRowKey(colHeader, rowHeader), value); } if (!_colsInOrder.Contains(colHeader)) { _colsInOrder.Add(colHeader); } if (!_rowsInOrder.Contains(rowHeader)) { _rowsInOrder.Add(rowHeader); } } } /// /// Empties all containers (Lists, dictionaries...) /// public void clear() { _colsInOrder.Clear(); _rowsInOrder.Clear(); _data.Clear(); _colsInOrder.Add(""); } } }