Skip to content

Instantly share code, notes, and snippets.

@NotoriousPyro
Last active April 2, 2024 03:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NotoriousPyro/17c23e92079322f2bbc55b82cba5f28e to your computer and use it in GitHub Desktop.
Save NotoriousPyro/17c23e92079322f2bbc55b82cba5f28e to your computer and use it in GitHub Desktop.
Tests for a class implementing Jupiter's QuoteResponse for profitable quotes. Here is the test, you must implement the class... good luck!
// By Pyro @ www.sexonsol.com
// Price class, basic types and interface for PriceManager provided for you:
import { QuoteResponse } from '@jup-ag/api';
import { BigNumber } from 'bignumber.js';
enum WellKnownTokenMint {
USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
USDCet = "A9mUU4qviSctJVPJdBJWkb28deg915LYJKrzQ19ji3FM",
USDT = "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
USDTet = "Dn4noZ5jgGfkntzcQSUZ8czkreiZ1ForXYoV2H8Dm7S1",
UXD = "7kbnvuGBxxj8AG9qp8Scn56muWGaRaFqxg1FsRp3PaFT",
Solana = "So11111111111111111111111111111111111111112",
Sex = "DARpE2GaVrazeh6mopWXbTT1hV3EbNNvHrJMMqJXUm6i",
Bozo = "BoZoQQRAmYkr5iJhqo7DChAs7DPDwEZ5cv1vkYC9yzJG",
Tholana = "EKCW975DWdt1roK1NVQDf4QGfaGTcQPU5tFD1DMcMe9Q",
Fart = "Ek1zzALu4d4iPu7bwUL1TU4aCwNokecihX6y6aBEo6b4",
mSOL = "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So",
stSOL = "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj",
bSOL = "bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1",
JitoSOL = "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn",
JSOL = "7Q2afV64in6N6SeZsAAB81TJzwDoD6zpqmHkzi9Dcavn",
Infinity = "5oVNBeEEQvYi1cX3ir8Dx5n1P7pdxydbGF2X4TxVusJm",
WBTC = "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh",
JLP = "27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4"
}
export default class PriceHistory {
private prices: BigNumber[];
private start: number;
private end: number;
private isFull: boolean;
constructor(private maxItems: number = 200) {
this.prices = new Array(maxItems);
this.start = 0;
this.end = 0;
this.isFull = false;
}
get length(): number {
return this.isFull ? this.maxItems : this.end;
}
get average(): BigNumber {
let sum = new BigNumber(0);
const count = this.length;
for (let i = 0; i < count; i++) {
sum = sum.plus(this.prices[i]);
}
return sum.div(count);
}
add(item: BigNumber) {
this.prices[this.end] = item;
this.end = (this.end + 1) % this.maxItems;
if (this.end === this.start) {
this.start = (this.start + 1) % this.maxItems;
this.isFull = true;
}
}
}
class Price {
constructor(args: Partial<Price>) {
Object.assign(this, args);
}
price: BigNumber;
priceHistory: PriceHistory;
quote: QuoteResponse;
get gain() {
return this.price.minus(this.priceHistory.average).dividedBy(this.price.plus(this.priceHistory.average).dividedBy(2)).multipliedBy(100);
}
}
type PriceData = Map<WellKnownTokenMint, Price>
interface PriceManager {
priceData: Map<WellKnownTokenMint, PriceData>;
sampleThreshold: BigNumber;
constructor(sampleThreshold: BigNumber): void;
addPrice(quote: QuoteResponse): Promise<void>;
isQuoteOutputGreaterThanMinimumRequired(quote: QuoteResponse, outputMintMinimumAmount: BigNumber): Promise<boolean>;
isProfitableVsPriceAverage (quote: QuoteResponse): Promise<boolean>;
}
// You can use the map inside addPrice, first function is given to you like so:
class PriceManager implements PriceManager {
priceData: Map<WellKnownTokenMint, PriceData>;
sampleThreshold: BigNumber;
constructor(
sampleThreshold: BigNumber = new BigNumber(10),
) {
this.priceData = new Map();
this.sampleThreshold = sampleThreshold;
}
// Just call this every time you ask for a quote and stuff it in there
public addPrice = async (
quote: QuoteResponse,
) => {
const price = new BigNumber(quote.otherAmountThreshold).dividedBy(new BigNumber(quote.inAmount)); // buy div sell
if (!this.priceData.has(quote.inputMint as WellKnownTokenMint)) {
this.priceData.set(quote.inputMint as WellKnownTokenMint, new Map());
}
if (!this.priceData.get(quote.inputMint as WellKnownTokenMint).has(quote.outputMint as WellKnownTokenMint)) {
const priceHistory = new PriceHistory();
priceHistory.add(price);
this.priceData.get(quote.inputMint as WellKnownTokenMint).set(
quote.outputMint as WellKnownTokenMint,
new Price({
price,
priceHistory,
quote,
})
);
return Promise.resolve();
}
const priceData = this.priceData
.get(quote.inputMint as WellKnownTokenMint)
.get(quote.outputMint as WellKnownTokenMint)
;
priceData.priceHistory.add(price);
priceData.price = price;
priceData.quote = quote;
this.priceData
.get(quote.inputMint as WellKnownTokenMint)
.set(quote.outputMint as WellKnownTokenMint, priceData)
;
return Promise.resolve();
}
}
// End impl code
// Begin test code
import { QuoteResponse } from "@jup-ag/api";
import { PriceManager } from "../src/lib/managers/price";
import BigNumber from "bignumber.js";
describe("PriceManager", () => {
it("should sell prices higher than average and not buy it", async () => {
const priceManager = new PriceManager(new BigNumber(2));
const quote: QuoteResponse = {
inputMint: "SEX",
outputMint: "WSOL",
inAmount: "30000",
otherAmountThreshold: "6000000",
outAmount: "6010000",
swapMode: "ExactIn",
slippageBps: 0,
priceImpactPct: "0",
routePlan: [],
};
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
// This number is basically the minimum you would accept for an order, it's best to compare this to otherAmountThreshold in your priceManager impl
// As this is the minimum you will receive before your slippage is hit. Checking the amount you expect to receive is higher than this is a good
// way to ensure profitability. For these tests, they're all set to otherAmountThreshold.
new BigNumber(quote.otherAmountThreshold),
)).toBe(false); // First quote, should not be profitable
// Increase out amount (profit) - result: true
quote.otherAmountThreshold = "6100000";
quote.outAmount = "6200000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(true); // Should be profitable vs average
// Increase out amount (profit) - result: true
quote.otherAmountThreshold = "6200000";
quote.outAmount = "6300000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(true); // Increased again, should be profitable again
// Decrease out amount (loss) - result: false
quote.otherAmountThreshold = "5950000";
quote.outAmount = "6000000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(false); // The output amount has gone below average, should not be profitable
// Increase out amount (profit) - result: true
quote.otherAmountThreshold = "6200000";
quote.outAmount = "6300000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(true); // Increased again, should be profitable again
});
it("should buy prices lower than average and not sell it", async () => {
const priceManager = new PriceManager(new BigNumber(2));
const quote: QuoteResponse = {
inputMint: "WSOL",
outputMint: "SEX",
inAmount: "6000000",
otherAmountThreshold: "30100",
outAmount: "30200",
swapMode: "ExactIn",
slippageBps: 0,
priceImpactPct: "0",
routePlan: [],
};
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(false); // First order, should not be profitable
// Decrease spend (profit) - result: true
quote.inAmount = "5950000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(true); // Should be profitable
// Increase spend (loss) - result: false
quote.inAmount = "6100000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(false); // The input amount has gone above average, should not be profitable
// Increase out threshold (profit) - result: true
quote.otherAmountThreshold = "34000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(true); // The output amount has gone above average, should be profitable
});
it("should buy the cheap side and sell the expensive side", async () => {
const priceManager = new PriceManager(new BigNumber(2));
const quote: QuoteResponse = {
inputMint: "SEX",
outputMint: "WSOL",
inAmount: "30000",
otherAmountThreshold: "6000000",
outAmount: "6100000",
swapMode: "ExactIn",
slippageBps: 0,
priceImpactPct: "0",
routePlan: [],
};
const quote2: QuoteResponse = {
inputMint: "WSOL",
outputMint: "SEX",
inAmount: "6000000",
otherAmountThreshold: "30000",
outAmount: "30100",
swapMode: "ExactIn",
slippageBps: 0,
priceImpactPct: "0",
routePlan: [],
};
// Quote 1: SELL
await priceManager.addPrice(quote);
// Increase out amount (profit) - result: true
quote.otherAmountThreshold = "6100000";
quote.outAmount = "6200000";
await priceManager.addPrice(quote);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote,
new BigNumber(quote.otherAmountThreshold),
)).toBe(true); // Should be profitable
// Quote 2: BUY
await priceManager.addPrice(quote2);
quote2.otherAmountThreshold = "30100";
quote2.outAmount = "30200";
// Decrease spend (profit) - result: true
await priceManager.addPrice(quote2);
expect(await priceManager.isQuoteOutputGreaterThanMinimumRequired(
quote2,
new BigNumber(quote2.otherAmountThreshold),
)).toBe(true); // Should be profitable
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment