nautilus_model/data/
status.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// -------------------------------------------------------------------------------------------------
//  Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
//  https://nautechsystems.io
//
//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
//  You may not use this file except in compliance with the License.
//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// -------------------------------------------------------------------------------------------------

//! An `InstrumentStatus` data type representing a change in an instrument market status.

use std::{
    collections::HashMap,
    fmt::{Display, Formatter},
    hash::Hash,
};

use derive_builder::Builder;
use nautilus_core::{nanos::UnixNanos, serialization::Serializable};
use serde::{Deserialize, Serialize};
use ustr::Ustr;

use super::GetTsInit;
use crate::{enums::MarketStatusAction, identifiers::InstrumentId};

/// Represents an event that indicates a change in an instrument market status.
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Builder)]
#[serde(tag = "type")]
#[cfg_attr(
    feature = "python",
    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct InstrumentStatus {
    /// The instrument ID for the status change.
    pub instrument_id: InstrumentId,
    /// The instrument market status action.
    pub action: MarketStatusAction,
    /// UNIX timestamp (nanoseconds) when the status event occurred.
    pub ts_event: UnixNanos,
    /// UNIX timestamp (nanoseconds) when the struct was initialized.
    pub ts_init: UnixNanos,
    /// Additional details about the cause of the status change.
    pub reason: Option<Ustr>,
    /// Further information about the status change (if provided).
    pub trading_event: Option<Ustr>,
    /// The state of trading in the instrument.
    pub is_trading: Option<bool>,
    /// The state of quoting in the instrument.
    pub is_quoting: Option<bool>,
    /// The state of short sell restrictions for the instrument (if applicable).
    pub is_short_sell_restricted: Option<bool>,
}

impl InstrumentStatus {
    /// Creates a new [`InstrumentStatus`] instance.
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        instrument_id: InstrumentId,
        action: MarketStatusAction,
        ts_event: UnixNanos,
        ts_init: UnixNanos,
        reason: Option<Ustr>,
        trading_event: Option<Ustr>,
        is_trading: Option<bool>,
        is_quoting: Option<bool>,
        is_short_sell_restricted: Option<bool>,
    ) -> Self {
        Self {
            instrument_id,
            action,
            ts_event,
            ts_init,
            reason,
            trading_event,
            is_trading,
            is_quoting,
            is_short_sell_restricted,
        }
    }

    /// Returns the metadata for the type, for use with serialization formats.
    #[must_use]
    pub fn get_metadata(instrument_id: &InstrumentId) -> HashMap<String, String> {
        let mut metadata = HashMap::new();
        metadata.insert("instrument_id".to_string(), instrument_id.to_string());
        metadata
    }
}

// TODO: Revisit this
impl Display for InstrumentStatus {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{},{},{},{}",
            self.instrument_id, self.action, self.ts_event, self.ts_init,
        )
    }
}

impl Serializable for InstrumentStatus {}

impl GetTsInit for InstrumentStatus {
    fn ts_init(&self) -> UnixNanos {
        self.ts_init
    }
}

////////////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
mod tests {
    use nautilus_core::serialization::Serializable;
    use rstest::rstest;

    use super::*;
    use crate::data::stubs::stub_instrument_status;

    #[rstest]
    fn test_to_string(stub_instrument_status: InstrumentStatus) {
        assert_eq!(stub_instrument_status.to_string(), "MSFT.XNAS,TRADING,1,2");
    }

    #[rstest]
    fn test_json_serialization(stub_instrument_status: InstrumentStatus) {
        let serialized = stub_instrument_status.as_json_bytes().unwrap();
        let deserialized = InstrumentStatus::from_json_bytes(serialized.as_ref()).unwrap();
        assert_eq!(deserialized, stub_instrument_status);
    }

    #[rstest]
    fn test_msgpack_serialization(stub_instrument_status: InstrumentStatus) {
        let serialized = stub_instrument_status.as_msgpack_bytes().unwrap();
        let deserialized = InstrumentStatus::from_msgpack_bytes(serialized.as_ref()).unwrap();
        assert_eq!(deserialized, stub_instrument_status);
    }
}