Skip to content

Commit

Permalink
Ride trend from other data source than a swap pool (#51)
Browse files Browse the repository at this point in the history
* Ride trend from other data source than a swap pool

* Fix price difference limit

* Update parameters
  • Loading branch information
jonas-lj authored Jun 27, 2023
1 parent 5169d60 commit 066b101
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 5 deletions.
22 changes: 18 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TurbosPool } from "./dexs/turbos/turbos";
import { Arbitrage } from "./strategies/arbitrage";
import { MarketDifference } from "./strategies/market_difference";
import { RideTheTrend } from "./strategies/ride_the_trend";
import { RideTheExternalTrend } from "./strategies/ride_the_external_trend";

// Convenience map from name to address for commonly used coins
export const coins = {
Expand Down Expand Up @@ -37,7 +38,7 @@ defaultAmount[coins.WBTC] = 3_000;
// A conservative upper limit on the max gas price per transaction block in SUI
export const MAX_GAS_PRICE_PER_TRANSACTION = 4_400_000;

const RIDE_THE_THREAD_LIMIT = 1.000005;
const RIDE_THE_TREND_LIMIT = 1.000005;
const ARBITRAGE_RELATIVE_LIMIT = 1.0001;
const MARKET_DIFFERENCE_LIMIT = 1.01;

Expand Down Expand Up @@ -90,7 +91,7 @@ capybot.addStrategy(
defaultAmount[cetusUSDCtoSUI.coinTypeA],
defaultAmount[cetusUSDCtoSUI.coinTypeB],
],
RIDE_THE_THREAD_LIMIT,
RIDE_THE_TREND_LIMIT,
"RideTheTrend (USDC/SUI)"
)
);
Expand All @@ -103,7 +104,7 @@ capybot.addStrategy(
defaultAmount[cetusCETUStoSUI.coinTypeA],
defaultAmount[cetusCETUStoSUI.coinTypeB],
],
RIDE_THE_THREAD_LIMIT,
RIDE_THE_TREND_LIMIT,
"RideTheTrend (CETUS/SUI)"
)
);
Expand All @@ -116,7 +117,7 @@ capybot.addStrategy(
defaultAmount[cetusUSDCtoCETUS.coinTypeA],
defaultAmount[cetusUSDCtoCETUS.coinTypeB],
],
RIDE_THE_THREAD_LIMIT,
RIDE_THE_TREND_LIMIT,
"RideTheTrend (USDC/CETUS)"
)
);
Expand Down Expand Up @@ -172,5 +173,18 @@ capybot.addStrategy(
)
);

capybot.addStrategy(
new RideTheExternalTrend(
cetusWBTCtoUSDC.uri,
"BinanceBTCtoUSDC",
5,
10,
[defaultAmount[coins.WBTC], defaultAmount[coins.USDC]],
RIDE_THE_TREND_LIMIT,
1.0001,
"Ride external trend: (W)BTC/USDC, Binance vs CETUS"
)
);

// Start the bot
capybot.loop(3.6e6, 1000);
151 changes: 151 additions & 0 deletions src/strategies/ride_the_external_trend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { DataPoint, DataType } from "../data_sources/data_point";
import { average } from "simple-statistics";
import { Strategy } from "./strategy";
import { TradeOrder } from "./order";

/**
* If the price of a token from some data source, say an exchange, looks like it's going into a period where it's price is increasing we
* should buy the token on a trading pool. The trend is determined by comparing a short moving average to a longer moving average.
*/
export class RideTheExternalTrend extends Strategy {
private readonly short: number;
private readonly long: number;
private lastDecision: number = 0;
private readonly pool: string;
private latestPoolPrice: number = 0;

private history: Array<number> = [];
private readonly limit: number;
private readonly defaultAmounts: [number, number];
private readonly exchange: string;
private readonly priceRatio: number;

/**
*
* @param pool The swap pool to make trades on.
* @param data_source The source of pricing data.
* @param short The length of the short moving average.
* @param long The length of the long moving average.
* @param defaultAmounts The number of tokens to swap of coin type A and B resp. when the trend changes.
* @param limit Minimum ratio in the averages to do a trade.
* @param priceRatio: Minimum ratio between pool price and exchange price.
* @param name A human readable name for this strategy.
*/
constructor(
pool: string,
data_source: string,
short: number,
long: number,
defaultAmounts: [number, number],
limit: number,
priceRatio: number,
name: string
) {
super({
name: name,
pool: pool,
exchange: data_source,
short: short,
long: long,
});
this.short = short;
this.long = long;
this.pool = pool;
this.exchange = data_source;
this.defaultAmounts = defaultAmounts;
this.limit = limit;
this.priceRatio = priceRatio;
}

evaluate(data: DataPoint): Array<TradeOrder> {
console.log(data);

// Unknown data type
if (data.type != DataType.Price) {
return [];
}

// This strategy is only interested in the price from the pool it's observing and the external data source.
if (data.source_uri == this.pool) {
this.latestPoolPrice = data.price;
return [];
}

// Keep track of last time this strategy called for a trade. If it was very recent, our trade might have influenced the price.
this.lastDecision++;

// Get the current price from the data point
let price = data.price;

// Add the new data point to the history
this.history.push(price);
if (this.history.length < this.long) {
return [];
}

// Only keep the history we need
if (this.history.length > this.long) {
this.history.shift();
}

// We're certain that the history has length this.long at this point
// TODO: We can do this by streaming instead of recomputing the average every time
let short_average = average(
this.history.slice(this.history.length - this.short, this.history.length)
);
let long_average = average(this.history);

this.logStatus({
pool: this.latestPoolPrice,
exchange: price,
short_average: short_average,
long_average: long_average,
});

// The last trade could have influenced the price, so we wait until this effect has passed
if (this.lastDecision > this.long + 1) {
if (short_average / long_average < 1 / this.limit) {
// The value of A is going down. Sell A and hope it goes even lower.

if (price > this.latestPoolPrice * this.priceRatio) {
// The pool price is not as good as the exchange price, so we do not trade anything now
return [];
}

this.lastDecision = 0;
return [
{
pool: this.pool,
amountIn: this.defaultAmounts[0],
estimatedPrice: this.latestPoolPrice,
a2b: true,
},
];
} else if (short_average / long_average > this.limit) {
// The value of A is going up. Buy and hope it continues.
this.lastDecision = 0;

if (price < this.latestPoolPrice / this.priceRatio) {
// The pool price is not as good as the exchange price, so we do not trade anything now
return [];
}

return [
{
pool: this.pool,
amountIn: this.defaultAmounts[1],
estimatedPrice: 1 / this.latestPoolPrice,
a2b: false,
},
];
}
}

// No decision can be made at this point
return [];
}

subscribes_to(): Array<string> {
return [this.pool, this.exchange];
}
}
2 changes: 1 addition & 1 deletion src/types/token_price.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fee, Percentage } from "./Percentage";
import { Fee, Percentage } from "./percentage";
import { CoinType, TokenAmount } from "./token_amount";

/**
Expand Down

0 comments on commit 066b101

Please sign in to comment.