Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions ts/src/upbit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,122 @@ export default class upbit extends Exchange {
return this.parseOrder (response);
}

/**
* @method
* @name upbit#editOrder
* @see https://docs.upbit.com/reference/%EC%B7%A8%EC%86%8C-%ED%9B%84-%EC%9E%AC%EC%A3%BC%EB%AC%B8
* @description canceled existing order and create new order. It's only generated same side and symbol as the canceled order. it returns the data of the canceled order, except for `new_order_uuid` and `new_identifier`. to get the details of the new order, use `fetchOrder(new_order_uuid)`.
* @param {string} id the uuid of the previous order you want to edit.
* @param {string} symbol the symbol of the new order. it must be the same as the symbol of the previous order.
* @param {string} type the type of the new order. only limit or market is accepted. if params.newOrdType is set to best, a best-type order will be created regardless of the value of type.
* @param {string} side the side of the new order. it must be the same as the side of the previous order.
* @param {number} amount the amount of the asset you want to buy or sell. It could be overridden by specifying the new_volume parameter in params.
* @param {number} price the price of the asset you want to buy or sell. It could be overridden by specifying the new_price parameter in params.
* @param {object} [params] extra parameters specific to the exchange API endpoint.
* @param {string} [params.prevClientOrderId] to identify the previous order, either the id or this field is required in this method.
* @param {float} [params.cost] for market buy and best buy orders, the quote quantity that can be used as an alternative for the amount.
* @param {string} [params.newTimeInForce] 'IOC' or 'FOK'. only for limit or best type orders. this field is required when the order type is 'best'.
* @param {string} [params.newClientOrderId] the order ID that the user can define.
* @param {string} [params.newOrdType] this field only accepts limit, price, market, or best. You can refer to the Upbit developer documentation for details on how to use this field.
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
async editOrder (id: string, symbol: string, type: OrderType, side: OrderSide, amount: Num = undefined, price: Num = undefined, params = {}): Promise<Order> {
await this.loadMarkets ();
const request: Dict = {};
if (type === undefined) {
throw new ArgumentsRequired (this.id + ' type is required in the editOrder method.');
}
const prevClientOrderId = this.safeString (params, 'prevClientOrderId');
params = this.omit (params, 'prevClientOrderId');
if (id !== undefined) {
request['prev_order_uuid'] = id;
} else if (prevClientOrderId !== undefined) {
request['prev_order_identifier'] = prevClientOrderId;
} else {
throw new ArgumentsRequired (this.id + ' editOrder() is required prevOrderId or prevClientOrderId.');
}
if (type === 'limit') {
if (price === undefined || amount === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder() is required price and amount to create limit type order.');
}
request['new_ord_type'] = 'limit';
request['new_price'] = this.priceToPrecision (symbol, price);
request['new_volume'] = this.amountToPrecision (symbol, amount);
} else if (type === 'market') {
if (side === 'buy') {
request['new_ord_type'] = 'price';
const orderPrice = this.calcOrderPrice (symbol, amount, price, params);
request['new_price'] = orderPrice;
} else {
if (amount === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder() is required amount to create market sell type order.');
}
request['new_ord_type'] = 'market';
request['new_volume'] = this.amountToPrecision (symbol, amount);
}
} else {
throw new InvalidOrder (this.id + ' editOrder() supports only limit or market types in the type argument.');
}
const customType = this.safeString2 (params, 'newOrdType', 'new_ord_type');
if (customType === 'best') {
params = this.omit (params, [ 'newOrdType', 'new_ord_type' ]);
request['new_ord_type'] = 'best';
if (side === 'buy') {
const orderPrice = this.calcOrderPrice (symbol, amount, price, params);
request['new_price'] = orderPrice;
} else {
if (amount === undefined) {
throw new ArgumentsRequired (this.id + ' editOrder() is required amount to create best sell order.');
}
request['new_volume'] = this.amountToPrecision (symbol, amount);
}
}
const clientOrderId = this.safeString (params, 'newClientOrderId');
if (clientOrderId !== undefined) {
request['new_identifier'] = clientOrderId;
}
if (request['new_ord_type'] !== 'market' && request['new_ord_type'] !== 'price') {
const timeInForce = this.safeStringLower2 (params, 'newTimeInForce', 'new_time_in_force');
params = this.omit (params, [ 'newTimeInForce', 'new_time_in_force' ]);
if (timeInForce !== undefined) {
request['new_time_in_force'] = timeInForce;
} else {
if (request['new_ord_type'] === 'best') {
throw new ArgumentsRequired (this.id + ' the best type order is required timeInForce.');
}
}
}
params = this.omit (params, [ 'newClientOrderId', 'cost' ]);
// console.log ('check the each request params: ', request);
const response = await this.privatePostOrdersCancelAndNew (this.extend (request, params));
// {
// uuid: '63b38774-27db-4439-ac20-1be16a24d18e', //previous order data
// side: 'bid', //previous order data
// ord_type: 'limit', //previous order data
// price: '100000000', //previous order data
// state: 'wait', //previous order data
// market: 'KRW-BTC', //previous order data
// created_at: '2025-04-01T15:30:47+09:00', //previous order data
// volume: '0.00008', //previous order data
// remaining_volume: '0.00008', //previous order data
// reserved_fee: '4', //previous order data
// remaining_fee: '4', //previous order data
// paid_fee: '0', //previous order data
// locked: '8004', //previous order data
// executed_volume: '0', //previous order data
// trades_count: '0', //previous order data
// identifier: '21', //previous order data
// new_order_uuid: 'cb1cce56-6237-4a78-bc11-4cfffc1bb4c2', // new order data
// new_order_identifier: '22' // new order data
// }
const result: Dict = {};
result['uuid'] = this.safeString (response, 'new_order_uuid');
result['identifier'] = this.safeString (response, 'new_order_identifier');
result['side'] = this.safeString (response, 'side');
result['market'] = this.safeString (response, 'market');
return this.parseOrder (result);
Comment on lines +1393 to +1419
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Consider adding error handling for the privatePostOrdersCancelAndNew API call. The current implementation assumes that if the request is successful, the new order is always created. However, the API might return an error for the new order creation part of the cancel and new order request. Adding error handling would ensure that the code handles this scenario gracefully.

}

/**
* @method
* @name upbit#fetchDeposits
Expand Down Expand Up @@ -1630,6 +1746,26 @@ export default class upbit extends Exchange {
// "time_in_force": "ioc"
// }
//
// {
// uuid: '63b38774-27db-4439-ac20-1be16a24d18e',
// side: 'bid',
// ord_type: 'limit',
// price: '100000000',
// state: 'wait',
// market: 'KRW-BTC',
// created_at: '2025-04-01T15:30:47+09:00',
// volume: '0.00008',
// remaining_volume: '0.00008',
// reserved_fee: '4',
// remaining_fee: '4',
// paid_fee: '0',
// locked: '8004',
// executed_volume: '0',
// trades_count: '0',
// identifier: '21',
// new_order_uuid: 'cb1cce56-6237-4a78-bc11-4cfffc1bb4c2',
// new_order_identifier: '22'
// }
const id = this.safeString (order, 'uuid');
let side = this.safeString (order, 'side');
if (side === 'bid') {
Expand Down