MT5とtrans2quik.dll。 - ページ 5

 

MT5とQuickの連携は完全に断念し、Quick(DEEサーバー+trans2quik.dll)のみに落ち着きました。

このプログラムの実現を検討しています。


Selector1は常にDDEサーバーからデータを取得し、Storageに「保存」し、対応するChildでOnTick関数を 呼び出します。

GetStorageDataを呼び出すと、DDEサーバーは一時停止し、データはStorageに保存される必要があります。

Selector2がコールバックを呼び出すと、DDEサーバーとストレージの記録が中断され、GetStorageDataの呼び出しが無効になるはずです。

つまり、Selector2が高優先度、GetStorageDataが通常、Selector1が低優先度ということです。

質問です。

Selector1、Selector2、GetStorageDataを優雅に同期させるにはどうしたらよいですか?

もしかしたら、そのような同期の具体例があるのかもしれません(私はそのようなものを実装したことがありません)。

 
prostotrader:

MT5とQuickの連携を断念し、Quickのみ(DEEサーバ+trans2quik.dll)に落ち着く。

このプログラムの実現を検討しています。

1.Quickだけを残したのは、非常に正しい判断だと思います。

2.DEEによる接続は、非常に議論のあるソリューションです。DDEは不安定だと言う人が多いけど、どうなんだろう。

私見では、より良い、より汎用的なソリューションとして、Lua-DLLアプリケーションを使用します。私はこのオプションを使用しています。もちろん、オーナー次第です。

 
Yuriy Asaulenko:

1.クイックだけを残すという判断は、正しかったと思う。

2.DDEによる通信は、非常に議論のある決定です。DDEは不安定だと言う人が多いのですが、、、どうなんでしょう。

私見では、より良い、より汎用的なソリューションとして、Lua-DLLアプリケーションを使用します。私はこのオプションを使用しています。もちろん、主催者次第です。

昔、Quickquick用の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:

図のトピックで問題を説明しました

すみません、気が付きませんでした。私が正しく理解していれば

MS SQL Serverなどの DBMSをストレージとして 使用することが解決策になります。これは部分的な解決策かもしれません。

もうひとつは、ラスト・イン、ファースト・アウトのような中間バッファ-・コレクションを使用することである。まあ、それとスレッドの分離ですね。

そうすれば、何も止める必要はなく、すべてがバッファに書き込まれるだけである。まあ、DBMSはマルチユーザーアクセスが可能ですしね。

これだけ適用していますが、パスカルとは6から友達ではありません。

PS PascalからNETのライブラリが使えるそうです。Storageとして 使うには、System.DataSystem.Data.Data、System. Data.Data.Dataを使うのが理にかなっているかも しれない。DataSetとSystem.Data.DataTable です。確か、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つのスレッドを同期させる(基本的にはSynchronizerを書く)だけなんだけどね。

どうしたらいいのかわからない。


理由: