Skip to content

Commit

Permalink
fix(vault): add chart pnl for trading vault (#1692)
Browse files Browse the repository at this point in the history
  • Loading branch information
NguyenHuy1812 authored Jun 20, 2024
1 parent 8502265 commit 95bd8d8
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 3 deletions.
16 changes: 16 additions & 0 deletions src/adapters/mochi-pay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,22 @@ class MochiPay extends Fetcher {
}
return data
}

async getInvestorPnls(
profileId: string,
vaultId: string,
query?: { trade_set_id: string },
): Promise<any> {
const { ok, data: res } = await this.jsonFetch(
`${MOCHI_PAY_API_BASE_URL}/profiles/${profileId}/syndicates/earning-vaults/${vaultId}/pnl`,
{ query },
)
let data = null
if (ok) {
data = res as any
}
return data
}
}

export default new MochiPay()
65 changes: 62 additions & 3 deletions src/commands/vault/info/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
MessageActionRow,
MessageButton,
MessageSelectMenu,
MessageAttachment,
} from "discord.js"
import { InternalError, OriginalMessage } from "errors"
import { APIError } from "errors"
Expand All @@ -30,6 +31,7 @@ import {
import mochiPay from "adapters/mochi-pay"
import moment from "moment"
import { utils } from "@consolelabs/mochi-formatter"
import { drawLineChart } from "utils/chart"

const getPnlIcon = (n: number) => (n >= 0 ? ":green_circle:" : ":red_circle:")

Expand Down Expand Up @@ -323,9 +325,7 @@ export async function runGetVaultDetail({
value: Number(report.vault_equity.floating_profit ?? 0),
shorten: false,
})}`,
`${getEmoji(
"ANIMATED_PARTY_POPPER",
)} \`Claimable amount. \` ${utils.formatUsdPriceDigit({
`:tada: \`Claimable amount. \` ${utils.formatUsdPriceDigit({
value: Number(report.vault_equity.claimable ?? 0),
shorten: false,
})}`,
Expand Down Expand Up @@ -358,6 +358,51 @@ export async function runGetVaultDetail({
)}\``,
].join("\n")

// add chart Pnl
const latestTrade = open_trades ?? close_trades?.[0]

if (!latestTrade) {
throw new InternalError({
msgOrInteraction: interaction,
title: "No Trading Data",
description: originalError,
})
}

const dataPnl = await mochiPay.getInvestorPnls(profileId, selectedVault, {
trade_set_id: latestTrade.id!,
})
if (!dataPnl?.length) {
throw new InternalError({
msgOrInteraction: interaction,
title: "No Pnl Data",
description: originalError,
})
}

// if current minute > 10, pnl snapshot might be outdated
const outdatable = new Date().getMinutes() > 10
const hasOpenTrade = latestTrade.status === 1
if (outdatable && hasOpenTrade) {
dataPnl.push({
time: new Date().toISOString(),
sum_pnl: Number(latestTrade.unrealized_pnl!),
account_id: "",
account_trade_set_id: undefined,
realized_pnl: 0,
unrealized_pnl: 0,
})
}

// draw chart pnl
const labels = dataPnl.map((r: { time: string | undefined }, i: any) =>
formatDateTime(r.time),
)
const buffer = await drawLineChart({
title: "PNL (USD)",
labels,
data: dataPnl.map((r: { sum_pnl: any }) => r.sum_pnl || 0),
})
const description = composeTradesDescription({
description: `${basicInfo}\n\n${vaultEquity}\n\n${address}\n\n${roundFields}\n\n${openTrades}`,
trades: close_trades,
Expand All @@ -367,6 +412,7 @@ export async function runGetVaultDetail({
color: msgColors.BLUE,
author: ["Trading vault info", getEmojiURL(emojis.ANIMATED_DIAMOND)],
description,
image: "attachment://chart_pnl.png",
})

return {
Expand All @@ -380,6 +426,7 @@ export async function runGetVaultDetail({
},
msgOpts: {
embeds: [embed],
files: [new MessageAttachment(buffer, "chart_pnl.png")],
components: [
new MessageActionRow().addComponents(
new MessageButton()
Expand Down Expand Up @@ -729,3 +776,15 @@ export function buildTreasurerFields(data: any): any {
}
return []
}

function formatDateTime(s: string | undefined, timeOnly?: boolean) {
if (!s) return "N/A"
const d = new Date(s)
return `${
timeOnly
? ""
: `${d.toLocaleDateString("en-US", { day: "numeric", month: "short" })}, `
}${d
.toLocaleTimeString("en-US", { hour12: true, hour: "numeric" })
.replace(" ", "")}`
}
80 changes: 80 additions & 0 deletions src/utils/chart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { createCanvas } from "canvas"
import { ChartJSNodeCanvas } from "chartjs-node-canvas"

function getGradientColor(fromColor: string, toColor: string) {
const canvas = createCanvas(100, 100)
const ctx = canvas.getContext("2d")
const gradient = ctx.createLinearGradient(0, 0, 0, 400)
gradient.addColorStop(0, fromColor)
gradient.addColorStop(1, toColor)
return gradient
}

export async function drawLineChart({
title,
labels,
data,
}: {
title?: string
labels: string[]
data: number[]
}) {
const borderColor = "#009cdb"
const backgroundColor = getGradientColor(
"rgba(53,83,192,0.9)",
"rgba(58,69,110,0.5)",
)
const chartCanvas = new ChartJSNodeCanvas({
width: 700,
height: 450,
backgroundColour: "#202020",
})
const axisConfig = {
ticks: {
font: { size: 16 },
color: borderColor,
},
grid: {
color: "rgba(0,0,0,0.2)",
borderWidth: 1,
borderColor: borderColor,
},
}
return chartCanvas.renderToBuffer({
type: "line",
data: {
labels,
datasets: [
{
label: title,
data,
borderWidth: 2,
pointRadius: 0,
fill: true,
borderColor,
backgroundColor,
tension: 0.5,
},
],
},
options: {
layout: {
padding: { left: 15, bottom: 15, top: 15, right: 15 },
},
scales: {
y: axisConfig,
x: axisConfig,
},
plugins: {
legend: {
labels: {
// This more specific font property overrides the global property
font: {
size: 18,
},
},
},
},
},
})
}

0 comments on commit 95bd8d8

Please sign in to comment.