Skip to content

Commit

Permalink
feat: async arbitrageur trait, improvements to DefaultArbitrageur
Browse files Browse the repository at this point in the history
  • Loading branch information
ts0yu authored Aug 21, 2024
1 parent 381a649 commit 5f30573
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 6 deletions.
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ alloy = { version = "0.2.1", features = ["full", "node-bindings", "json"] }
alloy-contract = "0.2.1"
alloy-sol-macro = "0.7.7"
alloy-sol-types = "0.7.7"
async-trait = "0.1.81"
plotly = "0.9.0"
rand = "0.8.5"
rand_distr = "0.4.3"
rug = "1.25.0"
tokio = { version = "1.39.2", features = ["macros"] }
9 changes: 7 additions & 2 deletions src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ impl<V> Arena<V> {
self.pool.clone(),
U256::from(79228162514264337593543950336_u128),
Bytes::default(),
).nonce(5)
)
.nonce(5)
.send()
.await
.unwrap()
Expand All @@ -105,6 +106,7 @@ impl<V> Arena<V> {
self.providers[&(idx + 1)].clone(),
Signal::new(
*pool_manager.address(),
*fetcher.address(),
self.pool.clone(),
self.feed.current_value(),
None,
Expand All @@ -128,6 +130,7 @@ impl<V> Arena<V> {

let signal = Signal::new(
*pool_manager.address(),
*fetcher.address(),
self.pool.clone(),
self.feed.current_value(),
Some(step),
Expand All @@ -149,7 +152,9 @@ impl<V> Arena<V> {

self.nonce += 1;

self.arbitrageur.arbitrage(&signal, admin_provider.clone());
self.arbitrageur
.arbitrage(&signal, admin_provider.clone())
.await;

for (idx, strategy) in self.strategies.iter_mut().enumerate() {
strategy.process(
Expand Down
78 changes: 75 additions & 3 deletions src/engine/arbitrageur.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,86 @@
use crate::{AnvilProvider, Signal};
use async_trait::async_trait;
use rug::{ops::Pow, Float};

use crate::{types::Fetcher, AnvilProvider, Signal};

/// Generic trait allowing user defined arbitrage strategies.
#[async_trait]
pub trait Arbitrageur {
/// Perform an arbitrage based on a [`Signal`].
fn arbitrage(&self, signal: &Signal, provider: AnvilProvider);
async fn arbitrage(&self, signal: &Signal, provider: AnvilProvider);
}

/// Default implementation of an [`Arbitrageur`] that uses the closed-form optimal swap amount to determine the optimal arbitrage.
pub struct DefaultArbitrageur;

#[async_trait]
impl Arbitrageur for DefaultArbitrageur {
async fn arbitrage(&self, signal: &Signal, provider: AnvilProvider) {
let base = Float::with_val(53, 1.0001);
let price = Float::with_val(53, signal.current_value);

let target_tick = price.log10() / base.log10();
let current_tick = Float::with_val(53, signal.tick);

let (start, end) = if current_tick < target_tick {
(current_tick, target_tick)
} else {
(target_tick, current_tick)
};

let (a, b) = self
.get_tick_range_liquidity(
signal,
provider,
start.to_i32_saturating().unwrap(),
end.to_i32_saturating().unwrap(),
)
.await;
}
}

impl DefaultArbitrageur {
async fn get_tick_range_liquidity(
&self,
signal: &Signal,
provider: AnvilProvider,
start: i32,
end: i32,
) -> (Float, Float) {
let fetcher = Fetcher::new(signal.fetcher, provider);

let mut liquidity_a = Float::with_val(53, 0);
let mut liquidity_b = Float::with_val(53, 0);

for tick in start..end {
let pool_id = fetcher
.toId(signal.pool.clone().into())
.call()
.await
.unwrap()
.poolId;

let tick_info = fetcher
.getTickInfo(signal.manager, pool_id, tick)
.call()
.await
.unwrap();
let sqrt_price = Float::with_val(53, Float::with_val(53, 1.0001).pow(tick / 2));

let tick_liquidity = Float::with_val(53, tick_info.liquidityNet);

liquidity_a += tick_liquidity.clone() / sqrt_price.clone();
liquidity_b += tick_liquidity * sqrt_price;
}

(liquidity_a, liquidity_b)
}
}

/// No-op implementation of an [`Arbitrageur`] for custom usecases.
pub struct EmptyArbitrageur;

#[async_trait]
impl Arbitrageur for EmptyArbitrageur {
fn arbitrage(&self, _signal: &Signal, _provider: AnvilProvider) {}
async fn arbitrage(&self, _signal: &Signal, _provider: AnvilProvider) {}
}
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ pub struct Signal {
/// Address of the pool manager.
pub manager: Address,

/// Address of the fetcher.
pub fetcher: Address,

/// Key of the pool.
pub pool: PoolKey,

Expand All @@ -126,6 +129,7 @@ impl Signal {
/// Public constructor function for a new [`Signal`].
pub fn new(
manager: Address,
fetcher: Address,
pool: PoolKey,
current_value: f64,
step: Option<usize>,
Expand All @@ -134,6 +138,7 @@ impl Signal {
) -> Self {
Self {
manager,
fetcher,
pool,
current_value,
step,
Expand All @@ -150,7 +155,7 @@ mod tests {
arena::{Arena, ArenaBuilder},
config::Config,
engine::{
arbitrageur::{Arbitrageur, EmptyArbitrageur},
arbitrageur::{Arbitrageur, DefaultArbitrageur, EmptyArbitrageur},
inspector::EmptyInspector,
},
feed::OrnsteinUhlenbeck,
Expand Down

0 comments on commit 5f30573

Please sign in to comment.