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
34 changes: 24 additions & 10 deletions src/services/binance-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,29 @@ export class BinanceService {

// Round to nearest valid step size
const stepSize = minQty; // Use minQty as step size for simplicity
const roundedQuantity = Math.floor(quantityNum / stepSize) * stepSize;

// Convert to integer arithmetic to avoid floating point precision issues
// For example: precision=2 means we multiply by 100 to work with integers
const factor = Math.pow(10, precision);

// Always round quantity down to avoid exceeding user's intended size
const quantityInSmallestUnit = Math.floor(quantityNum * factor + Number.EPSILON);

// Step size should stay aligned to the nearest integer unit (never zero)
const stepSizeInSmallestUnit = Math.max(1, Math.round(stepSize * factor));

// Perform integer division to get number of steps
const numberOfSteps = Math.floor(quantityInSmallestUnit / stepSizeInSmallestUnit);
const roundedQuantityInSmallestUnit = numberOfSteps * stepSizeInSmallestUnit;

// Convert back to decimal
const roundedQuantity = roundedQuantityInSmallestUnit / factor;

// Ensure we don't go below minimum
const finalQuantity = Math.max(roundedQuantity, minQty);

// Format to correct precision - keep trailing zeros for precision requirements
// Format to correct precision to avoid floating point errors
// toFixed returns string with exact decimal places, which Binance accepts
Comment on lines +232 to +233
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

The comment should clarify that toFixed() not only avoids floating point errors but also ensures trailing zeros are preserved, which is critical for meeting Binance's precision requirements (as mentioned in the original comment).

Suggested change
// Format to correct precision to avoid floating point errors
// toFixed returns string with exact decimal places, which Binance accepts
// Format to correct precision: toFixed() not only avoids floating point errors,
// but also ensures trailing zeros are preserved, which is critical for meeting
// Binance's precision requirements (Binance expects exact number of decimal places).

Copilot uses AI. Check for mistakes.
const formattedQuantity = finalQuantity.toFixed(precision);
return formattedQuantity;
}
Expand Down Expand Up @@ -331,12 +348,10 @@ export class BinanceService {
const errorData = error.response.data as any;
const errorCode = errorData?.code;
const errorMessage = errorData?.msg || errorData?.message || error.message;
const statusText = error.response.statusText;
const statusCode = error.response.status;

// Log error details for debugging
console.error(`API Error [${errorCode || 'UNKNOWN'}]: ${errorMessage}`);

// 处理时间同步错误 (-1021)
if (errorCode === -1021) {
console.warn('⏰ Timestamp error detected, syncing server time and retrying...');
Expand Down Expand Up @@ -390,8 +405,6 @@ export class BinanceService {
const errorData = error.response.data as any;
const errorCode = errorData?.code;
const errorMessage = errorData?.msg || errorData?.message || error.message;
const statusText = error.response.statusText;
const statusCode = error.response.status;

// Log error details for debugging
console.error(`API Error [${errorCode || 'UNKNOWN'}]: ${errorMessage}`);
Expand Down Expand Up @@ -492,12 +505,13 @@ export class BinanceService {
};

// 如果使用 closePosition,则不需要 quantity
// NOTE: order.quantity is already formatted string from convertToBinanceOrder, no need to format again
if (order.closePosition !== "true") {
params.quantity = this.formatQuantity(order.quantity, order.symbol);
params.quantity = order.quantity;
}

if (order.price) params.price = this.formatPrice(order.price, order.symbol);
if (order.stopPrice) params.stopPrice = this.formatPrice(order.stopPrice, order.symbol);
if (order.price) params.price = order.price;
if (order.stopPrice) params.stopPrice = order.stopPrice;
Comment on lines +513 to +514
Copy link

Copilot AI Oct 28, 2025

Choose a reason for hiding this comment

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

Removing the formatPrice() calls for price and stopPrice may cause precision issues similar to what was fixed for quantity. These values should still be formatted according to the symbol's price precision requirements, or the comment on line 504 should be updated to clarify that price and stopPrice are also pre-formatted.

Copilot uses AI. Check for mistakes.
if (order.timeInForce) params.timeInForce = order.timeInForce;
if (order.closePosition) params.closePosition = order.closePosition;

Expand Down