Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: only sell what you have bought + add pnl for a specific trade #36

Merged
merged 23 commits into from
Feb 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8408319
only sell what you have bought
monilpat Feb 20, 2025
97f2542
add trade specific pnl
monilpat Feb 21, 2025
405493f
chore: add pre commit auto formatting (#37)
snobbee Feb 20, 2025
5faac7c
docs: Update TG on website (#39)
TimKozak Feb 20, 2025
85bf879
feat: Add provider for token address + staking instructions (#40)
monilpat Feb 21, 2025
3c67dff
docs: Update PROSPER token README.md (#43)
mihai169 Feb 21, 2025
dfcf3b9
docs: Link to Prosper website from main RS website (#42)
TimKozak Feb 21, 2025
e1315f0
docs: Add stakeprosper.com link in coverpage (#41)
TimKozak Feb 21, 2025
9496d6e
chore: fix biome errors (#45)
snobbee Feb 21, 2025
7e381dd
fix: Filters, Template Manager, Dynamic chaarcters list (#38)
VisionOra Feb 21, 2025
02b70ab
Update index.ts with bridging instructions (#46)
mihai169 Feb 22, 2025
56e5b27
only sell what you have bought
monilpat Feb 20, 2025
f235d61
add trade specific pnl
monilpat Feb 21, 2025
834672f
clean up
monilpat Feb 22, 2025
3e8fc90
add wiining streak
monilpat Feb 22, 2025
669f9b1
formatting
monilpat Feb 22, 2025
8723085
clean up
monilpat Feb 22, 2025
8349644
clean up
monilpat Feb 22, 2025
53b47a9
get sell pnl winning streak original amount bought working
monilpat Feb 23, 2025
21fc1a8
get sell pnl winning streak original amount bought working
monilpat Feb 23, 2025
e5661fc
get trade pnl working
monilpat Feb 23, 2025
3b3ab47
Merge remote-tracking branch 'origin/main' into adjustTradingSizeBase…
monilpat Feb 23, 2025
7494da8
fix lockfile
monilpat Feb 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 96 additions & 21 deletions clients/client-coinbase/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export class CoinbaseClient implements Client {
private server: express.Application;
private port: number;
private wallets: CoinbaseWallet[];
private initialBuyAmountInCurrency: number | null;
private winningStreak: number;

constructor(runtime: IAgentRuntime) {
this.runtime = runtime;
Expand All @@ -65,6 +67,8 @@ export class CoinbaseClient implements Client {
this.server = express();
this.port = Number(runtime.getSetting("COINBASE_WEBHOOK_PORT")) || 3001;
this.wallets = [];
this.initialBuyAmountInCurrency = null;
this.winningStreak = 0;
}

async initialize(): Promise<void> {
Expand Down Expand Up @@ -101,7 +105,7 @@ export class CoinbaseClient implements Client {
next: express.NextFunction,
) => {
const event = req.body as WebhookEvent;
elizaLogger.info("event ", JSON.stringify(event));
elizaLogger.info(`event ${JSON.stringify(event)}`);
if (!event.event || !event.ticker || !event.timestamp || !event.price) {
res.status(400).json({ error: "Invalid webhook payload" });
return;
Expand Down Expand Up @@ -182,13 +186,13 @@ export class CoinbaseClient implements Client {
];
const networkId = Coinbase.networks.BaseMainnet;
for (const walletType of walletTypes) {
elizaLogger.info("walletType ", walletType);
elizaLogger.info(`walletType ${walletType}`);
const wallet = await initializeWallet(
this.runtime,
networkId,
walletType,
);
elizaLogger.info("Successfully loaded wallet ", wallet.wallet.getId());
elizaLogger.info(`Successfully loaded wallet ${wallet.wallet.getId()}`);
this.wallets.push(wallet);
}
}
Expand All @@ -197,6 +201,7 @@ export class CoinbaseClient implements Client {
event: WebhookEvent,
amountInCurrency: number,
pnl: string,
sellTradePNL: string,
formattedTimestamp: string,
state: State,
hash: string | null,
Expand Down Expand Up @@ -247,9 +252,9 @@ Generate only the tweet text, no commentary or markdown.`;
context,
modelClass: ModelClass.LARGE,
});

const isSellTrade = event.event.toUpperCase() === "SELL";
const trimmedContent = tweetContent.trim();
const finalContent = `${trimmedContent} PNL: ${pnl} ${blockExplorerBaseTxUrl(hash)}`;
const finalContent = `${trimmedContent} ${isSellTrade ? `Trade PNL: ${sellTradePNL}` : ""} Overall PNL: ${pnl} Winning Streak: ${this.winningStreak} ${blockExplorerBaseTxUrl(hash)}`;
return finalContent.length > 280
? `${finalContent.substring(0, 277)}...`
: finalContent;
Expand Down Expand Up @@ -288,34 +293,63 @@ Generate only the tweet text, no commentary or markdown.`;

// Execute token swap
const buy = event.event.toUpperCase() === "BUY";
const price = event.price;
// if sell, use the initial buy amount in currency instead of the current price
let amountInCurrency: number;
const tokenMetadata = getTokenMetadata(event.ticker);
const usdcMetadata = getTokenMetadata("USDC");
const tokenDecimals = tokenMetadata?.decimals || 18; // Default to 18 if not found
const usdcDecimals = usdcMetadata?.decimals || 6; // Default to 6 if not found
const amountSellInCurrencyInBaseUnits = Math.floor(
(amount / Number(event.price)) * 10 ** tokenDecimals,
);

const amountInCurrency = Math.floor(
amountInCurrency = Math.floor(
buy
? amount * 10 ** usdcDecimals // Convert USD amount to USDC base units
: (amount / Number(event.price)) * 10 ** tokenDecimals, // Convert to token base units
: amountSellInCurrencyInBaseUnits, // Convert to token base units
);
if (buy) {
this.initialBuyAmountInCurrency = amountSellInCurrencyInBaseUnits;
}
if (!buy && this.initialBuyAmountInCurrency !== null) {
amountInCurrency = this.initialBuyAmountInCurrency;
}
elizaLogger.info(`buy ${buy}`);
elizaLogger.info(
"amountInCurrency non base units ",
amount / Number(event.price),
`this.initialBuyAmountInCurrency ${this.initialBuyAmountInCurrency}`,
);

const pnl = await calculateOverallPNL(
this.runtime,
this.runtime.getSetting("WALLET_PUBLIC_KEY") as `0x${string}`,
1000,
);
elizaLogger.info("pnl ", pnl);
elizaLogger.info("amountInCurrency ", amountInCurrency);
const sellTradePNL = await calculateSellTradePNL(
this.runtime,
this.initialBuyAmountInCurrency !== null
? this.initialBuyAmountInCurrency
: amountSellInCurrencyInBaseUnits,
amountSellInCurrencyInBaseUnits,
Number(price),
tokenDecimals,
);
elizaLogger.info(`pnl ${pnl}`);

// Check if the PNL is positive or negative
if (Number.parseFloat(sellTradePNL) > 0) {
this.winningStreak++;
} else {
this.winningStreak = 0;
}
elizaLogger.info(`winningStreak ${this.winningStreak}`);
const enoughBalance = await hasEnoughBalance(
this.runtime,
this.runtime.getSetting("WALLET_PUBLIC_KEY") as `0x${string}`,
buy ? "USDC" : event.ticker,
amountInCurrency,
);
elizaLogger.info("enoughBalance ", enoughBalance);
elizaLogger.info(`enoughBalance ${enoughBalance}`);
if (!enoughBalance) {
elizaLogger.error("Not enough balance to trade");
return;
Expand All @@ -325,13 +359,20 @@ Generate only the tweet text, no commentary or markdown.`;
elizaLogger.error("txHash is null");
return;
}
elizaLogger.info("txHash ", txHash);

elizaLogger.info(`txHash ${txHash}`);
let amountInUSD;
if (buy) {
amountInUSD = amount;
} else {
amountInUSD = (amountInCurrency / 10 ** tokenDecimals) * price;
}
elizaLogger.info(`amountInUSD ${amountInUSD}`);
// Generate and post tweet
await this.handleMediaPosting(
event,
amount,
amountInUSD,
pnl,
sellTradePNL,
formattedTimestamp,
state,
txHash,
Expand Down Expand Up @@ -399,8 +440,9 @@ Generate only the tweet text, no commentary or markdown.`;

private async handleMediaPosting(
event: WebhookEvent,
amount: number,
amountInUSD: number,
pnl: string,
sellTradePNL: string,
formattedTimestamp: string,
state: State,
txHash: string,
Expand All @@ -409,20 +451,21 @@ Generate only the tweet text, no commentary or markdown.`;
try {
mediaContent = await this.generateMediaContent(
event,
amount,
amountInUSD,
pnl,
sellTradePNL,
formattedTimestamp,
state,
txHash,
);
elizaLogger.info("Generated media content:", mediaContent);
elizaLogger.info(`Generated media content: ${mediaContent}`);

if (this.runtime.getSetting("TWITTER_DRY_RUN").toLowerCase() === "true") {
elizaLogger.info("Dry run mode enabled. Skipping tweet posting.");
} else {
// post tweet to twitter
const response = await postTweet(this.runtime, mediaContent);
elizaLogger.info("Tweet response:", response);
elizaLogger.info(`Tweet response: ${response}`);
}
} catch (error) {
elizaLogger.error("Failed to post tweet:", error);
Expand All @@ -439,7 +482,6 @@ Generate only the tweet text, no commentary or markdown.`;
} else {
// post message to telegram
if (mediaContent.length > 0) {
// TODO: remove hardcoded channel id
await this.runtime.clients.telegram.messageManager.bot.telegram.sendMessage(
this.runtime.getSetting("TELEGRAM_CHANNEL_ID"),
mediaContent,
Expand Down Expand Up @@ -523,6 +565,39 @@ export const calculateOverallPNL = async (
return formattedPNLUSD;
};

export const calculateSellTradePNL = async (
runtime: IAgentRuntime,
initialBuyAmountInCurrency: number,
amountSellInCurrencyInBaseUnits: number,
price: number,
tokenDecimals: number,
): Promise<string> => {
elizaLogger.info("calculateSellTradePNL");
elizaLogger.info(`initialBuyAmountInCurrency ${initialBuyAmountInCurrency}`);
elizaLogger.info(
`amountSellInCurrencyInBaseUnits ${amountSellInCurrencyInBaseUnits}`,
);
// its in base units
const pnlCurrencyInBaseUnits =
initialBuyAmountInCurrency - amountSellInCurrencyInBaseUnits;
elizaLogger.info(`pnlCurrencyInBaseUnits ${pnlCurrencyInBaseUnits}`);
const pnlUSD = (pnlCurrencyInBaseUnits / 10 ** tokenDecimals) * price;
elizaLogger.info(`pnlUSD ${pnlUSD}`);
elizaLogger.info(`Sell Trade pnlUSD ${pnlUSD}`);
const absoluteValuePNL = Math.abs(pnlUSD);
elizaLogger.info(`Sell Trade absoluteValuePNL ${absoluteValuePNL}`);
const formattedPNL = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(absoluteValuePNL);
elizaLogger.info(`Sell Trade formattedPNL ${formattedPNL}`);
const formattedPNLUSD = `${pnlUSD <= -0.005 ? "-" : ""}${formattedPNL}`;
elizaLogger.info(`Sell Trade formattedPNLUSD ${formattedPNLUSD}`);
return formattedPNLUSD;
};

export async function getTotalBalanceUSD(
runtime: IAgentRuntime,
publicKey: `0x${string}`,
Expand Down Expand Up @@ -683,7 +758,7 @@ export const pnlProvider: Provider = {
runtime.getSetting("WALLET_PUBLIC_KEY") as `0x${string}`,
1000,
);
elizaLogger.info("pnl ", pnl);
elizaLogger.info(`pnl ${pnl}`);
return `PNL: ${pnl}`;
} catch (error) {
elizaLogger.error("Error in pnlProvider: ", error.message);
Expand Down
Loading
Loading