Skip to content

Commit 1cb4def

Browse files
committed
get working
1 parent 9cd39e5 commit 1cb4def

File tree

3 files changed

+82
-70
lines changed

3 files changed

+82
-70
lines changed

packages/client-coinbase/src/index.ts

+11-42
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import {
88
stringToUuid,
99
composeContext,
1010
generateText,
11-
ModelClass
11+
ModelClass,
12+
State
1213
} from "@elizaos/core";
1314
import { postTweet } from "@elizaos/plugin-twitter";
1415
import express from "express";
1516
import { WebhookEvent } from "./types";
16-
// import { pnlProvider } from "@elizaos/plugin-coinbase";
17+
import { pnlProvider } from "@elizaos/plugin-coinbase";
1718

1819
export class CoinbaseClient implements Client {
1920
private runtime: IAgentRuntime;
@@ -94,18 +95,15 @@ export class CoinbaseClient implements Client {
9495
});
9596
}
9697

97-
private async generateTweetContent(event: WebhookEvent, _tradeAmount: number, formattedTimestamp: string): Promise<string> {
98+
private async generateTweetContent(event: WebhookEvent, amountInCurrency: number, formattedTimestamp: string, state: State): Promise<string> {
9899
try {
99-
const roomId = stringToUuid("coinbase-trading");
100-
const amount = Number(this.runtime.getSetting('COINBASE_TRADING_AMOUNT')) ?? 1;
101-
102100
const tradeTweetTemplate = `
103101
# Task
104102
Create an engaging and unique tweet announcing a Coinbase trade. Be creative but professional.
105103
106104
Trade details:
107105
- ${event.event.toUpperCase()} order for ${event.ticker}
108-
- Trading amount: $${amount.toFixed(2)}
106+
- Trading amount: $${amountInCurrency.toFixed(2)}
109107
- Current price: $${Number(event.price).toFixed(2)}
110108
- Time: ${formattedTimestamp}
111109
@@ -127,28 +125,9 @@ Example variations for sells:
127125
"📊 Strategic exit: Released $1,000 of BTC at $52,000.00"
128126
129127
Generate only the tweet text, no commentary or markdown.`;
130-
131128
const context = composeContext({
132129
template: tradeTweetTemplate,
133-
state: {
134-
event: event.event.toUpperCase(),
135-
ticker: event.ticker,
136-
amount: `${amount.toFixed(2)}`,
137-
price: `${Number(event.price).toFixed(2)}`,
138-
timestamp: formattedTimestamp,
139-
bio: '',
140-
lore: '',
141-
messageDirections: '',
142-
postDirections: '',
143-
persona: '',
144-
personality: '',
145-
role: '',
146-
scenario: '',
147-
roomId,
148-
actors: '',
149-
recentMessages: '',
150-
recentMessagesData: []
151-
}
130+
state
152131
});
153132

154133
const tweetContent = await generateText({
@@ -180,7 +159,7 @@ Generate only the tweet text, no commentary or markdown.`;
180159
agentId: this.runtime.agentId,
181160
roomId,
182161
content: {
183-
text: `Place an advanced market order to ${event.event.toLowerCase()} $${amount} worth of ${event.ticker}`,
162+
text: `Place an advanced trade market order to ${event.event.toLowerCase()} $${amount} worth of ${event.ticker}`,
184163
action: "EXECUTE_ADVANCED_TRADE",
185164
source: "coinbase",
186165
metadata: {
@@ -195,7 +174,7 @@ Generate only the tweet text, no commentary or markdown.`;
195174
};
196175

197176
await this.runtime.messageManager.createMemory(memory);
198-
177+
const state = await this.runtime.composeState(memory);
199178
const callback: HandlerCallback = async (content: Content) => {
200179
if (!content.text.includes("Trade executed successfully")) {
201180
return [];
@@ -208,20 +187,12 @@ Generate only the tweet text, no commentary or markdown.`;
208187
timeZoneName: 'short'
209188
}).format(new Date(event.timestamp));
210189

211-
// const pnl = await pnlProvider.get(this.runtime, memory);
212-
213-
214-
// const pnlText = pnl ? `Realized PNL: ${JSON.stringify(pnl.realizedPnl)}, Unrealized PNL: ${JSON.stringify(pnl.unrealizedPnl)}` : "";
190+
const pnl = await pnlProvider.get(this.runtime, memory);
215191

216-
// const tweetContent = `🚀 ${event.event.toUpperCase()} for ${event.ticker}!
217-
// Amount: $${amount}.
218-
// Price: $${event.price}.
219-
// Time: ${formattedTimestamp} 🌀
220-
// ${pnlText}
221-
// `;
192+
const pnlText = pnl ? `Realized PNL: ${JSON.stringify(pnl.realizedPnl)}, Unrealized PNL: ${JSON.stringify(pnl.unrealizedPnl)}` : "";
222193

223194
try {
224-
const tweetContent = await this.generateTweetContent(event, amount, formattedTimestamp);
195+
const tweetContent = await this.generateTweetContent(event, amount, formattedTimestamp, state);
225196
elizaLogger.info("Generated tweet content:", tweetContent);
226197
const response = await postTweet(tweetContent);
227198
elizaLogger.info("Tweet response:", response);
@@ -231,9 +202,7 @@ Generate only the tweet text, no commentary or markdown.`;
231202
return [];
232203
};
233204

234-
const state = await this.runtime.composeState(memory);
235205
await this.runtime.processActions(memory, [memory], state, callback);
236-
237206
}
238207

239208
async stop(): Promise<void> {

packages/plugin-coinbase/src/plugins/advancedTrade.ts

+69-24
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import { createArrayCsvWriter } from "csv-writer";
2323
import {
2424
OrderSide,
2525
OrderConfiguration,
26+
Position,
27+
PortfolioType,
2628
} from "../../advanced-sdk-ts/src/rest/types/common-types";
2729
import { CreateOrderResponse } from "../../advanced-sdk-ts/src/rest/types/orders-types";
2830

@@ -109,18 +111,40 @@ export const pnlProvider: Provider = {
109111
runtime.getSetting("COINBASE_API_KEY") ?? process.env.COINBASE_API_KEY,
110112
runtime.getSetting("COINBASE_PRIVATE_KEY") ?? process.env.COINBASE_PRIVATE_KEY
111113
);
112-
const portfolioId = runtime.getSetting("COINBASE_PORTFOLIO_ID") ?? process.env.COINBASE_PORTFOLIO_ID;
114+
const portfolios = JSON.parse(await client.listPortfolios({portfolioType: PortfolioType.DEFAULT}));
115+
elizaLogger.info("Portfolios:", portfolios);
116+
const portfolioId = portfolios.portfolios[0].uuid;
117+
elizaLogger.info("Portfolio ID:", portfolioId);
118+
return { realizedPnl: 0, unrealizedPnl: 0 };
113119
if (!portfolioId) {
114120
elizaLogger.error("Portfolio ID is not set");
115121
return { realizedPnl: 0, unrealizedPnl: 0 };
116122
}
117-
client.getAccount
118-
const balanceSummary= await client.getPerpetualsPortfolioSummary({portfolioUuid: portfolioId});
119-
const portfolioBalances = balanceSummary?.summary;
120-
elizaLogger.info("Balance Summary:", balanceSummary);
121-
const unrealizedPnl = portfolioBalances?.unrealized_pnl || 0;
122-
elizaLogger.info("Unrealized PNL:", unrealizedPnl);
123-
return unrealizedPnl;
123+
124+
let btcPosition: Position, ethPosition: Position;
125+
try {
126+
btcPosition = JSON.parse(await client.getPortfolioBalances({portfolioUuid: portfolioId, symbol: "BTC-USD"})).position;
127+
ethPosition = JSON.parse(await client.getPortfolioBalances({portfolioUuid: portfolioId, symbol: "ETH-USD"})).position;
128+
elizaLogger.info("BTC Position:", btcPosition);
129+
elizaLogger.info("ETH Position:", ethPosition);
130+
} catch (error) {
131+
elizaLogger.error("Error fetching portfolio balances:", error.message);
132+
return { realizedPnl: 0, unrealizedPnl: 0 };
133+
}
134+
135+
const btcUnrealizedPnl = btcPosition?.unrealized_pnl || 0;
136+
const ethUnrealizedPnl = ethPosition?.unrealized_pnl || 0;
137+
const ethAggregatedPnl = ethPosition?.aggregated_pnl || 0;
138+
const btcAggregatedPnl = btcPosition?.aggregated_pnl || 0;
139+
elizaLogger.info("BTC Unrealized PNL:", JSON.stringify(btcUnrealizedPnl));
140+
elizaLogger.info("ETH Unrealized PNL:", JSON.stringify(ethUnrealizedPnl));
141+
elizaLogger.info("ETH Aggregated PNL:", JSON.stringify(ethAggregatedPnl));
142+
return {
143+
btcUnrealizedPnl: JSON.stringify(btcUnrealizedPnl),
144+
ethUnrealizedPnl: JSON.stringify(ethUnrealizedPnl),
145+
ethAggregatedPnl: JSON.stringify(ethAggregatedPnl),
146+
btcAggregatedPnl: JSON.stringify(btcAggregatedPnl)
147+
}
124148
}
125149
}
126150

@@ -140,8 +164,8 @@ export async function appendTradeToCsv(tradeResult: any) {
140164
tradeResult.failure_response?.order_id ||
141165
"",
142166
tradeResult.success,
143-
// JSON.stringify(tradeResult.order_configuration || {}),
144-
// JSON.stringify(tradeResult.success_response || tradeResult.failure_response || {})
167+
JSON.stringify(tradeResult.order_configuration || {}),
168+
JSON.stringify(tradeResult.success_response || tradeResult.failure_response || {})
145169
];
146170

147171
elizaLogger.info("Formatted trade for CSV:", formattedTrade);
@@ -166,7 +190,6 @@ async function hasEnoughBalance(
166190
try {
167191
const response = await client.listAccounts({});
168192
const accounts = JSON.parse(response);
169-
elizaLogger.info("Accounts:", accounts);
170193
const checkCurrency = side === "BUY" ? "USD" : currency;
171194
elizaLogger.info(
172195
`Checking balance for ${side} order of ${amount} ${checkCurrency}`
@@ -208,7 +231,25 @@ async function hasEnoughBalance(
208231
}
209232
}
210233

211-
async function getPrice(client: RESTClient, productId: string) {
234+
export async function getPrice(client: RESTClient, productId: string) {
235+
elizaLogger.debug("Fetching product info for productId:", productId);
236+
try {
237+
const productInfo = await client.getProduct({productId});
238+
const price = JSON.parse(productInfo)?.price;
239+
elizaLogger.info("Product info retrieved:", productInfo);
240+
elizaLogger.info("Price:", price);
241+
return Number(price);
242+
} catch (error) {
243+
elizaLogger.error("Error fetching product info:", error);
244+
return null;
245+
}
246+
}
247+
248+
export async function getPricUSD(runtime: IAgentRuntime, productId: string) {
249+
const client = new RESTClient(
250+
runtime.getSetting("COINBASE_API_KEY") ?? process.env.COINBASE_API_KEY,
251+
runtime.getSetting("COINBASE_PRIVATE_KEY") ?? process.env.COINBASE_PRIVATE_KEY
252+
);
212253
elizaLogger.debug("Fetching product info for productId:", productId);
213254
try {
214255
const productInfo = await client.getProduct({productId});
@@ -246,7 +287,7 @@ export const executeAdvancedTradeAction: Action = {
246287
],
247288
handler: async (
248289
runtime: IAgentRuntime,
249-
_message: Memory,
290+
message: Memory,
250291
state: State,
251292
_options: any,
252293
callback: HandlerCallback
@@ -276,12 +317,14 @@ export const executeAdvancedTradeAction: Action = {
276317

277318
// Generate trade details
278319
let tradeDetails;
320+
const updatedState = state;
321+
updatedState.message = message.content.text;
279322
elizaLogger.debug("Starting trade details generation");
280323
try {
281324
tradeDetails = await generateObject({
282325
runtime,
283326
context: composeContext({
284-
state,
327+
state: updatedState,
285328
template: advancedTradeTemplate,
286329
}),
287330
modelClass: ModelClass.LARGE,
@@ -316,7 +359,7 @@ export const executeAdvancedTradeAction: Action = {
316359
let amountInCurrency = amount;
317360
// Configure order
318361
let orderConfiguration: OrderConfiguration;
319-
elizaLogger.debug("Starting order configuration");
362+
elizaLogger.debug("Starting order configuration", {productId, amount, side, orderType, limitPrice});
320363
try {
321364
if (orderType === "MARKET") {
322365
const priceInUSD = await getPrice(client, productId);
@@ -343,7 +386,7 @@ export const executeAdvancedTradeAction: Action = {
343386
}
344387
orderConfiguration = {
345388
limit_limit_gtc: {
346-
baseSize: amount.toString(),
389+
baseSize: amountInCurrency.toString(),
347390
limitPrice: limitPrice.toString(),
348391
postOnly: false,
349392
},
@@ -387,23 +430,25 @@ export const executeAdvancedTradeAction: Action = {
387430
);
388431
return;
389432
}
390-
391-
order = await client.createOrder({
392-
clientOrderId: crypto.randomUUID(),
433+
const orderId = crypto.randomUUID();
434+
order = JSON.parse(await client.createOrder({
435+
clientOrderId: orderId,
393436
productId,
394437
side: side === "BUY" ? OrderSide.BUY : OrderSide.SELL,
395438
orderConfiguration,
396-
});
397-
439+
}));
440+
elizaLogger.info("Order:", JSON.stringify(order));
398441
if (order.success) {
442+
elizaLogger.info("Trade executed successfully:", JSON.stringify(order));
399443
callback(
400444
{
401-
text: `Trade executed successfully: ${order.order_id}`,
445+
text: `Trade executed successfully: ${JSON.stringify(order)}`,
402446
},
403447
[]
404448
);
405449
elizaLogger.info("Trade executed successfully:", order);
406450
} else {
451+
elizaLogger.error("Trade execution failed:", (order as any)?.error_response?.message);
407452
callback(
408453
{
409454
text: `Failed to execute trade: ${(order as any)?.error_response?.message ?? "Unknown error occurred"}`,
@@ -423,7 +468,7 @@ export const executeAdvancedTradeAction: Action = {
423468
}
424469
// Log trade to CSV
425470
try {
426-
// await appendTradeToCsv(order);
471+
await appendTradeToCsv(order);
427472
elizaLogger.info("Trade logged to CSV");
428473
} catch (csvError) {
429474
elizaLogger.warn("Failed to log trade to CSV:", csvError);
@@ -519,5 +564,5 @@ export const advancedTradePlugin: Plugin = {
519564
name: "advancedTradePlugin",
520565
description: "Enables advanced trading using Coinbase Advanced Trading API",
521566
actions: [executeAdvancedTradeAction],
522-
providers: [tradeProvider],
567+
providers: [tradeProvider, pnlProvider],
523568
};

packages/plugin-coinbase/src/templates.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ Here are the recent user messages for context:
109109
`;
110110

111111
export const advancedTradeTemplate = `
112-
Extract the following details for processing an advanced trade using the Coinbase Advanced Trading API:
112+
Extract the following details for processing an advanced trade using the Coinbase Advanced Trading API for
113+
{{message}}
113114
- **productId** (string): The trading pair ID (e.g., "BTC-USD", "ETH-USD", "SOL-USD")
114115
- **side** (string): The side of the trade (must be either "BUY" or "SELL")
115116
- **amount** (number): The amount to trade
@@ -134,9 +135,6 @@ Provide the details in the following JSON format:
134135
"limitPrice": <limit_price>
135136
}
136137
\`\`\`
137-
138-
Here are the recent user messages for context:
139-
{{recentMessages}}
140138
`;
141139

142140

0 commit comments

Comments
 (0)