MT5 및 trans2quik.dll - 페이지 5

 

MT5와 Quick을 연결한다는 생각을 완전히 포기하고, Quick(DEE 서버 + trans2quik.dll)에서만 멈췄습니다.

이 프로그램을 고려하고 있습니다.


Selector1은 DDE 서버에서 지속적으로 데이터를 수신하여 Storage에 "저장"하고 해당 자식에서 OnTick 함수를 호출합니다 .

GetStorageData를 호출하면 DDE 서버를 일시 중지하고 Storage에 기록해야 합니다.

Selector2에서 콜백이 도착하면 DDE 서버와 Storage에 대한 쓰기가 모두 일시 중단되고 GetStorageData 호출이 차단되어야 합니다.

저것들. Selector2는 높은 우선 순위, GetStorageData는 보통, Selector1은 낮습니다.

질문:

Selector1, Selector2 및 GetStorageData의 작업을 올바르게 동기화하는 방법은 무엇입니까?

현재 동기화의 특정 예가 있을 수 있습니다(나는 그런 것을 구현한 적이 없음)?

 
prostotrader :

MT5와 Quick을 연결한다는 생각을 버리고, Quick(DEE 서버 + trans2quik.dll)으로만 정착

이 프로그램을 고려하고 있습니다.

1. 퀵만 떠나는 것이 옳은 결정입니다.

2. DDE를 통한 통신은 매우 논란의 여지가 있는 결정입니다. 많은 분들이 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로 전송된 다음 테이블의 행만 전송됩니다.

변화가 일어난 곳.

문자열 자체(전체 전송됨)에는 악기 이름이 있습니다(예: 나를 위해). 이것은 식별자입니다.


 

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 :

다이어그램으로 주제에 문제를 설명했습니다.

죄송합니다, 깨닫지 못했습니다. 내가 올바르게 이해하면 다음과 같이됩니다.

IMHO, 솔루션은 DBMS를 스토리지 로 사용하는 것입니다 (예: MS SQL Server) . 이것은 부분적인 해결책일 가능성이 있습니다.

둘째, 후입선출과 같은 중간 수집 버퍼의 사용입니다. 글쎄, 그리고 흐름의 분할.

그런 다음 아무 것도 중지할 필요가 없으며 모든 것이 단순히 버퍼에 기록됩니다. DBMS에는 다중 사용자 액세스가 있습니다.

이거 다 쓰는데 6일부터 파스칼이랑 친구가 안되네요.

PS Pascal에서 NET 라이브러리를 사용할 수 있다고 합니다. 저장소 로 사용하려면 할 수 있습니다. 사용하는 것이 합리적입니다   System.Data , System.Data . DataSetSystem.Data . 데이터 테이블 . 내가 기억하는 한 DataTable 에 대한 다중 사용자 액세스에는 문제가 없었습니다.

ZY2 이제 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개의 쓰레드의 작업을 동기화하기만 하면 됩니다(사실, Synchronizer 작성). 하지만

방법을 모르겠어.