05
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
--
>>
<<
--
LATEST ENTRY
CATEGORY
ARCHIVE
PROFILE
SEARCH
RECENT COMMENT
MOBILE
qrcode
OTHERS
<< 【QUICKFIX】 FX自動売買への道 20 【レート要求情報の保存】 | top | 【QUICKFIX】 FX自動売買への道 22 【レート情報取得が拒否られた時(MarketDataRequestReject)】 >>
スポンサーサイト

一定期間更新がないため広告を表示しています

スポンサードリンク | - | | - | - |
【QUICKFIX】 FX自動売買への道 21 【レート情報の取得(MarketDataIncrementalRefresh)】
レート要求をDB管理できるようになったので、どんどん送られてくる
レート情報を取得してDB保存するように改造です。

ついでにレート情報を表示する簡易WEBページも作っときます。
 

■レート情報テーブルを作成
レートの最新値のみを保持するテーブル market_data を作ります
いわゆる「板」は入れない想定
「板」用のテーブルはあとで作成予定
ローソク用の始値・高値・安値・終値のテーブルも後で

    DROP TABLE IF EXISTS market_data;
    CREATE TABLE market_data
    (
      `Symbol`           VARCHAR(7)    NOT NULL                  COMMENT '55',
      `Type`             TINYINT(1)    NOT NULL                  COMMENT '269 0=Bid,1=Offer..',
      `Action`           TINYINT(1)    NOT NULL                  COMMENT '279 0=New,2=Del',
      `Px`               REAL          NULL                      COMMENT '270',
      `Size`             INT UNSIGNED  NULL                      COMMENT '271',
      `Orders`           INT UNSIGNED  NULL                      COMMENT '346',
      `RespDateTime`     CHAR(21)      NULL                      COMMENT '52',
      PRIMARY KEY (`Symbol`, `Type`)
    ) ENGINE = InnoDB
    COMMENT 'MarketData Depth=TOP';



レートの過去情報を保持する履歴テーブル market_data_hist も作成
このテーブルは膨大なレコード数になることが想定されるので、BIGINT で

    DROP TABLE IF EXISTS market_data_hist;
    CREATE TABLE market_data_hist
    (
      `id`            BIGINT UNSIGNED  NOT NULL  AUTO_INCREMENT,
      `Symbol`           VARCHAR(7)    NOT NULL                  COMMENT '55',
      `Type`             TINYINT(1)    NOT NULL                  COMMENT '269 0=Bid,1=Offer..',
      `Action`           TINYINT(1)    NOT NULL                  COMMENT '279 0=New,2=Del',
      `Px`               REAL          NULL                      COMMENT '270',
      `Size`             INT UNSIGNED  NULL                      COMMENT '271',
      `Orders`           INT UNSIGNED  NULL                      COMMENT '346',
      `Created_at`       DATETIME      NOT NULL  DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`),
      INDEX idx_MD_hist_Sym_Type (`Symbol`, `Type`),
      INDEX idx_MD_hist_Crete (`Created_at`)
    ) ENGINE = InnoDB
    COMMENT 'MarketData Depth=TOP';




履歴テーブルにデータを転記するためのトリガーも作成
こっちは UPDATE のタイミングのみにしときます
(INSERT なんて通貨の最初の1回だけだし..)

    DROP TRIGGER IF EXISTS trg_bf_u_market_data;
    DELIMITER //
    CREATE TRIGGER trg_bf_u_market_data
      BEFORE UPDATE ON market_data FOR EACH ROW
    BEGIN
      INSERT INTO market_data_hist SET 
        `Symbol`       = NEW.`Symbol`  
      , `Type`         = NEW.`Type`    
      , `Action`       = NEW.`Action`  
      , `Px`           = NEW.`Px`      
      , `Size`         = NEW.`Size`    
      , `Orders`       = NEW.`Orders`  
      ;
    END;
    //
    DELIMITER ;





■プログラム(MarketDataIncrementalRefresh)
レート情報は < X > MarketDataIncrementalRefresh でどんどん送られてくるので
どんどんDBへ格納します。
でも、まだ TOP の情報のみしか扱いません。
「板」情報は。。そのうち対応します。。

tradeclient]# cat MarketDataIncrementalRefresh.cpp
        #include "config.h"
        #include "Application.h"
        #include <iostream>

        /* < X > */
        void Application::onMessage( const FIX44::MarketDataIncrementalRefresh& message, const FIX::SessionID& sessionID )
        {
          const FIX::Dictionary& dic = m_settings.get( sessionID );
          if (dic.has("Debug_X_MarketDataIncrementalRefresh"))
            std::cout << message.toXML() << std::endl;

          /* init message data */
          /* 52   */ FIX::FieldBase respDateTime(FIX::FIELD::SendingTime, "");
          /* 262  */ FIX::FieldBase reqID(FIX::FIELD::MDReqID, "NA");

          /* get message data */
          /* 52  */ message.getHeader().getFieldIfSet(respDateTime);
          /* 262 */ message.getFieldIfSet(reqID);

          /* Update RespDateTime */
          std::ostringstream u;
       u << "UPDATE `market_data_request` SET " <<
            "          `RespDateTime` = '"  << respDateTime << "' " <<
            "    WHERE `ReqID` = '" << reqID << "' ";
          MySQLExec( u.str() );

          /* Get Request */
          std::ostringstream s;
       s << "SELECT `Depth` " <<
                "    , `Aggregate` " <<
                "  FROM `market_data_request` " <<
                " WHERE `ReqID` = '" << reqID << "' ";
          FIX::MySQLQuery q = MySQLExec( s.str() );

          /* depth = TOP */
       if (std::string(q.getValue(0,0)) == "1")
            InsertMarketData( message );
        }

        void Application::InsertMarketData( const FIX::Message& message )
        {
          /* 52   */ FIX::FieldBase respDateTime(FIX::FIELD::SendingTime, "");
          /* 268  */ FIX::FieldBase entries(FIX::FIELD::NoMDEntries, "0");

          /* 52  */ message.getHeader().getFieldIfSet(respDateTime);
          /* 268 */ message.getFieldIfSet(entries);

          /* Loop: Instrment Group */
          for( int i=1; i <= std::stoi(entries.getString()); i++ )
          {
            /* 146  */ FIX44::MarketDataIncrementalRefresh::NoMDEntries e;
            message.getGroup(i, e);

            /* init group data */
            /* 278  */ FIX::FieldBase entryID(FIX::FIELD::MDEntryID, "0");
            /* 55   */ FIX::FieldBase symbol(FIX::FIELD::Symbol, "NA");

            /* 269  */ FIX::FieldBase type(FIX::FIELD::MDEntryType, "0");
            /* 279  */ FIX::FieldBase action(FIX::FIELD::MDUpdateAction, "0");

            /* 270  */ FIX::FieldBase px(FIX::FIELD::MDEntryPx, "0");
            /* 271  */ FIX::FieldBase size(FIX::FIELD::MDEntrySize, "0");
            /* 346  */ FIX::FieldBase orders(FIX::FIELD::NumberOfOrders, "0");

            /* get group data */
            e.getFieldIfSet(entryID);

            e.getFieldIfSet(symbol);
            e.getFieldIfSet(type);
            e.getFieldIfSet(action);

            e.getFieldIfSet(px);
            e.getFieldIfSet(size);
            e.getFieldIfSet(orders);


            std::ostringstream s2;
            s2 <<  "INSERT INTO `market_data` SET " <<
              /*-55   */  "   `Symbol`        = '" << symbol        << "' " <<
              /*-269  */  " , `Type`          = '" << type          << "' " <<
              /*-279  */  " , `Action`        = '" << action        << "' " <<
              /*-270  */  " , `Px`            = '" << px            << "' " <<
              /*-271  */  " , `Size`          = '" << size          << "' " <<
              /*-346  */  " , `Orders`        = '" << orders        << "' " <<
              /*-52   */  " , `RespDateTime`  = '" << respDateTime  << "' " <<
            " ON DUPLICATE KEY UPDATE " <<
              "  `Symbol`       =  VALUES(`Symbol`) "      <<
              " ,`Type`         =  VALUES(`Type`) "        <<
              " ,`Action`       =  VALUES(`Action`) "      <<
              " ,`Px`           =  VALUES(`Px`) "          <<
              " ,`Size`         =  VALUES(`Size`) "        <<
              " ,`Orders`       =  VALUES(`Orders`) "      <<
              " ,`RespDateTime` =  VALUES(`RespDateTime`)";

            MySQLExec( s2.str() );
          }
        }

     /* - message sample Depth = TOP ---
          <body>
            <field number="262"><![CDATA[USD/JPY]]></field>
            <field number="268"><![CDATA[5]]></field>
            <group>
              <field number="279"><![CDATA[0]]></field>
              <field number="269"><![CDATA[0]]></field>                 -Bid
              <field number="278"><![CDATA[1]]></field>
              <field number="55"><![CDATA[USD/JPY]]></field>
              <field number="270"><![CDATA[108.05]]></field>
              <field number="271"><![CDATA[2000000]]></field>
              <field number="346"><![CDATA[1]]></field>
            </group>
            <group>
              <field number="279"><![CDATA[0]]></field>
              <field number="269"><![CDATA[1]]></field>                 -Offer
              <field number="278"><![CDATA[2]]></field>
              <field number="55"><![CDATA[USD/JPY]]></field>
              <field number="270"><![CDATA[108.055]]></field>
              <field number="271"><![CDATA[1000000]]></field>
              <field number="346"><![CDATA[1]]></field>
            </group>
            <group>
              <field number="279"><![CDATA[0]]></field>
              <field number="269"><![CDATA[7]]></field>                 -High
              <field number="278"><![CDATA[3]]></field>
              <field number="55"><![CDATA[USD/JPY]]></field>
              <field number="270"><![CDATA[108.328]]></field>
            </group>
            <group>
              <field number="279"><![CDATA[0]]></field>
              <field number="269"><![CDATA[8]]></field>                 -Low
              <field number="278"><![CDATA[4]]></field>
              <field number="55"><![CDATA[USD/JPY]]></field>
              <field number="270"><![CDATA[107.88]]></field>
            </group>
            <group>
              <field number="279"><![CDATA[0]]></field>
              <field number="269"><![CDATA[9]]></field>                 -Spread
              <field number="278"><![CDATA[5]]></field>
              <field number="55"><![CDATA[USD/JPY]]></field>
              <field number="270"><![CDATA[0.5000]]></field>
            </group>
          </body>
        */

ーーーーーーーーーーーーーーー
レート情報取得時間を保存
要求タイプをDBから取り出し
要求タイプが TOP の時だけ、レート情報をDBへ保存
参考までにメッセージ内容を。。


ヘッダーファイルも
tradeclient]# vi Application.h
ーーーー抜粋ーーーーーーーーーーーーーーーーーーーーーーーーー
          /* X  */ void onMessage( const FIX44::MarketDataIncrementalRefresh&, const FIX::SessionID& );
                   void InsertMarketData( const FIX::Message& );


ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー


■コンパイル&実行
コンパイルして実行して、USD/JPYとEUR/USDのレート要求を投げます。
手順省略しますが、画面上にどんどんレート情報が流れること。
レート情報をどんどん取得したまま、下の手順へ。。


■レート情報表示WEB作成
とりあえず、レート情報をどんどん表示するページを作っときます。
見た目の装飾はなんもなしです!

# vi  /var/www/html/rate.php
        <?php
        function h($str) {
          return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
        }

        try {
          $pdo = new PDO( 'mysql:'. 'dbname=quickfix;'. 'host=localhost;'. 'charset=utf8',
            'DBuser',
            'DBpass',
            array(
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            , PDO::ATTR_PERSISTENT => true
            , PDO::ATTR_EMULATE_PREPARES => false
            , PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
            )
          );
          $rows = $pdo->query('
            SELECT `Px`
                 , `Symbol`
                 , `Type`
                 , Case `Type` When 0 THEN "BID"
                               When 1 THEN "ASK"
                               When 7 THEN "High"
                               When 8 THEN "Low"
                               When 9 THEN "Spread"
                   END as `TypeValue`
              FROM `market_data`
             ORDER BY 2, 3
          ')->fetchAll(PDO::FETCH_ASSOC);
        } catch (PDOException $e) {
          $error = $e->getMessage();
        }

        header('Content-Type: text/html; charset=utf-8');
        ?>
        <!DOCTYPE html>
        <html>
        <head>
        <script language="JavaScript">
          <!--
          function rld() { location.reload(); }
          // -->
        </script>
        </head>
        <body>
        <?php if (isset($error)): ?>
          <p><?=h($error)?></p>
        <?php else: ?>
          <table>
            <?php foreach ($rows as $row): ?>
            <tr>
              <td><?= $row['Symbol']?> </td>
              <td><?= $row['TypeValue']?> </td>
              <td><?= $row['Px']?></td>
              <td style="padding: .3em 0em .5em 0em;">&nbsp;</td>
            </tr>
            <?php endforeach; ?>
          </table>
        <?php endif; ?>
        <script language="JavaScript">
        <!--
          setTimeout("rld()", (2 * 1000));
        // -->
        </script>

        </body>
        </html>


青字部分の秒数毎にどんどんページを更新します。

作ったらブラウザで表示!!
こんな感じに表示されて、指定秒数(2秒)毎にどんどんレート情報が変わる。。ハズ。。

    


まぁレート情報が取得できてれば、OKです。




 
まこ | FIX | 22:42 | comments(0) | trackbacks(0) |
スポンサーサイト
スポンサードリンク | - | 22:42 | - | - |
Comment









Trackback
URL: