diff --git a/src/routes/dashboard/(components)/app-sidebar.svelte b/src/routes/dashboard/(components)/app-sidebar.svelte
index f11f99b..e5399de 100644
--- a/src/routes/dashboard/(components)/app-sidebar.svelte
+++ b/src/routes/dashboard/(components)/app-sidebar.svelte
@@ -42,6 +42,12 @@
icon: CoinsIcon,
isActive: page.url.pathname === `/dashboard/bybit`,
},
+ {
+ title: 'ByBit Trading Stop',
+ url: `/dashboard/trading-stop`,
+ icon: CoinsIcon,
+ isActive: page.url.pathname === `/dashboard/trading-stop`,
+ },
{
title: 'Logs',
url: `/dashboard/logs`,
diff --git a/src/routes/dashboard/trading-stop/+page.svelte b/src/routes/dashboard/trading-stop/+page.svelte
new file mode 100644
index 0000000..a080e73
--- /dev/null
+++ b/src/routes/dashboard/trading-stop/+page.svelte
@@ -0,0 +1,189 @@
+
+
+
diff --git a/src/routes/dashboard/trading-stop/+page.ts b/src/routes/dashboard/trading-stop/+page.ts
new file mode 100644
index 0000000..0dfc171
--- /dev/null
+++ b/src/routes/dashboard/trading-stop/+page.ts
@@ -0,0 +1,17 @@
+import { superValidate } from 'sveltekit-superforms'
+import { zod } from 'sveltekit-superforms/adapters'
+import { formSchema } from './schema'
+
+export const load = async () => {
+ const form = await superValidate(
+ {
+ key: '',
+ secret: '',
+ symbol: 'BTCUSDT',
+ },
+ zod(formSchema),
+ { errors: false },
+ )
+
+ return { form }
+}
diff --git a/src/routes/dashboard/trading-stop/schema.ts b/src/routes/dashboard/trading-stop/schema.ts
new file mode 100644
index 0000000..f2d5c79
--- /dev/null
+++ b/src/routes/dashboard/trading-stop/schema.ts
@@ -0,0 +1,15 @@
+import { z } from 'zod'
+
+export const formSchema = z.object({
+ key: z.string(),
+ secret: z.string(),
+ symbol: z.string(),
+ qtyPercent: z.number(),
+ qtyDecimalPoint: z.number(),
+ leverage: z.number().default(1),
+ takeProfitPercent: z.number(),
+ stopLossPercent: z.number(),
+ demo: z.boolean().default(true),
+})
+
+export type FormSchema = typeof formSchema
diff --git a/src/routes/webhook/tradingview/tradingstop/+server.ts b/src/routes/webhook/tradingview/tradingstop/+server.ts
new file mode 100644
index 0000000..1acc880
--- /dev/null
+++ b/src/routes/webhook/tradingview/tradingstop/+server.ts
@@ -0,0 +1,178 @@
+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(),
+ 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 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 buyOrder = orders[0]
+ 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(),
+ })
+
+ const sellOrder = orders[1]
+ 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(),
+ })
+
+ return new Response()
+ } catch (error) {
+ console.log('Error', error)
+ } finally {
+ return new Response()
+ }
+}