Skip to content

Commit 7de8562

Browse files
committed
ux: introduced limit price tolerance
1 parent 7a961c1 commit 7de8562

File tree

3 files changed

+17
-155
lines changed

3 files changed

+17
-155
lines changed

projects/binance/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ Integration between HeyAnon.ai and Binance Spot and Futures trading.
2929
- Market buy 1 BTC on @binance when the price goes below 50000 USDT
3030
- Buy 1 BTC at 45000 USDT on @binance when the price goes below 50000 USDT
3131
- Sell 100 USDT for BTC for 10% profit on @binance
32+
- Buy 1 BTC for USDT with a stop loss of 15% on @binance
3233

3334
1. **Take profit & stop loss (OCO)**
3435

35-
- Sell 0.01 BTC with a 10% take profit and a 15% stop loss on @binance
36-
- Sell all of my BTC with a 10% take profit and a 15% stop loss on @binance
36+
- Sell 1 BTC for USDT with a 10% take profit and a 15% stop loss on @binance
37+
- Sell all of my BTC for USDT with a 10% take profit and a 15% stop loss on @binance
38+
- Buy BTC with 1000 USDT a 10% take profit and a 15% stop loss on @binance
3739

3840
1. **Cancel orders**
3941

@@ -52,6 +54,7 @@ Integration between HeyAnon.ai and Binance Spot and Futures trading.
5254

5355
## Binance specific behaviors
5456

57+
- OCO order not supported on CCXT > implemented helper function `createBinanceOcoOrder`
5558
- `fetchOrder` and `cancelOrder` require both `id` and `symbol`
5659
- `cancelAllOrders` requires a `symbol`
5760
- Binance APIs [lack a trigger price parameter](https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints#new-order-trade), so specifying `triggerPrice` automatically creates a `STOP_LOSS` order ([see Discord](https://discord.com/channels/690203284119617602/690203284727660739/1367189081418633389))

projects/binance/src/constants.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,12 @@
33
* important to avoid the 500 token limit for getters.
44
*/
55
export const MAX_ORDERS_IN_RESULTS = 25;
6+
7+
/**
8+
* By default, attempts of sending a limit order that will execute immediately
9+
* will be rejected. This tolerance allows for a small deviation from the
10+
* current price. Set to 0 to block all limit orders that will execute immediately.
11+
* Set to a few permille to allow for some deviation that might happens due to
12+
* current price fluctuations while the order is being placed.
13+
*/
14+
export const LIMIT_PRICE_TOLERANCE = 0.005;

projects/binance/src/helpers/orders.ts

Lines changed: 3 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import { Exchange, Order } from 'ccxt';
22
import { getMarketLastPriceBySymbol } from './markets';
33
import util from 'util';
4+
import { LIMIT_PRICE_TOLERANCE } from '../constants';
45
/**
56
* Create a simple order, that is, an order that has no triggers attached to it.
67
*/
78
export async function createSimpleOrder(exchange: Exchange, symbol: string, side: 'buy' | 'sell', amount: number, limitPrice?: number): Promise<Order> {
89
// Warn the user if their limit price is useless
910
if (limitPrice) {
1011
const lastPrice = await getMarketLastPriceBySymbol(symbol, exchange);
11-
if (side === 'buy' && limitPrice > lastPrice) {
12+
if (side === 'buy' && limitPrice * (1 - LIMIT_PRICE_TOLERANCE) > lastPrice) {
1213
throw new Error(`Current price ${lastPrice} is higher than your limit price ${limitPrice}, so the order will be filled immediately. Use a market order instead.`);
1314
}
14-
if (side === 'sell' && limitPrice < lastPrice) {
15+
if (side === 'sell' && limitPrice * (1 + LIMIT_PRICE_TOLERANCE) < lastPrice) {
1516
throw new Error(`Current price ${lastPrice} is lower than your limit price ${limitPrice}, so the order will be filled immediately. Use a market order instead.`);
1617
}
1718
}
@@ -104,157 +105,6 @@ export async function createBinanceOcoOrder(
104105
return response;
105106
}
106107

107-
// /**
108-
// * Create an advanced order with support for various order types.
109-
// *
110-
// * @link https://docs.ccxt.com/#/README?id=placing-orders
111-
// *
112-
// * @param exchange CCXT exchange instance
113-
// * @param symbol Trading pair symbol, e.g. "BTC/USDT"
114-
// * @param type Order type: 'market', 'limit', 'trigger', 'stop_loss', 'take_profit', 'oco', 'trailing'
115-
// * @param side Order side: 'buy' or 'sell'
116-
// * @param amount Amount to trade
117-
// * @param price Price for limit orders (required for limit orders)
118-
// * @param options Additional options based on order type
119-
// * @returns The created order
120-
// */
121-
// export async function createOrder(
122-
// exchange: Exchange,
123-
// symbol: string,
124-
// type: (typeof ORDER_TYPES)[number],
125-
// side: string,
126-
// amount: number,
127-
// price: number | null,
128-
// options: any = {},
129-
// ): Promise<Order> {
130-
// // Default params for all order types
131-
// const params: any = {};
132-
133-
// let ccxtType = type; // Default, will be overridden for special order types
134-
135-
// // Handle different order types
136-
// switch (type) {
137-
// case 'market':
138-
// case 'limit':
139-
// // Standard order types, no special handling needed
140-
// ccxtType = type;
141-
// break;
142-
143-
// case 'trigger':
144-
// // Trigger orders use the triggerPrice parameter
145-
// if (!options.triggerPrice) {
146-
// throw new Error('Trigger orders require a triggerPrice');
147-
// }
148-
149-
// ccxtType = price ? 'limit' : 'market';
150-
151-
// // params.reduceOnly = options.reduceOnly !== false; // Default to true for take profit
152-
153-
// // TODO: A command like this:
154-
// // 'Send a sell order for 0.05 AAVE at 110 USDC when AAVE price goes below 180 USDC'
155-
// // if AAVE current price is already below 180 USDC, it should trigger immediately
156-
// // but with our logic it sets up a trigger for >=180 USDC, because we 180 USDC is larger
157-
// // than current price, so we classify the order as a take profit order. We should get
158-
// // the triggerDirection as well from the command, so that at least we can stop the user
159-
// // with a message "The current price is already below 180 USDC, so the trigger price should be
160-
// // less than 180 USDC".
161-
162-
// // Other case:
163-
// // Sell 0.05 AAVE at 200 USDC with a 10% stop loss
164-
// // Should:
165-
// // 1. If current price of AAVE is above 200 USDC, it should stop and warn the user
166-
// // 2. If current price is not
167-
// // 2. Otherwise, it should create a take profit order at 200 USDC and a market stop loss
168-
// // order at 180 USDC
169-
170-
// // Trigger orders are not supported by Binance
171-
// // @link https://developers.binance.com/docs/binance-spot-api-docs/enums#order-types-ordertypes-type
172-
// // CCXT automatically converts them to STOP_LOSS orders
173-
// // @link https://discord.com/channels/690203284119617602/690203284727660739/1367185601572376636
174-
// // So, we need to manually check if the user wants a take profit order,
175-
// // lest Binance return with error "Stop price would trigger immediately".
176-
// // In other words, we are simulating what the UI does when you select
177-
// // a "Stop limit" or a "Stop market" order from the dropdown menu.
178-
// const lastPrice = await getMarketLastPriceBySymbol(symbol, exchange);
179-
// if ((options.triggerPrice > lastPrice && side === 'sell') || (options.triggerPrice < lastPrice && side === 'buy')) {
180-
// console.log('take profit order');
181-
// // a take profit order is a trigger order with direction from below (sell) or above (buy)
182-
// params.takeProfitPrice = options.triggerPrice;
183-
// } else if ((options.triggerPrice > lastPrice && side === 'buy') || (options.triggerPrice < lastPrice && side === 'sell')) {
184-
// console.log('stop loss order');
185-
// // a stop loss order is a trigger order with direction from above (sell) or below (buy)
186-
// params.stopLossPrice = options.triggerPrice;
187-
// }
188-
// break;
189-
190-
// case 'oco':
191-
// // One Cancels the Other orders (stop loss + take profit)
192-
// if (!options.ocoConfiguration.ocoStopLoss || !options.ocoConfiguration.ocoTakeProfit) {
193-
// throw new Error('OCO orders require both stopLoss and takeProfit configurations');
194-
// }
195-
196-
// ccxtType = price ? 'limit' : 'market';
197-
198-
// // Configure stop loss
199-
// params.stopLoss = {
200-
// triggerPrice: options.ocoConfiguration.ocoStopLoss.triggerPrice,
201-
// };
202-
203-
// if (options.ocoConfiguration.ocoStopLoss.price) {
204-
// params.stopLoss.price = options.ocoConfiguration.ocoStopLoss.price;
205-
// }
206-
207-
// // Configure take profit
208-
// params.takeProfit = {
209-
// triggerPrice: options.ocoConfiguration.ocoTakeProfit.triggerPrice,
210-
// };
211-
212-
// if (options.ocoConfiguration.ocoTakeProfit.price) {
213-
// params.takeProfit.price = options.ocoConfiguration.ocoTakeProfit.price;
214-
// }
215-
// break;
216-
217-
// case 'trailing':
218-
// // Trailing stop orders
219-
// if (options.trailingPercent === undefined && options.trailingAmount === undefined) {
220-
// throw new Error('Trailing orders require either trailingPercent or trailingAmount');
221-
// }
222-
223-
// ccxtType = price ? 'limit' : 'market';
224-
225-
// if (options.trailingPercent !== undefined) {
226-
// params.trailingPercent = options.trailingPercent;
227-
// }
228-
229-
// if (options.trailingAmount !== undefined) {
230-
// params.trailingAmount = options.trailingAmount;
231-
// }
232-
233-
// if (options.triggerPrice) {
234-
// params.trailingTriggerPrice = options.triggerPrice;
235-
// }
236-
// break;
237-
238-
// default:
239-
// throw new Error(`Unsupported order type: ${type}`);
240-
// }
241-
242-
// if (ccxtType === 'limit' && price === null) {
243-
// throw new Error('Limit orders require a price');
244-
// }
245-
246-
// // Throw if any of the parameters is null or undefined
247-
// for (const key in params) {
248-
// if (params[key] === null || params[key] === undefined) {
249-
// throw new Error(`createOrder: Parameter ${key} is null or undefined`);
250-
// }
251-
// }
252-
253-
// // Create the order with CCXT
254-
// const order = await exchange.createOrder(symbol, ccxtType, side, amount, price === null ? undefined : price, params);
255-
// return order;
256-
// }
257-
258108
/**
259109
* Get all open orders of the user on the given exchange
260110
*

0 commit comments

Comments
 (0)