Escultor

Ayer estuve sentado tranquilamente, como siempre no molesto a nadie. Aquí, desde dos contactos diferentes, envían casi simultáneamente un enlace al conocido tweet sobre JSON de SQL. Uno de los mensajes se veía así:





Ya era un desafío directo. No pude ignorarlo. Así que decidí contar una historia que aún me evoca sentimientos ambivalentes. Tres años despues.



En ese momento bendecido, todos soñaban con criptomonedas, participaban en ICO e intercambios criptográficos esculpidos. Realmente fue algo nuevo. Tenía experiencia en la creación de sistemas para la gestión de activos clásicos (acciones, bonos, etc.). El problema fue que se formó en torno a los sistemas contables. Quería realizarme a mí mismo creando un intercambio. Como era de esperar, me sumergí felizmente en este caldero hirviendo.



Este es el trasfondo. Hubo muchas cosas interesantes, pero hoy quiero contarles sobre un caso especĂ­fico: cĂłmo creamos nuestro comparador.



Matcher, este es el núcleo del intercambio. Es en él donde tienen lugar las transacciones. En el estereotipo clásico, este es un subsistema de alto rendimiento. Pero esto es cierto para los grandes intercambios. Allí, las aplicaciones abiertas para la compra y venta se cuentan por cientos de miles.



“ ” . . — , . — .



.



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 . .



, . . , . . .. .



, ? :



  • . .
  • , , . .


:



  • . , . .
  • . . .


Por supuesto, no comparto el optimismo del autor del post original de que todo se puede hacer a través de un DBMS, pero la experiencia demuestra que todo se puede hacer a través de un DBMS. Si quieres. Por lo tanto, debes tener cuidado con tus deseos. De lo contrario, tendrá que experimentar dolor mental durante años.



¡Todo bien!



Tweet original




All Articles