2023-03-11 14:50:22 -08:00
|
|
|
use std::cmp::{max, min};
|
|
|
|
|
2023-03-12 00:13:57 -08:00
|
|
|
use chrono::{DateTime, Duration, DurationRound, Utc};
|
2023-03-27 10:11:58 -07:00
|
|
|
use sqlx::{pool::PoolConnection, types::Decimal, Postgres};
|
2023-03-11 14:50:22 -08:00
|
|
|
|
2023-03-13 09:51:30 -07:00
|
|
|
use crate::{
|
|
|
|
database::fetch::{fetch_earliest_fill, fetch_fills_from, fetch_latest_finished_candle},
|
|
|
|
structs::{
|
|
|
|
candle::Candle,
|
|
|
|
markets::MarketInfo,
|
|
|
|
openbook::{calculate_fill_price_and_size, PgOpenBookFill},
|
|
|
|
resolution::{day, Resolution},
|
|
|
|
},
|
|
|
|
};
|
2023-03-11 14:50:22 -08:00
|
|
|
|
2023-03-12 00:13:57 -08:00
|
|
|
pub async fn batch_1m_candles(
|
2023-03-27 10:11:58 -07:00
|
|
|
conn: &mut PoolConnection<Postgres>,
|
2023-03-13 22:31:00 -07:00
|
|
|
market: &MarketInfo,
|
2023-03-11 14:50:22 -08:00
|
|
|
) -> anyhow::Result<Vec<Candle>> {
|
2023-03-12 18:03:39 -07:00
|
|
|
let market_name = &market.name;
|
|
|
|
let market_address = &market.address;
|
2023-03-27 10:11:58 -07:00
|
|
|
let latest_candle = fetch_latest_finished_candle(conn, market_name, Resolution::R1m).await?;
|
2023-03-11 14:50:22 -08:00
|
|
|
|
|
|
|
match latest_candle {
|
|
|
|
Some(candle) => {
|
|
|
|
let start_time = candle.end_time;
|
|
|
|
let end_time = min(
|
2023-03-12 16:32:12 -07:00
|
|
|
start_time + day(),
|
2023-03-11 14:50:22 -08:00
|
|
|
Utc::now().duration_trunc(Duration::minutes(1))?,
|
|
|
|
);
|
2023-03-27 10:11:58 -07:00
|
|
|
let mut fills = fetch_fills_from(conn, market_address, start_time, end_time).await?;
|
2023-03-11 14:50:22 -08:00
|
|
|
let candles = combine_fills_into_1m_candles(
|
|
|
|
&mut fills,
|
|
|
|
market,
|
|
|
|
start_time,
|
|
|
|
end_time,
|
|
|
|
Some(candle.close),
|
|
|
|
);
|
|
|
|
Ok(candles)
|
|
|
|
}
|
|
|
|
None => {
|
2023-03-27 10:11:58 -07:00
|
|
|
let earliest_fill = fetch_earliest_fill(conn, market_address).await?;
|
2023-03-11 14:50:22 -08:00
|
|
|
|
|
|
|
if earliest_fill.is_none() {
|
2023-03-12 18:03:39 -07:00
|
|
|
println!("No fills found for: {:?}", market_name);
|
2023-03-11 14:50:22 -08:00
|
|
|
return Ok(Vec::new());
|
|
|
|
}
|
|
|
|
|
|
|
|
let start_time = earliest_fill
|
|
|
|
.unwrap()
|
|
|
|
.time
|
|
|
|
.duration_trunc(Duration::minutes(1))?;
|
|
|
|
let end_time = min(
|
2023-03-12 16:32:12 -07:00
|
|
|
start_time + day(),
|
2023-03-11 14:50:22 -08:00
|
|
|
Utc::now().duration_trunc(Duration::minutes(1))?,
|
|
|
|
);
|
2023-03-27 10:11:58 -07:00
|
|
|
let mut fills = fetch_fills_from(conn, market_address, start_time, end_time).await?;
|
2023-03-27 12:01:48 -07:00
|
|
|
if fills.len() > 0 {
|
|
|
|
let candles =
|
|
|
|
combine_fills_into_1m_candles(&mut fills, market, start_time, end_time, None);
|
|
|
|
Ok(candles)
|
|
|
|
} else {
|
|
|
|
Ok(Vec::new())
|
|
|
|
}
|
2023-03-11 14:50:22 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn combine_fills_into_1m_candles(
|
|
|
|
fills: &mut Vec<PgOpenBookFill>,
|
2023-03-13 22:31:00 -07:00
|
|
|
market: &MarketInfo,
|
2023-03-11 14:50:22 -08:00
|
|
|
st: DateTime<Utc>,
|
|
|
|
et: DateTime<Utc>,
|
|
|
|
maybe_last_price: Option<Decimal>,
|
|
|
|
) -> Vec<Candle> {
|
2023-03-13 22:31:00 -07:00
|
|
|
let empty_candle = Candle::create_empty_candle(market.name.clone(), Resolution::R1m);
|
2023-03-11 14:50:22 -08:00
|
|
|
|
|
|
|
let minutes = (et - st).num_minutes();
|
|
|
|
let mut candles = vec![empty_candle; minutes as usize];
|
|
|
|
|
|
|
|
let mut fills_iter = fills.iter_mut().peekable();
|
|
|
|
let mut start_time = st.clone();
|
|
|
|
let mut end_time = start_time + Duration::minutes(1);
|
|
|
|
|
2023-03-13 22:31:00 -07:00
|
|
|
let mut last_price = match maybe_last_price {
|
|
|
|
Some(p) => p,
|
2023-03-13 22:34:23 -07:00
|
|
|
None => {
|
2023-03-13 22:31:00 -07:00
|
|
|
let first = fills_iter.peek().clone().unwrap();
|
|
|
|
let (price, _) =
|
|
|
|
calculate_fill_price_and_size(**first, market.base_decimals, market.quote_decimals);
|
|
|
|
price
|
2023-03-13 22:34:23 -07:00
|
|
|
}
|
2023-03-13 22:31:00 -07:00
|
|
|
};
|
2023-03-11 14:50:22 -08:00
|
|
|
|
|
|
|
for i in 0..candles.len() {
|
|
|
|
candles[i].open = last_price;
|
|
|
|
candles[i].close = last_price;
|
|
|
|
candles[i].low = last_price;
|
|
|
|
candles[i].high = last_price;
|
|
|
|
|
|
|
|
while matches!(fills_iter.peek(), Some(f) if f.time < end_time) {
|
|
|
|
let fill = fills_iter.next().unwrap();
|
|
|
|
|
|
|
|
let (price, volume) =
|
|
|
|
calculate_fill_price_and_size(*fill, market.base_decimals, market.quote_decimals);
|
|
|
|
|
|
|
|
candles[i].close = price;
|
|
|
|
candles[i].low = min(price, candles[i].low);
|
|
|
|
candles[i].high = max(price, candles[i].high);
|
|
|
|
candles[i].volume += volume;
|
|
|
|
|
|
|
|
last_price = price;
|
|
|
|
}
|
|
|
|
|
|
|
|
candles[i].start_time = start_time;
|
|
|
|
candles[i].end_time = end_time;
|
|
|
|
candles[i].complete = matches!(fills_iter.peek(), Some(f) if f.time > end_time);
|
|
|
|
|
|
|
|
start_time = end_time;
|
|
|
|
end_time = end_time + Duration::minutes(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
candles
|
|
|
|
}
|