From 5d0c77310c8085c1034125e0cd02ae2aa8e3585f Mon Sep 17 00:00:00 2001 From: Daniel Newton Date: Tue, 14 Nov 2017 18:56:33 +1300 Subject: [PATCH 1/6] make order.put_market (side = bid) behave like other orders.. that is the "amount" parameter now refers to the order "stock" --- matchengine/me_market.c | 53 ++++++++++------------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/matchengine/me_market.c b/matchengine/me_market.c index 8ed2844b..f8956dcc 100644 --- a/matchengine/me_market.c +++ b/matchengine/me_market.c @@ -769,26 +769,11 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) order_t *maker = node->value; mpd_copy(price, maker->price, &mpd_ctx); - - mpd_div(amount, taker->left, price, &mpd_ctx); - mpd_rescale(amount, amount, -m->stock_prec, &mpd_ctx); - while (true) { - mpd_mul(result, amount, price, &mpd_ctx); - if (mpd_cmp(result, taker->left, &mpd_ctx) > 0) { - mpd_set_i32(result, -m->stock_prec, &mpd_ctx); - mpd_pow(result, mpd_ten, result, &mpd_ctx); - mpd_sub(amount, amount, result, &mpd_ctx); - } else { - break; - } - } - - if (mpd_cmp(amount, maker->left, &mpd_ctx) > 0) { + if (mpd_cmp(taker->left, maker->left, &mpd_ctx) < 0) { + mpd_copy(amount, taker->left, &mpd_ctx); + } else { mpd_copy(amount, maker->left, &mpd_ctx); } - if (mpd_cmp(amount, mpd_zero, &mpd_ctx) == 0) { - break; - } mpd_mul(deal, price, amount, &mpd_ctx); mpd_mul(ask_fee, deal, maker->maker_fee, &mpd_ctx); @@ -867,12 +852,16 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *taker_fee, const char *source) { - if (side == MARKET_ORDER_SIDE_ASK) { - mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->stock); - if (!balance || mpd_cmp(balance, amount, &mpd_ctx) < 0) { - return -1; - } + mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->stock); + if (!balance || mpd_cmp(balance, amount, &mpd_ctx) < 0) { + return -1; + } + if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { + return -2; + } + + if (side == MARKET_ORDER_SIDE_ASK) { skiplist_iter *iter = skiplist_get_iterator(m->bids); skiplist_node *node = skiplist_next(iter); if (node == NULL) { @@ -880,16 +869,7 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us return -3; } skiplist_release_iterator(iter); - - if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { - return -2; - } } else { - mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->money); - if (!balance || mpd_cmp(balance, amount, &mpd_ctx) < 0) { - return -1; - } - skiplist_iter *iter = skiplist_get_iterator(m->asks); skiplist_node *node = skiplist_next(iter); if (node == NULL) { @@ -897,15 +877,6 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us return -3; } skiplist_release_iterator(iter); - - order_t *order = node->value; - mpd_t *require = mpd_new(&mpd_ctx); - mpd_mul(require, order->price, m->min_amount, &mpd_ctx); - if (mpd_cmp(amount, require, &mpd_ctx) < 0) { - mpd_del(require); - return -2; - } - mpd_del(require); } order_t *order = malloc(sizeof(order_t)); From 5bbbd09f0213f2c1ab4ecd7862595a4a2f6b616f Mon Sep 17 00:00:00 2001 From: Daniel Newton Date: Thu, 16 Nov 2017 15:47:50 +1300 Subject: [PATCH 2/6] fix market bid order validation --- matchengine/me_market.c | 69 +++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/matchengine/me_market.c b/matchengine/me_market.c index f8956dcc..85e8512c 100644 --- a/matchengine/me_market.c +++ b/matchengine/me_market.c @@ -852,16 +852,14 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *taker_fee, const char *source) { - mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->stock); - if (!balance || mpd_cmp(balance, amount, &mpd_ctx) < 0) { - return -1; - } - - if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { - return -2; - } - if (side == MARKET_ORDER_SIDE_ASK) { + // validate user has the balance available to fulfil order + mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->stock); + if (!balance || mpd_cmp(balance, amount, &mpd_ctx) < 0) { + return -1; + } + + // validate there are any market participants on the other side of the order skiplist_iter *iter = skiplist_get_iterator(m->bids); skiplist_node *node = skiplist_next(iter); if (node == NULL) { @@ -869,14 +867,59 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us return -3; } skiplist_release_iterator(iter); + } else { + // calculate money required and number of asks + mpd_t *money_required = mpd_new(&mpd_ctx); + mpd_t *left = mpd_new(&mpd_ctx); + mpd_t *amount_tx = mpd_new(&mpd_ctx); + mpd_t *price = mpd_new(&mpd_ctx); + mpd_t *deal = mpd_new(&mpd_ctx); + mpd_copy(money_required, mpd_zero, &mpd_ctx); + mpd_copy(left, amount, &mpd_ctx); + int ask_count = 0; + skiplist_node *node; skiplist_iter *iter = skiplist_get_iterator(m->asks); - skiplist_node *node = skiplist_next(iter); - if (node == NULL) { - skiplist_release_iterator(iter); - return -3; + while ((node = skiplist_next(iter)) != NULL) { + ask_count++; + if (mpd_cmp(left, mpd_zero, &mpd_ctx) == 0) { + break; + } + + order_t *maker = node->value; + mpd_copy(price, maker->price, &mpd_ctx); + if (mpd_cmp(maker->left, left, &mpd_ctx) < 0) { + mpd_copy(amount_tx, maker->left, &mpd_ctx); + } else { + mpd_copy(amount_tx, left, &mpd_ctx); + } + + mpd_mul(deal, price, amount_tx, &mpd_ctx); + mpd_sub(left, left, amount_tx, &mpd_ctx); + mpd_add(money_required, money_required, deal, &mpd_ctx); } skiplist_release_iterator(iter); + mpd_del(money_required); + mpd_del(left); + mpd_del(amount_tx); + mpd_del(price); + mpd_del(deal); + + // validate user has the balance available to fulfil order + mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->money); + if (!balance || mpd_cmp(balance, money_required, &mpd_ctx) < 0) { + return -1; + } + + // validate there are any market participants on the other side of the order + if (ask_count == 0) { + return -3; + } + } + + // validate order amount is at least the minimum order amount + if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { + return -2; } order_t *order = malloc(sizeof(order_t)); From 86edfecdd9b37a9335e364f7851ab6197d8bd9e6 Mon Sep 17 00:00:00 2001 From: Daniel Newton Date: Thu, 16 Nov 2017 16:43:29 +1300 Subject: [PATCH 3/6] fix order taker "amount left" calculation for market bids --- matchengine/me_market.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matchengine/me_market.c b/matchengine/me_market.c index 85e8512c..0ee23c40 100644 --- a/matchengine/me_market.c +++ b/matchengine/me_market.c @@ -786,7 +786,7 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) push_deal_message(taker->update_time, m->name, maker, taker, price, amount, ask_fee, bid_fee, MARKET_ORDER_SIDE_BID, deal_id, m->stock, m->money); } - mpd_sub(taker->left, taker->left, deal, &mpd_ctx); + mpd_sub(taker->left, taker->left, amount, &mpd_ctx); mpd_add(taker->deal_stock, taker->deal_stock, amount, &mpd_ctx); mpd_add(taker->deal_money, taker->deal_money, deal, &mpd_ctx); mpd_add(taker->deal_fee, taker->deal_fee, bid_fee, &mpd_ctx); From 54531bd3ec21908292995c477d0f88a4cd2ac8a6 Mon Sep 17 00:00:00 2001 From: Daniel Newton Date: Fri, 17 Nov 2017 15:01:30 +1300 Subject: [PATCH 4/6] added extra optional parameter "bid_amount_money" to "order.put_market_stock" RPC. Default value is true and has no change to behavior. If the value is false then the "amount" parameter of a market bid is treated as "stock" instead of "money" --- matchengine/me_market.c | 162 +++++++++++++++++++++++++++------------- matchengine/me_market.h | 2 +- matchengine/me_server.c | 13 +++- 3 files changed, 122 insertions(+), 55 deletions(-) diff --git a/matchengine/me_market.c b/matchengine/me_market.c index 0ee23c40..e4d97e03 100644 --- a/matchengine/me_market.c +++ b/matchengine/me_market.c @@ -751,7 +751,7 @@ static int execute_market_ask_order(bool real, market_t *m, order_t *taker) return 0; } -static int execute_market_bid_order(bool real, market_t *m, order_t *taker) +static int execute_market_bid_order(bool real, market_t *m, order_t *taker, bool bid_amount_money) { mpd_t *price = mpd_new(&mpd_ctx); mpd_t *amount = mpd_new(&mpd_ctx); @@ -769,10 +769,33 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) order_t *maker = node->value; mpd_copy(price, maker->price, &mpd_ctx); - if (mpd_cmp(taker->left, maker->left, &mpd_ctx) < 0) { - mpd_copy(amount, taker->left, &mpd_ctx); + + if (bid_amount_money) { + mpd_div(amount, taker->left, price, &mpd_ctx); + mpd_rescale(amount, amount, -m->stock_prec, &mpd_ctx); + while (true) { + mpd_mul(result, amount, price, &mpd_ctx); + if (mpd_cmp(result, taker->left, &mpd_ctx) > 0) { + mpd_set_i32(result, -m->stock_prec, &mpd_ctx); + mpd_pow(result, mpd_ten, result, &mpd_ctx); + mpd_sub(amount, amount, result, &mpd_ctx); + } else { + break; + } + } + + if (mpd_cmp(amount, maker->left, &mpd_ctx) > 0) { + mpd_copy(amount, maker->left, &mpd_ctx); + } + if (mpd_cmp(amount, mpd_zero, &mpd_ctx) == 0) { + break; + } } else { - mpd_copy(amount, maker->left, &mpd_ctx); + if (mpd_cmp(taker->left, maker->left, &mpd_ctx) < 0) { + mpd_copy(amount, taker->left, &mpd_ctx); + } else { + mpd_copy(amount, maker->left, &mpd_ctx); + } } mpd_mul(deal, price, amount, &mpd_ctx); @@ -786,7 +809,11 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) push_deal_message(taker->update_time, m->name, maker, taker, price, amount, ask_fee, bid_fee, MARKET_ORDER_SIDE_BID, deal_id, m->stock, m->money); } - mpd_sub(taker->left, taker->left, amount, &mpd_ctx); + if (bid_amount_money) { + mpd_sub(taker->left, taker->left, deal, &mpd_ctx); + } else { + mpd_sub(taker->left, taker->left, amount, &mpd_ctx); + } mpd_add(taker->deal_stock, taker->deal_stock, amount, &mpd_ctx); mpd_add(taker->deal_money, taker->deal_money, deal, &mpd_ctx); mpd_add(taker->deal_fee, taker->deal_fee, bid_fee, &mpd_ctx); @@ -850,7 +877,7 @@ static int execute_market_bid_order(bool real, market_t *m, order_t *taker) return 0; } -int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *taker_fee, const char *source) +int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *taker_fee, const char *source, bool bid_amount_money) { if (side == MARKET_ORDER_SIDE_ASK) { // validate user has the balance available to fulfil order @@ -867,61 +894,92 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us return -3; } skiplist_release_iterator(iter); - + + // validate order amount is at least the minimum order amount + if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { + return -2; + } } else { - // calculate money required and number of asks - mpd_t *money_required = mpd_new(&mpd_ctx); - mpd_t *left = mpd_new(&mpd_ctx); - mpd_t *amount_tx = mpd_new(&mpd_ctx); - mpd_t *price = mpd_new(&mpd_ctx); - mpd_t *deal = mpd_new(&mpd_ctx); - mpd_copy(money_required, mpd_zero, &mpd_ctx); - mpd_copy(left, amount, &mpd_ctx); - int ask_count = 0; - skiplist_node *node; - skiplist_iter *iter = skiplist_get_iterator(m->asks); - while ((node = skiplist_next(iter)) != NULL) { - ask_count++; - if (mpd_cmp(left, mpd_zero, &mpd_ctx) == 0) { - break; + if (bid_amount_money) { + // validate user has the balance available to fulfil order + mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->money); + if (!balance || mpd_cmp(balance, amount, &mpd_ctx) < 0) { + return -1; } - order_t *maker = node->value; - mpd_copy(price, maker->price, &mpd_ctx); - if (mpd_cmp(maker->left, left, &mpd_ctx) < 0) { - mpd_copy(amount_tx, maker->left, &mpd_ctx); - } else { - mpd_copy(amount_tx, left, &mpd_ctx); + // validate there are any market participants on the other side of the order + skiplist_iter *iter = skiplist_get_iterator(m->asks); + skiplist_node *node = skiplist_next(iter); + if (node == NULL) { + skiplist_release_iterator(iter); + return -3; } + skiplist_release_iterator(iter); - mpd_mul(deal, price, amount_tx, &mpd_ctx); - mpd_sub(left, left, amount_tx, &mpd_ctx); - mpd_add(money_required, money_required, deal, &mpd_ctx); - } - skiplist_release_iterator(iter); - mpd_del(money_required); - mpd_del(left); - mpd_del(amount_tx); - mpd_del(price); - mpd_del(deal); + // validate order amount is at least the minimum order amount + order_t *order = node->value; + mpd_t *require = mpd_new(&mpd_ctx); + mpd_mul(require, order->price, m->min_amount, &mpd_ctx); + if (mpd_cmp(amount, require, &mpd_ctx) < 0) { + mpd_del(require); + return -2; + } + mpd_del(require); + } else { + // calculate money required and number of asks + mpd_t *money_required = mpd_new(&mpd_ctx); + mpd_t *left = mpd_new(&mpd_ctx); + mpd_t *amount_tx = mpd_new(&mpd_ctx); + mpd_t *price = mpd_new(&mpd_ctx); + mpd_t *deal = mpd_new(&mpd_ctx); + mpd_copy(money_required, mpd_zero, &mpd_ctx); + mpd_copy(left, amount, &mpd_ctx); + int ask_count = 0; + skiplist_node *node; + skiplist_iter *iter = skiplist_get_iterator(m->asks); + while ((node = skiplist_next(iter)) != NULL) { + ask_count++; + if (mpd_cmp(left, mpd_zero, &mpd_ctx) == 0) { + break; + } + + order_t *maker = node->value; + mpd_copy(price, maker->price, &mpd_ctx); + if (mpd_cmp(maker->left, left, &mpd_ctx) < 0) { + mpd_copy(amount_tx, maker->left, &mpd_ctx); + } else { + mpd_copy(amount_tx, left, &mpd_ctx); + } + + mpd_mul(deal, price, amount_tx, &mpd_ctx); + mpd_sub(left, left, amount_tx, &mpd_ctx); + mpd_add(money_required, money_required, deal, &mpd_ctx); + } + skiplist_release_iterator(iter); + mpd_del(money_required); + mpd_del(left); + mpd_del(amount_tx); + mpd_del(price); + mpd_del(deal); + + // validate user has the balance available to fulfil order + mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->money); + if (!balance || mpd_cmp(balance, money_required, &mpd_ctx) < 0) { + return -1; + } - // validate user has the balance available to fulfil order - mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->money); - if (!balance || mpd_cmp(balance, money_required, &mpd_ctx) < 0) { - return -1; - } + // validate there are any market participants on the other side of the order + if (ask_count == 0) { + return -3; + } - // validate there are any market participants on the other side of the order - if (ask_count == 0) { - return -3; + // validate order amount is at least the minimum order amount + if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { + return -2; + } } } - // validate order amount is at least the minimum order amount - if (mpd_cmp(amount, m->min_amount, &mpd_ctx) < 0) { - return -2; - } - order_t *order = malloc(sizeof(order_t)); if (order == NULL) { return -__LINE__; @@ -959,7 +1017,7 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us if (side == MARKET_ORDER_SIDE_ASK) { ret = execute_market_ask_order(real, m, order); } else { - ret = execute_market_bid_order(real, m, order); + ret = execute_market_bid_order(real, m, order, bid_amount_money); } if (ret < 0) { log_error("execute order: %"PRIu64" fail: %d", order->id, ret); diff --git a/matchengine/me_market.h b/matchengine/me_market.h index 33bbdbc7..9e1ec956 100644 --- a/matchengine/me_market.h +++ b/matchengine/me_market.h @@ -52,7 +52,7 @@ market_t *market_create(struct market *conf); int market_get_status(market_t *m, size_t *ask_count, mpd_t *ask_amount, size_t *bid_count, mpd_t *bid_amount); int market_put_limit_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *price, mpd_t *taker_fee, mpd_t *maker_fee, const char *source); -int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *taker_fee, const char *source); +int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t user_id, uint32_t side, mpd_t *amount, mpd_t *taker_fee, const char *source, bool bid_amount_money); int market_cancel_order(bool real, json_t **result, market_t *m, order_t *order); int market_put_order(market_t *m, order_t *order); diff --git a/matchengine/me_server.c b/matchengine/me_server.c index ca5d0e6e..720f0e72 100644 --- a/matchengine/me_server.c +++ b/matchengine/me_server.c @@ -457,7 +457,7 @@ static int on_cmd_order_put_limit(nw_ses *ses, rpc_pkg *pkg, json_t *params) static int on_cmd_order_put_market(nw_ses *ses, rpc_pkg *pkg, json_t *params) { - if (json_array_size(params) != 6) + if (json_array_size(params) != 6 && json_array_size(params) != 7) return reply_error_invalid_argument(ses, pkg); // user_id @@ -504,8 +504,17 @@ static int on_cmd_order_put_market(nw_ses *ses, rpc_pkg *pkg, json_t *params) if (strlen(source) >= SOURCE_MAX_LEN) goto invalid_argument; + // bid amount money (should the bid amount be treated as "money" or "stock") + // optional parameter, default is true + bool bid_amount_money = true; + if (json_array_size(params) == 7) { + if (!json_is_boolean(json_array_get(params, 6))) + goto invalid_argument; + bid_amount_money = json_boolean_value(json_array_get(params, 6)); + } + json_t *result = NULL; - int ret = market_put_market_order(true, &result, market, user_id, side, amount, taker_fee, source); + int ret = market_put_market_order(true, &result, market, user_id, side, amount, taker_fee, source, bid_amount_money); mpd_del(amount); mpd_del(taker_fee); From b7b5e5e58cf6220ac56297a556ab3b6e31c3b5e9 Mon Sep 17 00:00:00 2001 From: Daniel Newton Date: Thu, 22 Nov 2018 18:07:36 +1300 Subject: [PATCH 5/6] update me_load.c --- matchengine/me_load.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/matchengine/me_load.c b/matchengine/me_load.c index cd1c4801..a7191c85 100644 --- a/matchengine/me_load.c +++ b/matchengine/me_load.c @@ -262,7 +262,7 @@ static int load_limit_order(json_t *params) static int load_market_order(json_t *params) { - if (json_array_size(params) != 6) + if (json_array_size(params) != 6 && json_array_size(params) != 7) return -__LINE__; // user_id @@ -313,7 +313,16 @@ static int load_market_order(json_t *params) if (strlen(source) > SOURCE_MAX_LEN) goto error; - int ret = market_put_market_order(false, NULL, market, user_id, side, amount, taker_fee, source); + // bid amount money (should the bid amount be treated as "money" or "stock") + // optional parameter, default is true + bool bid_amount_money = true; + if (json_array_size(params) == 7) { + if (!json_is_boolean(json_array_get(params, 6))) + goto error; + bid_amount_money = json_boolean_value(json_array_get(params, 6)); + } + + int ret = market_put_market_order(false, NULL, market, user_id, side, amount, taker_fee, source, bid_amount_money); mpd_del(amount); mpd_del(taker_fee); From 3f789c2e08c6e1e38bb3abe82cfe3e13444d35ee Mon Sep 17 00:00:00 2001 From: Daniel Newton Date: Thu, 8 Aug 2019 21:04:05 -0500 Subject: [PATCH 6/6] move the cleanup of money_required to after its final use --- matchengine/me_market.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/matchengine/me_market.c b/matchengine/me_market.c index e4d97e03..d3618444 100644 --- a/matchengine/me_market.c +++ b/matchengine/me_market.c @@ -956,7 +956,6 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us mpd_add(money_required, money_required, deal, &mpd_ctx); } skiplist_release_iterator(iter); - mpd_del(money_required); mpd_del(left); mpd_del(amount_tx); mpd_del(price); @@ -965,8 +964,10 @@ int market_put_market_order(bool real, json_t **result, market_t *m, uint32_t us // validate user has the balance available to fulfil order mpd_t *balance = balance_get(user_id, BALANCE_TYPE_AVAILABLE, m->money); if (!balance || mpd_cmp(balance, money_required, &mpd_ctx) < 0) { + mpd_del(money_required); return -1; } + mpd_del(money_required); // validate there are any market participants on the other side of the order if (ask_count == 0) {