@icgio/icg-exchanges-wrapper 1.22.1 → 1.22.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -505,20 +505,29 @@ module.exports = class Orders {
505
505
  }
506
506
  this.Exchanges[exchange].cancel_order_by_order(order, (res) => {
507
507
  if (res.success || (!res.success && res.error === 'order-not-open' && original_status === 'CANCELING')) {
508
+ // The ack only means the request was processed — fills may have landed first (the
509
+ // trades push books them before the REST ack returns). A row already at or above
510
+ // f_filled_threshold is a filled order, not a canceled one.
511
+ const acked = _.get(this.order_info, [exchange, pair, order.type, order.order_id])
512
+ const acked_f_amount = round_amount(exchange, pair, (acked && acked.f_amount) || 0)
513
+ const acked_t_amount = round_amount(exchange, pair, (acked && acked.t_amount) || 0)
514
+ const acked_filled = acked_t_amount > 0 && acked_f_amount / acked_t_amount > this.f_filled_threshold
515
+ const ack_note = order.order_type + (acked_filled ? ' canceled-but-filled' : ' canceled')
508
516
  this.update_order_info(
509
517
  exchange,
510
518
  pair,
511
519
  order.type,
512
520
  order.order_id,
513
521
  {
514
- status: 'CANCELED',
522
+ status: acked_filled ? 'F-FILLED' : 'CANCELED',
523
+ ...(acked_filled ? { amount: 0, f_amount: acked_f_amount, f_percentage: acked_f_amount / acked_t_amount } : {}),
515
524
  close_time: Date.now(),
516
525
  decision_context: cancel_decision_context,
517
526
  du_ns_cancel_latency: hrtime_diff(_.get(res, ['meta', 't_cancel_sent']), _.get(res, ['meta', 't_cancel_ack'])),
518
527
  du_ns_rate_to_cancel_ack: hrtime_diff(decision_context?.hr_rate_root, _.get(res, ['meta', 't_cancel_ack'])),
519
528
  event_hrtime: _.get(res, ['meta', 't_cancel_ack']),
520
529
  },
521
- order.order_type + ' canceled',
530
+ ack_note,
522
531
  )
523
532
  setTimeout(() => {
524
533
  this.delete_order_info(exchange, pair, order.type, order.order_id, order.order_type + ' cancel-order-success')
@@ -664,14 +673,20 @@ module.exports = class Orders {
664
673
  const order_info_f_amount = round_amount(exchange, pair, order_info.f_amount)
665
674
  if (_.includes(['CANCELED', 'CANCELING'], order_info.status) && trade_amount > order_info_f_amount) {
666
675
  // console.log('TRADES: %s ORDER FILLED', order_info.status, trade, order_info)
667
- this.log_terminal_divergence('trades', exchange, pair, order_id, order_info.status, 'P-FILLED')
676
+ // At or above f_filled_threshold the order is filled — book the terminal truth
677
+ // instead of leaving CANCELED for the query poll to flip (never P-FILLED here:
678
+ // resurrecting a live status on a canceled row is the failure the old comment feared)
679
+ const trade_filled = trade_amount / order_info.t_amount > this.f_filled_threshold
680
+ if (!trade_filled) {
681
+ this.log_terminal_divergence('trades', exchange, pair, order_id, order_info.status, 'P-FILLED')
682
+ }
668
683
  this.update_order_info(
669
684
  exchange,
670
685
  pair,
671
686
  type,
672
687
  order_id,
673
688
  {
674
- // status: trade.amount / order_info.t_amount > this.f_filled_threshold ? 'F-FILLED' : 'P-FILLED', // could cause update undefined order error
689
+ ...(trade_filled ? { status: 'F-FILLED' } : {}),
675
690
  price: trade.price,
676
691
  amount: 0,
677
692
  f_amount: trade_amount,
@@ -954,24 +969,34 @@ module.exports = class Orders {
954
969
  }
955
970
  })
956
971
 
957
- // If CANCELING orders are no longer in open_orders, infer they are canceled.
972
+ // If CANCELING orders are no longer in open_orders, infer they are canceled — unless
973
+ // trades already show the order effectively filled: absence from the book is no
974
+ // evidence of cancellation for a filled order (push-tier races made that booking
975
+ // ping-pong CANCELED -> F-FILLED through the query reconciler ~1/min on busy venues).
958
976
  const open_orders_ids_set = new Set(open_orders[exchange][pair].open_orders.map((o) => o.order_id))
959
977
  this.get_order_info_status_all(exchange, pair, 'both', 'CANCELING').map((order_info) => {
960
978
  if (order_info.order_id && !open_orders_ids_set.has(order_info.order_id)) {
961
- this.update_order_info(
962
- exchange,
963
- pair,
964
- order_info.type,
965
- order_info.order_id,
966
- {
967
- status: 'CANCELED',
968
- close_time: Date.now(),
969
- event_hrtime: process.hrtime(),
970
- },
971
- (order_info.order_type || '') + ' canceling-canceled-from-open-orders',
972
- )
979
+ const swept_f_amount = round_amount(exchange, pair, order_info.f_amount || 0)
980
+ const swept_t_amount = round_amount(exchange, pair, order_info.t_amount || 0)
981
+ const effectively_filled = swept_t_amount > 0 && swept_f_amount / swept_t_amount > this.f_filled_threshold
982
+ const note = (order_info.order_type || '') + (effectively_filled ? ' canceling-filled-from-open-orders' : ' canceling-canceled-from-open-orders')
983
+ const update_dict = effectively_filled
984
+ ? {
985
+ status: 'F-FILLED',
986
+ amount: 0,
987
+ f_amount: swept_f_amount,
988
+ f_percentage: swept_f_amount / swept_t_amount,
989
+ close_time: Date.now(),
990
+ event_hrtime: process.hrtime(),
991
+ }
992
+ : {
993
+ status: 'CANCELED',
994
+ close_time: Date.now(),
995
+ event_hrtime: process.hrtime(),
996
+ }
997
+ this.update_order_info(exchange, pair, order_info.type, order_info.order_id, update_dict, note)
973
998
  setTimeout(() => {
974
- this.delete_order_info(exchange, pair, order_info.type, order_info.order_id, (order_info.order_type || '') + ' canceling-canceled-from-open-orders')
999
+ this.delete_order_info(exchange, pair, order_info.type, order_info.order_id, note)
975
1000
  }, this.delete_finished_orders_timeout)
976
1001
  }
977
1002
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icgio/icg-exchanges-wrapper",
3
- "version": "1.22.1",
3
+ "version": "1.22.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {