Skip to main content

nautilus_serialization/arrow/
funding.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::collections::HashMap;
17
18use arrow::record_batch::RecordBatch;
19use nautilus_model::data::{Data, FundingRateUpdate};
20
21use super::{
22    DecodeDataFromRecordBatch, DecodeTypedFromRecordBatch, EncodingError,
23    json::{JsonFieldSpec, impl_json_arrow},
24};
25
26const FUNDING_RATE_UPDATE_FIELDS: &[JsonFieldSpec] = &[
27    JsonFieldSpec::utf8("instrument_id", false),
28    JsonFieldSpec::utf8("rate", false),
29    JsonFieldSpec::u64("interval", true),
30    JsonFieldSpec::u64("next_funding_ns", true),
31    JsonFieldSpec::u64("ts_event", false),
32    JsonFieldSpec::u64("ts_init", false),
33];
34
35impl_json_arrow!(instrument FundingRateUpdate, "FundingRateUpdate", FUNDING_RATE_UPDATE_FIELDS);
36
37impl DecodeDataFromRecordBatch for FundingRateUpdate {
38    fn decode_data_batch(
39        metadata: &HashMap<String, String>,
40        record_batch: RecordBatch,
41    ) -> Result<Vec<Data>, EncodingError> {
42        let updates = Self::decode_typed_batch(metadata, record_batch)?;
43        Ok(updates.into_iter().map(Data::from).collect())
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use std::str::FromStr;
50
51    use nautilus_core::UnixNanos;
52    use nautilus_model::identifiers::InstrumentId;
53    use rstest::rstest;
54    use rust_decimal::Decimal;
55
56    use super::*;
57    use crate::arrow::EncodeToRecordBatch;
58
59    #[rstest]
60    fn test_funding_rate_update_round_trip_preserves_decimal_precision() {
61        let update = FundingRateUpdate::new(
62            InstrumentId::from("BTCUSDT-PERP.BINANCE"),
63            Decimal::from_str("0.000123456789123456789").unwrap(),
64            Some(480),
65            Some(UnixNanos::from(9_000_000_000)),
66            UnixNanos::from(1_000_000_000),
67            UnixNanos::from(2_000_000_000),
68        );
69        let metadata = update.metadata();
70        let batch = FundingRateUpdate::encode_batch(&metadata, &[update]).unwrap();
71        let decoded =
72            FundingRateUpdate::decode_typed_batch(batch.schema().metadata(), batch).unwrap();
73
74        assert_eq!(decoded, vec![update]);
75    }
76
77    #[rstest]
78    fn test_funding_rate_update_round_trip_null_optionals() {
79        let update = FundingRateUpdate::new(
80            InstrumentId::from("BTCUSDT-PERP.BINANCE"),
81            Decimal::from_str("0.0001").unwrap(),
82            None,
83            None,
84            UnixNanos::from(1_000_000_000),
85            UnixNanos::from(2_000_000_000),
86        );
87        let metadata = update.metadata();
88        let batch = FundingRateUpdate::encode_batch(&metadata, &[update]).unwrap();
89        let decoded =
90            FundingRateUpdate::decode_typed_batch(batch.schema().metadata(), batch).unwrap();
91
92        assert_eq!(decoded, vec![update]);
93        assert!(decoded[0].interval.is_none());
94        assert!(decoded[0].next_funding_ns.is_none());
95    }
96}