Skip to content

Commit

Permalink
pindexer: implement symmetrization of charts
Browse files Browse the repository at this point in the history
Now charts implement price information from both directions of a pair.
  • Loading branch information
cronokirby committed Nov 22, 2024
1 parent 09ffaee commit 968dd3e
Showing 1 changed file with 42 additions and 7 deletions.
49 changes: 42 additions & 7 deletions crates/bin/pindexer/src/dex_ex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ mod candle {
use penumbra_dex::CandlestickData;
use std::fmt::Display;

fn geo_mean(a: f64, b: f64) -> f64 {
(a * b).sqrt()
}

/// Candlestick data, unmoored from the prison of a particular block height.
///
/// In other words, this can represent candlesticks which span arbitrary windows,
Expand Down Expand Up @@ -57,6 +61,31 @@ mod candle {
self.direct_volume += that.direct_volume;
self.swap_volume += that.swap_volume;
}

/// Mix this candle with a candle going in the opposite direction of the pair.
pub fn mix(&mut self, op: &Self) {
// We use the geometric mean, resulting in all the prices in a.mix(b) being
// the inverse of the prices in b.mix(a), and the volumes being equal.
self.close /= geo_mean(self.close, op.close);
self.open /= geo_mean(self.open, op.open);
self.low = self.low.min(1.0 / op.low);
self.high = self.high.min(1.0 / op.high);
// Using the closing price to look backwards at volume.
self.direct_volume += op.direct_volume / self.close;
self.swap_volume += op.swap_volume / self.close;
}

/// Flip this candle to get the equivalent in the other direction.
pub fn flip(&self) -> Self {
Self {
open: 1.0 / self.open,
close: 1.0 / self.close,
low: 1.0 / self.low,
high: 1.0 / self.high,
direct_volume: self.direct_volume / self.close,
swap_volume: self.swap_volume / self.close,
}
}
}

impl From<CandlestickData> for Candle {
Expand Down Expand Up @@ -616,14 +645,20 @@ impl Events {
}

fn with_candle(&mut self, pair: DirectedTradingPair, candle: Candle) {
match self.candles.get_mut(&pair) {
None => {
self.candles.insert(pair, candle);
}
Some(current) => {
current.merge(&candle);
// Popular both this pair and the flipped pair, and if the flipped pair
// is already populated, we need to mix the two candles together.
let flip = pair.flip();
let new_candle = match self.candles.get(&flip).cloned() {
None => candle,
Some(flipped) => {
let mut out = candle;
out.mix(&flipped);
out
}
}
};

self.candles.insert(pair, new_candle);
self.candles.insert(flip, new_candle.flip());
}

fn metric(&mut self, pair: &DirectedTradingPair) -> &mut PairMetrics {
Expand Down

0 comments on commit 968dd3e

Please sign in to comment.