From 3ce7e2f6ae4a75d818c8874a031ea514b59826fa Mon Sep 17 00:00:00 2001 From: Lucas Soriano del Pino Date: Wed, 29 May 2024 17:17:27 +1000 Subject: [PATCH] feat: Display next funding rate in app --- coordinator/src/db/funding_rates.rs | 27 ++++++--- coordinator/src/funding_fee.rs | 56 ++----------------- coordinator/src/lib.rs | 28 ---------- coordinator/src/orderbook/websocket.rs | 27 +++++++++ coordinator/src/routes/admin.rs | 7 +-- crates/tests-e2e/src/test_subscriber.rs | 3 + .../xxi-node/src/commons/funding_fee_event.rs | 46 +++++++++++++++ crates/xxi-node/src/commons/message.rs | 3 + crates/xxi-node/src/commons/mod.rs | 28 +++++++++- mobile/lib/backend.dart | 3 + mobile/lib/common/init_service.dart | 5 ++ .../trade/funding_rate_change_notifier.dart | 25 +++++++++ mobile/lib/features/trade/trade_screen.dart | 39 ++++++++++++- mobile/lib/util/constants.dart | 2 + mobile/native/src/event/api.rs | 5 ++ mobile/native/src/event/mod.rs | 5 ++ mobile/native/src/orderbook.rs | 4 ++ 17 files changed, 218 insertions(+), 95 deletions(-) create mode 100644 mobile/lib/features/trade/funding_rate_change_notifier.dart diff --git a/coordinator/src/db/funding_rates.rs b/coordinator/src/db/funding_rates.rs index 7e04d91f8..108016d8e 100644 --- a/coordinator/src/db/funding_rates.rs +++ b/coordinator/src/db/funding_rates.rs @@ -1,6 +1,4 @@ -use crate::funding_fee; use crate::schema::funding_rates; -use crate::to_nearest_hour_in_the_past; use anyhow::Context; use anyhow::Result; use diesel::prelude::*; @@ -8,6 +6,7 @@ use rust_decimal::prelude::FromPrimitive; use rust_decimal::prelude::ToPrimitive; use rust_decimal::Decimal; use time::OffsetDateTime; +use xxi_node::commons::to_nearest_hour_in_the_past; #[derive(Queryable, Debug)] struct FundingRate { @@ -22,7 +21,7 @@ struct FundingRate { pub(crate) fn insert( conn: &mut PgConnection, - funding_rates: &[funding_fee::FundingRate], + funding_rates: &[xxi_node::commons::FundingRate], ) -> Result<()> { conn.transaction(|conn| { for funding_rate in funding_rates { @@ -34,7 +33,7 @@ pub(crate) fn insert( }) } -fn insert_one(conn: &mut PgConnection, params: &funding_fee::FundingRate) -> QueryResult<()> { +fn insert_one(conn: &mut PgConnection, params: &xxi_node::commons::FundingRate) -> QueryResult<()> { let affected_rows = diesel::insert_into(funding_rates::table) .values(&( funding_rates::start_date.eq(params.start_date()), @@ -50,10 +49,24 @@ fn insert_one(conn: &mut PgConnection, params: &funding_fee::FundingRate) -> Que Ok(()) } +// TODO: Check that `end_date` isn't already in the past. +pub(crate) fn get_next_funding_rate( + conn: &mut PgConnection, +) -> QueryResult> { + let funding_rate: Option = funding_rates::table + .order(funding_rates::end_date.desc()) + .first::(conn) + .optional()?; + + let funding_rate = funding_rate.map(xxi_node::commons::FundingRate::from); + + Ok(funding_rate) +} + /// Get the funding rate with an end date that is equal to the current date to the nearest hour. pub(crate) fn get_funding_rate_charged_in_the_last_hour( conn: &mut PgConnection, -) -> QueryResult> { +) -> QueryResult> { let now = OffsetDateTime::now_utc(); let now = to_nearest_hour_in_the_past(now); @@ -62,10 +75,10 @@ pub(crate) fn get_funding_rate_charged_in_the_last_hour( .first::(conn) .optional()?; - Ok(funding_rate.map(funding_fee::FundingRate::from)) + Ok(funding_rate.map(xxi_node::commons::FundingRate::from)) } -impl From for funding_fee::FundingRate { +impl From for xxi_node::commons::FundingRate { fn from(value: FundingRate) -> Self { Self::new( Decimal::from_f32(value.rate).expect("to fit"), diff --git a/coordinator/src/funding_fee.rs b/coordinator/src/funding_fee.rs index 7b0dc7479..4026a5032 100644 --- a/coordinator/src/funding_fee.rs +++ b/coordinator/src/funding_fee.rs @@ -1,7 +1,6 @@ use crate::db; use crate::decimal_from_f32; use crate::message::OrderbookMessage; -use crate::to_nearest_hour_in_the_past; use anyhow::bail; use anyhow::Context; use anyhow::Result; @@ -26,51 +25,6 @@ use xxi_node::commons::Message; const RETRY_INTERVAL: Duration = Duration::from_secs(5); -/// The funding rate for any position opened before the `end_date`, which remained open through the -/// `end_date`. -#[derive(Clone, Debug)] -pub struct FundingRate { - /// A positive funding rate indicates that longs pay shorts; a negative funding rate indicates - /// that shorts pay longs. - rate: Decimal, - /// The start date for the funding rate period. This value is only used for informational - /// purposes. - /// - /// The `start_date` is always a whole hour. - start_date: OffsetDateTime, - /// The end date for the funding rate period. When the end date has passed, all active - /// positions that were created before the end date should be charged a funding fee based - /// on the `rate`. - /// - /// The `end_date` is always a whole hour. - end_date: OffsetDateTime, -} - -impl FundingRate { - pub(crate) fn new(rate: Decimal, start_date: OffsetDateTime, end_date: OffsetDateTime) -> Self { - let start_date = to_nearest_hour_in_the_past(start_date); - let end_date = to_nearest_hour_in_the_past(end_date); - - Self { - rate, - start_date, - end_date, - } - } - - pub fn rate(&self) -> Decimal { - self.rate - } - - pub fn start_date(&self) -> OffsetDateTime { - self.start_date - } - - pub fn end_date(&self) -> OffsetDateTime { - self.end_date - } -} - /// A record that a funding fee is owed between the coordinator and a trader. #[derive(Clone, Copy, Debug)] pub struct FundingFeeEvent { @@ -181,7 +135,7 @@ fn generate_funding_fee_events( let index_price = match index_price_source { IndexPriceSource::Bitmex => block_in_place(move || { let current_index_price = - get_bitmex_index_price(&contract_symbol, funding_rate.end_date)?; + get_bitmex_index_price(&contract_symbol, funding_rate.end_date())?; anyhow::Ok(current_index_price) })?, @@ -200,12 +154,12 @@ fn generate_funding_fee_events( // We exclude active positions which were open after this funding period ended. let positions = db::positions::Position::get_all_active_positions_open_before( &mut conn, - funding_rate.end_date, + funding_rate.end_date(), )?; for position in positions { let amount = calculate_funding_fee( position.quantity, - funding_rate.rate, + funding_rate.rate(), index_price, position.trader_direction, ); @@ -215,9 +169,9 @@ fn generate_funding_fee_events( amount, position.trader, position.id, - funding_rate.end_date, + funding_rate.end_date(), index_price, - funding_rate.rate, + funding_rate.rate(), ) .context("Failed to insert funding fee event")? { diff --git a/coordinator/src/lib.rs b/coordinator/src/lib.rs index c61f6a5f2..4d4afb4c2 100644 --- a/coordinator/src/lib.rs +++ b/coordinator/src/lib.rs @@ -16,8 +16,6 @@ use rust_decimal::prelude::FromPrimitive; use rust_decimal::prelude::ToPrimitive; use rust_decimal::Decimal; use serde_json::json; -use time::OffsetDateTime; -use time::Time; use xxi_node::commons; mod collaborative_revert; @@ -124,29 +122,3 @@ pub enum FundingFee { CoordinatorPays(Amount), TraderPays(Amount), } - -/// Remove minutes, seconds and nano seconds from a given [`OffsetDateTime`]. -pub fn to_nearest_hour_in_the_past(start_date: OffsetDateTime) -> OffsetDateTime { - OffsetDateTime::new_utc( - start_date.date(), - Time::from_hms_nano(start_date.time().hour(), 0, 0, 0).expect("to be valid time"), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_remove_small_units() { - let start_date = OffsetDateTime::now_utc(); - - // Act - let result = to_nearest_hour_in_the_past(start_date); - - // Assert - assert_eq!(result.hour(), start_date.time().hour()); - assert_eq!(result.minute(), 0); - assert_eq!(result.second(), 0); - } -} diff --git a/coordinator/src/orderbook/websocket.rs b/coordinator/src/orderbook/websocket.rs index 2adf06aba..01b0360a3 100644 --- a/coordinator/src/orderbook/websocket.rs +++ b/coordinator/src/orderbook/websocket.rs @@ -1,5 +1,6 @@ use crate::db; use crate::db::funding_fee_events; +use crate::db::funding_rates; use crate::db::user; use crate::message::NewUserMessage; use crate::orderbook::db::orders; @@ -276,6 +277,32 @@ pub async fn websocket_connection(stream: WebSocket, state: Arc) { } } + match funding_rates::get_next_funding_rate(&mut conn) { + Ok(Some(funding_rate)) => { + if let Err(e) = local_sender + .send(Message::NextFundingRate(funding_rate)) + .await + { + tracing::error!( + %trader_id, + "Failed to send next funding rate: {e}" + ); + } + } + Ok(None) => { + tracing::error!( + %trader_id, + "No next funding rate found in DB" + ); + } + Err(e) => { + tracing::error!( + %trader_id, + "Failed to load next funding rate: {e}" + ); + } + } + let token = fcm_token.unwrap_or("unavailable".to_string()); if let Err(e) = user::login_user(&mut conn, trader_id, token, version, os) diff --git a/coordinator/src/routes/admin.rs b/coordinator/src/routes/admin.rs index d9f04b6e6..2feefcabc 100644 --- a/coordinator/src/routes/admin.rs +++ b/coordinator/src/routes/admin.rs @@ -1,6 +1,5 @@ use crate::collaborative_revert; use crate::db; -use crate::funding_fee; use crate::parse_dlc_channel_id; use crate::position::models::Position; use crate::referrals; @@ -682,7 +681,7 @@ pub async fn post_funding_rates( .0 .iter() .copied() - .map(funding_fee::FundingRate::from) + .map(xxi_node::commons::FundingRate::from) .collect::>(); db::funding_rates::insert(&mut conn, &funding_rates) @@ -703,9 +702,9 @@ pub struct FundingRate { end_date: OffsetDateTime, } -impl From for funding_fee::FundingRate { +impl From for xxi_node::commons::FundingRate { fn from(value: FundingRate) -> Self { - funding_fee::FundingRate::new(value.rate, value.start_date, value.end_date) + xxi_node::commons::FundingRate::new(value.rate, value.start_date, value.end_date) } } diff --git a/crates/tests-e2e/src/test_subscriber.rs b/crates/tests-e2e/src/test_subscriber.rs index dca5531a4..492e56619 100644 --- a/crates/tests-e2e/src/test_subscriber.rs +++ b/crates/tests-e2e/src/test_subscriber.rs @@ -203,6 +203,9 @@ impl Senders { native::event::EventInternal::FundingFeeEvent(_) => { // ignored } + native::event::EventInternal::NextFundingRate(_) => { + // ignored + } } Ok(()) } diff --git a/crates/xxi-node/src/commons/funding_fee_event.rs b/crates/xxi-node/src/commons/funding_fee_event.rs index c8a78b6cf..7b5dd35a9 100644 --- a/crates/xxi-node/src/commons/funding_fee_event.rs +++ b/crates/xxi-node/src/commons/funding_fee_event.rs @@ -1,3 +1,4 @@ +use crate::commons::to_nearest_hour_in_the_past; use crate::commons::ContractSymbol; use crate::commons::Direction; use bitcoin::SignedAmount; @@ -6,6 +7,51 @@ use serde::Deserialize; use serde::Serialize; use time::OffsetDateTime; +/// The funding rate for any position opened before the `end_date`, which remained open through the +/// `end_date`. +#[derive(Serialize, Clone, Copy, Deserialize, Debug)] +pub struct FundingRate { + /// A positive funding rate indicates that longs pay shorts; a negative funding rate indicates + /// that shorts pay longs. + rate: Decimal, + /// The start date for the funding rate period. This value is only used for informational + /// purposes. + /// + /// The `start_date` is always a whole hour. + start_date: OffsetDateTime, + /// The end date for the funding rate period. When the end date has passed, all active + /// positions that were created before the end date should be charged a funding fee based + /// on the `rate`. + /// + /// The `end_date` is always a whole hour. + end_date: OffsetDateTime, +} + +impl FundingRate { + pub fn new(rate: Decimal, start_date: OffsetDateTime, end_date: OffsetDateTime) -> Self { + let start_date = to_nearest_hour_in_the_past(start_date); + let end_date = to_nearest_hour_in_the_past(end_date); + + Self { + rate, + start_date, + end_date, + } + } + + pub fn rate(&self) -> Decimal { + self.rate + } + + pub fn start_date(&self) -> OffsetDateTime { + self.start_date + } + + pub fn end_date(&self) -> OffsetDateTime { + self.end_date + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub struct FundingFeeEvent { pub contract_symbol: ContractSymbol, diff --git a/crates/xxi-node/src/commons/message.rs b/crates/xxi-node/src/commons/message.rs index 3c576e1ba..0f2456c7a 100644 --- a/crates/xxi-node/src/commons/message.rs +++ b/crates/xxi-node/src/commons/message.rs @@ -1,5 +1,6 @@ use crate::commons::order::Order; use crate::commons::signature::Signature; +use crate::commons::FundingRate; use crate::commons::LiquidityOption; use crate::commons::NewLimitOrder; use crate::commons::ReferralStatus; @@ -52,6 +53,7 @@ pub enum Message { }, FundingFeeEvent(FundingFeeEvent), AllFundingFeeEvents(Vec), + NextFundingRate(FundingRate), } #[derive(Serialize, Deserialize, Clone, Error, Debug, PartialEq)] @@ -116,6 +118,7 @@ impl Display for Message { Message::LnPaymentReceived { .. } => "LnPaymentReceived", Message::FundingFeeEvent(_) => "FundingFeeEvent", Message::AllFundingFeeEvents(_) => "FundingFeeEvent", + Message::NextFundingRate(_) => "NextFundingRate", }; f.write_str(s) diff --git a/crates/xxi-node/src/commons/mod.rs b/crates/xxi-node/src/commons/mod.rs index 4d5e3117b..b23370540 100644 --- a/crates/xxi-node/src/commons/mod.rs +++ b/crates/xxi-node/src/commons/mod.rs @@ -5,6 +5,8 @@ use serde::Deserialize; use serde::Serialize; use std::fmt; use std::str::FromStr; +use time::OffsetDateTime; +use time::Time; mod backup; mod collab_revert; @@ -24,7 +26,7 @@ mod trade; pub use crate::commons::trade::*; pub use backup::*; pub use collab_revert::*; -pub use funding_fee_event::FundingFeeEvent; +pub use funding_fee_event::*; pub use liquidity_option::*; pub use message::*; pub use order::*; @@ -201,10 +203,17 @@ impl fmt::Display for ContractSymbol { } } +/// Remove minutes, seconds and nano seconds from a given [`OffsetDateTime`]. +pub fn to_nearest_hour_in_the_past(start_date: OffsetDateTime) -> OffsetDateTime { + OffsetDateTime::new_utc( + start_date.date(), + Time::from_hms_nano(start_date.time().hour(), 0, 0, 0).expect("to be valid time"), + ) +} + #[cfg(test)] pub mod tests { - use crate::commons::referral_from_pubkey; - use crate::commons::ContractSymbol; + use super::*; use secp256k1::PublicKey; use std::str::FromStr; @@ -235,4 +244,17 @@ pub mod tests { let referral = referral_from_pubkey(pk); assert_eq!(referral, "DDD166".to_string()); } + + #[test] + fn test_remove_small_units() { + let start_date = OffsetDateTime::now_utc(); + + // Act + let result = to_nearest_hour_in_the_past(start_date); + + // Assert + assert_eq!(result.hour(), start_date.time().hour()); + assert_eq!(result.minute(), 0); + assert_eq!(result.second(), 0); + } } diff --git a/mobile/lib/backend.dart b/mobile/lib/backend.dart index 373354ce6..e363eef2e 100644 --- a/mobile/lib/backend.dart +++ b/mobile/lib/backend.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get_10101/common/dlc_channel_change_notifier.dart'; +import 'package:get_10101/features/trade/funding_rate_change_notifier.dart'; import 'package:get_10101/features/trade/order_change_notifier.dart'; import 'package:get_10101/features/trade/position_change_notifier.dart'; import 'package:get_10101/features/trade/trade_change_notifier.dart'; @@ -58,6 +59,7 @@ Future runBackend(BuildContext context) async { final orderChangeNotifier = context.read(); final positionChangeNotifier = context.read(); final tradeChangeNotifier = context.read(); + final fundingRateChangeNotifier = context.read(); final dlcChannelChangeNotifier = context.read(); final seedDir = (await getApplicationSupportDirectory()).path; @@ -78,6 +80,7 @@ Future runBackend(BuildContext context) async { await positionChangeNotifier.initialize(); await tradeChangeNotifier.initialize(); await dlcChannelChangeNotifier.initialize(); + await fundingRateChangeNotifier.initialize(); } void _setupRustLogging() { diff --git a/mobile/lib/common/init_service.dart b/mobile/lib/common/init_service.dart index 614409884..0290be5d8 100644 --- a/mobile/lib/common/init_service.dart +++ b/mobile/lib/common/init_service.dart @@ -10,6 +10,7 @@ import 'package:get_10101/common/funding_channel_task_change_notifier.dart'; import 'package:get_10101/features/brag/meme_service.dart'; import 'package:get_10101/features/trade/application/trade_service.dart'; import 'package:get_10101/features/trade/domain/trade.dart'; +import 'package:get_10101/features/trade/funding_rate_change_notifier.dart'; import 'package:get_10101/features/trade/order_change_notifier.dart'; import 'package:get_10101/features/trade/position_change_notifier.dart'; import 'package:get_10101/common/amount_denomination_change_notifier.dart'; @@ -56,6 +57,7 @@ List createProviders() { ChangeNotifierProvider(create: (context) => OrderChangeNotifier(OrderService())), ChangeNotifierProvider(create: (context) => PositionChangeNotifier(PositionService())), ChangeNotifierProvider(create: (context) => TradeChangeNotifier(TradeService())), + ChangeNotifierProvider(create: (context) => FundingRateChangeNotifier()), ChangeNotifierProvider(create: (context) => WalletChangeNotifier(const WalletService())), ChangeNotifierProvider(create: (context) => ServiceStatusNotifier()), ChangeNotifierProvider(create: (context) => DlcChannelChangeNotifier(dlcChannelService)), @@ -81,6 +83,7 @@ void subscribeToNotifiers(BuildContext context) { final orderChangeNotifier = context.read(); final tradeChangeNotifier = context.read(); + final fundingRateChangeNotifier = context.read(); final positionChangeNotifier = context.read(); final walletChangeNotifier = context.read(); final tradeValuesChangeNotifier = context.read(); @@ -95,6 +98,8 @@ void subscribeToNotifiers(BuildContext context) { eventService.subscribe(tradeChangeNotifier, bridge.Event.newTrade(Trade.apiDummy())); + eventService.subscribe(fundingRateChangeNotifier, const bridge.Event.nextFundingRate(0.0)); + eventService.subscribe( positionChangeNotifier, bridge.Event.positionUpdateNotification(Position.apiDummy())); diff --git a/mobile/lib/features/trade/funding_rate_change_notifier.dart b/mobile/lib/features/trade/funding_rate_change_notifier.dart new file mode 100644 index 000000000..0dda815f1 --- /dev/null +++ b/mobile/lib/features/trade/funding_rate_change_notifier.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:get_10101/logger/logger.dart'; +import 'package:get_10101/bridge_generated/bridge_definitions.dart' as bridge; +import 'package:get_10101/common/application/event_service.dart'; + +class FundingRateChangeNotifier extends ChangeNotifier implements Subscriber { + double? nextRate; + + Future initialize() async { + notifyListeners(); + } + + FundingRateChangeNotifier(); + + @override + void notify(bridge.Event event) { + if (event is bridge.Event_NextFundingRate) { + nextRate = event.field0; + + notifyListeners(); + } else { + logger.w("Received unexpected event: ${event.toString()}"); + } + } +} diff --git a/mobile/lib/features/trade/trade_screen.dart b/mobile/lib/features/trade/trade_screen.dart index 72b656df3..fce10e2d1 100644 --- a/mobile/lib/features/trade/trade_screen.dart +++ b/mobile/lib/features/trade/trade_screen.dart @@ -1,8 +1,11 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:get_10101/common/domain/model.dart'; import 'package:get_10101/features/trade/domain/direction.dart'; import 'package:get_10101/features/trade/domain/order.dart'; import 'package:get_10101/features/trade/domain/position.dart'; +import 'package:get_10101/features/trade/funding_rate_change_notifier.dart'; import 'package:get_10101/features/trade/order_change_notifier.dart'; import 'package:get_10101/features/trade/order_list_item.dart'; import 'package:get_10101/features/trade/position_change_notifier.dart'; @@ -65,16 +68,22 @@ class TradeScreen extends StatelessWidget { }, builder: (context, price, child) { return LatestPriceWidget( innerKey: tradeScreenAskPrice, - label: "Latest Ask: ", + label: "Ask: ", price: Usd.fromDouble(price ?? 0.0), ); }), + Selector(selector: (_, provider) { + return provider.nextRate; + }, builder: (context, rate, child) { + return FundingRateWidget( + rate: rate, label: "Funding Rate: ", innerKey: tradeScreenFundingRate); + }), Selector(selector: (_, provider) { return provider.getBidPrice(); }, builder: (context, price, child) { return LatestPriceWidget( innerKey: tradeScreenBidPrice, - label: "Latest Bid: ", + label: "Bid: ", price: Usd.fromDouble(price ?? 0.0), ); }), @@ -320,3 +329,29 @@ class LatestPriceWidget extends StatelessWidget { ); } } + +class FundingRateWidget extends StatelessWidget { + final double? rate; + final String label; + final Key innerKey; + + const FundingRateWidget( + {super.key, required this.rate, required this.label, required this.innerKey}); + + @override + Widget build(BuildContext context) { + return RichText( + key: innerKey, + text: TextSpan( + text: label, + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: rate != null ? "${(rate! * 100).toStringAsFixed(2)}%" : "n/a", + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ], + ), + ); + } +} diff --git a/mobile/lib/util/constants.dart b/mobile/lib/util/constants.dart index 56c6330f8..e96b293ab 100644 --- a/mobile/lib/util/constants.dart +++ b/mobile/lib/util/constants.dart @@ -68,12 +68,14 @@ const tabTrade = Key(_tabs + _trade); const _ask = "ask"; const _bid = "bid"; +const _fundingRate = "fundingRate"; const _marketPrice = "marketPrice"; const _quantityInput = "quantityInput"; const _marginField = "marginField"; const tradeScreenAskPrice = Key(_trade + _tabs + _ask); const tradeScreenBidPrice = Key(_trade + _tabs + _bid); +const tradeScreenFundingRate = Key(_trade + _tabs + _fundingRate); const tradeButtonSheetMarketPrice = Key(_trade + _tabs + _bottomSheet + _marketPrice); const tradeButtonSheetQuantityInput = Key(_trade + _tabs + _bottomSheet + _quantityInput); diff --git a/mobile/native/src/event/api.rs b/mobile/native/src/event/api.rs index 23f09b619..200a39ab8 100644 --- a/mobile/native/src/event/api.rs +++ b/mobile/native/src/event/api.rs @@ -34,6 +34,7 @@ pub enum Event { FundingChannelNotification(FundingChannelTask), LnPaymentReceived { r_hash: String }, NewTrade(Trade), + NextFundingRate(f32), } #[frb] @@ -97,6 +98,9 @@ impl From for Event { } EventInternal::LnPaymentReceived { r_hash } => Event::LnPaymentReceived { r_hash }, EventInternal::NewTrade(trade) => Event::NewTrade(trade.into()), + EventInternal::NextFundingRate(funding_rate) => { + Event::NextFundingRate(funding_rate.rate().to_f32().expect("to fit")) + } EventInternal::FundingFeeEvent(event) => Event::NewTrade(event.into()), } } @@ -139,6 +143,7 @@ impl Subscriber for FlutterSubscriber { EventType::Authenticated, EventType::DlcChannelEvent, EventType::NewTrade, + EventType::NextFundingRate, ] } } diff --git a/mobile/native/src/event/mod.rs b/mobile/native/src/event/mod.rs index 1ddb85019..61b9ead42 100644 --- a/mobile/native/src/event/mod.rs +++ b/mobile/native/src/event/mod.rs @@ -11,6 +11,7 @@ use rust_decimal::Decimal; use std::fmt; use std::hash::Hash; use xxi_node::commons::ContractSymbol; +use xxi_node::commons::FundingRate; use xxi_node::commons::TenTenOneConfig; mod event_hub; @@ -45,6 +46,7 @@ pub enum EventInternal { LnPaymentReceived { r_hash: String }, NewTrade(Trade), FundingFeeEvent(FundingFeeEvent), + NextFundingRate(FundingRate), } #[derive(Clone, Debug)] @@ -93,6 +95,7 @@ impl fmt::Display for EventInternal { EventInternal::LnPaymentReceived { .. } => "LnPaymentReceived", EventInternal::NewTrade(_) => "NewTrade", EventInternal::FundingFeeEvent(_) => "NewFundingFeeEvent", + EventInternal::NextFundingRate(_) => "NextFundingRate", } .fmt(f) } @@ -120,6 +123,7 @@ impl From for EventType { EventInternal::LnPaymentReceived { .. } => EventType::LnPaymentReceived, EventInternal::NewTrade(_) => EventType::NewTrade, EventInternal::FundingFeeEvent(_) => EventType::NewTrade, + EventInternal::NextFundingRate(_) => EventType::NextFundingRate, } } } @@ -145,4 +149,5 @@ pub enum EventType { BidPriceUpdateNotification, FundingChannelNotification, NewTrade, + NextFundingRate, } diff --git a/mobile/native/src/orderbook.rs b/mobile/native/src/orderbook.rs index 3cf3e4ff3..29ed3c44a 100644 --- a/mobile/native/src/orderbook.rs +++ b/mobile/native/src/orderbook.rs @@ -251,6 +251,10 @@ async fn handle_orderbook_message( position::handler::handle_funding_fee_events(&new_funding_fee_events) .context("Failed to apply all funding fee events from coordinator")?; } + Message::NextFundingRate(funding_rate) => { + tracing::info!(?funding_rate, "Got next funding rate"); + event::publish(&EventInternal::NextFundingRate(funding_rate)); + } Message::FundingFeeEvent(funding_fee_event) => { let new_funding_fee_events = funding_fee_event::handler::handle_unpaid_funding_fee_events(&[