График исторических значений Dow Jones Industrial Average в C#
Эта программа отображает исторические цены на индекс Dow Jones Industrial Average. Я получил данные с этого URL:
ichart.finance.yahoo.com/table.csv?s=^DJI&a=0&b=1&c=1990&d=0&e=1&f=2020&g=d&ignore=.csv
Поля в URL:
Результаты приведены в примере в файле DjiPrices.csv. Во время разработки я добавил файл в проект и установил его свойство «Копировать в выходной каталог» в «Копировать, если новый», поэтому файл входит в выходной каталог с исполняемой программой, поэтому программа легко найти. р>
Программа сохраняет данные о ценах в следующей структуре.
// Структура для хранения данных о ценах. private struct PriceData { public DateTime Date; public float Price; public PriceData(DateTime new_Date, float new_Price) { Date = new_Date; Price = new_Price; } };
Эта структура просто содержит дату и соответствующую цену закрытия. Вы можете легко добавить другие поля, такие как цена открытия и общий объем, если вы хотите отобразить эти данные. Структура определяет конструктор, чтобы упростить инициализацию новой структуры PriceData.
Следующий код показывает, как программа загружает данные о ценах.
// Получите исторические цены. private ListGetDjiPrices() { // Получаем данные по строкам. string[] lines = File.ReadAllLines("DjiPrices.csv"); // Смотрите, какие поля заголовка содержат Date и Adj Close. string[] fields = lines[0].Split(','); int date_field = -1, close_field = -1; for (int i = 0; i < fields.Length; i++) { if (fields[i].ToLower() == "adj close") close_field = i; else if (fields[i].ToLower() == "date") date_field = i; } // Обработать строки, пропустив заголовок. List price_data = new List (); for (int i = 1; i < lines.Length; i++) { fields = lines[i].Split(','); price_data.Add(new PriceData( DateTime.Parse(fields[date_field]), float.Parse(fields[close_field]))); } // Обратный, чтобы данные находились в историческом порядке. price_data.Reverse(); return price_data; }
Этот код использует File.ReadAllLines для чтения строк в файле данных в массив строк. Он считывает строку заголовка, чтобы найти столбцы, содержащие дату и скорректированную цену закрытия. (Они должны быть первым и последним столбцами.)
Затем код перебирает оставшиеся строки в файле. Для каждой строки программа создает структуру PriceData, чтобы удерживать цену закрытия Dow Jones этого дня и добавляет ее в список.
Следующий код показывает, как программа рисует данные о ценах.
// Рисуем график. private void DrawGraph(List price_data) { // Создаем растровое изображение. Bitmap bm = new Bitmap( picGraph.ClientSize.Width, picGraph.ClientSize.Height); using (Graphics gr = Graphics.FromImage(bm)) { gr.Clear(Color.White); gr.SmoothingMode = SmoothingMode.AntiAlias; // Получите самую большую цену. var max_query = from PriceData data in price_data select data.Price; float max_price = max_query.Max() + 500; // Масштабируем и переводим график. float scale_x = picGraph.ClientSize.Width / (float)price_data.Count; float scale_y = -picGraph.ClientSize.Height / max_price; gr.ScaleTransform(scale_x, scale_y); gr.TranslateTransform( 0, picGraph.ClientSize.Height, System.Drawing.Drawing2D.MatrixOrder.Append); using (Pen thin_pen = new Pen(Color.Gray, 0)) { // Нарисуем горизонтальные линии сетки. for (int y = 0; y <= max_price; y += 1000) { // Нарисовать линию. gr.DrawLine(thin_pen, 0, y, price_data.Count, y); // Нарисуем значение. if (y > 0) DrawTextAt(gr, y.ToString("C"), 10, y, Color.Blue, StringAlignment.Near, StringAlignment.Far); } // Рисуем вертикальные линии сетки. using (StringFormat string_format = new StringFormat()) { string_format.Alignment = StringAlignment.Center; string_format.LineAlignment = StringAlignment.Center; int last_year = 0; for (int i = 0; i < price_data.Count; i++) { // Посмотрим, станет ли это началом нового года. if (price_data[i].Date.Year > last_year) { last_year = price_data[i].Date.Year; // Нарисуем строку за год. gr.DrawLine(thin_pen, i, 0, i, 750); DrawTextAt(gr, last_year.ToString(), i, 0, Color.Blue, StringAlignment.Center, StringAlignment.Far); } } } } PointF[] points = new PointF[price_data.Count]; for (int i = 0; i < price_data.Count; i++) { points[i] = new PointF(i, price_data[i].Price); } using (Pen thin_pen = new Pen(Color.Black, 0)) { gr.DrawLines(thin_pen, points); } } picGraph.Image = bm; }
Код начинается с создания растрового изображения для сохранения результата. Он использует запрос LINQ, чтобы найти самую большую цену, чтобы он мог масштабировать график, чтобы он хорошо соответствовал. Затем он дает Graphics масштаб объекта и преобразования трансляции, чтобы сделать будущий рисунок объекта подходящим растровым изображением.
Затем программа рисует горизонтальные линии, представляющие цены, кратные 1000 долларов. Для каждой цены он вызывает метод DrawTextAt (описанный в скором времени) для отображения цены на графике.
В коде они рисуют вертикальные линии сетки, где начинается каждый год. Для этого он просматривает данные, сравнивая каждый год структуры PriceData с предыдущим. Если год изменился, программа рисует небольшую вертикальную линию и отображает год.
Наконец, код проходит через данные, создавая Point для каждого значения. Затем он рисует линии, соединяющие точки.
В следующем коде показан метод DrawTextAt, который рисует непереведенный текст в определенной точке графика.
// Нарисуйте текст в указанном месте. private void DrawTextAt(Graphics gr, string txt, float x, float y, Color clr, StringAlignment alignment, StringAlignment line_alignment) { // Смотрите, где находится точка в координатах PictureBox. Matrix old_transformation = gr.Transform; PointF[] pt = { new PointF(x, y) }; gr.Transform.TransformPoints(pt); // Сбросить преобразование. gr.ResetTransform(); // Рисуем текст. using (Font small_font = new Font("Arial", 8)) { using (SolidBrush br = new SolidBrush(clr)) { using (StringFormat string_format = new StringFormat()) { string_format.Alignment = alignment; string_format.LineAlignment = line_alignment; gr.DrawString(txt, small_font, br, pt[0].X, pt[0].Y, string_format); } } } // Восстановить исходное преобразование. gr.Transform = old_transformation; }
Трюк здесь заключается в том, что точка, в которой текст должен быть нарисован, находится в преобразованной системе координат графа, но текст не следует преобразовывать, чтобы он не растягивался или не перевернулся вверх ногами. Метод DrawTextAt сначала сохраняет свойство Transform объекта Graphics объекта, которое представляет текущее преобразование. Затем он применяет текущее преобразование к точке (x, y), где текст должен выглядеть так, чтобы видеть, где на графике находится эта точка.