Skip to content

Commit eaba000

Browse files
authored
Merge branch 'main' into withdraw
2 parents c4abdab + d6b0c30 commit eaba000

File tree

19 files changed

+8487
-5254
lines changed

19 files changed

+8487
-5254
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Pull Request
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
types:
11+
runs-on: ubuntu-latest
12+
env:
13+
BITTE_API_KEY: fake-api-key
14+
NEAR_PK: fake-key
15+
NEXT_PUBLIC_ACCOUNT_ID: fake-account
16+
CRON_SECRET: fake-secret
17+
18+
steps:
19+
- uses: actions/checkout@v5
20+
21+
- name: Setup PNPM
22+
uses: pnpm/action-setup@v4
23+
with:
24+
version: 10
25+
run_install: false
26+
27+
- name: Setup Node
28+
uses: actions/setup-node@v5
29+
with:
30+
node-version: "22"
31+
cache: "pnpm"
32+
33+
- name: Install Dependencies
34+
run: pnpm install --frozen-lockfile
35+
36+
- name: Lint, Typecheck & Build
37+
run: |
38+
pnpm lint
39+
pnpm types
40+
pnpm build

README.md

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Auto Trader Agent
1+
# Auto Trader Agent
22

33
An autonomous crypto trading agent that references market decisions and then uses near intents to execute trades across many chains via a single near account
44

@@ -8,30 +8,34 @@ An autonomous crypto trading agent that references market decisions and then use
88

99
Simple strategy customization - just pass a config object to override the default Wall Street strategy.
1010

11-
1211
### Default Strategy
1312

1413
The system comes with a proven Wall Street 3-step strategy:
1514

1615
```typescript
1716
// From lib/strategies/index.ts
1817
export const DEFAULT_STRATEGY: StrategyConfig = {
19-
overview: "Wall Street 3-Step: Data-driven day trading with clear profit/loss targets and risk management",
18+
overview:
19+
"Wall Street 3-Step: Data-driven day trading with clear profit/loss targets and risk management",
2020
riskParams: {
21-
profitTarget: 2, // +2% profit target
22-
stopLoss: -1.5, // -1.5% stop loss
23-
maxPositions: 4, // Max 4 open positions
24-
positionSize: "5-15% of USDC"
21+
profitTarget: 2, // +2% profit target
22+
stopLoss: -1.5, // -1.5% stop loss
23+
maxPositions: 4, // Max 4 open positions
24+
positionSize: "5-15% of USDC",
2525
},
26-
step1Rules: "Risk targets: SELL at +2% profit OR -1.5% loss. Close losing positions faster than winners (cut losses, let profits run). .",
27-
step2Rules: "Screen for high-probability setups: Price momentum >3% with volume confirmation, Fear/Greed extremes, Order book imbalances. Use 1 analysis tool only if market data insufficient. Only trade clear directional moves.",
28-
step3Rules: "Dynamic sizing: 5-15% per trade (scales with account). Size calculation: Min($10, Max($5, USDC_balance * 0.10)). Account for slippage: Minimum $8 positions. Max 3-4 open positions at once."
26+
step1Rules:
27+
"Risk targets: SELL at +2% profit OR -1.5% loss. Close losing positions faster than winners (cut losses, let profits run). .",
28+
step2Rules:
29+
"Screen for high-probability setups: Price momentum >3% with volume confirmation, Fear/Greed extremes, Order book imbalances. Use 1 analysis tool only if market data insufficient. Only trade clear directional moves.",
30+
step3Rules:
31+
"Dynamic sizing: 5-15% per trade (scales with account). Size calculation: Min($10, Max($5, USDC_balance * 0.10)). Account for slippage: Minimum $8 positions. Max 3-4 open positions at once.",
2932
};
3033
```
3134

3235
### Available Tools
3336

3437
All strategies have access to these trading tools:
38+
3539
- `klines`: Trend confirmation, support/resistance levels
3640
- `fearGreed`: Market sentiment (0-100 scale, <20 = fear, >80 = greed)
3741
- `orderBook`: Liquidity depth and spread analysis
@@ -44,21 +48,21 @@ All strategies have access to these trading tools:
4448
const template: StrategyConfig = {
4549
overview: "Your Strategy Name: What does your strategy do?",
4650
riskParams: {
47-
profitTarget: 2, // % profit to exit
48-
stopLoss: -1.5, // % loss to exit
49-
maxPositions: 4, // Max open positions
50-
positionSize: "5-15% of USDC" // Position sizing
51+
profitTarget: 2, // % profit to exit
52+
stopLoss: -1.5, // % loss to exit
53+
maxPositions: 4, // Max open positions
54+
positionSize: "5-15% of USDC", // Position sizing
5155
},
5256
step1Rules: "When and how to close existing positions...",
53-
step2Rules: "What market conditions to look for...",
54-
step3Rules: "How to size and execute new positions..."
57+
step2Rules: "What market conditions to look for...",
58+
step3Rules: "How to size and execute new positions...",
5559
};
5660
```
5761

5862
### Core Infrastructure
5963

6064
- All trading through USDC pairs
61-
- Same asset list and tools
65+
- Same asset list and tools
6266
- Same execution rules
6367
- Same portfolio tracking
6468
- Same API integration

app/api/deposit/route.ts

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,54 @@
1-
import { NextRequest, NextResponse } from 'next/server'
2-
import { BALANCE_UPDATE_DELAY } from '@/lib/utils'
3-
import { initializeNearAccount, depositUSDC, getUSDCBalance } from '@/lib/near'
4-
import { formatUnits } from '@/lib/viem';
5-
import { ACCOUNT_ID } from '@/lib/env';
6-
import { withCronSecret } from '@/lib/api-auth';
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { BALANCE_UPDATE_DELAY } from "@/lib/utils";
3+
import { initializeNearAccount, depositUSDC, getUSDCBalance } from "@/lib/near";
4+
import { formatUnits } from "@/lib/viem";
5+
import { ACCOUNT_ID } from "@/lib/env";
6+
import { withCronSecret } from "@/lib/api-auth";
77

88
async function depositHandler(request: NextRequest) {
99
try {
10-
const { searchParams } = new URL(request.url)
11-
const depositStr = searchParams.get('amount');
10+
const { searchParams } = new URL(request.url);
11+
const depositStr = searchParams.get("amount");
1212
if (!depositStr) {
13-
return NextResponse.json({ error: 'unspecified amount' }, { status: 400 })
13+
return NextResponse.json(
14+
{ error: "unspecified amount" },
15+
{ status: 400 },
16+
);
1417
}
1518

1619
const depositAmount = BigInt(depositStr);
17-
18-
const account = await initializeNearAccount(ACCOUNT_ID)
19-
20-
const usdcBalance = await getUSDCBalance(account)
20+
21+
const account = await initializeNearAccount(ACCOUNT_ID);
22+
23+
const usdcBalance = await getUSDCBalance(account);
2124

2225
if (usdcBalance < depositAmount) {
23-
return NextResponse.json({
24-
error: `Insufficient USDC balance (required: $${depositAmount}, available: $${usdcBalance})`
25-
}, { status: 400 })
26+
return NextResponse.json(
27+
{
28+
error: `Insufficient USDC balance (required: $${depositAmount}, available: $${usdcBalance})`,
29+
},
30+
{ status: 400 },
31+
);
2632
}
2733

28-
const tx = await depositUSDC(account, depositAmount)
29-
30-
await new Promise(resolve => setTimeout(resolve, BALANCE_UPDATE_DELAY))
31-
34+
const tx = await depositUSDC(account, depositAmount);
35+
36+
await new Promise((resolve) => setTimeout(resolve, BALANCE_UPDATE_DELAY));
37+
3238
const uiAmount = formatUnits(depositAmount, 6);
3339

34-
return NextResponse.json({
40+
return NextResponse.json({
3541
message: `Successfully deposited $${uiAmount} USDC`,
3642
transactionHash: tx.transaction.hash,
37-
amount: uiAmount
38-
})
39-
43+
amount: uiAmount,
44+
});
4045
} catch (error) {
41-
console.error('Error in deposit endpoint:', error)
42-
return NextResponse.json({ error: 'Failed to process deposit request' }, { status: 500 })
46+
console.error("Error in deposit endpoint:", error);
47+
return NextResponse.json(
48+
{ error: "Failed to process deposit request" },
49+
{ status: 500 },
50+
);
4351
}
4452
}
4553

46-
export const GET = withCronSecret(depositHandler);
54+
export const GET = withCronSecret(depositHandler);

app/api/trade/route.ts

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,71 @@
1-
import { NextResponse } from 'next/server'
2-
import { BALANCE_UPDATE_DELAY, logTradingAgentData } from '@/lib/utils'
3-
import { storeTrade, storePortfolioSnapshot } from '@/lib/api-helpers'
4-
import { buildTransactionPayload, initializeNearAccount } from '@/lib/near'
5-
import { buildAgentContext } from '@/lib/agent-context'
6-
import { callAgent } from '@bitte-ai/agent-sdk'
7-
import { ToolResult } from '@/lib/types'
8-
import { ACCOUNT_ID } from '@/lib/env'
9-
import { withCronSecret } from '@/lib/api-auth'
10-
1+
import { NextResponse } from "next/server";
2+
import { BALANCE_UPDATE_DELAY, logTradingAgentData } from "@/lib/utils";
3+
import { storeTrade, storePortfolioSnapshot } from "@/lib/api-helpers";
4+
import { buildTransactionPayload, initializeNearAccount } from "@/lib/near";
5+
import { buildAgentContext } from "@/lib/agent-context";
6+
import { callAgent } from "@bitte-ai/agent-sdk";
7+
import { ToolResult } from "@/lib/types";
8+
import { ACCOUNT_ID } from "@/lib/env";
9+
import { withCronSecret } from "@/lib/api-auth";
1110

1211
async function tradeHandler(): Promise<NextResponse> {
13-
try {
14-
const agentId = 'trading-agent-kappa.vercel.app'
15-
16-
const account = await initializeNearAccount(ACCOUNT_ID)
17-
18-
const context = await buildAgentContext(ACCOUNT_ID, account)
19-
20-
const { content, toolResults } = await callAgent(ACCOUNT_ID, context.systemPrompt, agentId)
21-
22-
const quoteResult = (toolResults as ToolResult[]).find(callResult =>
23-
callResult.result?.data?.data?.quote
24-
)
25-
const quote = quoteResult?.result?.data?.data?.quote
26-
12+
try {
13+
const agentId = "trading-agent-kappa.vercel.app";
14+
15+
const account = await initializeNearAccount(ACCOUNT_ID);
16+
17+
const context = await buildAgentContext(ACCOUNT_ID, account);
18+
19+
const { content, toolResults } = await callAgent(
20+
ACCOUNT_ID,
21+
context.systemPrompt,
22+
agentId,
23+
);
24+
25+
const quoteResult = (toolResults as ToolResult[]).find(
26+
(callResult) => callResult.result?.data?.data?.quote,
27+
);
28+
const quote = quoteResult?.result?.data?.data?.quote;
29+
2730
logTradingAgentData({
2831
context,
2932
content,
3033
pnlUsd: context.totalPnl,
31-
quoteResult
32-
})
34+
quoteResult,
35+
});
3336

3437
if (quote) {
35-
const tx = await account.signAndSendTransaction(buildTransactionPayload(quote))
36-
console.log('Trade executed:', tx.transaction.hash)
37-
await new Promise(resolve => setTimeout(resolve, BALANCE_UPDATE_DELAY))
38-
await storeTrade(quote)
39-
40-
const updatedContext = await buildAgentContext(ACCOUNT_ID, account)
41-
42-
await storePortfolioSnapshot(updatedContext.positionsWithPnl, updatedContext.totalUsd, context.totalUsd, content)
43-
}
44-
else {
45-
await storePortfolioSnapshot(context.positionsWithPnl, context.totalUsd, context.totalUsd, content)
38+
const tx = await account.signAndSendTransaction(
39+
buildTransactionPayload(quote),
40+
);
41+
console.log("Trade executed:", tx.transaction.hash);
42+
await new Promise((resolve) => setTimeout(resolve, BALANCE_UPDATE_DELAY));
43+
await storeTrade(quote);
44+
45+
const updatedContext = await buildAgentContext(ACCOUNT_ID, account);
46+
47+
await storePortfolioSnapshot(
48+
updatedContext.positionsWithPnl,
49+
updatedContext.totalUsd,
50+
context.totalUsd,
51+
content,
52+
);
53+
} else {
54+
await storePortfolioSnapshot(
55+
context.positionsWithPnl,
56+
context.totalUsd,
57+
context.totalUsd,
58+
content,
59+
);
4660
}
47-
return NextResponse.json({ content })
48-
61+
return NextResponse.json({ content });
4962
} catch (error) {
50-
console.error('Error in trading endpoint:', error)
51-
return NextResponse.json({ error: 'Failed to process trading request' }, { status: 500 })
63+
console.error("Error in trading endpoint:", error);
64+
return NextResponse.json(
65+
{ error: "Failed to process trading request" },
66+
{ status: 500 },
67+
);
5268
}
5369
}
5470

55-
export const GET = withCronSecret(tradeHandler);
71+
export const GET = withCronSecret(tradeHandler);

app/layout.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export const metadata = {
2-
title: 'Autonomous Trading Agent',
3-
description: 'Deployment status page',
4-
}
2+
title: "Autonomous Trading Agent",
3+
description: "Deployment status page",
4+
};
55

66
const styles = `
77
* { margin: 0; padding: 0; box-sizing: border-box; }
@@ -23,17 +23,20 @@ const styles = `
2323
export default function RootLayout({
2424
children,
2525
}: {
26-
children: React.ReactNode
26+
children: React.ReactNode;
2727
}) {
2828
return (
2929
<html lang="en">
3030
<head>
31-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap" rel="stylesheet" />
31+
<link
32+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap"
33+
rel="stylesheet"
34+
/>
3235
</head>
3336
<body>
3437
<style>{styles}</style>
3538
{children}
3639
</body>
3740
</html>
38-
)
41+
);
3942
}

app/page.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ export default function Page() {
77
const [deployed, setDeployed] = useState<boolean | null>(null);
88
const [loading, setLoading] = useState(true);
99

10-
1110
const baseUrl = "https://bitte-autonomous-agent-dashboard.vercel.app";
12-
1311

1412
useEffect(() => {
1513
fetch(`${baseUrl}/api/deployment/check?accountId=${ACCOUNT_ID}`)
@@ -33,7 +31,8 @@ export default function Page() {
3331
) : deployed === true ? (
3432
<>
3533
<p className="description">
36-
Your agent is registered and ready. Next step: deposit USDC to start trading.
34+
Your agent is registered and ready. Next step: deposit USDC to
35+
start trading.
3736
</p>
3837
<a href={`${baseUrl}/deposit-usdc`} className="button">
3938
Deposit USDC
@@ -42,9 +41,13 @@ export default function Page() {
4241
) : (
4342
<>
4443
<p className="description">
45-
You`&apos;ve completed the deployment step. Now you need to register your agent.
44+
You&apos;ve completed the deployment step. Now you need to
45+
register your agent.
4646
</p>
47-
<a href={`${baseUrl}/deploy-url?url=${encodeURIComponent(DEPLOYMENT_URL)}`} className="button">
47+
<a
48+
href={`${baseUrl}/deploy-url?url=${encodeURIComponent(DEPLOYMENT_URL)}`}
49+
className="button"
50+
>
4851
Register Agent
4952
</a>
5053
</>
@@ -53,4 +56,3 @@ export default function Page() {
5356
</div>
5457
);
5558
}
56-

0 commit comments

Comments
 (0)