Уф... видать никого данная тема не интересует.
Тогда конкретный вопрос разработчикам. Скорее всего, даже Renat'у, так как, насколько я понял, реализация DDE в МТ4 - его рук дело.
В каких случаях МТ4 выдает указанную ошибку?
Если не сочтете это наглостью, то можете просто привести кусок кода из обработчика исключений, так чтоб мне было понятно, в каком месте мне руки рашпилем подточить.
С уважением,
Дмитрий
Тогда конкретный вопрос разработчикам. Скорее всего, даже Renat'у, так как, насколько я понял, реализация DDE в МТ4 - его рук дело.
В каких случаях МТ4 выдает указанную ошибку?
Если не сочтете это наглостью, то можете просто привести кусок кода из обработчика исключений, так чтоб мне было понятно, в каком месте мне руки рашпилем подточить.
С уважением,
Дмитрий
Не знаю как там на C, но на Delphi это прекрасно работает, притом использую это эдак с версии 180-й....
Вот пример работающего класса, который берет котировки.
Важно: работает в активном режиме, т.е. запускать метод RequestMTBidAsk нужно из-под таймера.
Вызывающая сторона хранит предыдущие котировки и заботится о том, чтобы распознать именение котировок.
type
TprBA = record
Bid: Currency;
Ask: Currency;
end;
{ TprMTAsker - Запросы в МТ на котировки }
TprMTAsker = class(TPersistent)
private
FNoDataCounter: Integer;
FOnDataModeChanged: TNotifyEvent;
protected
DdeClientConv: TDdeClientConv;
DdeClientItem: TDdeClientItem;
public
constructor Create(const AOwner: TComponent); virtual;
destructor Destroy; override;
function RequestMTBidAsk(const ASymbolName: string; var ABA: TprBA): Boolean;
property OnDataModeChanged: TNotifyEvent read FOnDataModeChanged write FOnDataModeChanged;
property NoDataCounter: Integer read FNoDataCounter;
end;
{ TprMTAsker }
constructor TprMTAsker.Create(const AOwner: TComponent);
begin
inherited Create;
FNoDataCounter := 0;
DdeClientConv := TDdeClientConv.Create(nil);
DdeClientConv.ConnectMode := ddeAutomatic;
DdeClientItem := TDdeClientItem.Create(nil);
DdeClientItem.DdeConv := DdeClientConv;
end;
destructor TprMTAsker.Destroy;
begin
if Assigned(DdeClientConv) then
DdeClientConv.CloseLink;
if Assigned(DdeClientItem) then
begin
DdeClientItem.DdeConv := nil;
FreeAndNil(DdeClientItem);
end;
if Assigned(DdeClientConv) then FreeAndNil(DdeClientConv);
inherited Destroy;
end;
{ ASymbolName - строковое значение символа (например 'EURUSD')
ABA - запись с котировкой, уходящая вверх в вызывающий метод.
Внутри ведется счетчик неудачных попыток получить котировки, при превышении им
некоторого порога запускается эвент }
function TprMTAsker.RequestMTBidAsk(const ASymbolName: string; var ABA: TprBA): Boolean;
var
FStr, FBid, FAsk: string;
FPos, FPriorNoDataCounter: Integer;
begin
Result := False;
try
try
if (DdeClientConv.ConnectMode <> ddeAutomatic) then
DdeClientConv.ConnectMode := ddeAutomatic;
DdeClientConv.SetLink('MT4', 'QUOTE');
DdeClientItem.DdeItem := ASymbolName;
FStr := DdeClientItem.Text;
DdeClientConv.CloseLink;
if (FStr <> '') then
begin
FPos := Pos(':', FStr);
if (FPos > 0) then
begin
FStr := Trim(DelStartStr2(FStr, FPos + 3));
FBid := Trim(ExtractStrUntilChar(FStr, ' '));
FAsk := Trim(DelStartStr(FStr, FBid + ' '));
if IsValidStringForDouble(FBid) and IsValidStringForDouble(FAsk) then
begin
ABA.Bid := StrToFloat(FBid);
ABA.Ask := StrToFloat(FAsk);
Result := True;
FPriorNoDataCounter := FNoDataCounter;
FNoDataCounter := 0;
if (FPriorNoDataCounter <> 0) then
begin
if Assigned(OnDataModeChanged) then
OnDataModeChanged(Self);
end;
end;
end;
end
else
begin
FNoDataCounter := FNoDataCounter + 1;
if (FNoDataCounter > 100) then
FNoDataCounter := 100;
if (FNoDataCounter > 10) then
begin
if Assigned(OnDataModeChanged) then
OnDataModeChanged(Self);
end;
end;
finally
DdeClientConv.CloseLink;
end;
except
end;
end;
{ TprMTAsker }
--
IsValidStringForDouble, DelStartStr2, ExtractStrUntilChar, DelStartStr - некоторые кустомные функции работы со строками, заменить потом на свои.
Вот пример работающего класса, который берет котировки.
Важно: работает в активном режиме, т.е. запускать метод RequestMTBidAsk нужно из-под таймера.
Вызывающая сторона хранит предыдущие котировки и заботится о том, чтобы распознать именение котировок.
type
TprBA = record
Bid: Currency;
Ask: Currency;
end;
{ TprMTAsker - Запросы в МТ на котировки }
TprMTAsker = class(TPersistent)
private
FNoDataCounter: Integer;
FOnDataModeChanged: TNotifyEvent;
protected
DdeClientConv: TDdeClientConv;
DdeClientItem: TDdeClientItem;
public
constructor Create(const AOwner: TComponent); virtual;
destructor Destroy; override;
function RequestMTBidAsk(const ASymbolName: string; var ABA: TprBA): Boolean;
property OnDataModeChanged: TNotifyEvent read FOnDataModeChanged write FOnDataModeChanged;
property NoDataCounter: Integer read FNoDataCounter;
end;
{ TprMTAsker }
constructor TprMTAsker.Create(const AOwner: TComponent);
begin
inherited Create;
FNoDataCounter := 0;
DdeClientConv := TDdeClientConv.Create(nil);
DdeClientConv.ConnectMode := ddeAutomatic;
DdeClientItem := TDdeClientItem.Create(nil);
DdeClientItem.DdeConv := DdeClientConv;
end;
destructor TprMTAsker.Destroy;
begin
if Assigned(DdeClientConv) then
DdeClientConv.CloseLink;
if Assigned(DdeClientItem) then
begin
DdeClientItem.DdeConv := nil;
FreeAndNil(DdeClientItem);
end;
if Assigned(DdeClientConv) then FreeAndNil(DdeClientConv);
inherited Destroy;
end;
{ ASymbolName - строковое значение символа (например 'EURUSD')
ABA - запись с котировкой, уходящая вверх в вызывающий метод.
Внутри ведется счетчик неудачных попыток получить котировки, при превышении им
некоторого порога запускается эвент }
function TprMTAsker.RequestMTBidAsk(const ASymbolName: string; var ABA: TprBA): Boolean;
var
FStr, FBid, FAsk: string;
FPos, FPriorNoDataCounter: Integer;
begin
Result := False;
try
try
if (DdeClientConv.ConnectMode <> ddeAutomatic) then
DdeClientConv.ConnectMode := ddeAutomatic;
DdeClientConv.SetLink('MT4', 'QUOTE');
DdeClientItem.DdeItem := ASymbolName;
FStr := DdeClientItem.Text;
DdeClientConv.CloseLink;
if (FStr <> '') then
begin
FPos := Pos(':', FStr);
if (FPos > 0) then
begin
FStr := Trim(DelStartStr2(FStr, FPos + 3));
FBid := Trim(ExtractStrUntilChar(FStr, ' '));
FAsk := Trim(DelStartStr(FStr, FBid + ' '));
if IsValidStringForDouble(FBid) and IsValidStringForDouble(FAsk) then
begin
ABA.Bid := StrToFloat(FBid);
ABA.Ask := StrToFloat(FAsk);
Result := True;
FPriorNoDataCounter := FNoDataCounter;
FNoDataCounter := 0;
if (FPriorNoDataCounter <> 0) then
begin
if Assigned(OnDataModeChanged) then
OnDataModeChanged(Self);
end;
end;
end;
end
else
begin
FNoDataCounter := FNoDataCounter + 1;
if (FNoDataCounter > 100) then
FNoDataCounter := 100;
if (FNoDataCounter > 10) then
begin
if Assigned(OnDataModeChanged) then
OnDataModeChanged(Self);
end;
end;
finally
DdeClientConv.CloseLink;
end;
except
end;
end;
{ TprMTAsker }
--
IsValidStringForDouble, DelStartStr2, ExtractStrUntilChar, DelStartStr - некоторые кустомные функции работы со строками, заменить потом на свои.
FZfin:
Не знаю как там на C, но на Delphi это прекрасно работает, притом использую это эдак с версии 180-й....
Вот пример работающего класса, который берет котировки.
Спасибо, камрад, за помощь, но твой выстрел, мягко говоря, не в ту сторону.Не знаю как там на C, но на Delphi это прекрасно работает, притом использую это эдак с версии 180-й....
Вот пример работающего класса, который берет котировки.
Тем более, что вот это:
FZfin:
Важно: работает в активном режиме, т.е. запускать метод RequestMTBidAsk нужно из-под таймера.
Вызывающая сторона хранит предыдущие котировки и заботится о том, чтобы распознать именение котировок.
ИМХО, не есть правильно.Важно: работает в активном режиме, т.е. запускать метод RequestMTBidAsk нужно из-под таймера.
Вызывающая сторона хранит предыдущие котировки и заботится о том, чтобы распознать именение котировок.
ЗЫ: с проблемой разобрался сам. Всем спасибо.
![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
Вы упускаете торговые возможности:
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Регистрация
Вход
Вы принимаете политику сайта и условия использования
Если у вас нет учетной записи, зарегистрируйтесь
В настоящий момент занимаюсь написанием некоей приблуды для МТ4. Для обновления котировки инструмента использую горячее соединение DDE.
После посылки серверу сообщения WM_DDE_UNADVISE, МТ4 в журнале выдает следующее сообщение: DDE: stop advise failed for '' (в конце именно две одинарные кавычки)
WM_DDE_UNADVISE посылаю следующим образом:
пробовал так:
результат тот же.
Привожу фрагмент кода оконной процедуры окна-клиента (весь код не влез и пришлось убрать из него инициализацию горячего DDE-соединения и обработку сообщения WM_DDE_DATA)
ЗЫ: опыт разработки программ на чистом WinAPI - довольно богатый, но с DDE работаю впервые, так что сильно по почкам не бейте :)
Писал на основе примера Петцольда