Light Style© by Fisana

Перейти к содержимому


Инвестиционные фонды NordFx: профессиональное управление и прозрачность


NordFX

Фотография

Начальное обучение (Практика)


  • Пожалуйста, авторизуйтесь, чтобы ответить
22 ответов в этой теме

#1 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 17 Декабрь 2009 - 09:03

Начальное обучение языку С++
ICQ 296461926

#2 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 13 Февраль 2010 - 07:42

Практическое занятие № 1
Вступление
Технический анализ — это исследование динамики рынка с целью прогнозирования дальнейшей динамики цен. Чаще всего анализ проводится посредством графиков. Поэтому очень важно иметь исторические данные цен по всем используемым финансовым инструментам и периодам. Исторические данные постоянно формируются и хранятся на сервере. Подключаясь к нему, клиентский терминал скачивает все необходимые данные. В дальнейшем они используются для построения графиков, тестирования и оптимизации экспертов. Для управления историческими данными в терминале имеется специальное окно "Архив котировок". Это окно можно открыть, выполнив команду меню "Сервис — Архив котировок" либо нажав клавишу F2.

21_mn_tools.gif

После закрытия терминала все накопленные исторические данные сохраняются в "Архиве котировок".

94_win_history_center.gif

Причем размеры файлов исторических котировок не превышают заданных в настройках значений.

9_win_setup_charts.gif

Если объем накопленных исторических данных превышает величину, установленную в поле " Макс. баров истории:", то при сохранении удаляются самые старые бары. Для каждого таймфрейма формируется отдельный файл истории с именем SSSSSSPP.hst (где SSSSSS - обозначение финансового инструмента, PP - временной период в минутах) и сохраняется в директории /HISTORY. В дальнейшем сохраненные данные используются для построения графиков, а также для тестирования торговых стратегий.

Тема практического занятия № 1 "Написание альтернативной программы для чтения котировок"

Итак начнем. Для того чтобы начать писать программу нам необходимо в первую очередь разобраться с форматом файла исторических данных (*.hst). Файл истории состоит из двух частей. Первая часть-это заголовок базы и вторая собственно массив баров. Для начала разберемся с заголовками файла.

Формат файлов истории (HST-файлы)Заголовок базы

struct HistoryHeader
{
  int               version;           // версия базы
  char              copyright[64];      // копирайт
  char              symbol[12];         // инструмент
  int               period;             // период инструмента
  int               digits;             // число знаков после запятой в инструменте
  time_t            timesign;           // временной отпечаток создания базы
  time_t            last_sync;          // время последней синхронизации
  int               unused[13];         // для будущего использования
};

Формат файлов истории (HST-файлы)массив баров
struct RateInfo
{
 time_t ctm; // Текущее время в секундах от 1 января 1970 года (3:00)
 double open;// Цена открытия бара
 double low;// Минимальная цена бара
 double high;//Максимальная цена бара
 double close;//Цена закрытия бара
 double vol;//Объем
};

ICQ 296461926

#3 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 14 Февраль 2010 - 09:28

Практическое занятие № 1 (продолжение)

Если вы обратили внимания то в наших структурах есть переменные с типом time_t. Если мы начнем собирать программу то компилятор нам выдаст вот такую ошибку [C++ Error] Unit1.cpp(25): E2303 Type name expected. Если это перевести на великий и могучий то это будет означать, что компилятор не может понять, что за тип данных мы ему пытаемся подсунуть. Данная проблема решается очень легко путем включения в программу заголовочного файла time.h.
#include <time.h>
time.h — заголовочный файл стандартной библиотеки языка программирования СИ, содержащий типы и функции для работы с датой и временем.
Теперь вернемся к структуре RateInfo. У массива баров должно быть однобайтовое выравнивание. Немного поясню суть для тех кто не в теме...

Выравнивании полей структуры/класса по границам байта, 2,4 и выше байт.
Поясню на примере:
К примеру, структура:
struct some
{
char A;
char B;
char C;
};
С выравниванием по границе двойного слова (4 байта) займет 4 байта, причем 4-й байт в стуктуре инициализирован не будет никогда - он используется для выравнивания.
В случае с большими структурами данных и перемешанными типами
struct
{
char A;
int B;
short C;
float D;
char E
char F
float G;
};
Накладные расходы на байты выравнивания будут увеличивать размер структуры. Кроме того, выравнивание может повлечь за собой баги при загрузке/сохранении этих структур, если при записи и загрузке используются различные выравнивания. Для таких структур используют
#pragma pack (push,1) ... #pragma pack(pop)
, которые выставляют выравнивание по границе байта.

В нашем случае всё будет выглядеть вот так.
#pragma pack(push,1)
struct RateInfo
{
 time_t ctm; // текущее время в секундах
 double open;//цена открытия
 double low;// минимум
 double high;//максимум
 double close;//закрытие
 double vol;//обьем
};
#pragma pack(pop)

И так с файлом из которого будем выгружать данные разобрались теперь пришло время разобраться с файлом куда эти данные будем подгружать. Я выбрал для этого файл Excel (*.xls).

Определяем - переменные,отражающие иерархию объектов Excel
Variant   vVarApp;//Приложение
Variant vVarBooks;//Набор книг
Variant vVarBook;//Книга
Variant vVarSheets;//Набор страниц,
Variant vVarSheet;//Страница
Variant vVarCells;//Ячейки
Variant vVarCell;//Ячейка
И еще одну переменную fStart
bool fStart;//переменная fStart служит индикатором того, что сервер запущен

ICQ 296461926

#4 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 16 Февраль 2010 - 08:19

Практическое занятие № 1 (продолжение)

Бросаем на форму кнопку. Попытаемся запустить Excel. Вот для чего нам пригодилась переменная fStart

if(!fStart)//если  Excel не запущен
 {
  try        //попытка   
  {
   vVarApp=CreateOleObject("Excel.Application");[/i]//запускаем
   fStart=true;                             
  }
  catch(...)// исключение
  {
   MessageBox(0, "Ошибка при открытии сервера Excel"[/i],// сообщаем об ошибке 
                "Ошибка", MB_OK);                  //а может у тебя Excel не установлен или ты фанат Openоffice
  return;                                           // ну и конечно же возврат
  }
 }

vVarBooks=vVarApp.OlePropertyGet("Workbooks");//книга
vVarApp.OlePropertySet("SheetsInNewWorkbook",1);//задает количество листов в создаваемой книге я создаю один 
vVarBooks.OleProcedure("Add");//добавляем книгу в объект vVarBooks.
vVarBook=vVarBooks.OlePropertyGet("Item",1);//Переменная vVarBook содержит ссылку на текущую книгу.Пусть текущая книга 1
vVarSheets=vVarBook.OlePropertyGet("Worksheets");//Переменной vVarSheets присваиваем значение Worksheets 
//свойство объекта Excel.Application, содержащее набор страниц книги Excel    
vVarSheet=vVarSheets.OlePropertyGet("Item",1);//первая страница
vVarSheet.OlePropertySet("Name","Я любимый");//название листа можно гордо назвать своим именем 
vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",1,1);//Определяем ячейку
vVarCell.OlePropertySet("Value","Дата");//Присваиваем название первой колонке
vVarCell.OlePropertySet("ColumnWidth",22);//устанавливаем ширину колонки чтобы влезла вся дата

Дальше я подумал, что не плохо было бы иметь возможность выгружать только те данные которые необходимы. Например тебя интересует только цена закрытия. Хорошо. Можем выгрузить только её. Или максимальная и минимальная цена. Тоже можно...
Для этого я бросил на форму 5 CheckBox-ов. Если галка стоит в CheckBox1 то выгружаем цену открытия. CheckBox2 -минимальная цена, CheckBox3- максимальная цена, CheckBox4-цена закрытия, CheckBox5-объём.

int pole1=1;//переменная первого поля
int pole2,pole3,pole4,pole5,pole6;// и 5 переменных остальных полей

if(Form1->CheckBox1->Checked==True)//если галочка стоит использовать цену открытия
 {[/i]
  [pole2=++pole1;
   vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",1,pole2);//определяем ячейку
   vVarCell.OlePropertySet("ColumnWidth",14); //выставляем ширину так, чтобы вместились данные
   vVarCell.OlePropertySet("Value","Цена открытия");//называем колонку Цена открытия
 }
  if(Form1->CheckBox2->Checked==True)// И так далее по всем колонкам по аналогии
  {
   pole3=++pole1;
   vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",1,pole3);
   vVarCell.OlePropertySet("Value","Минимум");
   vVarCell.OlePropertySet("ColumnWidth",14);
 }
 if(Form1->CheckBox3->Checked==True)
 {
   pole4=++pole1;
   vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",1,pole4);
   vVarCell.OlePropertySet("Value","Максимум");
   vVarCell.OlePropertySet("ColumnWidth",14);
 }
 if(Form1->CheckBox4->Checked==True)
 {
   pole5=++pole1;
   vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",1,pole5);
   vVarCell.OlePropertySet("Value","Цена закрытия");
   vVarCell.OlePropertySet("ColumnWidth",14);
 }
 if(Form1->CheckBox5->Checked==True)
 {
   pole6=++pole1;
   vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",1,pole6);
   vVarCell.OlePropertySet("Value","Объём");
   vVarCell.OlePropertySet("ColumnWidth",14);
 }
Вот внешний вид программы.

1.JPG
ICQ 296461926

#5 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 17 Февраль 2010 - 10:29

Практическое занятие № 1 (Окончание)
Итак, до финиша остался последний рывок. И так я предусмотрел в программе возможность открытие файла Excel до того как он сформируется или после. Разница в том, что когда сразу открывается файл, то можно увидеть как программа формирует котировки. Время формирования файла увеличиватся. Вот код:
if (Form1->RadioButton3->Checked==True)[/i]// если сразу хотим показать
   vVarApp.OlePropertySet("Visible",true);[/i]//делаем наш файлик видимым

Далее объявляем переменные

HistoryHeader hdr;
 int r,i;
 int count=2;
 FILE *stream;
 RateInfo rate;
 struct tm *tm;
 char *s;
Что за переменные я думаю понятно...
Бросам на форму OpenDialog c закладки Dialogs и Edit и пишем...
if (OpenDialog1->Execute())// открываем диалог
  {
   Edit2->Text = OpenDialog1->FileName;// загоняем имя файла
  }

FILE *hst= fopen(PChar(Edit2->Text.c_str()),"rb"); //открываем

  if(hst==NULL)//если не смогли открыть
    {
    MessageBox(0, "Не могу открыть файл","Ошибка открытия файла", MB_OK);
     return;
    }
Далее в цикле
r=fread(&hdr, 1, sizeof(hdr), hst);//читаем заголовок
   if((tm=localtime(&(rate.ctm))))// если вдруг в истории дырки
    {
   s=asctime(tm);//преобразуем число в дату будет что-то типа Mon Jan 05 07:54:00 1999
Если не нравиться формат всегда можно переделать строчку в "Понедельник 5 января 1999 года 07:54:00".
vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",count,1);//получаем ячейку
   vVarCell.OlePropertySet("Value",s);присваиваем дату

           if(Form1->CheckBox1->Checked==True)// если выбранно использовать цену
            {
              vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",count,pole2);//получаем ячейку
              vVarCell.OlePropertySet("Value",rate.open);// присваиваем цену открытия
            }
           if(Form1->CheckBox2->Checked==True)// и так далее по аналогии
            {
              vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",count,pole3);
              vVarCell.OlePropertySet("Value",rate.low);
            }
           if(Form1->CheckBox3->Checked==True)
           {
              vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",count,pole4);
              vVarCell.OlePropertySet("Value",rate.high);
           }
           if(Form1->CheckBox4->Checked==True)
           {
              vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",count,pole5);
              vVarCell.OlePropertySet("Value",rate.close);
           }
           if(Form1->CheckBox5->Checked==True)
           {
              vVarCell=vVarSheet.OlePropertyGet("Cells").OlePropertyGet("Item",count,pole6);
              vVarCell.OlePropertySet("Value",rate.vol);
           }
Ну и напоследок закрываем Excel.
vVarApp.OleProcedure("Quit");
Вот в итоге, что должно получиться

1.JPG

На этом практическое занятие закончено. Если есть вопросы задавать в ветке "Вопросы и ответы..."
ICQ 296461926

#6 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 20 Февраль 2010 - 09:21

Практическое занятие №2 Экспорт котировок

Исходными данными, на базе которых строится вся аналитическая работа пользователя терминала, являются данные о динамике цен финансовых инструментов. Эту информацию поставляет брокерская компания. Ценовые данные позволяют строить графики финансовых инструментов, исследовать финансовые рынки, использовать различные торговые тактики и принимать торговые решения. Котировки представляют собой файлы с записями в формате "SYMBOL, BID, ASK, DATE" (финансовый инструмент, цена покупки, цена продажи, дата и время) и поступают в терминал автоматически после установки связи с сервером.

Терминал позволяет экспортировать текущие котировки в другие программы в режиме реального времени по протоколу "DDE" (Dynamic Data Exchange). Это — протокол операционных систем MS Windows для динамической передачи данных между различными приложениями. Котировки по DDE выдаются только по приходе новых тиков (режим ADVISE), а не сразу же по запросу (режим REQUEST) с выдачей последней известной цены. N/A выдается при первом запросе REQUEST, а после прихода новой цены появляются котировки.

Для активизации режима экспорта котировок из клиентского терминала по протоколу DDE необходимо включить опцию "Разрешить DDE сервер" в настройках терминала.

Ctrl0.gif

20OK.gif

Внимание: исторические данные по протоколу DDE не транслируются. Передача текущих котировок производится только при работающем клиентском терминале.

Форматы DDE-запросов с их возможными результатами на примере файла "DDE-sample.xls"(файл включен в стандартную поставку терминала):

запрос BID: = MT4|BID!USDCHF результат: 1.5773 
запрос ASK: = MT4|ASK!USDCHF результат: 1.5778 
запрос HIGH: = MT4|HIGH!USDCHF результат: 1.5801 
запрос LOW: = MT4|LOW!USDCHF результат: 1.5741 
запрос TIME: = MT4|TIME!USDCHF результат: 21.05.02 9:52 
запрос QUOTE: = MT4|QUOTE!USDCHF результат: 21.05.02 9:52 1.5773 1.5778 1.5776

Внимание: для правильного отображения данных в MS Excel нужно включить опцию в меню "Сервис — Параметры — Переход — Преобразование формул в формат Excel при вводе".

1.JPG

P.S Выкладываю файл "DDE-sample.xls" вдруг кто-то у себя его не обнаружит

Вложенный файл  DDE-Sample.rar   1,97 КБ   380 Скачано
ICQ 296461926

#7 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 24 Февраль 2010 - 10:22

Практическое занятие №3 Экспорт котировок с помощью индикатора BidAsk (Вступление)

На этом практическом занятии с помощью индикатора BidAsk мы получим котировки 5 валютных пар в реальном времени. Котировки будем получать по следующим валютным парам EUR/USD, GBP/USD, AUD/USD, USD/CAD, USD/CHF. Для каждой валютной пары будем получать в определенный момент времени две цены, цену покупки и цену продажи. И так, для начала скачиваем и распаковываем индикатор BidAsk.

Вложенный файл  BidAsk.rar   1,62 КБ   335 Скачано

Копируем распакованный файл в папку с индикаторами. У меня она располагается по пути C:\Program Files\MetaTrader 4\experts\indicators После копирования запускаем MetaTrader и в пользовательских индикаторах видим появившийся BidAsk.

1.JPG

Выолняем его на любом из графиков (минутный это или часовой значения не имеет).

1.JPG

После выполнения индикатора на графике видимых изменений на графике не произойдет. Принцип работы данного индикатора -это получение котировок в реальном времени и запись их в текстовой файл. Текстовой файл будет находиться по пути C:\Program Files\MetaTrader 4\experts\files и будет называться BidAsk.txt Давайте откорем этот файл и посмотрим, что записал туда индикатор.
Вот, что у меня получилось.
1.JPG

Теперь давайте разберемся что записал индикатор. 1.454 - это первая строчка Bid пары EUR/USD, 1.4542 -это вторая строчка Ask пары EUR/USD и так далее по всем 5 валютным парам в последовательности EUR/USD, GBP/USD, AUD/USD, USD/CAD, USD/CHF. Теперь нам нужно будет во время разработки программы открыть этот файл, прочитать котировки и вывести их в программе.
ICQ 296461926

#8 nsg

nsg

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 105 сообщений

Опубликовано 27 Февраль 2010 - 10:15

Практическое занятие №3 Экспорт котировок с помощью индикатора BidAsk (Окончание)

Запускаем компилятор. Если кто еще не поставил его себе как ставить смотрим тут После запуска откроется пустая форма. Переименуем её. Зайдем в свойста формы "Caption" и напишем "Котировки"(хотя название программе можно дать любое) см. рис 1.

1.jpg
Рисунок 1

Далее на форму добавляем кнопку(Button1). Кнопка находиться на закладке Standard(см. рис 2). И добавляем StringGrid1 с закладки Additional(см. рис.2).

2.JPG

Теперь начнем писать код. Щелкнем по нашей кнопке 2 раза и наш компилятор автоматически создаст событие(нажатие на кнопку).
Вот как это выглядит.
void __fastcall TForm1::Button1Click(TObject *Sender)
{

}
TStringList *Table1 = new TStringList ;  //создадим (вручную) вспомогательную таблицу
Table1->LoadFromFile("С:\\Program Files\MetaTrade 4\expert\files\BidAsk.txt") ;    
//загружаем в нашу вспомогательную таблицу файл BidAsk.txt проверьте правильность пути  
StringGrid1->Cells[1][0] = "Покупка";  //Заполняем шапку(название колонок покупка и продажа)
StringGrid1->Cells[2][0] = "Продажа";  //обратите внимание Cells[1][0] - это колонки
StringGrid1->Cells[0][1] = "EUR/USD";  //Заполняем название валют
StringGrid1->Cells[0][2] = "GBP/USD";  //Здесь вам будет необходимо добавить 3 недостающие валюты
int p=1;                               // объявляем вспомогательную переменную
for(int i = 0 ; i<4 ; i++)/здесь загружаем 4 котировки(2 покупки и 2 продажи) здесь тоже надо кое- что поменять    
    {                    //  Копируем (построчно) данные с
     StringGrid1->Cells[1][p]= Table1->Strings;  //  вспомогательной табл. в StringGrid1. это строчка покупка
     i++ ;                                           //увеличиваем i на 1
     StringGrid1->Cells[2][p]= Table1->Strings ;  //это строчка продажа
     p++;                                            //увеличиваем р на 1
    }
Вот собственно и весь код. Теперь уменьшим форму и у кнопке в свойстве "Caption" напишем Загрузить(см. рис. 3)

3.JPG
Рисунок 3

Исходники проекта Вложенный файл  Котировки.rar   434,42 КБ   347 Скачано

Домашнее задание
Дописать котировки для остальных валютных пар.
ICQ 296461926

#9 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 08 Апрель 2010 - 08:47

Практическое занятие № 4 (4.1) Получение котировок используя DDE.
Сергей (nsg) уже упоминал о DDE и показал на примере (Excel) как использовать эту технологию для получения котировок он-лайн. Я же покажу как работать с этой технологией программно. В конце каждого занятия (кроме 4.1 и 4.2) Вы сможете скачать исходник (вариант для ленивых или для тех у кого не получилось) и релиз исполняемого файла для тех у кого нет среды и хочет просто посмотреть :blush:. Примеры программ (и соответственно исходники) будет даваться в среде быстрой разработки Borland C++ Builder 6.

Это практическое занятие будет разбито на несколько уроков.

Но начнем мы все равно с описания DDE - repetitio est mater studiorium (лат. повторение - мать учения).

Практическое занятие № 4.1
DDE и MetaTrader 4
Технология DDE (Dynamic Data Exchange) была разработана компанией Microsoft для обеспечения взаимодействия между приложениями в Windows и OS/2.

Dynamic Data Exchange получило свое имя потому, что позволяет двум приложениям обмениваться данными (текстовыми, через глобальную память) динамически во время выполнения. Связь между двумя программами можно установить таким образом, что изменения в одном приложении будут отражаться во втором. Например, если Вы меняете число в электронной таблице, то во втором приложении данные обновятся автоматически и отобразят изменения. Кроме того, с помощью DDE можно из своего приложения управлять другими приложениями, например Excel.

Сегодня эта технология устарела и для обмена данными между процессами используются более продвинутые и безопасные методы OLE, а также сетевые технологии, каналы (pipes). Microsoft уже давно отказалась от этой бесперспективной технологии.
Но с другой стороны простота работы с DDE порой перевешивает, тем более других интерфейсов в MT4 все равно нету.

Кстати, в MT5 поддержки DDE нет. Жаль. Будем искать другой способ получать котировки он-лайн. Хотя до релиза еще далеко - может что-то и поменяется. DDE вряд ли вернут, а вот другой какой-нибудь интерфейс, может и встроят. Но мы сейчас работаем с MT4 и было бы глупо не воспользоваться этой технологией, тем более MT4 ДЦ еще не скоро отменят - деньги-то заплачены... :tongue:

DDE функционирует по принципу клиент-сервер. Т.е. клиент делает запрос, а сервер его обрабатывает и, возможно, отправляет ответ. Запрос DDE имеет следующий синтаксис:

имя приложения | имя документа или раздела документа ! диапазон ячеек, значение, поле или данные, на которые сделана ссылка

и выглядит следующим образом:
SERVICE|TOPIC!VALUE

Для активизации режима экспорта котировок из клиентского терминала по протоколу DDE необходимо включить опцию "Разрешить DDE сервер" в настройках терминала. После установки флажка DDE (точнее после нажатия кнопки ОК) сервер сразу заработает, перегрузка терминала не обязательна.


Размещенное изображение


Теперь посмотрим на формат запросов, которые можно делать DDE-серверу MT4.

Пример из справки MetaTraider`а:
запрос BID: = MT4|BID!USDCHF	результат: 1.5773 
запрос ASK: = MT4|ASK!USDCHF	результат: 1.5778 
запрос HIGH: = MT4|HIGH!USDCHF результат: 1.5801 
запрос LOW: = MT4|LOW!USDCHF	результат: 1.5741 
запрос TIME: = MT4|TIME!USDCHF результат: 21.05.02 9:52 
запрос QUOTE: = MT4|QUOTE!USDCHF результат: 21.05.02 9:52 1.5773 1.5778 1.5776 
ЗЫ: Как Вы сами потом увидите – формат даты и ответ quote отличаются от этого примера из справки.

Думаю интуитивно запросы понятны, но все же разберем:

Размещенное изображение

Разделы:
BID – получение цены спроса (bid)
ASK – получение цены предложения (ask)
HIGH – максимум дня (HD1)
LOW – минимум дня (LD1)
TIME – текущие дата и время (с точностью до минут) в терминале
QUOTE – дата, время, bid, ask

Т.е. запрос на рисунке будет возвращать цены предложения (ask) по валютной паре USDCHF.

Внимание: исторические данные по протоколу DDE не транслируются. Передача текущих котировок производится только при работающем клиентском терминале.
Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.

#10 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 08 Апрель 2010 - 08:52

Практическое занятие № 4.2 Первые шаги.
Теперь давайте посмотрим, как нам программно подключится к DDE-серверу, сделать запрос и получить ответ. В Builder`е есть 4 стандартных компонента для работы с DDE. Два клиентских (DdeClientConv, DdeClientItem), с которыми мы и будем работать. И, соответственно, два серверных (DdeServerConv, DdeServerItem), на случай если Вы захотите сделать DDE-сервер в своем приложении.Dde…Conv (DDE Conversation) – DDE-диалог или DDE-соединение (или DDE-связь - как хотите. Я в описаниях занятий буду пользоваться всеми).

Размещенное изображение

Итак, приступим.
1. Создадим новый проект (или воспользуемся старым), в общем подготовим для себя пустую форму для работы. Думаю здесь ни у кого не возникнет проблем.
2. С вкладки System перетащим на форму два компонента: DdeClientConv и DdeClientItem

Размещенное изображение

3. Настройка DdeClientConv. Выделим объект и переключимся на ObjectInspector.
Компонент отвечает за установление соединения с конкретному разделом DDE-сервера.

Размещенное изображение

ConnectMode – Режим подключения (выставляем ddeManual):
. ddeManual – вручную (для подключения нужно будет вызвать метод OpenLink)
. ddeAutomatic – автоматически после установки значений DdeService и DdeTopic посредствам метода SetLink.
DdeService – Сервис. В нашем случае MT4
DdeTopic – Раздел. В нашем случае выбираем один из разделов MT4 (BID, ASK, HIGH, LOW, TIME, QUOTE).
FormatChars – Форматирование спец. символов, оставляем по умолчанию - false. Нужно (значение true), если вы будете передавать серверу спец. символы (табуляция, возврат каретки и т.п.)
Name – Имя компонента в вашей программе. Можете оставить по умолчанию, но я советую всегда именовать компоненты в соответствии с тем, что они делают, например так ddeConvQuote. В уроке будем работать с именем ddeConvQuote.
ServiceApplication – Приложение, которое отвечает за работу DDE-сервера, можно оставить пустым. В это поле заносится путь к программе MetaTrader. Если при обращении к DDE-серверу MetaTrader не будет запущен, ваша программа запустит его автоматически.
Tag – Это поле для Вас. Оно есть у всех компонентов Builder`а. Можете сюда сохранять любые целочисленные данные.

Теперь можно проверить подключение. После настройки компонента выставим ConnectMode в ddeAutomatic. Если не появилось сообщений об ошибке, то все сделано правильно.

Особо не увлекайтесь, второй раз подключится не получится, т.к. компонент не имеет функции закрытия соединения (программно это сделать, конечно же можно). Поэтому если Вы второй раз переводите ConnectMode в ddeAutomatic (т.е. инициируете соединение), то вы получите сообщение об ошибке

Размещенное изображение

Для повторного соединения нужно сбросить соединение любым способом: перегрузить проект, перегрузить среду, перегрузить MetaTrader, перегрузить комп в конце концов – в общем как угодно, главное разорвать соединение. Самое простое перегрузить DDE-сервер – убрать и выставить галочку в настройках MT (с нажатием кнопки ОК).

4. Настройка DdeClientItem. Выделим объект и переключимся на ObjectInspector.

Объект DdeClientItem отвечает за поставку данных, которые будем запрашивать через подключенный DDE диалог – DdeClientConv.

Размещенное изображение

DdeConv – Связывает наш item c DDE-диалогом. Выбираем из списка созданный на 3 шаге ddeConvQuote.
DdeItem – Наименование запрашиваемого значения (в нашем случае GBPUSD).
Lines – В это поле объекта TDdeClientItem будет поступать запрошенная информация, если размер ответа больше 255 символов, если меньше ответ записывается в поле Text.
Name – Имя объекта – ddeItemGBPUSD.
Text – В это поле объекта TDdeClientItem будет поступать запрошенная информация, если размер ответа меньше 255 символов, если больше ответ записывается в поле Lines.

Теперь проверим:

Размещенное изображение

Выставим ConnectMode в ddeAutomatic. Если все сделали правильно, то в поле Text – увидим ответ на наш запрос (MT4|QUOTE!GBPUSD) - 2010/03/29 14:42 1.4978 1.4981 (сравните с примером из справки MT), причем значения будут меняться, как только котировки будут обновляться на сервере.

Через один диалог можно получать несколько значений. Например, вы можете создать еще один DdeClientItem для пары EURUSD, и подключить к тому же диалогу ddeConvQuote.

Итак, второй урок закончен. Заметьте, мы даже еще программу не начали писать, а уже получаем котировки он-лайн, через DDE-сервер MT4.

Самостоятельно:
- Создайте DDE-диалоги для всех остальных разделов (ASK, BID, HIGH, LOW, TIME)
- Создайте запросы к разным разделам, разными фин. инструментами.
Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.

#11 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 09 Апрель 2010 - 08:13

Практическое занятие № 4.3. Приложение.

Теперь создадим приложение, которое будет выводить на экран запрашиваемые котировки. Котировки будем именно запрашивать… по кнопке.

Сразу давайте договоримся: имена объектов Вы делаете какие хотите, но в уроках будут те имена объектов, которые я присвоил. Будьте внимательны, чтобы не возникло путаницы.

Сначала создадим форму и накидаем на нее необходимые визуальные компоненты.

* Кнопка подключения/отключения (TButton)
Name = btnConnect
Caption = Соединенить - наименование

* Кнопка запроса (обновления) котировок (TButton)
Name = btnUpdate
Caption = Обновить
Enabled = false – доступность элемента (в данном случае кнопка не доступна)


* Таблица, куда будем выводить результаты (TStringGrid)
Name = sgBidAsk
ColCount = 6 – кол-во колонок
DefaultRowHeight = 20 – высота строк

Форме (Form1) тоже можно задать Caption = Котировки

Теперь создадим пять экземпляров DdeClientConv. ConnectMode выставляем в ddeManual. Почему пять? Считаем: ASK, BID, HIGH, LOW, TIME – вроде пять. Вспоминая урок 4.2 п.п. 3, настроим их на MT4.
Теперь создадим для каждого фин. инструмента (читай валютной пары) по пять DdeClientItem. Я сделаю для двух EURUSD и GBPUSD.

Примечание: создаваемые объекты именуем как в уроке 2.

В итоге у Вас должно получиться нечто что-то вроде этого:
Размещенное изображение

Теперь займемся кодингом (написанием кода программы).

1. Запрограммируем кнопку «Соединить».
Кликнем по ней 2 раза… в этот момент среда создаст описание функции нажатия кнопки, поместит ее в разделе публикуемых методов класса TForm1, создаст пустую реализацию этой функции в cpp файле, и поставит туда курсор – можем сразу писать…
 void __fastcall TForm1::btnConnectClick(TObject *Sender)
{
 // вызываем методы OpenLink у всех Dde-диалогов
 if (!btnUpdate->Enabled)
 {
 	ddeConvAsk->OpenLink();
 	ddeConvBid->OpenLink();
 	ddeConvHigh->OpenLink();
 		ddeConvLow->OpenLink();
 		ddeConvTime->OpenLink();
 	btnConnect->Caption = "Разъединить"; // меняем название кнопки
	}
	else
	{
 		ddeConvAsk->CloseLink();
 	ddeConvBid->CloseLink();
 		ddeConvHigh->CloseLink();
 	ddeConvLow->CloseLink();
 		ddeConvTime->CloseLink();
 		btnConnect->Caption = "Соединить";
	}
	btnUpdate->Enabled = !btnUpdate->Enabled; // меняем флаг подключения, заодно активируем/деактевируем кнопку обновления.
}

Вообще-то по-хорошему бы обработать установилось ли подключение, но чтобы не усложнять сейчас задачу мы этого делать не будем – немного поэкстрималим.

По поводу закрытия DDE-диалогов. По идее вызвав метод CloseLink должен произойти разрыв соединения… Разрыв может и происходит, но подключится второй раз не получается – либо компоненты кривые, либо сервер – вообще, как Вы потом увидите это не единственная странность при работе с DDE MT4. В уроке посвященном динамике мы исправим это.

Как вы уже поняли в качестве флага подключения мы используем свойство Enabled нашей кнопки обновления (btnUpdate). Так мы убиваем сразу двух зайцев – нам не нужно вводить дополнительную логическую переменную для флага, и реализация фичи интерфейса – когда нет соединения кнопка обновления будет недоступна.

2. Запрограммируем кнопку «Обновить»
Во-первых, давайте посмотрим как происходит запрос данных

char* quote = ddeAskEurUsd->DdeConv->RequestData(ddeAskEurUsd->DdeItem);
или, что эквивалентно
char* quote = ddeConvAsk->RequestData(ddeAskEurUsd->DdeItem);

Метод RequestData возвращает ответ в виде массива символов char * (не присваивать к AnsiString - объяснения ниже)
 // Обновляем все значения
// EURUSD
char* aeu = ddeAskEurUsd->DdeConv->RequestData(ddeAskEurUsd->DdeItem);
char* beu = ddeBidEurUsd->DdeConv->RequestData(ddeBidEurUsd->DdeItem);
char* heu = ddeHighEurUsd->DdeConv->RequestData(ddeHighEurUsd->DdeItem);
char* leu = ddeLowEurUsd->DdeConv->RequestData(ddeLowEurUsd->DdeItem);
char* teu = ddeTimeEurUsd->DdeConv->RequestData(ddeTimeEurUsd->DdeItem);
// GBPUSD
char* agu = ddeAskGbpUsd->DdeConv->RequestData(ddeAskGbpUsd->DdeItem);
char* bgu = ddeBidGbpUsd->DdeConv->RequestData(ddeBidGbpUsd->DdeItem);
char* hgu = ddeHighGbpUsd->DdeConv->RequestData(ddeHighGbpUsd->DdeItem);
char* lgu = ddeLowGbpUsd->DdeConv->RequestData(ddeLowGbpUsd->DdeItem);
char* tgu = ddeTimeGbpUsd->DdeConv->RequestData(ddeTimeGbpUsd->DdeItem);

Теперь заполняем таблицу. Так выглядит массив ячеек StringGrid. Доступ к ячейке осуществяется как раз через этот массив Cell[col][row]
Размещенное изображение

// Заполняем значения таблицы
// EURUSD
sgBidAsk->Cells[1][1] = aeu;
sgBidAsk->Cells[2][1] = beu;
sgBidAsk->Cells[3][1] = heu;
sgBidAsk->Cells[4][1] = leu;
sgBidAsk->Cells[5][1] = teu;
// GBPUSD
sgBidAsk->Cells[1][2] = agu;
sgBidAsk->Cells[2][2] = bgu;
sgBidAsk->Cells[3][2] = hgu;
sgBidAsk->Cells[4][2] = lgu;
sgBidAsk->Cells[5][2] = tgu;


Ну, и наконец, почистим за собой, уничтожив выделенную память. Метод RequestData сам выделяет память для ответа, а вот удалять ее нужно нам. Если память не чистить произойдет не очень хорошая вещь (точнее очень нехорошая вещь): Memory Leak – утечка памяти – ночной кошмар всех программистов (правда программисты C# и Managed C++ уверяют что «спят спокойно», т.к. за них память чистит Garbage Collector :cool2:).

// уничтожаем выделенную память
StrDispose(aeu);
StrDispose(beu);
StrDispose(heu);
StrDispose(leu);
StrDispose(teu);
StrDispose(agu);
StrDispose(bgu);
StrDispose(hgu);
StrDispose(lgu);
StrDispose(tgu);
Важное замечание: ни в коем случае не присваивайте ответ RequestData к AnsiString (или String) - получите утечку памяти. На первый взгляд может показаться, что менеджер кучи уничтожит выделенную память AnsiString после завершения функции, т.к. переменная локальная для функции. Но не стоит забывать, что оператор = у класса AnsiString перегружен, и на самом деле происходит копирование (а не присваивание адреса массива символов). Т.е. контроллер кучи выполнит свою работу правильно - уничтожит память, выделенную под массив символов, НО для объекта AnsiString вместе с самим объектом, а память, выделенная методом RequestData так и останется в куче не тронутой - вот отсюда утечка памяти. Если это для Вас пока не совсем греческий язык (точнее C++ :wink: ), просто оставьте как у меня.

Ах да, чуть не забыли )). Сделать подписи к таблице.
Для этого

Размещенное изображение

Выберем форму, в инспекторе объектов перейдем на вкладку Events. И кликнем дважды на событии OnCreate. Так мы создадим функцию, которая сработает во время создания формы. В этой функции мы и подпишем таблицу.

 void __fastcall TForm1::FormCreate(TObject *Sender)
{
	// подписи к колонкам
	sgBidAsk->Cells[1][0] = "ASK";
	sgBidAsk->Cells[2][0] = "BID";
	sgBidAsk->Cells[3][0] = "HIGH";
	sgBidAsk->Cells[4][0] = "LOW";
	sgBidAsk->Cells[5][0] = "DATE TIME";
	// подписи к строкам
	sgBidAsk->Cells[0][1] = "EUR/USD";
	sgBidAsk->Cells[0][2] = "GBP/USD";
}
Ну вот вроде и все… Жмем F9 (компилируем, собираем, запускаем) или жмем на эту зеленую кнопку
Размещенное изображение

Наслаждаемся…
Размещенное изображение

Правда, кнопку нужно постоянно тыкать - неудобно. В следующем уроке мы автоматизируем процесс получения котировок.

Самостоятельно:
- Расширить таблицу для других валютных пар.

Вложенные файлы


Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.

#12 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 09 Апрель 2010 - 08:52

Практическое занятие № 4.4. Приложение. Автоматизация по таймеру.
Теперь автоматизируем процесс получения котировок по таймеру и событиям.

Ну, с таймером все просто
Кидаем на форму таймер (находится в той же вкладке, что и компоненты DDE)
Размещенное изображение

Настроим наш таймер
Размещенное изображение

Enabled – флаг запуска. True – таймер запущен, false –таймер остановлен
Interval – Интервал срабатывания в миллисекундах (1000 мс = 1 с).

Перейдем теперь на вкладку Events
Размещенное изображение

Здесь единственное событие OnTimer. Кликнем 2 раза по нему. Теперь пишем код. Вы наверное уже догадались, что код будет идентичным коду по кнопке btnUpdate. Так что просто копируем его.

И еще чтобы все было честно, флаг таймера ставим в false. А в функцию соединения добавим две строчки
void __fastcall TForm1::btnConnectClick(TObject *Sender)
{
	// вызываем методы OpenLink у всех Dde-диалогов
	if (!btnUpdate->Enabled)
	{
		…
		tmrUpdate->Enabled = true; // запуск таймера
	}
	else
	{
		…
		tmrUpdate->Enabled = false; // остановка таймера
	}
	btnUpdate->Enabled = !btnUpdate->Enabled; // меняем флаг подключения, заодно активируем/деактевируем кнопку обновления.
}
А кнопку Обновить мы немного переделаем. По этой кнопке мы будем менять время срабатывания таймера. Кинем на форму объект Edit с панели компонентов Standard.
Размещенное изображение

Настроим наш Edit.
Name = edtTimerInterval
Text = 1000 – укажем значение таймера по умолчанию


А по кнопке btnUpdate напишем следующий код.
tmrUpdate->Interval = edtTimerInterval->Text.ToInt();
Если в поле мы введем не совсем целое число (буквы, знаки и т.п.), то метод ToInt() завершиться с ошибкой и мы увидим следующее сообщение
Размещенное изображение

Если Вы сами хотите обработать эту ситуацию, то воспользуемся конструкцией try…catch()

try
 {
  edtTimerInterval->Text.ToInt();
 }
catch(...) // ловим всех
 {
  MessageBox(Application->Handle, "Введите целое число", "Ошибка ввода", MB_ICONERROR | MB_OK); // сообщение
  return;
 }
* MB_IConerror пишем все буквы в верхнем регистре (наверное какая то особенность обработки символов у робота форума :unsure:)

try…catch() используется для «отлова» некорректных событий. В данном случае мы пытаемся перевести текст введенный в поле edtTimerInterval в число, чтобы можно было его присвоить параметру Interval у таймера. В данном случае мы пользуемся функцией ToInt() класса AnsiString (можно и другими способами). Понятно, что если будет введено нечто другое чем целое число вызов этой функции приведет к ошибке. Поэтому мы отлавливаем эту ошибку с помощью try…catch() и выдаем сообщение об ошибке ввода и возвращаемся из функции не меняя интервал таймера. Если все нормально, т.е. введено целое число и преобразование прошло успешно, то произойдет изменение интервала у таймера.
Замечание:
Если вы запускаете программу из под среды (F9), то сначала выскочит ошибка, генерируемая дебагером, а потом уже наше сообщение. Если программа запускается обычным образом (через exe файл) то сообщений дебагера Вы конечно же не увидите.
Размещенное изображение

Примечание. Также стоит обратить внимание на метод ToIntDef, который (в случае неудачного преобразования) возвращает значение по умолчанию, переданное ему в качестве параметра.

Запускаем. Теперь котировки обновляются сами через каждую секунду (или какой там интервал Вы поставили).

Самостоятельно:
- Расширить таблицу для других валютных пар.

Вложенные файлы


Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.

#13 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 09 Апрель 2010 - 09:37

Практическое занятие № 4.5. Приложение. Автоматизация событиями.
Самый простой и эффективный способ работы с DDE. Зачем тогда я описывал все выше? А это чтобы потом можно было насладиться простотой событий ))... Ну, на самом деле, для общего развития – раз, и вдруг кому понадобится – два.

Таймер, кнопка обновления и поле ввода нам больше не понадобятся!!! Можно удалить. Только будьте аккуратнее (сделайте резервную копию!!!) – сначала удаляете весь код внутри функций у этих объектов. Потом удаляем сами объекты. Вообще удаление объектов в Builder`е – дело неблагодарное – у новичков не получается. Легче переделать проект с нуля, чем удалить, чтобы потом не было ошибок – я видел и такое. Кстати пустые функции автоматически созданные средой (аля, Buttonclick и т.п.) также автоматически удаляются средой после сохранения. Если нужно оставить на будущее просто вставьте комментарий (//).

Если сомневаетесь – не удаляйте, только таймер отключите. А если раздражают – спрячьте их с формы, выставив значение Visible в false.

Кстати когда удалите кнопку btnUpdate, Вам придется переписать алгоритм подключения, вызываемый кнопкой btnConnect. Если помните, мы использовали значение Enabled в качестве флага. Теперь кнопки нет и поле это мы естественно использовать не можем. Можно ввести булевою переменную как флаг… мне лень было, я использовал поле Tag кнопки.
if (btnConnect->Tag == 0)
{
 …
 btnConnect->Tag = 1
}
else
{
 …
 btnConnect->Tag = 0;
}
Можно также вызвать метод StartAdvise у всех объектов ddeConv после открытия соединения, для запуска цикла опроса у всех подключенных айтемов. Но, как показала практика, в этом нет необходимости – что это, опять реализация DDE-компонентов Builder`а?

Итак. Выделим первый DdeClientItem (например, ddеAskEurUsd) и переключимся на вкладку Events в инспекторе объектов.
Размещенное изображение

И создадим событие onchange.
sgBidAsk void __fastcall TForm1::ddeAskEurUsdChange(TObject *Sender)
{
 sgBidAsk->Cells[1][1] = ddeAskEurUsd->Text;
}

И все... Теперь если произойдет изменение в котировке Ask пары EUR/USD у нас автоматически она обновиться в таблице. Правда просто… ))) Только такие события нужно прописать у всех айтемов. Муторно, а если нужно десять валютных пар? В 8 уроке мы будем создавать объекты динамически, прямо из программы будем добавлять и удалять, а может и сортировать валютные пары.

Запускаем и … не наслаждаемся…

Почему данные меняются только у одного столбца?! Почему не работают события?!
События работают, но только у первого открытого DDE-диалога, т.е. DDEConv, у которого первого установили соединение.
Что ж, добро пожаловать в мир глюков Builder`а (будем надеяться, что это именно Builder, а не сервер MT4). Я не знаю почему события onchange срабатывает только у айтемов подключенных к одному определенному диалогу. Вопрос разработчикам компонентов. Ответ можно поискать самому в исходном тексте модуля Ddeman. В Интернете, много людей плюются на реализацию DDE Borland`а, и именно всех заботит вопрос как же обойти это ограничение. Мы не будем биться головой о стену – переписывать модули Builder`а или писать модуль Dde-клиента самим на WinAPI – Windows Application Program Interface – мы поступим по умному (читай по другому).

Кстати, по поводу Вынь Апи – это не самый плохой вариант. Правда придется самим обработчик писать и вклинивать его главную процедуру приложения – но это не трудно. А вот на форуме MetaQuotes нашел подобную реализацию, но автор был очень не доволен работой Dde-сервера MT4. Честно признаться сам не реализовывал, может руки как-нибудь дойдут…

Так, и что теперь? Что делать то? Отказаться от Dde вовсе?!
А теперь главный вопрос: а зачем нам собственно обрабатывать ВСЕ события?
1. Можно брать котировки только Bid или Ask, а остальные данные подгружать вручную, как мы это делали в уроках 3 и 4.
2.Работать с разделом QUOTE, если помните, то при запросе этого раздела возвращаются дата, время, цена спроса и цена предложения в одном ответе. Останется только пропарсить строку. За минимумом и максимумом дня можно следить программно или запрашивать вручную, если эти данные вообще вам нужны.

Итак, я думаю остановиться на варианте №2
Во-первых, меньше будет диалогов и айтемов – уже приятнее.
Во-вторых, (вытекает из во-первых) меньше запросов вручную.

Подведем итоги. Убедились в глючности компонентов DDE - не работают сразу несколько item`ов, подключенных к разным DDE-диалогам. Но, теоретически, нашли решение. Кстати, такого решения этой проблемы я не видел на просторах Интернета (может плохо искал...) - все пытаются переписывать ddeman, писать свои реализации dde-клиентов на winapi, использовать другие методы изврата - я надеюсь мы будем впереди.
В следующем занятии мы реализуем получение ВСЕХ котировок через DDE, причем переписывать будем только СВОЙ код, и то чуть-чуть.

В этом занятии я не буду выкладывать исходники и exe-шник - чего смотреть на то что не работает...
Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.

#14 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 09 Апрель 2010 - 10:03

Практическое занятие № 4.6. Приложение. Автоматизация событиями. (Продолжение)
Приступим к переделке «наших событий».
Во-первых удалим ddeConvAsk и ddeConvAsk, и все подключенные к ним айтемы. Важно: сначала удаляете DdeClientItem`ы, а потом уже DdeConv`ы, иначе ошибок не оберетесь.
Во-вторых поменяем ddeConvTime. Сделаем его ddeConvQuote, соответственно меняем topic на QUOTE. Соответственно меняем его айтемы (ну если хотите)

Не забываем поправить весь код связанный с этими компонентами (компилятор подскажет).

И давайте, наконец, поменяем местами Bid и Ask
void __fastcall TForm1::FormCreate(TObject *Sender)
{
 // подписи к колонкам
 sgBidAsk->Cells[1][0] = "BID";
 sgBidAsk->Cells[2][0] = "ASK";
 …
}
Теперь описываем событие onchange у айтемов, подключенных к ddeConvQuote. По этому событию будем парсить строку с ответом. Я переформатирую еще дату – как-то привычней
AnsiString quote = ddeQouteEurUsd->Text;
// 1. Вытащим дату
// Подстрока до первого символа пробела
AnsiString date = quote.SubString(1, quote.Pos(" ") - 1);
// Удаляем выделенную подстроку
quote.Delete(1, quote.Pos(" "));
// 2. Переформатируем дату
// Выделяем год
AnsiString year = date.SubString(1, date.Pos("/") - 1);
date.Delete(1, date.Pos("/"));
// Выделяем месяц
AnsiString month = date.SubString(1, date.Pos("/") - 1);
date.Delete(1, date.Pos("/"));
// Собираем заново строку даты и записываем ее таблицу
sgBidAsk->Cells[5][1] = date + "." + month + "." + year + " " + quote.SubString(1, quote.Pos(" ") - 1);
// Удаляем время
quote = quote.TrimLeft();
quote.Delete(1, quote.Pos(" "));
// 3. Вытаскиваем bid
sgBidAsk->Cells[1][1] = quote.SubString(1, quote.Pos(" ") - 1);
quote.Delete(1, quote.Pos(" "));
// 4. записываем ask - то что осталось от строки quote
sgBidAsk->Cells[2][1] = quote;
// 5. Запросим котировки максимума и минимума дня
char * max = ddeConvHigh->RequestData(ddeHighEurUsd->DdeItem);
char * min = ddeConvLow->RequestData(ddeHighEurUsd->DdeItem);
sgBidAsk->Cells[3][1] = max;
sgBidAsk->Cells[4][1] = min;
StrDispose(max);
StrDispose(min);

Запускаем, проверяем:
Размещенное изображение

Самостоятельно:
- Остальные валютные пары

Вложенные файлы


Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.

#15 sm1ke

sm1ke

    живет тут

  • Пользователи ST test (off)
  • PipPipPipPipPip
  • 174 сообщений

Опубликовано 09 Апрель 2010 - 10:36

Практическое занятие № 4.7. Раскраска
Теперь раскрасим таблицу: когда котировка приходит выше предыдущей – в зеленый, в противном случае – красный. Берем фломастеры и красим экран – это есть такой миф, как один студент сдавал лабу…
Мы поступим умнее – заставим компьютер красить экран.

Добавим для начала новый элемент интерфейса – группу переключателей - RadioGroup
Размещенное изображение

Параметры RadioGroup:
Name = rgColor
Caption = Цветная таблица
Columns = 3 // в три колонки
ItemIndex = 0 // по умолчанию будет выделено значение «Нет»
Items = Нет Текст Ячейка // пишем каждое слово с новой строки

Расширим возможности нашего сборщика котировок. В зависимости от переколючателя будем менять стиль раскрашивания. Текст – будет означать подкрашивание текста в ячейках, Ячейка – красить будем фон ячейки. Нет, значит нет.
Объявим в классе TForm1 одну функцию
double TextToDouble(AnsiString text);
и один двумерный массив 2х2
double PrevQuote[2][2];

Функция необходима для преобразования котировки к типу с плавающей точкой, чтобы можно было сравнивать (в нашем случае double).
Массив понадобится для хранения предыдущих значений котировок, чтобы было с чем сравнивать. Почему двумерный 2x2? Я использую две валютные пары, у каждой из которых буду хранить две котировки Bid и Ask. У Вас может быть больше валютных пар, тогда массив нужно объявлять как double PrevQuote[N][2], где N - кол-во Ваших валютных пар.

Реализация нашей функции. Сразу мы не можем вызвать метод ToDouble, т.к. в строке с числом нам необходима точка, а не запятая. Вообще-то разделитель целой и дробной части зависит от настроек системы. Как показала практика в разных версиях ОС Windows по умолчанию стоят разные настройки (для России по умолчанию запятая)
Размещенное изображение

Поэтому мы воспользуемся другой функцией strtod. Этой функции из библиотеке stdlib все равно, какой разделитель в строек используется точка или запятая (если в строке будет запятая, то дробная часть будет отброшена, а во второй параметр будет записана отброшенная строка. Но нам не страшно, т.к. данные от DDE-сервера приходят с точкой). Правда, по-хорошему, ее нужно дополнительно обработать, на случай если преобразование закончится неудачно. Но мы этого делать не будем :cool2:
double TForm1::TextToDouble(AnsiString text)
{
 	char* endptr;
	return strtod(text.c_str(), &endptr);
}


Массив нужно проинициализировать, иначе в нем может храниться все что угодно – хотя, нам в принципе это не важно. Инициализация переменных – хороший стиль программирования.

Инициализировать будем в конструкторе класса TForm1. Можете также это сделать в обработчике события FormCreate.
__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner) // конструктор
{
 // цикл по строчкам
 for (int i = 0; i < 2; i++)
 {
 // цикл по столбцам
 for (int j = 0; j < 2; j++)
 {
 	PrevQuote[i][j] = 0.0f;
 }
 }
} 

Теперь позаботимся о заполнении массива. Изменять данные в массиве будем тогда, когда приходит новая котировка Bid или Ask по всем валютным парам.
Понятно, что делать это мы будем в соответствующих обработчиках событий onchange объектов DdeClientItem. Я приведу один пример – остальные допишите сами
…
PrevQuote[0][0] = TextToDouble([sgBidAsk->Cells[1][1]); // записываем в массив предыдущее значение bid 
sgBidAsk->Cells[1][1] = quote.SubString(1, quote.Pos(" ") - 1);
// 3. Вытаскиваем bid
quote.Delete(1, quote.Pos(" "));[b]
PrevQuote[1][0] = TextToDouble(sgBidAsk->Cells[2][1]); // записываем в массив предыдущее значение ask
// 4. записываем ask - то что осталось от строки quote
sgBidAsk->Cells[2][1] = quote;
…
Не забудьте запомнить (о как!) значения для других валютных пар.
Правила Уоррена Баффета
1. Беречь деньги.
2. Помнить первое правило.




Посетителей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных пользователей

Рейтинг брокеров форекс: кто лидер, кто аутсайдер и почему?




Masterforex-V NordFX

Rambler's Top100

Принимаем Z-Payment