|
1 | 1 | import { Exchange, Order } from 'ccxt'; |
2 | 2 | import { getMarketLastPriceBySymbol } from './markets'; |
3 | 3 | import util from 'util'; |
| 4 | +import { LIMIT_PRICE_TOLERANCE } from '../constants'; |
4 | 5 | /** |
5 | 6 | * Create a simple order, that is, an order that has no triggers attached to it. |
6 | 7 | */ |
7 | 8 | export async function createSimpleOrder(exchange: Exchange, symbol: string, side: 'buy' | 'sell', amount: number, limitPrice?: number): Promise<Order> { |
8 | 9 | // Warn the user if their limit price is useless |
9 | 10 | if (limitPrice) { |
10 | 11 | const lastPrice = await getMarketLastPriceBySymbol(symbol, exchange); |
11 | | - if (side === 'buy' && limitPrice > lastPrice) { |
| 12 | + if (side === 'buy' && limitPrice * (1 - LIMIT_PRICE_TOLERANCE) > lastPrice) { |
12 | 13 | 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.`); |
13 | 14 | } |
14 | | - if (side === 'sell' && limitPrice < lastPrice) { |
| 15 | + if (side === 'sell' && limitPrice * (1 + LIMIT_PRICE_TOLERANCE) < lastPrice) { |
15 | 16 | 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.`); |
16 | 17 | } |
17 | 18 | } |
@@ -104,157 +105,6 @@ export async function createBinanceOcoOrder( |
104 | 105 | return response; |
105 | 106 | } |
106 | 107 |
|
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 | | - |
258 | 108 | /** |
259 | 109 | * Get all open orders of the user on the given exchange |
260 | 110 | * |
|
0 commit comments