MT5和trans2quik.dll - 页 5

 

我已经完全放弃了将MT5和Quick联系起来的想法,我只在Quick(DEE服务器+trans2quik.dll)上安顿下来。

我正在考虑实现这一方案。


选择器1不断地从DDE服务器获取数据,并将其 "存储 "在存储器中,并在相应的子程序中调用OnTick函数

当调用GetStorageData时,DDE服务器应该被暂停,数据应该被存储在Storage中。

当Selector2调用回调时,DDE服务器和存储记录应被暂停,GetStorageData调用应被禁用。

即Selector2有高优先级,GetStorageData正常,Selector1低。

问题。

我怎样才能优雅地同步选择器1、选择器2和GetStorageData?

也许有这种同步的具体例子(我从未实现过这种东西)?

 
prostotrader:

放弃了连接MT5和Quick的想法,只在Quick上解决(DEE服务器+ trans2quik.dll)。

我正在考虑实现这一方案。

1.非常正确的决定,只留下Quick。

2.通过DEE连接是一个非常有争议的解决方案。许多人说DDE是不稳定的,但我不知道。

在我看来,一个更好、更通用的解决方案,是Lua-DLL应用程序。我使用这个选项。当然,这要看主人的意思。

 
Yuriy Asaulenko:

1.只保留快手的决定是正确的。

2.通过DDE通信是一个非常有争议的决定。许多人说,DDE是不稳定的,但是,我不知道。

在我看来,一个更好、更通用的解决方案,是Lua-DLL应用程序。我使用这个选项。当然,这要看主人的意思。

我在很久以前为Quick写了一个DDE服务器--工作顺利,速度够快(不比Lua-DLL慢)。

而且完全没有必要为Lua和DDL数据接收器编写额外的代码。

添加

事实上,我已经写好 了图中所示的程序(而且可以运行),但遇到了一个同步问题。

 
prostotrader:

我在很久以前为Quick写了一个DDE服务器--工作顺利,速度够快(不比Lua-DLL慢)。

而且根本不需要在Lua中编写额外的代码。

由于我没有做过任何DDE,问题是--DDE是如何制作的?我认为有必要制作一个带有数据的表格,然后通过DDE运行它。

有一个与事件相混淆的问题。有些东西已经改变了,看起来整个表被传递给了DDE。还是我错了?

假设我错了。那么如何识别接收方的事件呢?

娼者。

事实上,我已经写好 了图中所示的程序(并且可以运行),但我遇到了一个同步问题。

用什么?

在Lua中,这个问题通过从DLL到任意数据的回调来解决。

 
Yuriy Asaulenko:

由于我没有和DDE打过交道,问题是DDE是如何制造的?看来你必须制作一个有数据的表格,然后通过DDE运行它。

有一个与事件相混淆的问题。有些东西已经改变了,看起来整个表被传递给了DDE。还是我错了?

假设我错了。那么如何识别接收方的事件呢?

在Quick中,所需的表格被生成用于输出。

最后,我们用DDE服务器启动我们自己的应用程序,通过DDE输出这个表。

在第一次从Quick输出时,整个表被发送到DEE,然后只有表中的行被发送到DEE。

在其中发生变化。

在表格本身(它被完整地传送)中,有(例如在我的例子中)一个工具名称--这是标识符。


 

DDE服务器本身有几行字(我是用Pascal语言写的,但互联网上有很多其他语言的例子)。

unit DdeUnit;

interface

uses
  Winapi.Windows, System.Classes, System.Types, Vcl.Forms, Winapi.DdeMl,
  System.SysUtils, System.StrUtils, Vcl.Controls, Winapi.Messages, QTypes;

const
  WM_DDE_ADDQUE = WM_USER + 2;

var
  ServiceName: string = 'quikdde';             // DDE server name

//  procedure ClearTable(Table: TDataTable); overload;

type
  TPokeAction = (paAccept, paPass, paReject);
  TDdePokeEvent = procedure(const Topic: string; var Action: TPokeAction) of object;
  TDdeDataEvent = procedure(const Topic: string; Cells: TRect; Data: TDataTable) of object;

  PDdeQueItem = ^TDdeQueItem;
  TDdeQueItem = packed record
    data: Pointer;
    size: integer;
    sTopic: String;
    sCells: String;
  end;

  TDdeServer = class(TWinControl)
  private
    Inst: Integer;
    ServiceHSz: HSz;
    TopicHSz: HSz;
    fOnPoke: TDdePokeEvent;
    fOnData: TDdeDataEvent;
    fDdeQue: TThreadList;
  protected
    function XLTDecodeV(data: pointer; datasize: integer): TDataTable;
    function DecodeCellAddr(CellAddr: string): TRect;
    procedure AddQue(var Message: TWMSysCommand); message WM_DDE_ADDQUE;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property OnPoke: TDdePokeEvent read fOnPoke write fOnPoke;
    property OnData: TDdeDataEvent read fOnData write fOnData;
  end;

//--- Pointer functions ----
function addp(p: Pointer; increment: int64 = 1): Pointer; overload;
function addp(p: Pointer; increment: pointer): Pointer; overload;
function subp(p: Pointer; decrement: int64 = 1): Pointer; overload;
function subp(p: Pointer; decrement: pointer): Pointer; overload;


implementation
uses main;

function CallbackProc(CallType, Fmt: UINT; Conv: HConv; hsz1, hsz2: HSZ;
                      Data: HDDEData; Data1, Data2: DWORD): HDDEData stdcall;
var
  action: TPokeAction;
  sTopic: String;
  P: PDdeQueItem;
  Buff: array[0..255] of WideChar;
begin
  result:= DDE_FNOTPROCESSED;
//---
  case CallType of
    XTYP_CONNECT: result:= 1;
    XTYP_POKE: begin
      DdeQueryString(QTrader.DdeServer.Inst, HSz1, @Buff,
                                        sizeof(Buff), CP_WINUNICODE);
      sTopic:= string(Buff);
      action:= paPass;
      if Assigned(QTrader.DdeServer.fOnPoke) then
        QTrader.DdeServer.fOnPoke(sTopic, action);
//---
      case action of
        paAccept: begin
          DdeQueryString(QTrader.DdeServer.Inst, HSz2,
                         @Buff, sizeof(Buff), CP_WINUNICODE);
          New(P);
          P^.sTopic:= sTopic;
          P^.sCells:= string(Buff);
          P^.size:= DdeGetData(Data, nil, 0, 0);
          GetMem(P^.data, P^.size);
          DdeGetData(Data, P^.data, P^.size, 0);
//---
          with QTrader.DdeServer.fDdeQue.LockList do
          try
            add(P);
          finally
            QTrader.DdeServer.fDdeQue.UnlockList;
          end;
          if (QTrader.DdeServer.Handle <> 0) then
            PostMessage(QTrader.DdeServer.Handle, WM_DDE_ADDQUE, 0, 0);
          result:= DDE_FACK;
        end;
        paPass: result:=  DDE_FACK;
        paReject: result:=  DDE_FNOTPROCESSED;
      end;
    end;
  end;
end;


constructor TDdeServer.Create;
begin
  inherited;
  fDdeQue := TThreadList.Create;
  Parent := TWinControl(AOwner);
  CreateHandle;
  Inst := 0;
  if (DdeInitialize(Inst, @CallbackProc, APPCLASS_STANDARD, 0) = DMLERR_NO_ERROR) then
  begin
    ServiceHSz := DdeCreateStringHandle(Inst, PWideChar(ServiceName), CP_WINUNICODE);
    if(DdeNameService(Inst, ServiceHSz, 0, DNS_REGISTER) = 0) then
      raise Exception.Create( 'Не удалось зарегистрировать имя DDE-сервиса ''' +
                                                           ServiceName + '''' );
    TopicHSz := DdeCreateStringHandle(Inst, PWideChar('Topic'), CP_WINUNICODE);
  end else
    raise Exception.Create( 'Не удалось выполнить инициализацию DDE' );
end;

destructor TDdeServer.Destroy;
begin
  DdeNameService(Inst, ServiceHsz, 0, DNS_UNREGISTER);
  fDdeQue.Free;
  DdeFreeStringHandle(Inst, ServiceHsz);
  DdeUninitialize(Inst);
  inherited;
end;

procedure TDdeServer.AddQue(var Message: TWMSysCommand);
var
  vt: TDataTable;
  Cells: TRect;
  p: PDdeQueItem;
  i: integer;
begin
  with fDdeQue.LockList do
  try
    p:= PDdeQueItem(Items[Count - 1]);
    Cells:= DecodeCellAddr(p^.sCells);
    vt:= XLTDecodeV(p^.data, p^.size);
    if (Assigned(QTrader.DdeServer.fOnData)) then
      QTrader.DdeServer.fOnData(p^.sTopic, Cells, vt);
    for i:= (Count - 1) downto 0 do
    begin
      p:= Items[i];
      FreeMem(p^.data, p^.size);
      Delete(i);
      Dispose(p);
    end;
  finally
    fDdeQue.UnlockList;
  end;
end;


function TDdeServer.XLTDecodeV(data: pointer; datasize: integer): TDataTable;
var
  i: integer;
  curr: pointer;
  BlockType: word;
  BlockSize: word;
  StringSize: byte;
  RealData: real;
  StringData: shortstring;
  DataNum: integer;
begin
  curr:= addp(data, 4);
  result.RowCount := Word(curr^);
  curr:= addp(curr, 2);
  result.ColCount := Word(curr^);
  curr:= addp(curr, 2);
//--- set array size ---
  SetLength(result.Cells, result.RowCount);
//---
  for i := 0 to result.RowCount - 1 do
    SetLength(result.Cells[i], result.ColCount);
//--- data block --------
  DataNum := 0;
  while(Integer(subp(curr, data)) < datasize) do
  begin
    BlockType:= Word(curr^);
    curr:= addp(curr, 2);
    BlockSize:= Word(curr^);
    curr:= addp(curr, 2);
    case BlockType of
      1: begin
           while(BlockSize > 0) do
           begin
             RealData:= Real(curr^);
             curr:= addp(curr, 8);
             dec(BlockSize, 8);
             result.Cells[(DataNum div result.ColCount),
                          (DataNum mod result.ColCount)]:= FloatToStr(RealData);
             inc(DataNum);
           end;
         end;
      2: begin
           while(BlockSize > 0) do
           begin
             StringSize:= Byte(curr^);
             curr:= addp( curr );
             StringData[0]:= AnsiChar(Chr(StringSize));
//---
             for i:= 1 to StringSize do
             begin
               StringData[i]:= AnsiChar(Char(curr^));
               curr:= addp(curr);
             end;
             result.Cells[(DataNum div result.ColCount),
                          (Datanum mod result.ColCount)]:= string(StringData);
             inc(DataNum);
             dec(BlockSize, StringSize + 1);
           end;
         end;
    end;
  end;
end;


function TDdeServer.DecodeCellAddr( CellAddr: string ): TRect;
var
  tmp1, tmp2: integer;
begin
  CellAddr:= UpperCase(CellAddr);
  tmp1:= PosEx('R', CellAddr);
  tmp2:= PosEx('C', CellAddr, tmp1);
  try
    result.Top:= StrToInt(copy(CellAddr, tmp1 + 1, tmp2 - tmp1 - 1)) - 1;
  except
    exit;
  end;
  tmp1:= PosEx('R', CellAddr, tmp2);
  try
    Result.Left:= StrToInt(copy(CellAddr, tmp2 + 1, tmp1 - tmp2 - 1 - 1)) - 1;
  except
    exit;
  end;
  tmp2:= PosEx('C', CellAddr, tmp1);
  try
    result.Bottom:= StrToInt(copy(CellAddr, tmp1 + 1, tmp2 - tmp1 - 1)) - 1;
    result.Right:= StrToInt(copy(CellAddr, tmp2 + 1, Length(CellAddr) - tmp2)) - 1;
  except
    exit;
  end;
end;

function addp(P: Pointer; increment: int64 = 1): Pointer; overload;
begin
  result:= Pointer(Int64(p) + increment);
end;

function addp(P: Pointer; increment: Pointer): Pointer; overload;
begin
  result:= Pointer(Int64(p) + Int64(increment));
end;

function subp(P: Pointer; decrement: int64 = 1): Pointer; overload;
begin
  result:= Pointer(Int64(p) - decrement);
end;

function subp(P: Pointer; decrement: Pointer): Pointer; overload;
begin
  result:= Pointer(Int64(p) - Int64(decrement));
end;

end.
 

通过工具名称创建一个子窗口(如MT 5)。


 
Yuriy Asaulenko:

用什么?


我在图示主题中描述了这个问题

 
prostotrader:

我在图示主题中描述了这个问题

对不起,没有意识到。如果我理解正确的话。

我认为,解决方案是使用DBMS作为存储,例如MS SQL Server。这可能是一个部分解决方案。

第二种是使用中间的缓冲区--收集,如后进先出。嗯,还有分离线程。

那么就不需要停止任何事情,一切都简单地写入缓冲区。嗯,而且DBMS有多用户访问。

我适用这一切,但我和帕斯卡尔从6岁起就不是朋友。

PS 他们说你可以从Pascal中使用NET库。要作为存储使用,使用System.DataSystem.DataDataSetSystem.Data数据表。我记得,在DataTable 中,多用户访问没有问题。

ZZY2 现在我正在尝试使用SQLite作为数据库,但还没有明确的结果。它当然不是一个DBMS,但在一个精简的形式下,多用户访问是可能的,而且有可能在内存中创建一个数据库。

System.Data Namespace
System.Data Namespace
  • douglaslMS
  • docs.microsoft.com
Пространство имен обеспечивает доступ к классам, представляющим архитектуру ADO.NET. The namespace provides access to classes that represent the ADO.NET architecture. ADO.NET позволяет создавать…
 

不,我只需要同步3个线程(基本上写一个同步器),但

我不知道如何。


原因: