Skip to main content

ids_apis/
kdo.v1.pair.rs

1// @generated
2// This file is @generated by prost-build.
3// ============================================================================
4// Pair Entity
5// ============================================================================
6
7/// Pair 전략 — 두 심볼의 가격 조건에 따른 동시 주문 설정
8#[allow(clippy::derive_partial_eq_without_eq)]
9#[derive(Clone, PartialEq, ::prost::Message)]
10pub struct Pair {
11    /// 리소스 이름 (pairs/{id})
12    #[prost(string, tag="1")]
13    pub name: ::prost::alloc::string::String,
14    /// Pair ID
15    #[prost(int32, tag="2")]
16    pub id: i32,
17    /// 전략 이름 (고유)
18    #[prost(string, tag="3")]
19    pub display_name: ::prost::alloc::string::String,
20    /// Portfolio ID
21    #[prost(int32, tag="4")]
22    pub portfolio_id: i32,
23    /// Base 엔트리
24    #[prost(message, optional, tag="5")]
25    pub base: ::core::option::Option<PairEntry>,
26    /// Counter 엔트리
27    #[prost(message, optional, tag="6")]
28    pub counter: ::core::option::Option<PairEntry>,
29    /// 상태
30    #[prost(enumeration="PairStatus", tag="9")]
31    pub status: i32,
32    /// 생성 시간
33    #[prost(message, optional, tag="11")]
34    pub create_time: ::core::option::Option<super::super::super::google::protobuf::Timestamp>,
35    /// 수정 시간
36    #[prost(message, optional, tag="12")]
37    pub update_time: ::core::option::Option<super::super::super::google::protobuf::Timestamp>,
38    /// 트리거 — 언제 발사할지. 실행(execution)과 독립적으로 조합한다.
39    #[prost(message, optional, tag="13")]
40    pub trigger: ::core::option::Option<TriggerCondition>,
41    /// 실행 — 발사 시 무엇을 할지. 트리거(trigger)와 독립적으로 조합한다.
42    #[prost(message, optional, tag="14")]
43    pub execution: ::core::option::Option<OrderExecution>,
44    /// ETF↔Future NAV 환산 설정 — TargetNavQuantityImbalance 트리거·BaseMakeCounterIocAndBalance/CounterIocTpSlExecution
45    /// execution 이 공유. NAV 미사용 페어는 미설정.
46    #[prost(message, optional, tag="15")]
47    pub nav: ::core::option::Option<Nav>,
48}
49// ============================================================================
50// Pair Entry
51// ============================================================================
52
53/// 페어의 한쪽 엔트리 (단일 심볼 주문 스펙)
54///
55/// execution 종류별 필드 사용:
56/// - DualSubmitExecution: side/quantity/price_source 사용 (지정가 = price_source 추출 가격).
57/// - BaseMakeCounterIocAndBalanceExecution (IOC imbalance):
58///    - base.side / base.quantity: 사용 (deficit 트리거 방향 / 사이클 base 주문 수량).
59///    - counter.side: 사용자가 직접 지정 (정방향 ETF → base.side 반대, 역방향 ETF → base.side 와 동일).
60///    - counter.quantity: 무시 (런타임 = base.quantity × hedge_ratio).
61///    - price_source (양 슬롯): 무시. base 가격 = base.side 1호가(=BestMake) 고정,
62///      counter 가격 = NAV 기반 BEP 고정. 사용자가 지정해도 서버에서 UNSPECIFIED 로 정규화.
63/// - CounterIocTpSlExecution: counter 엔트리만 사용.
64#[allow(clippy::derive_partial_eq_without_eq)]
65#[derive(Clone, PartialEq, ::prost::Message)]
66pub struct PairEntry {
67    /// 종목 심볼
68    #[prost(string, tag="1")]
69    pub symbol: ::prost::alloc::string::String,
70    /// 주문에 사용할 펀드 코드
71    #[prost(string, tag="2")]
72    pub fund_code: ::prost::alloc::string::String,
73    /// 주문 방향
74    #[prost(enumeration="PairSide", tag="3")]
75    pub side: i32,
76    /// 주문 수량 (1 이상)
77    /// BaseMakeCounterIocAndBalanceExecution: base 는 필수(사이클 주문 수량), counter 는 0 허용(런타임 = base × hedge_ratio).
78    #[prost(int64, tag="4")]
79    pub quantity: i64,
80    /// 참조 가격 소스 (entry.side 기준 자기/상대 호가).
81    /// BaseMakeCounterIocAndBalanceExecution 에선 무시(base 가격은 항상 base.side 의 1호가). 입력값은 UNSPECIFIED 로 정규화된다.
82    #[prost(enumeration="PriceSource", tag="5")]
83    pub price_source: i32,
84    /// 주문 tp_code (NONE=일반, LP=유동성공급자). 이 레그의 신규 주문에 적용. 미지정 시 NONE.
85    #[prost(enumeration="super::hedge::OrderTpCode", tag="7")]
86    pub tp_code: i32,
87}
88// ============================================================================
89// Pair Condition (oneof wrapper)
90// ============================================================================
91
92/// 페어 가격 비교 조건 (세 가지 variant 중 하나)
93#[allow(clippy::derive_partial_eq_without_eq)]
94#[derive(Clone, Copy, PartialEq, ::prost::Message)]
95pub struct PairCondition {
96    #[prost(oneof="pair_condition::Kind", tags="1, 2, 3")]
97    pub kind: ::core::option::Option<pair_condition::Kind>,
98}
99/// Nested message and enum types in `PairCondition`.
100pub mod pair_condition {
101    #[allow(clippy::derive_partial_eq_without_eq)]
102#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]
103    pub enum Kind {
104        /// 절대 스프레드 금액 기준
105        #[prost(message, tag="1")]
106        SpreadAmount(super::SpreadAmountCondition),
107        /// 상대 스프레드 (bps) 기준
108        #[prost(message, tag="2")]
109        SpreadBps(super::SpreadBpsCondition),
110        /// 가격 비율 기준
111        #[prost(message, tag="3")]
112        PriceRatio(super::PriceRatioCondition),
113    }
114}
115/// 절대 스프레드 금액 조건 (|base - counter| >= threshold)
116#[allow(clippy::derive_partial_eq_without_eq)]
117#[derive(Clone, Copy, PartialEq, ::prost::Message)]
118pub struct SpreadAmountCondition {
119    /// 스프레드 임계값 (원, 1 이상)
120    #[prost(int64, tag="1")]
121    pub threshold: i64,
122    /// 트리거 방향
123    #[prost(enumeration="SpreadDirection", tag="2")]
124    pub direction: i32,
125}
126/// 상대 스프레드 (bps) 조건 (|spread| / mid * 10000 >= threshold_bps, base 기준)
127#[allow(clippy::derive_partial_eq_without_eq)]
128#[derive(Clone, Copy, PartialEq, ::prost::Message)]
129pub struct SpreadBpsCondition {
130    /// 스프레드 임계값 (bps, 1bp = 0.01%)
131    #[prost(double, tag="1")]
132    pub threshold_bps: f64,
133    /// 트리거 방향
134    #[prost(enumeration="SpreadDirection", tag="2")]
135    pub direction: i32,
136}
137/// 가격 비율 조건 (base / counter)
138#[allow(clippy::derive_partial_eq_without_eq)]
139#[derive(Clone, Copy, PartialEq, ::prost::Message)]
140pub struct PriceRatioCondition {
141    /// 최소 비율 (이 값 미만이면 CounterHigh 트리거)
142    #[prost(double, tag="1")]
143    pub min_ratio: f64,
144    /// 최대 비율 (이 값 초과 시 BaseHigh 트리거)
145    #[prost(double, tag="2")]
146    pub max_ratio: f64,
147}
148/// ETF↔Future 페어의 NAV 환산 설정 — 트리거(TargetNav)와 실행(counter BEP)이 공유.
149/// base/counter 중 한쪽이 ETF, 다른쪽이 Future 여야 한다 (방향 무관).
150#[allow(clippy::derive_partial_eq_without_eq)]
151#[derive(Clone, Copy, PartialEq, ::prost::Message)]
152pub struct Nav {
153    #[prost(enumeration="EtfNavKind", tag="1")]
154    pub nav_kind: i32,
155    /// NAV 베이시스 (선물 가격 축, base=선물 기준 단일값).
156    /// 양수면 선물 목표가를 보수적으로 조정.
157    #[prost(int64, tag="2")]
158    pub basis: i64,
159    /// 선물-ETF basis 동기화 버스 구독 여부.
160    /// true 이면 pair가 버스 이벤트를 수신해 Nav.basis를 자동 갱신한다. 기본 false.
161    #[prost(bool, tag="4")]
162    pub basis_subscribe_enabled: bool,
163}
164// ============================================================================
165// TriggerCondition — 트리거 축
166// ============================================================================
167
168/// 트리거 — 언제 발사할지. 실행(OrderExecution)과 독립적으로 조합한다.
169#[allow(clippy::derive_partial_eq_without_eq)]
170#[derive(Clone, PartialEq, ::prost::Message)]
171pub struct TriggerCondition {
172    #[prost(oneof="trigger_condition::Kind", tags="1, 2, 3")]
173    pub kind: ::core::option::Option<trigger_condition::Kind>,
174}
175/// Nested message and enum types in `TriggerCondition`.
176pub mod trigger_condition {
177    #[allow(clippy::derive_partial_eq_without_eq)]
178#[derive(Clone, PartialEq, ::prost::Oneof)]
179    pub enum Kind {
180        /// 양 슬롯 참조가격 비교 트리거 (구 SimultaneousCompare 의 트리거부)
181        #[prost(message, tag="1")]
182        PriceSpread(super::PriceSpreadTrigger),
183        /// base 자기측(BestMake) 1호가 수량 불균형 트리거. base.side 가 담당 deficit 방향.
184        #[prost(message, tag="2")]
185        BestMakeQuantityImbalance(super::BestMakeQuantityImbalanceTrigger),
186        /// counter 시세로 base 의 NAV 목표가를 환산해, base 자기측(BestMake) 1호가가
187        /// 목표가와 정확히 일치하면서 수량 불균형일 때 발사. nav 파라미터는 Pair.nav 공유 설정 사용.
188        #[prost(message, tag="3")]
189        TargetNavQuantityImbalance(super::TargetNavQuantityImbalanceTrigger),
190    }
191}
192/// 양 슬롯 참조가격 비교 트리거
193#[allow(clippy::derive_partial_eq_without_eq)]
194#[derive(Clone, Copy, PartialEq, ::prost::Message)]
195pub struct PriceSpreadTrigger {
196    /// 가격 비교 조건
197    #[prost(message, optional, tag="1")]
198    pub condition: ::core::option::Option<PairCondition>,
199    /// 트리거 후 재트리거까지 대기시간 (ms)
200    #[prost(uint64, tag="2")]
201    pub cooldown_ms: u64,
202}
203/// base 자기측(BestMake) 1호가 수량 불균형 트리거. base.side 가 담당 deficit 방향.
204#[allow(clippy::derive_partial_eq_without_eq)]
205#[derive(Clone, Copy, PartialEq, ::prost::Message)]
206pub struct BestMakeQuantityImbalanceTrigger {
207    /// base 1호가 imbalance 가 이 비율 미만이면 트리거
208    #[prost(double, tag="1")]
209    pub threshold_ratio: f64,
210    /// 트리거 후 재트리거까지 대기시간 (ms)
211    #[prost(uint64, tag="2")]
212    pub cooldown_ms: u64,
213    /// base 의 bid·ask 1호가 수량이 모두 이 값을 초과해야 트리거 발사. 0 = 미설정.
214    #[prost(int64, tag="4")]
215    pub min_base_qty: i64,
216}
217/// counter 시세로 base 의 NAV 목표가를 환산해, base 자기측(BestMake) 1호가가
218/// 목표가와 정확히 일치하면서 수량 불균형일 때 발사. nav 파라미터는 Pair.nav 공유 설정 사용.
219#[allow(clippy::derive_partial_eq_without_eq)]
220#[derive(Clone, PartialEq, ::prost::Message)]
221pub struct TargetNavQuantityImbalanceTrigger {
222    /// 수량 불균형 임계 (BestMakeQuantityImbalanceTrigger 와 동일 판정식)
223    #[prost(double, tag="1")]
224    pub threshold_ratio: f64,
225    /// 트리거 후 재트리거까지 대기시간 (ms)
226    #[prost(uint64, tag="2")]
227    pub cooldown_ms: u64,
228    /// qty imbalance 판정 전용 심볼 (미지정=base.symbol 동일, 기존 동작).
229    /// 지정 시 해당 호가(예: 현물)의 deficit_side 로 qty_gate 평가. price_gate(NAV 비교)와 주문 발주는 base 유지.
230    #[prost(string, optional, tag="3")]
231    pub imbalance_symbol: ::core::option::Option<::prost::alloc::string::String>,
232    /// base 의 bid·ask 1호가 수량이 모두 이 값을 초과해야 트리거 발사. 0 = 미설정.
233    #[prost(int64, tag="5")]
234    pub min_base_qty: i64,
235}
236// ============================================================================
237// OrderExecution — 실행 축
238// ============================================================================
239
240/// 실행 — 발사 시 무엇을 할지. 트리거와 독립적으로 조합한다.
241#[allow(clippy::derive_partial_eq_without_eq)]
242#[derive(Clone, Copy, PartialEq, ::prost::Message)]
243pub struct OrderExecution {
244    #[prost(oneof="order_execution::Kind", tags="1, 2, 3, 4")]
245    pub kind: ::core::option::Option<order_execution::Kind>,
246}
247/// Nested message and enum types in `OrderExecution`.
248pub mod order_execution {
249    #[allow(clippy::derive_partial_eq_without_eq)]
250#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]
251    pub enum Kind {
252        /// 양측 동시 발주 (구 SimultaneousCompare 의 실행부)
253        #[prost(message, tag="1")]
254        DualSubmit(super::DualSubmitExecution),
255        /// base Limit + counter IOC + settle/recovery/balance
256        #[prost(message, tag="2")]
257        BaseMakeCounterIocAndBalance(super::BaseMakeCounterIocAndBalanceExecution),
258        /// base 무발주 — counter(ETF) 상대호가±entry_aggressive_ticks pseudo-IOC 진입 + TP/SL 청산
259        #[prost(message, tag="3")]
260        CounterIocTpSl(super::CounterIocTpSlExecution),
261        /// base Limit + counter 잔류지정가(상대호가±틱, IOC 아님) + settle/recovery/balance
262        #[prost(message, tag="4")]
263        BaseMakeCounterTakeAndBalance(super::BaseMakeCounterTakeAndBalanceExecution),
264    }
265}
266/// 양측 동시 발주 (구 SimultaneousCompare 의 실행부)
267#[allow(clippy::derive_partial_eq_without_eq)]
268#[derive(Clone, Copy, PartialEq, ::prost::Message)]
269pub struct DualSubmitExecution {
270    /// 주문 유형
271    #[prost(enumeration="PairOrderType", tag="1")]
272    pub order_type: i32,
273}
274/// base Limit + counter IOC + settle/recovery/balance
275///
276/// PairEntry 필드 매핑:
277///    - base.symbol / base.fund_code / base.side / base.quantity: 사용 (필수).
278///    - counter.symbol / counter.fund_code / counter.side: 사용 (필수, 사용자가 직접 지정).
279///      정방향 ETF → counter.side = base.side 반대, 역방향 ETF → counter.side = base.side 와 동일.
280///    - counter.quantity: 무시 (런타임 = base.quantity × hedge_ratio). 0 으로 비워도 된다.
281///    - PairEntry.price_source (양 슬롯): 무시. base 가격은 base.side 의 1호가(=BestMake),
282///      counter 가격은 NAV 기반 BEP. 서버에서 UNSPECIFIED 로 정규화한다.
283#[allow(clippy::derive_partial_eq_without_eq)]
284#[derive(Clone, Copy, PartialEq, ::prost::Message)]
285pub struct BaseMakeCounterIocAndBalanceExecution {
286    /// base 측 잔량 비율이 이 값을 초과하면 base 공격적 정정→강제 체결
287    #[prost(double, tag="4")]
288    pub recovery_ratio: f64,
289    /// base 슬롯 공격적 정정 시 상대호가(cross price) 보다 얼마나 더 공격적으로 낼지 (tick 단위).
290    /// 0 이면 상대호가/ref 그대로. Bid 면 +N*tick, Ask 면 -N*tick.
291    #[prost(uint32, tag="7")]
292    pub base_recovery_aggressive_ticks: u32,
293    /// counter 슬롯 공격적 정정 시 상대호가에서 얼마나 더 공격적으로 낼지 (tick 단위).
294    /// 0 이면 BEP 그대로. counter.side 가 Bid 면 +N*tick, Ask 면 -N*tick.
295    #[prost(uint32, tag="8")]
296    pub counter_recovery_aggressive_ticks: u32,
297}
298/// base 무발주 — counter(ETF) 상대호가±entry_aggressive_ticks pseudo-IOC 진입,
299/// 마지막 진입 체결가 기준 take_profit_ticks/stop_loss_ticks 틱 익절/손절(TP/SL),
300/// exit_delay_ms 경과 후 판정. 진입은 상대 1호가에서 entry_aggressive_ticks 만큼
301/// 공격적으로 제출 후 잔량 즉시 취소(pseudo-IOC). 청산은 체결될 때까지 호가 추적(amend).
302#[allow(clippy::derive_partial_eq_without_eq)]
303#[derive(Clone, Copy, PartialEq, ::prost::Message)]
304pub struct CounterIocTpSlExecution {
305    /// 익절 임계 (틱)
306    #[prost(uint32, tag="4")]
307    pub take_profit_ticks: u32,
308    /// 손절 임계 (틱)
309    #[prost(uint32, tag="5")]
310    pub stop_loss_ticks: u32,
311    /// 청산 발주 시 상대 1호가 대비 추가 공격 틱 (0 = 상대호가 그대로)
312    #[prost(uint32, tag="6")]
313    pub exit_aggressive_ticks: u32,
314    /// 진입 발주 시 상대 1호가에서 추가로 공격적으로 낼 틱 수 (0 = 상대호가 그대로).
315    /// 진입은 BEP 가 아니라 상대호가 ± n틱의 pseudo-IOC (접수 후 잔량 즉시 취소).
316    #[prost(uint32, tag="7")]
317    pub entry_aggressive_ticks: u32,
318    /// 익절/손절 판정 대기 시간 (ms). 마지막 진입 체결 시각부터 이 시간 경과 후 판정 시작.
319    #[prost(uint64, tag="8")]
320    pub exit_delay_ms: u64,
321}
322/// base Limit + counter 잔류지정가(상대호가±틱, IOC 아님) + settle/recovery/balance
323///
324/// BaseMakeCounterIocAndBalanceExecution 과 구조가 동일하되, counter 주문 가격이 NAV BEP IOC 가 아니라
325/// counter 오더북 상대호가 ± counter_aggressive_ticks 틱의 지정가(체결까지 호가창에 잔류, 취소 없음)이다.
326/// NAV 파라미터(Pair.nav)는 이 execution 에서 사용하지 않는다.
327///
328/// PairEntry 필드 매핑:
329///    - base.symbol / base.fund_code / base.side / base.quantity: 사용 (필수).
330///    - counter.symbol / counter.fund_code / counter.side: 사용 (필수, 사용자가 직접 지정).
331///    - counter.quantity: 무시 (런타임 = base.quantity × hedge_ratio). 0 으로 비워도 된다.
332///    - PairEntry.price_source (양 슬롯): 무시. base 가격은 base.side 의 1호가(=BestMake),
333///      counter 가격은 상대호가 ± counter_aggressive_ticks. 서버에서 UNSPECIFIED 로 정규화한다.
334#[allow(clippy::derive_partial_eq_without_eq)]
335#[derive(Clone, Copy, PartialEq, ::prost::Message)]
336pub struct BaseMakeCounterTakeAndBalanceExecution {
337    /// base 호가 잔량 회복 비율. 회복 시 base 잔량을 상대호가로 공격 정정(강제 체결).
338    #[prost(double, tag="1")]
339    pub recovery_ratio: f64,
340    /// base 공격 정정 시 상대호가 대비 추가 틱. 0 이면 상대호가 그대로.
341    /// Bid 면 +N*tick, Ask 면 -N*tick.
342    #[prost(uint32, tag="4")]
343    pub base_recovery_aggressive_ticks: u32,
344    /// 종단 counter cover 발주 시 추가 틱. 0 이면 상대호가 그대로.
345    /// counter.side 가 Bid 면 +N*tick, Ask 면 -N*tick.
346    #[prost(uint32, tag="5")]
347    pub counter_recovery_aggressive_ticks: u32,
348    /// counter 진입 주문가 = counter 상대호가 ± 이 틱 수 (0 = 상대호가 그대로).
349    /// counter.side 가 Bid 면 +N*tick (더 공격적), Ask 면 -N*tick.
350    #[prost(uint32, tag="6")]
351    pub counter_aggressive_ticks: u32,
352}
353// ============================================================================
354// Request / Response Messages — CRUD
355// ============================================================================
356
357#[allow(clippy::derive_partial_eq_without_eq)]
358#[derive(Clone, PartialEq, ::prost::Message)]
359pub struct GetPairRequest {
360    /// 리소스 이름 (pairs/{id})
361    #[prost(string, tag="1")]
362    pub pair: ::prost::alloc::string::String,
363}
364#[allow(clippy::derive_partial_eq_without_eq)]
365#[derive(Clone, PartialEq, ::prost::Message)]
366pub struct ListPairsRequest {
367    /// 페이지 크기 (optional)
368    #[prost(int32, optional, tag="1")]
369    pub page_size: ::core::option::Option<i32>,
370    /// 페이지 토큰 (optional, for pagination)
371    #[prost(string, optional, tag="2")]
372    pub page_token: ::core::option::Option<::prost::alloc::string::String>,
373    /// 필터링 조건 (optional, AIP-160)
374    ///
375    /// Available Fields:
376    /// * status - 상태 (ACTIVE / PAUSED / ARCHIVED)
377    /// * portfolio_id - Portfolio ID
378    ///
379    /// Examples:
380    /// * status=ACTIVE
381    /// * portfolio_id=1
382    #[prost(string, tag="3")]
383    pub filter: ::prost::alloc::string::String,
384}
385#[allow(clippy::derive_partial_eq_without_eq)]
386#[derive(Clone, PartialEq, ::prost::Message)]
387pub struct ListPairsResponse {
388    /// Pair 목록
389    #[prost(message, repeated, tag="1")]
390    pub pairs: ::prost::alloc::vec::Vec<Pair>,
391    /// 다음 페이지 토큰
392    #[prost(string, tag="2")]
393    pub next_page_token: ::prost::alloc::string::String,
394}
395#[allow(clippy::derive_partial_eq_without_eq)]
396#[derive(Clone, PartialEq, ::prost::Message)]
397pub struct CreatePairRequest {
398    /// 생성할 Pair
399    #[prost(message, optional, tag="1")]
400    pub pair: ::core::option::Option<Pair>,
401}
402#[allow(clippy::derive_partial_eq_without_eq)]
403#[derive(Clone, PartialEq, ::prost::Message)]
404pub struct UpdatePairRequest {
405    /// 수정할 Pair
406    #[prost(message, optional, tag="1")]
407    pub pair: ::core::option::Option<Pair>,
408}
409#[allow(clippy::derive_partial_eq_without_eq)]
410#[derive(Clone, PartialEq, ::prost::Message)]
411pub struct DeletePairRequest {
412    /// 리소스 이름 (pairs/{id})
413    #[prost(string, tag="1")]
414    pub pair: ::prost::alloc::string::String,
415}
416// ============================================================================
417// Request / Response Messages — Lifecycle
418// ============================================================================
419
420#[allow(clippy::derive_partial_eq_without_eq)]
421#[derive(Clone, PartialEq, ::prost::Message)]
422pub struct ActivatePairRequest {
423    /// 리소스 이름 (pairs/{id})
424    #[prost(string, tag="1")]
425    pub pair: ::prost::alloc::string::String,
426}
427#[allow(clippy::derive_partial_eq_without_eq)]
428#[derive(Clone, PartialEq, ::prost::Message)]
429pub struct PausePairRequest {
430    /// 리소스 이름 (pairs/{id})
431    #[prost(string, tag="1")]
432    pub pair: ::prost::alloc::string::String,
433}
434// ============================================================================
435// Pair Execution Log (사이클별 기록)
436// ============================================================================
437
438/// 페어 실행 로그 레코드
439#[allow(clippy::derive_partial_eq_without_eq)]
440#[derive(Clone, PartialEq, ::prost::Message)]
441pub struct PairExecutionLog {
442    /// Pair ID
443    #[prost(int32, tag="1")]
444    pub pair_id: i32,
445    /// 시그널 시나리오 (예: "BASE_HIGH", "COUNTER_HIGH")
446    #[prost(string, tag="2")]
447    pub scenario: ::prost::alloc::string::String,
448    /// 실행 결과
449    #[prost(enumeration="PairExecutionOutcome", tag="3")]
450    pub outcome: i32,
451    /// Base 측 주문 ID (발주 성공 시)
452    #[prost(uint64, optional, tag="4")]
453    pub base_order_id: ::core::option::Option<u64>,
454    /// Counter 측 주문 ID (발주 성공 시)
455    #[prost(uint64, optional, tag="5")]
456    pub counter_order_id: ::core::option::Option<u64>,
457    /// Base 참조 가격 (원, raw int64)
458    #[prost(int64, tag="6")]
459    pub base_price: i64,
460    /// Counter 참조 가격 (원, raw int64)
461    #[prost(int64, tag="7")]
462    pub counter_price: i64,
463    /// 발주 시각
464    #[prost(message, optional, tag="9")]
465    pub dispatched_at: ::core::option::Option<super::super::super::google::protobuf::Timestamp>,
466    /// 상세 내용 (오류 메시지 등, optional)
467    #[prost(string, optional, tag="10")]
468    pub detail: ::core::option::Option<::prost::alloc::string::String>,
469    /// Base 슬롯 최종 체결 수량
470    #[prost(int64, tag="11")]
471    pub base_qty: i64,
472    /// Counter 슬롯 최종 체결 수량
473    #[prost(int64, tag="12")]
474    pub counter_qty: i64,
475    /// Base 슬롯 실제 평균 체결가 (원, raw int64; 미체결이면 0)
476    #[prost(int64, tag="13")]
477    pub base_fill_price: i64,
478    /// Counter 슬롯 실제 평균 체결가 (원, raw int64; 미체결이면 0)
479    #[prost(int64, tag="14")]
480    pub counter_fill_price: i64,
481    /// IOC 모드에서 트리거 마켓데이터 수신 시점부터 base 최초 주문 제출 직후까지의 경과 (us)
482    /// 비IOC 모드 또는 미적용 시 0
483    #[prost(int64, tag="15")]
484    pub trigger_to_base_submit_us: i64,
485    /// IOC 모드에서 트리거 마켓데이터 수신 시점부터 counter 최초 주문 제출 직후까지의 경과 (us)
486    /// 비IOC 모드 또는 미적용 시 0
487    #[prost(int64, tag="16")]
488    pub trigger_to_counter_submit_us: i64,
489    /// round-trip exit 슬롯 체결 수량 (CounterIocTpSl 전용; base+counter 실행은 미설정)
490    #[prost(int64, optional, tag="17")]
491    pub exit_qty: ::core::option::Option<i64>,
492    /// exit 슬롯 실제 평균 체결가 (원, raw int64; CounterIocTpSl 전용)
493    #[prost(int64, optional, tag="18")]
494    pub exit_fill_price: ::core::option::Option<i64>,
495    /// exit 슬롯 주문 ID — 정정 추적 시 lineage 최종 ID (CounterIocTpSl 전용)
496    #[prost(uint64, optional, tag="19")]
497    pub exit_order_id: ::core::option::Option<u64>,
498    /// 발주가 대비 체결 평균가 슬리피지(원). side 반영해 불리하게 체결될수록 양수
499    /// (매수=체결−발주, 매도=발주−체결). base 무발주 execution(CounterIocTpSl)에선 base_slippage=0.
500    #[prost(int64, tag="20")]
501    pub base_slippage: i64,
502    /// 발주가 대비 체결 평균가 슬리피지(원). side 반영해 불리하게 체결될수록 양수
503    /// (매수=체결−발주, 매도=발주−체결). base 무발주 execution(CounterIocTpSl)에선 base_slippage=0.
504    #[prost(int64, tag="21")]
505    pub counter_slippage: i64,
506    /// 실행 사이클 상관 id — IOC 엔진의 exchange_time 기반.
507    /// DualSubmit(fire-and-forget)·사이클 시작 전 스킵 경로는 사이클이 없어 미설정(null).
508    #[prost(uint32, optional, tag="22")]
509    pub cycle_id: ::core::option::Option<u32>,
510}
511#[allow(clippy::derive_partial_eq_without_eq)]
512#[derive(Clone, PartialEq, ::prost::Message)]
513pub struct ListPairExecutionLogsRequest {
514    /// 리소스 이름 (pairs/{id})
515    #[prost(string, tag="1")]
516    pub pair: ::prost::alloc::string::String,
517    /// 페이지 크기 (기본: 50, 최대: 200)
518    #[prost(int32, optional, tag="2")]
519    pub page_size: ::core::option::Option<i32>,
520    /// 페이지 토큰 (다음 페이지 조회용)
521    #[prost(string, optional, tag="3")]
522    pub page_token: ::core::option::Option<::prost::alloc::string::String>,
523    /// 정렬 기준 (기본: dispatched_at DESC)
524    #[prost(string, tag="4")]
525    pub order_by: ::prost::alloc::string::String,
526    /// outcome 필터 (UNSPECIFIED = 전체, 그 외 값이면 해당 outcome만 반환)
527    #[prost(enumeration="PairExecutionOutcome", tag="5")]
528    pub outcome: i32,
529    /// 날짜 필터 (YYYY-MM-DD, KST 기준 해당 일자만 조회. 빈 문자열이면 전체)
530    #[prost(string, tag="6")]
531    pub date: ::prost::alloc::string::String,
532    /// 시작 시각 필터 (HH:MM, KST. date와 함께 사용. 빈 문자열이면 해당 일자 00:00부터)
533    #[prost(string, tag="7")]
534    pub start_time: ::prost::alloc::string::String,
535    /// 종료 시각 필터 (HH:MM, KST. date와 함께 사용. 빈 문자열이면 해당 일자 끝까지. 지정 시 해당 분까지 포함)
536    #[prost(string, tag="8")]
537    pub end_time: ::prost::alloc::string::String,
538}
539#[allow(clippy::derive_partial_eq_without_eq)]
540#[derive(Clone, PartialEq, ::prost::Message)]
541pub struct ListPairExecutionLogsResponse {
542    /// 실행 로그 목록
543    #[prost(message, repeated, tag="1")]
544    pub execution_logs: ::prost::alloc::vec::Vec<PairExecutionLog>,
545    /// 다음 페이지 토큰
546    #[prost(string, tag="2")]
547    pub next_page_token: ::prost::alloc::string::String,
548    /// 전체 건수
549    #[prost(int32, tag="3")]
550    pub total_count: i32,
551}
552// ============================================================================
553// Real-time Status Streaming
554// ============================================================================
555
556/// StreamPairStatus 요청
557#[allow(clippy::derive_partial_eq_without_eq)]
558#[derive(Clone, PartialEq, ::prost::Message)]
559pub struct StreamPairStatusRequest {
560    /// 리소스 이름 (pairs/{id})
561    #[prost(string, tag="1")]
562    pub pair: ::prost::alloc::string::String,
563}
564/// StreamPairConfig 요청
565#[allow(clippy::derive_partial_eq_without_eq)]
566#[derive(Clone, PartialEq, ::prost::Message)]
567pub struct StreamPairConfigRequest {
568    /// 리소스 이름 (pairs/{id})
569    #[prost(string, tag="1")]
570    pub pair: ::prost::alloc::string::String,
571}
572/// 페어 단일 슬롯 실시간 상태 스냅샷
573/// (태그 번호는 클라이언트 UI 계약으로 보존됨)
574#[allow(clippy::derive_partial_eq_without_eq)]
575#[derive(Clone, Copy, PartialEq, ::prost::Message)]
576pub struct FillStatus {
577    /// 미체결 수량 (submitted - filled)
578    #[prost(int64, tag="1")]
579    pub unfilled_quantity: i64,
580    /// 누적 체결 수량 (현재 세션 메모리 카운터, 재시작 시 0)
581    #[prost(int64, tag="2")]
582    pub filled_quantity: i64,
583    /// 체결 VWAP (milli-won 단위)
584    #[prost(int64, tag="3")]
585    pub avg_fill_price: i64,
586    /// 누적 발주 수량 (현재 세션 메모리 카운터)
587    #[prost(int64, tag="4")]
588    pub submitted_quantity: i64,
589}
590/// StreamPairStatus 스트리밍 응답 — 페어 상태 스냅샷
591#[allow(clippy::derive_partial_eq_without_eq)]
592#[derive(Clone, PartialEq, ::prost::Message)]
593pub struct PairStatusUpdate {
594    /// 리소스 이름 (pairs/{id})
595    #[prost(string, tag="1")]
596    pub pair: ::prost::alloc::string::String,
597    /// Base 슬롯 상태
598    #[prost(message, optional, tag="2")]
599    pub base: ::core::option::Option<FillStatus>,
600    /// Counter 슬롯 상태
601    #[prost(message, optional, tag="3")]
602    pub counter: ::core::option::Option<FillStatus>,
603    /// 스냅샷 시각
604    #[prost(message, optional, tag="4")]
605    pub updated_at: ::core::option::Option<super::super::super::google::protobuf::Timestamp>,
606    /// exit(청산) 슬롯 상태 — CounterIocTpSl round-trip 전용.
607    /// base+counter 실행(DualSubmit/BaseMakeCounterIoc 등)은 0으로 채워짐.
608    #[prost(message, optional, tag="5")]
609    pub exit: ::core::option::Option<FillStatus>,
610}
611// ============================================================================
612// Pair Statistics
613// ============================================================================
614
615/// GetPairStatistics 요청
616#[allow(clippy::derive_partial_eq_without_eq)]
617#[derive(Clone, PartialEq, ::prost::Message)]
618pub struct GetPairStatisticsRequest {
619    /// 리소스 이름 (pairs/{id})
620    #[prost(string, tag="1")]
621    pub pair: ::prost::alloc::string::String,
622}
623/// 페어 누적 통계 스냅샷 (인메모리 카운터 기반)
624#[allow(clippy::derive_partial_eq_without_eq)]
625#[derive(Clone, PartialEq, ::prost::Message)]
626pub struct PairStatistics {
627    /// 리소스 이름 (pairs/{id})
628    #[prost(string, tag="1")]
629    pub pair: ::prost::alloc::string::String,
630    /// base + counter 누적 발주 수량
631    #[prost(int64, tag="2")]
632    pub total_submitted: i64,
633    /// base + counter 누적 체결 수량
634    #[prost(int64, tag="3")]
635    pub total_filled: i64,
636    /// 발주 성공 횟수
637    #[prost(int64, tag="4")]
638    pub execution_count: i64,
639    /// 실현 손익 (milli-won). 현재 메모리 카운터 기반.
640    #[prost(int64, tag="5")]
641    pub realized_pnl: i64,
642}
643/// 주문 방향
644#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
645#[repr(i32)]
646pub enum PairSide {
647    Unspecified = 0,
648    /// 매수
649    Bid = 1,
650    /// 매도
651    Ask = 2,
652}
653impl PairSide {
654    /// String value of the enum field names used in the ProtoBuf definition.
655    ///
656    /// The values are not transformed in any way and thus are considered stable
657    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
658    pub fn as_str_name(&self) -> &'static str {
659        match self {
660            PairSide::Unspecified => "PAIR_SIDE_UNSPECIFIED",
661            PairSide::Bid => "PAIR_SIDE_BID",
662            PairSide::Ask => "PAIR_SIDE_ASK",
663        }
664    }
665    /// Creates an enum from field names used in the ProtoBuf definition.
666    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
667        match value {
668            "PAIR_SIDE_UNSPECIFIED" => Some(Self::Unspecified),
669            "PAIR_SIDE_BID" => Some(Self::Bid),
670            "PAIR_SIDE_ASK" => Some(Self::Ask),
671            _ => None,
672        }
673    }
674}
675/// 참조 가격 소스 (entry.side 기준 자기/상대 호가).
676#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
677#[repr(i32)]
678pub enum PriceSource {
679    Unspecified = 0,
680    /// 자기호가 (entry.side 와 같은 방향의 1호가). Bid 주문 → bid1, Ask 주문 → ask1.
681    BestMake = 5,
682    /// 상대호가 (entry.side 반대 방향의 1호가). Bid 주문 → ask1, Ask 주문 → bid1.
683    BestTake = 6,
684}
685impl PriceSource {
686    /// String value of the enum field names used in the ProtoBuf definition.
687    ///
688    /// The values are not transformed in any way and thus are considered stable
689    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
690    pub fn as_str_name(&self) -> &'static str {
691        match self {
692            PriceSource::Unspecified => "PRICE_SOURCE_UNSPECIFIED",
693            PriceSource::BestMake => "PRICE_SOURCE_BEST_MAKE",
694            PriceSource::BestTake => "PRICE_SOURCE_BEST_TAKE",
695        }
696    }
697    /// Creates an enum from field names used in the ProtoBuf definition.
698    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
699        match value {
700            "PRICE_SOURCE_UNSPECIFIED" => Some(Self::Unspecified),
701            "PRICE_SOURCE_BEST_MAKE" => Some(Self::BestMake),
702            "PRICE_SOURCE_BEST_TAKE" => Some(Self::BestTake),
703            _ => None,
704        }
705    }
706}
707/// 스프레드 방향 (어느 쪽이 비쌀 때 트리거할지)
708#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
709#[repr(i32)]
710pub enum SpreadDirection {
711    Unspecified = 0,
712    /// Base가 비쌀 때만 트리거
713    BaseHigh = 1,
714    /// Counter가 비쌀 때만 트리거
715    CounterHigh = 2,
716    /// 양방향
717    Both = 3,
718}
719impl SpreadDirection {
720    /// String value of the enum field names used in the ProtoBuf definition.
721    ///
722    /// The values are not transformed in any way and thus are considered stable
723    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
724    pub fn as_str_name(&self) -> &'static str {
725        match self {
726            SpreadDirection::Unspecified => "SPREAD_DIRECTION_UNSPECIFIED",
727            SpreadDirection::BaseHigh => "SPREAD_DIRECTION_BASE_HIGH",
728            SpreadDirection::CounterHigh => "SPREAD_DIRECTION_COUNTER_HIGH",
729            SpreadDirection::Both => "SPREAD_DIRECTION_BOTH",
730        }
731    }
732    /// Creates an enum from field names used in the ProtoBuf definition.
733    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
734        match value {
735            "SPREAD_DIRECTION_UNSPECIFIED" => Some(Self::Unspecified),
736            "SPREAD_DIRECTION_BASE_HIGH" => Some(Self::BaseHigh),
737            "SPREAD_DIRECTION_COUNTER_HIGH" => Some(Self::CounterHigh),
738            "SPREAD_DIRECTION_BOTH" => Some(Self::Both),
739            _ => None,
740        }
741    }
742}
743/// 페어 주문 유형
744#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
745#[repr(i32)]
746pub enum PairOrderType {
747    Unspecified = 0,
748    /// 지정가
749    Limit = 1,
750    /// 시장가
751    Market = 2,
752    /// 공격적 지정가 (상대 최우선 호가 기반)
753    Aggressive = 3,
754}
755impl PairOrderType {
756    /// String value of the enum field names used in the ProtoBuf definition.
757    ///
758    /// The values are not transformed in any way and thus are considered stable
759    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
760    pub fn as_str_name(&self) -> &'static str {
761        match self {
762            PairOrderType::Unspecified => "PAIR_ORDER_TYPE_UNSPECIFIED",
763            PairOrderType::Limit => "PAIR_ORDER_TYPE_LIMIT",
764            PairOrderType::Market => "PAIR_ORDER_TYPE_MARKET",
765            PairOrderType::Aggressive => "PAIR_ORDER_TYPE_AGGRESSIVE",
766        }
767    }
768    /// Creates an enum from field names used in the ProtoBuf definition.
769    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
770        match value {
771            "PAIR_ORDER_TYPE_UNSPECIFIED" => Some(Self::Unspecified),
772            "PAIR_ORDER_TYPE_LIMIT" => Some(Self::Limit),
773            "PAIR_ORDER_TYPE_MARKET" => Some(Self::Market),
774            "PAIR_ORDER_TYPE_AGGRESSIVE" => Some(Self::Aggressive),
775            _ => None,
776        }
777    }
778}
779// ============================================================================
780// Pair Status
781// ============================================================================
782
783/// Pair 상태
784#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
785#[repr(i32)]
786pub enum PairStatus {
787    Unspecified = 0,
788    /// 운영 중 (hot loop 동작)
789    Active = 1,
790    /// 일시 중지
791    Paused = 2,
792    /// 보관 (더 이상 사용 안 함)
793    Archived = 3,
794}
795impl PairStatus {
796    /// String value of the enum field names used in the ProtoBuf definition.
797    ///
798    /// The values are not transformed in any way and thus are considered stable
799    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
800    pub fn as_str_name(&self) -> &'static str {
801        match self {
802            PairStatus::Unspecified => "PAIR_STATUS_UNSPECIFIED",
803            PairStatus::Active => "PAIR_STATUS_ACTIVE",
804            PairStatus::Paused => "PAIR_STATUS_PAUSED",
805            PairStatus::Archived => "PAIR_STATUS_ARCHIVED",
806        }
807    }
808    /// Creates an enum from field names used in the ProtoBuf definition.
809    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
810        match value {
811            "PAIR_STATUS_UNSPECIFIED" => Some(Self::Unspecified),
812            "PAIR_STATUS_ACTIVE" => Some(Self::Active),
813            "PAIR_STATUS_PAUSED" => Some(Self::Paused),
814            "PAIR_STATUS_ARCHIVED" => Some(Self::Archived),
815            _ => None,
816        }
817    }
818}
819// ============================================================================
820// ETF NAV Kind
821// ============================================================================
822
823/// ETF NAV 계산 공식 종류
824#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
825#[repr(i32)]
826pub enum EtfNavKind {
827    Unspecified = 0,
828    /// 지수 추종 헷지 프라이싱: (future + basis) * multiple + cash
829    IndexTrackingHedge = 1,
830    /// 선물 베이시스 기반: unit_delta * future / (prev_index + basis) + cash
831    FutureBasis = 2,
832    /// 레버리지/인버스 ETF용 선물 기반
833    LeverageFuture = 3,
834    /// PDF 구성종목 기반 헷지 프라이싱 (단일 선물 구성종목 전제, flatten 필수)
835    PdfDecomposeHedge = 4,
836}
837impl EtfNavKind {
838    /// String value of the enum field names used in the ProtoBuf definition.
839    ///
840    /// The values are not transformed in any way and thus are considered stable
841    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
842    pub fn as_str_name(&self) -> &'static str {
843        match self {
844            EtfNavKind::Unspecified => "ETF_NAV_KIND_UNSPECIFIED",
845            EtfNavKind::IndexTrackingHedge => "ETF_NAV_KIND_INDEX_TRACKING_HEDGE",
846            EtfNavKind::FutureBasis => "ETF_NAV_KIND_FUTURE_BASIS",
847            EtfNavKind::LeverageFuture => "ETF_NAV_KIND_LEVERAGE_FUTURE",
848            EtfNavKind::PdfDecomposeHedge => "ETF_NAV_KIND_PDF_DECOMPOSE_HEDGE",
849        }
850    }
851    /// Creates an enum from field names used in the ProtoBuf definition.
852    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
853        match value {
854            "ETF_NAV_KIND_UNSPECIFIED" => Some(Self::Unspecified),
855            "ETF_NAV_KIND_INDEX_TRACKING_HEDGE" => Some(Self::IndexTrackingHedge),
856            "ETF_NAV_KIND_FUTURE_BASIS" => Some(Self::FutureBasis),
857            "ETF_NAV_KIND_LEVERAGE_FUTURE" => Some(Self::LeverageFuture),
858            "ETF_NAV_KIND_PDF_DECOMPOSE_HEDGE" => Some(Self::PdfDecomposeHedge),
859            _ => None,
860        }
861    }
862}
863/// 페어 실행 결과
864#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
865#[repr(i32)]
866pub enum PairExecutionOutcome {
867    Unspecified = 0,
868    /// 발주 성공
869    Submitted = 1,
870    /// 사전 검증 실패로 스킵
871    SkippedPreValidation = 2,
872    /// 쿨다운 중 스킵
873    SkippedCooldown = 3,
874    /// 사전 계산 없음으로 스킵
875    SkippedNoPrecomputed = 4,
876    /// 한쪽만 발주 성공 (부분 실패)
877    PartialFailure = 5,
878    /// 전체 실패
879    Failed = 6,
880    /// 체결 수량 없음으로 스킵 (filled qty = 0)
881    SkippedNoFilled = 7,
882}
883impl PairExecutionOutcome {
884    /// String value of the enum field names used in the ProtoBuf definition.
885    ///
886    /// The values are not transformed in any way and thus are considered stable
887    /// (if the ProtoBuf definition does not change) and safe for programmatic use.
888    pub fn as_str_name(&self) -> &'static str {
889        match self {
890            PairExecutionOutcome::Unspecified => "PAIR_EXECUTION_OUTCOME_UNSPECIFIED",
891            PairExecutionOutcome::Submitted => "PAIR_EXECUTION_OUTCOME_SUBMITTED",
892            PairExecutionOutcome::SkippedPreValidation => "PAIR_EXECUTION_OUTCOME_SKIPPED_PRE_VALIDATION",
893            PairExecutionOutcome::SkippedCooldown => "PAIR_EXECUTION_OUTCOME_SKIPPED_COOLDOWN",
894            PairExecutionOutcome::SkippedNoPrecomputed => "PAIR_EXECUTION_OUTCOME_SKIPPED_NO_PRECOMPUTED",
895            PairExecutionOutcome::PartialFailure => "PAIR_EXECUTION_OUTCOME_PARTIAL_FAILURE",
896            PairExecutionOutcome::Failed => "PAIR_EXECUTION_OUTCOME_FAILED",
897            PairExecutionOutcome::SkippedNoFilled => "PAIR_EXECUTION_OUTCOME_SKIPPED_NO_FILLED",
898        }
899    }
900    /// Creates an enum from field names used in the ProtoBuf definition.
901    pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
902        match value {
903            "PAIR_EXECUTION_OUTCOME_UNSPECIFIED" => Some(Self::Unspecified),
904            "PAIR_EXECUTION_OUTCOME_SUBMITTED" => Some(Self::Submitted),
905            "PAIR_EXECUTION_OUTCOME_SKIPPED_PRE_VALIDATION" => Some(Self::SkippedPreValidation),
906            "PAIR_EXECUTION_OUTCOME_SKIPPED_COOLDOWN" => Some(Self::SkippedCooldown),
907            "PAIR_EXECUTION_OUTCOME_SKIPPED_NO_PRECOMPUTED" => Some(Self::SkippedNoPrecomputed),
908            "PAIR_EXECUTION_OUTCOME_PARTIAL_FAILURE" => Some(Self::PartialFailure),
909            "PAIR_EXECUTION_OUTCOME_FAILED" => Some(Self::Failed),
910            "PAIR_EXECUTION_OUTCOME_SKIPPED_NO_FILLED" => Some(Self::SkippedNoFilled),
911            _ => None,
912        }
913    }
914}
915include!("kdo.v1.pair.tonic.rs");
916include!("kdo.v1.pair.serde.rs");
917// @@protoc_insertion_point(module)