Escultor

Ontem eu estava sentado quieto, como sempre não incomodo ninguém. Aqui, a partir de dois contatos diferentes, eles enviam quase simultaneamente um link para o conhecido tweet sobre JSON do SQL. Uma das mensagens era assim:





Já era um desafio direto. Eu não podia ignorá-lo. Então decidi contar uma história que ainda evoca sentimentos ambivalentes em mim. Três anos depois.



Naquela época abençoada, todos sonhavam com criptografia, se envolviam em ICOs e esculpiam trocas de criptografia. Foi realmente algo novo. Tive experiência na criação de sistemas clássicos de gestão de ativos (ações, títulos, etc.). O problema é que ele se formou em torno dos sistemas de contabilidade. Eu queria me realizar criando uma troca. Não surpreendentemente, eu mergulhei feliz neste caldeirão fervente.



Este Ă© o pano de fundo. Houve muitas coisas interessantes, mas hoje quero contar a vocĂŞs sobre um caso especĂ­fico - como criamos nosso matcher.



Matcher, este é o cerne da troca. É nele que ocorrem as transações. No estereótipo clássico, este é um subsistema de alto desempenho. Mas isso é verdade para grandes bolsas. Lá, os aplicativos abertos para compra e venda chegam a centenas de milhares.



“ ” . . — , . — .



.



Java . , , . , . “” . .



. , , “” . . , 270 . RabbitMQ. ….



. . 2 . , , . , , .



. . .. , … , .. . - :



! ()

, . . … . .



, . , . , . — .



, . , 100, 5. 100 7 . , .



SQL . , . MySQL . , MySQL . .. . :



SET depth_sell.`limit` = depth_sell.`limit` - IF(
                    ((@tofill := IF(
                            depth_sell.`limit` <= @limit,
                            depth_sell.`limit`,
                            @limit
                        ))
                        + (@limit := @limit - @tofill)),
                    @tofill,
                    @tofill
                ),
        depth_sell.`executed` = @tofill
WHERE @limit > 0;




@limit := @limit - @tofill


() .



IN MEMORY . .. . .



. — . . , , .



CREATE TABLE transactions
(
    id INT AUTO_INCREMENT PRIMARY KEY,
    moment TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    side1 INT NOT NULL,
    side2 INT NOT NULL,
    price BIGINT NOT NULL,
    volume BIGINT NOT NULL
);

CREATE TABLE depth_buy
(
    id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
    order_id  BIGINT NOT NULL,
    type INT DEFAULT '0' NOT NULL,
    market INT DEFAULT '0' NOT NULL,
    account INT NOT NULL,
    price BIGINT DEFAULT '0' NOT NULL,
    `limit` BIGINT DEFAULT '0' NOT NULL,
    taker INT,
    rev_price BIGINT NOT NULL,
    executed BIGINT
) ENGINE = MEMORY;

CREATE TABLE depth_sell
(
    id BIGINT PRIMARY KEY NOT NULL AUTO_INCREMENT,
    order_id  INT NOT NULL,
    type INT DEFAULT '0' NOT NULL,
    market INT DEFAULT '0' NOT NULL,
    account INT NOT NULL,
    price BIGINT DEFAULT '0' NOT NULL,
    `limit` BIGINT DEFAULT '0' NOT NULL,
    taker INT,
    rev_price BIGINT NOT NULL,
    executed  BIGINT
) ENGINE = MEMORY;


CREATE PROCEDURE `make_order_v2`(IN order_id INT,
                                 IN order_type INT,
                                 IN order_account INT,
                                 IN order_market INT,
                                 IN order_limit BIGINT,
                                 IN order_price BIGINT)
BEGIN
    START TRANSACTION;
    SET @limit := order_limit;
    IF order_type = 21 THEN
        UPDATE depth_sell
            INNER JOIN (
                SELECT id
                FROM depth_sell
                WHERE market = order_market
                  AND depth_sell.price <= order_price
                ORDER BY depth_sell.price + id ASC
            ) source ON depth_sell.id = source.id
        SET depth_sell.taker      = order_id,
            depth_sell.`limit`    = depth_sell.`limit` - IF(
                    ((@tofill := IF(
                            depth_sell.`limit` <= @limit,
                            depth_sell.`limit`,
                            @limit
                        ))
                        + (@limit := @limit - @tofill)),
                    @tofill,
                    @tofill
                ),
            depth_sell.`executed` = @tofill
        WHERE @limit > 0;

        INSERT INTO transactions (moment, side1, side2, price, volume)
        SELECT now(), depth_sell.id, order_id, depth_sell.price, depth_sell.executed
        FROM depth_sell
        WHERE depth_sell.`taker` = order_id;

        DELETE
        FROM depth_sell
        WHERE market = order_market
          AND depth_sell.`limit` = 0;

        IF @limit > 0 THEN
            INSERT INTO depth_buy (order_id, type, market, account, price, rev_price, `limit`)
            VALUES (order_id, order_type, order_market, order_account, order_price, -order_price, @limit);
        END IF;
    ELSE
        UPDATE depth_buy
            INNER JOIN (
                SELECT id
                FROM depth_buy
                WHERE market = order_market
                  AND depth_buy.price >= order_price
                ORDER BY depth_buy.rev_price - id ASC
            ) source ON depth_buy.id = source.id
        SET depth_buy.taker      = order_id,
            depth_buy.`limit`    = depth_buy.`limit` - IF(
                    ((@tofill := IF(
                            depth_buy.`limit` <= @limit,
                            depth_buy.`limit`,
                            @limit
                        ))
                        + (@limit := @limit - @tofill)),
                    @tofill,
                    @tofill
                ),
            depth_buy.`executed` = @tofill
        WHERE @limit > 0;

        INSERT INTO transactions (moment, side1, side2, price, volume)
        SELECT now(), depth_buy.id, order_id, depth_buy.price, depth_buy.executed
        FROM depth_buy
        WHERE depth_buy.`taker` = order_id;

        DELETE
        FROM depth_buy
        WHERE market = order_market
          AND depth_buy.`limit` = 0;
        IF @limit > 0 THEN
            INSERT INTO depth_sell (order_id, type, market, account, price, rev_price, `limit`)
            VALUES (order_id, order_type, order_market, order_account, order_price, -order_price, @limit);
        END IF;
    END IF;
    COMMIT;
END;


CREATE PROCEDURE do_load_matcher_v2(IN market INT)
BEGIN
    DECLARE count INT DEFAULT 100000;
    WHILE count > 0 DO
            call make_order_v2(
                    count,
                    IF(count % 2 = 0, 21, 22),
                    1,
                    market,
                    FLOOR(1 + (RAND() * 1000)),
                    FLOOR(1 + (RAND() * 1000))
                );
            SET count = count - 1;
        END WHILE;
END;


, .. . :



  1. depth_buy — ;
  2. depth_sell — ;
  3. transaction — = .


make_order_v2 :



  1. order_id — .
  2. order_type — . 21 — , 22 — .
  3. order_account — ( ).
  4. order_market — . .
  5. order_limit — . . , 100. .. 1.15btc 115.
  6. order_price — . .


, . — . , . .



, .



, . . 100 . 95 . 1.46 . 570 . .



, . . , . . .. .



, ? :



  • . .
  • , , . .


:



  • . , . .
  • . . .


Claro, não compartilho do otimismo do autor do post original de que tudo pode ser feito através de um DBMS, mas a experiência mostra que tudo pode ser feito através de um DBMS. Se você quiser. Portanto, você precisa ter cuidado com seus desejos. Caso contrário, você terá que sentir dor mental por anos.



Tudo bom!



Tweet original




All Articles