MT5 ve trans2quik.dll - sayfa 5

 

MT5 ile Quick'i bağlama fikrinden tamamen vazgeçtim, sadece Quick'te durdum (DEE server + trans2quik.dll)

Bu programı düşünüyorum.


Selector1, DDE sunucusundan sürekli olarak veri alır ve bunları Depolamada "depolar" ve ayrıca ilgili Child'da OnTick işlevini çağırır.

GetStorageData'nın çağrılması, DDE sunucusunu duraklatmalı ve Depolama'ya yazmalıdır.

Ve Selector2'den Geri Arama geldiğinde, hem DDE sunucusu hem de Depolamaya yazma askıya alınmalı ve GetStorageData çağrısı engellenmelidir.

Onlar. Selector2 yüksek önceliğe sahiptir, GetStorageData normaldir ve Selector1 düşüktür.

Sorular:

Selector1, Selector2 ve GetStorageData'nın çalışması nasıl doğru şekilde senkronize edilir?

Belki mevcut senkronizasyonun belirli örnekleri vardır (böyle bir şeyi hiç uygulamadım)?

 
prostotrader :

MT5 ve Quick'i bağlama fikrini terk etti, yalnızca Quick'e yerleşti (DEE sunucusu + trans2quik.dll)

Bu programı düşünüyorum.

1. Sadece Hızlı ayrılmak için doğru kararı puanlar.

2. DDE aracılığıyla iletişim çok tartışmalı bir karardır. Birçok kişi DDE'nin kararsız olduğunu söylüyor ama ben bilmiyorum.

Bana göre en iyi ve çok yönlü çözüm Lua DLL uygulamasıdır. Bu seçeneği kullanıyorum. Tabii ki, bu bir iş meselesi.

 
Yuriy Asaulenko :

1. Sadece Hızlı ayrılmak için doğru kararı puanlar.

2. DDE aracılığıyla iletişim çok tartışmalı bir karardır. Birçok kişi DDE'nin kararsız olduğunu söylüyor ama ben bilmiyorum.

Bana göre en iyi ve çok yönlü çözüm Lua DLL uygulamasıdır. Bu seçeneği kullanıyorum. Tabii ki, bu bir iş meselesi.

Uzun zaman önce Quick için bir DDE sunucusu yazdım - hatasız çalışıyor ve yeterince hızlı (Lua - DLL'den daha yavaş değil),

aynı zamanda, Lua ve DDL veri alıcısına ek kod yazmak hiç de gerekli değildir.

Katma

Aslında şemada gösterilen programı zaten yazdım (ve çalışıyor), ancak bir senkronizasyon sorunuyla karşılaştım.

 
prostotrader :

Uzun zaman önce Quick için bir DDE sunucusu yazdım - hatasız çalışıyor ve yeterince hızlı (Lua - DLL'den daha yavaş değil),

Lua'da ek kod yazmak hiç gerekli değildir.

Çünkü DDE dahil değildi, soru şu ki - DDE nasıl yapılır? Orada olduğu gibi, tabloyu verilerle yapmak ve ardından DDE ile başlatmak gerekiyor.

Olaylarda bir yanlışlık var. Bir şeyler değişti ve tüm tablo DDE aracılığıyla gönderilmiş gibi görünüyor. Yoksa yanılıyor muyum?

Diyelim ki yanıldım. O zaman alıcı taraftaki olay nasıl belirlenir?

basit tüccar :

Aslında şemada gösterilen programı zaten yazdım (ve çalışıyor), ancak bir senkronizasyon sorunuyla karşılaştım.

Neyle?

Lua ile bu sorun DLL'den rastgele verilere yapılan geri aramalarla çözülür.

 
Yuriy Asaulenko :

Çünkü DDE dahil değildi, soru şu ki - DDE nasıl yapılır? Orada olduğu gibi, tabloyu verilerle yapmak ve ardından DDE ile başlatmak gerekiyor.

Olaylarda bir yanlışlık var. Bir şeyler değişti ve tüm tablo DDE aracılığıyla gönderilmiş gibi görünüyor. Yoksa yanılıyor muyum?

Diyelim ki yanıldım. O zaman alıcı taraftaki olay nasıl belirlenir?

Quick'da çıktı için gerekli tablo oluşturulur.

DDE sunucusu içeren uygulamanız başlatıldı, bu tabloyu DDE ile görüntülüyoruz

Quick'ten ilk çıktıda, tüm tablo DEE'ye aktarılır ve ardından tablodan sadece bir satır,

hangi değişiklikler meydana geldi.

Dizenin kendisinde (tam olarak iletilir) enstrümanın adı (örneğin benim için) vardır - bu tanımlayıcıdır


 

DDE sunucusunun kendisi birkaç satırdır (Pascal'da var, ancak internette başka dillerde birçok örnek var)

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.
 

Enstrümanın adıyla bir alt pencere oluşturulur (MT 5'te olduğu gibi).


 
Yuriy Asaulenko :

Neyle?


Sorunu bir konuda diyagramla anlattım

 
prostotrader :

Sorunu bir konuda diyagramla anlattım

Üzgünüm, farketmedim. Eğer doğru anladıysam, o zaman:

IMHO, çözüm, MS SQL Server diyelim , Depolama olarak bir DBMS'nin kullanılmasıdır. Bunun kısmi bir çözüm olması mümkündür.

İkincisi, son giren ilk çıkar gibi ara toplama arabelleklerinin kullanılması. Peki, ve akışların bölünmesi.

O zaman hiçbir şeyi durdurmanıza gerek yok, her şey sadece arabelleğe yazılır. DBMS'nin çok kullanıcılı erişimi var.

Bunların hepsini kullanıyorum ama 6'sından beri Pascal ile arkadaş olmadım.

PS Pascal'dan NET kitaplıklarının kullanılabileceğini söylüyorlar. Depolama olarak kullanmak için yapabilirsiniz. kullanmak mantıklı   System.Data , System.Data . DataSet ve System.Data . veri tablosu Hatırladığım kadarıyla DataTable'a çoklu kullanıcı erişiminde herhangi bir sorun yoktu.

ZY2 Şimdi SQLite'ı veritabanı olarak kullanmaya çalışıyorum ama henüz kesin bir sonuç yok. Ve bu kesinlikle bir DBMS değildir, ancak kesilmiş bir biçimde, çok kullanıcılı erişim mümkündür ve bellekte bir veritabanı oluşturmak mümkündür.

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 позволяет создавать…
 

Hayır, sadece 3 iş parçacığının çalışmasını senkronize etmeniz gerekiyor (aslında bir Senkronizatör yazın), ancak

Nasıl bilmiyorum.


Neden: