You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
6.2 KiB
TypeScript
263 lines
6.2 KiB
TypeScript
import { db } from '$lib/db/index.js'
|
|
import { bybit_logs } from '$lib/db/schema.js'
|
|
import { json } from '@sveltejs/kit'
|
|
import { RestClientV5 } from 'bybit-api'
|
|
import { z } from 'zod'
|
|
|
|
export const POST = async ({ locals, request }) => {
|
|
const type = request.headers.get('Content-Type')
|
|
console.log(type)
|
|
if (!type || !type.startsWith('application/json')) {
|
|
return new Response()
|
|
}
|
|
|
|
const body = await request.json()
|
|
|
|
console.log('Body:', body)
|
|
|
|
const schema = z.object({
|
|
key: z.string(),
|
|
secret: z.string(),
|
|
symbol: z.string(),
|
|
entryPrice: z.string(),
|
|
qtyPercent: z.string(),
|
|
qtyDecimalPoint: z.string(),
|
|
leverage: z.string().default('1'),
|
|
takeProfitPercent: z.string(),
|
|
stopLossPercent: z.string(),
|
|
activePricePercent: z.string(),
|
|
trailingDistancePercent: z.string(),
|
|
demo: z.enum(['true', 'false']),
|
|
})
|
|
|
|
const form = schema.safeParse(body)
|
|
|
|
if (!form.success) {
|
|
console.log(form.error)
|
|
return json({}, { status: 400 })
|
|
}
|
|
|
|
const { key, secret, symbol, demo } = form.data
|
|
|
|
try {
|
|
const client = new RestClientV5({
|
|
key: key,
|
|
secret: secret,
|
|
demoTrading: demo === 'true',
|
|
})
|
|
|
|
client.switchPositionMode({
|
|
category: 'linear',
|
|
mode: 3,
|
|
})
|
|
|
|
const position = await client.getPositionInfo({
|
|
category: 'linear',
|
|
symbol: symbol,
|
|
})
|
|
|
|
console.log('Positions:', position.result.list)
|
|
|
|
if (
|
|
position.result.list.reduce(
|
|
(acc, curr) => acc + Number(curr.positionValue),
|
|
0,
|
|
) > 0
|
|
) {
|
|
console.log('Order exist, exiting...')
|
|
return new Response()
|
|
}
|
|
|
|
const wallet = await client.getWalletBalance({
|
|
accountType: 'UNIFIED',
|
|
coin: 'USDT',
|
|
})
|
|
|
|
console.log(
|
|
'Available Balance:',
|
|
wallet.result.list[0].totalAvailableBalance,
|
|
)
|
|
|
|
console.log('calculate:')
|
|
console.log('Entry Price:', form.data.entryPrice)
|
|
console.log('Qty Percent:', form.data.qtyPercent)
|
|
console.log(
|
|
'Wallet Balance:',
|
|
wallet.result.list[0].totalAvailableBalance,
|
|
)
|
|
|
|
const [price, decimals] = form.data.entryPrice.split('.')
|
|
console.log('Decimals', decimals)
|
|
console.log('Decimals Length', decimals?.length || 0)
|
|
const decimalLength = decimals?.length || 0
|
|
|
|
const qty = (
|
|
(Number(wallet.result.list[0].totalAvailableBalance) *
|
|
((Number(form.data.qtyPercent) / 100) *
|
|
Number(form.data.leverage || '1'))) /
|
|
Number(form.data.entryPrice)
|
|
).toFixed(Number(form.data.qtyDecimalPoint))
|
|
|
|
const takeProfitBuy = (
|
|
Number(form.data.entryPrice) *
|
|
(1 + Number(form.data.takeProfitPercent) / 100)
|
|
).toFixed(decimalLength)
|
|
const stopLossBuy = (
|
|
Number(form.data.entryPrice) *
|
|
(1 - Number(form.data.stopLossPercent) / 100)
|
|
).toFixed(decimalLength)
|
|
|
|
const takeProfitSell = (
|
|
Number(form.data.entryPrice) *
|
|
(1 - Number(form.data.takeProfitPercent) / 100)
|
|
).toFixed(decimalLength)
|
|
const stopLossSell = (
|
|
Number(form.data.entryPrice) *
|
|
(1 + Number(form.data.stopLossPercent) / 100)
|
|
).toFixed(decimalLength)
|
|
|
|
console.log({
|
|
qty,
|
|
takeProfitBuy,
|
|
stopLossBuy,
|
|
takeProfitSell,
|
|
stopLossSell,
|
|
})
|
|
const orders = await Promise.all([
|
|
await client.submitOrder({
|
|
category: 'linear',
|
|
symbol,
|
|
side: 'Buy',
|
|
orderType: 'Market',
|
|
qty,
|
|
takeProfit: takeProfitBuy,
|
|
stopLoss: stopLossBuy,
|
|
isLeverage: form.data.leverage ? 1 : 0,
|
|
positionIdx: 1,
|
|
}),
|
|
await client.submitOrder({
|
|
category: 'linear',
|
|
symbol,
|
|
side: 'Sell',
|
|
orderType: 'Market',
|
|
qty,
|
|
takeProfit: takeProfitSell,
|
|
stopLoss: stopLossSell,
|
|
isLeverage: form.data.leverage ? 1 : 0,
|
|
positionIdx: 2,
|
|
}),
|
|
])
|
|
|
|
console.log('Orders:', orders)
|
|
|
|
const activePriceBuy = (
|
|
Number(form.data.entryPrice) *
|
|
(1 + Number(form.data.activePricePercent) / 100)
|
|
).toFixed(decimalLength)
|
|
|
|
const activePriceSell = (
|
|
Number(form.data.entryPrice) *
|
|
(1 - Number(form.data.activePricePercent) / 100)
|
|
).toFixed(decimalLength)
|
|
|
|
const trailingDistance = (
|
|
Number(form.data.entryPrice) *
|
|
(Number(form.data.activePricePercent) / 100)
|
|
).toFixed(decimalLength)
|
|
|
|
const tradingStops = await Promise.all([
|
|
await client.setTradingStop({
|
|
category: 'linear',
|
|
activePrice: activePriceBuy,
|
|
trailingStop: trailingDistance,
|
|
symbol: symbol,
|
|
positionIdx: 1,
|
|
}),
|
|
await client.setTradingStop({
|
|
category: 'linear',
|
|
activePrice: activePriceSell,
|
|
trailingStop: trailingDistance,
|
|
symbol: symbol,
|
|
positionIdx: 2,
|
|
}),
|
|
])
|
|
|
|
console.log('Trading Stops:', tradingStops)
|
|
|
|
const buyOrder = orders[0]
|
|
const sellOrder = orders[1]
|
|
const buyTradingStop = tradingStops[0]
|
|
const sellTradingStop = tradingStops[1]
|
|
const logs = await Promise.all([
|
|
await db.insert(bybit_logs).values({
|
|
status: buyOrder.retCode === 0 ? 'success' : 'failed',
|
|
request: form.data,
|
|
payload: {
|
|
category: 'linear',
|
|
symbol,
|
|
side: 'Buy',
|
|
orderType: 'Market',
|
|
qty,
|
|
takeProfit: takeProfitBuy,
|
|
stopLoss: stopLossBuy,
|
|
isLeverage: form.data.leverage ? 1 : 0,
|
|
positionIdx: 1,
|
|
},
|
|
response: buyOrder,
|
|
createdAt: Date.now(),
|
|
}),
|
|
await db.insert(bybit_logs).values({
|
|
status: sellOrder.retCode === 0 ? 'success' : 'failed',
|
|
request: form.data,
|
|
payload: {
|
|
category: 'linear',
|
|
symbol,
|
|
side: 'Sell',
|
|
orderType: 'Market',
|
|
qty,
|
|
takeProfit: takeProfitSell,
|
|
stopLoss: stopLossSell,
|
|
isLeverage: form.data.leverage ? 1 : 0,
|
|
positionIdx: 1,
|
|
},
|
|
response: sellOrder,
|
|
createdAt: Date.now(),
|
|
}),
|
|
await db.insert(bybit_logs).values({
|
|
status: buyTradingStop.retCode === 0 ? 'success' : 'failed',
|
|
type: 'trading stop',
|
|
request: form.data,
|
|
payload: {
|
|
category: 'linear',
|
|
activePrice: activePriceBuy,
|
|
trailingStop: trailingDistance,
|
|
symbol: symbol,
|
|
positionIdx: 1,
|
|
},
|
|
response: buyTradingStop,
|
|
createdAt: Date.now(),
|
|
}),
|
|
await db.insert(bybit_logs).values({
|
|
status: sellTradingStop.retCode === 0 ? 'success' : 'failed',
|
|
type: 'trading stop',
|
|
request: form.data,
|
|
payload: {
|
|
category: 'linear',
|
|
activePrice: activePriceSell,
|
|
trailingStop: trailingDistance,
|
|
symbol: symbol,
|
|
positionIdx: 2,
|
|
},
|
|
response: sellTradingStop,
|
|
createdAt: Date.now(),
|
|
}),
|
|
])
|
|
|
|
return new Response()
|
|
} catch (error) {
|
|
console.log('Error', error)
|
|
} finally {
|
|
return new Response()
|
|
}
|
|
}
|