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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! # tulip_rs_shared
//!
//! Shared constants and helper functions for candlestick bitmask encoding.
//!
//! This crate provides the single source of truth for bit positions and encoding
//! used by build.rs, proc macros, and runtime code.
//!
//! ## CandleBits layout
//!
//! CandleBits is split into two separate fields:
//!
//! ### `mandatory: u32` (always computed at bar creation, 27 bits used)
//!
//! Tight-packed — no byte-boundary waste:
//!
//! ```text
//! Bits 0– 5 Basic variants (6 types, 1-hot)
//! Bits 6–10 Doji variants (5 types, 1-hot)
//! Bits 11–16 Marubozu variants (6 types, 1-hot)
//! Bits 17–19 SpinningTop variants(3 types, 1-hot)
//! Bit 20 OTHER
//! Bit 21 COLOUR (GREEN=1, RED=0)
//! Bit 22 FILL (HOLLOW=1, FILLED=0)
//! Bit 23 TREND (UP=1, DOWN=0)
//! Bit 24 LINE_HEIGHT (LONG=1, SHORT=0)
//! Bit 25 LOWER_WICK_LT_BODY (lower wick < body height)
//! Bit 26 UPPER_WICK_LT_BODY (upper wick < body height)
//! Bit 27 BODY_HEIGHT (LONG=1, SHORT=0)
//! Bits 28–31 4 spare
//! ```
//!
//! ### `lazy_value / lazy_computed: u16` (computed on demand, all 16 bits used)
//!
//! ```text
//! Bit 0 OPEN_ABOVE_PREV_BODY_MID (my open > prev body midpoint)
//! Bit 1 OPEN_IN_PREV_BODY (my open ∈ prev body)
//! Bit 2 CLOSE_ABOVE_PREV_BODY_MID (my close > prev body midpoint)
//! Bit 3 CLOSE_IN_PREV_BODY (my close ∈ prev body)
//! Bit 4 HIGH_ABOVE_PREV_BODY_MID (my high > prev body midpoint)
//! Bit 5 HIGH_IN_PREV_BODY (my high ∈ prev body)
//! Bit 6 HIGH_IN_PREV_LINE (my high ∈ [prev LOW, prev HIGH])
//! Bit 7 LOW_ABOVE_PREV_BODY_MID (my low > prev body midpoint)
//! Bit 8 LOW_IN_PREV_BODY (my low ∈ prev body)
//! Bit 9 LOW_IN_PREV_LINE (my low ∈ [prev LOW, prev HIGH])
//! Bit 10 I_ENGULF_PREV_BODY (prev open AND prev close both ∈ my body)
//! Bit 11 PREV_HIGH_IN_MY_BODY (prev bar's high ∈ my body)
//! Bit 12 PREV_LOW_IN_MY_BODY (prev bar's low ∈ my body)
//! Bit 13 LOWER_WICK_LONG_2X (lower wick ≥ 2× body height)
//! Bit 14 UPPER_WICK_LONG_2X (upper wick ≥ 2× body height)
//! Bit 15 BODY_GT_PREV_BODY (body height > previous bar's body height)
//! ```
// ============================================================================
// MANDATORY BIT POSITION CONSTANTS (shift amounts into `mandatory: u32`)
// ============================================================================
pub const BASIC_OFFSET: u32 = 0;
pub const DOJI_OFFSET: u32 = 6;
pub const MARUBOZU_OFFSET: u32 = 11;
pub const SPINNING_TOP_OFFSET: u32 = 17;
pub const OTHER_BIT: u32 = 20;
pub const COLOUR_BIT: u32 = 21;
pub const FILL_BIT: u32 = 22;
pub const TREND_BIT: u32 = 23;
pub const LINE_HEIGHT_BIT: u32 = 24;
pub const LOWER_WICK_LT_BODY_BIT: u32 = 25;
pub const UPPER_WICK_LT_BODY_BIT: u32 = 26;
pub const BODY_HEIGHT_BIT: u32 = 27;
// ============================================================================
// LAZY BIT POSITION CONSTANTS (shift amounts into `lazy_value / lazy_computed: u16`)
// ============================================================================
pub const OPEN_ABOVE_PREV_BODY_MID_BIT: u32 = 0;
pub const OPEN_IN_PREV_BODY_BIT: u32 = 1;
pub const CLOSE_ABOVE_PREV_BODY_MID_BIT: u32 = 2;
pub const CLOSE_IN_PREV_BODY_BIT: u32 = 3;
pub const HIGH_ABOVE_PREV_BODY_MID_BIT: u32 = 4;
pub const HIGH_IN_PREV_BODY_BIT: u32 = 5;
pub const HIGH_IN_PREV_LINE_BIT: u32 = 6;
pub const LOW_ABOVE_PREV_BODY_MID_BIT: u32 = 7;
pub const LOW_IN_PREV_BODY_BIT: u32 = 8;
pub const LOW_IN_PREV_LINE_BIT: u32 = 9;
pub const I_ENGULF_PREV_BODY_BIT: u32 = 10;
pub const PREV_HIGH_IN_MY_BODY_BIT: u32 = 11;
pub const PREV_LOW_IN_MY_BODY_BIT: u32 = 12;
pub const LOWER_WICK_LONG_2X_BIT: u32 = 13;
pub const UPPER_WICK_LONG_2X_BIT: u32 = 14;
pub const BODY_GT_PREV_BODY_BIT: u32 = 15;
// ============================================================================
// MANDATORY BITMASK CONSTANTS (u32)
// ============================================================================
pub const BASIC_MASK: u32 = 0x3F; // bits 0–5
pub const DOJI_MASK: u32 = 0x1F << DOJI_OFFSET; // bits 6–10
pub const MARUBOZU_MASK: u32 = 0x3F << MARUBOZU_OFFSET; // bits 11–16
pub const SPINNING_TOP_MASK: u32 = 0x07 << SPINNING_TOP_OFFSET; // bits 17–19
pub const CANDLE_TYPE_MASK: u32 =
BASIC_MASK | DOJI_MASK | MARUBOZU_MASK | SPINNING_TOP_MASK | ;
/// Mask covering all compulsory mandatory bits
pub const COMPULSORY_MASK: u32 = CANDLE_TYPE_MASK
|
|
|
|
|
|
| ;
// ============================================================================
// LAZY BITMASK CONSTANTS (u16)
// ============================================================================
/// Mask covering all currently-defined lazy bits
pub const LAZY_MASK: u16 = 0xFFFF; // 16 bits used (bits 0–15)
// ============================================================================
// HELPER FUNCTIONS — Variant Encoding (produce u32 mandatory field bits)
// ============================================================================
/// Convert a variant discriminant to a 1-hot bit value
pub const
pub const
pub const
pub const
pub const
// ============================================================================
// MANDATORY BIT VALUE CONSTANTS (u32)
// ============================================================================
// === Basic Candle Types (bits 0–5) ===
pub const SHORT_WHITE_CANDLE: u32 = encode_basic_variant;
pub const WHITE_CANDLE: u32 = encode_basic_variant;
pub const LONG_WHITE_CANDLE: u32 = encode_basic_variant;
pub const SHORT_BLACK_CANDLE: u32 = encode_basic_variant;
pub const BLACK_CANDLE: u32 = encode_basic_variant;
pub const LONG_BLACK_CANDLE: u32 = encode_basic_variant;
// === Doji Types (bits 6–10) ===
pub const DOJI: u32 = encode_doji_variant;
pub const LONG_LEGGED_DOJI: u32 = encode_doji_variant;
pub const DRAGONFLY_DOJI: u32 = encode_doji_variant;
pub const GRAVESTONE_DOJI: u32 = encode_doji_variant;
pub const FOUR_PRICE_DOJI: u32 = encode_doji_variant;
// === Marubozu Types (bits 11–16) ===
pub const WHITE_MARUBOZU: u32 = encode_marubozu_variant;
pub const OPENING_WHITE_MARUBOZU: u32 = encode_marubozu_variant;
pub const CLOSING_WHITE_MARUBOZU: u32 = encode_marubozu_variant;
pub const BLACK_MARUBOZU: u32 = encode_marubozu_variant;
pub const OPENING_BLACK_MARUBOZU: u32 = encode_marubozu_variant;
pub const CLOSING_BLACK_MARUBOZU: u32 = encode_marubozu_variant;
// === SpinningTop Types (bits 17–19) ===
pub const WHITE_SPINNING_TOP: u32 = encode_spinning_top_variant;
pub const BLACK_SPINNING_TOP: u32 = encode_spinning_top_variant;
pub const HIGH_WAVE: u32 = encode_spinning_top_variant;
// === Other (bit 20) ===
pub const OTHER: u32 = 1u32 << OTHER_BIT;
// === Colour (bit 21) ===
pub const COLOUR_GREEN: u32 = 1u32 << COLOUR_BIT;
pub const COLOUR_RED: u32 = 0;
// === Fill (bit 22) ===
pub const FILL_HOLLOW: u32 = 1u32 << FILL_BIT;
pub const FILL_FILLED: u32 = 0;
// === Trend (bit 23) ===
pub const TREND_UP: u32 = 1u32 << TREND_BIT;
pub const TREND_DOWN: u32 = 0;
// === Line Height (bit 24) ===
pub const LINE_HEIGHT_LONG: u32 = 1u32 << LINE_HEIGHT_BIT;
pub const LINE_HEIGHT_SHORT: u32 = 0;
// === Lower/Upper Wick vs Body (bits 25–26) ===
pub const LOWER_WICK_LT_BODY: u32 = 1u32 << LOWER_WICK_LT_BODY_BIT;
pub const UPPER_WICK_LT_BODY: u32 = 1u32 << UPPER_WICK_LT_BODY_BIT;
// ============================================================================
// LAZY BIT VALUE CONSTANTS (u16)
// ============================================================================
// === Body Height (mandatory bit 27) ===
pub const BODY_HEIGHT_LONG: u32 = 1u32 << BODY_HEIGHT_BIT;
pub const BODY_HEIGHT_SHORT: u32 = 0;
// === Open vs Prev Body (lazy bits 1–2) ===
pub const OPEN_ABOVE_PREV_BODY_MID: u16 = 1u16 << OPEN_ABOVE_PREV_BODY_MID_BIT;
pub const OPEN_IN_PREV_BODY: u16 = 1u16 << OPEN_IN_PREV_BODY_BIT;
// === Close vs Prev Body (lazy bits 3–4) ===
pub const CLOSE_ABOVE_PREV_BODY_MID: u16 = 1u16 << CLOSE_ABOVE_PREV_BODY_MID_BIT;
pub const CLOSE_IN_PREV_BODY: u16 = 1u16 << CLOSE_IN_PREV_BODY_BIT;
// === High vs Prev Body/Line (lazy bits 5–7) ===
pub const HIGH_ABOVE_PREV_BODY_MID: u16 = 1u16 << HIGH_ABOVE_PREV_BODY_MID_BIT;
pub const HIGH_IN_PREV_BODY: u16 = 1u16 << HIGH_IN_PREV_BODY_BIT;
pub const HIGH_IN_PREV_LINE: u16 = 1u16 << HIGH_IN_PREV_LINE_BIT;
// === Low vs Prev Body/Line (lazy bits 8–10) ===
pub const LOW_ABOVE_PREV_BODY_MID: u16 = 1u16 << LOW_ABOVE_PREV_BODY_MID_BIT;
pub const LOW_IN_PREV_BODY: u16 = 1u16 << LOW_IN_PREV_BODY_BIT;
pub const LOW_IN_PREV_LINE: u16 = 1u16 << LOW_IN_PREV_LINE_BIT;
// === Engulfment relationships (lazy bits 11–13) ===
pub const I_ENGULF_PREV_BODY: u16 = 1u16 << I_ENGULF_PREV_BODY_BIT;
pub const PREV_HIGH_IN_MY_BODY: u16 = 1u16 << PREV_HIGH_IN_MY_BODY_BIT;
pub const PREV_LOW_IN_MY_BODY: u16 = 1u16 << PREV_LOW_IN_MY_BODY_BIT;
// === Wick length vs body (lazy bits 14–15) ===
pub const LOWER_WICK_LONG_2X: u16 = 1u16 << LOWER_WICK_LONG_2X_BIT;
pub const UPPER_WICK_LONG_2X: u16 = 1u16 << UPPER_WICK_LONG_2X_BIT;
pub const BODY_GT_PREV_BODY: u16 = 1u16 << BODY_GT_PREV_BODY_BIT;
// ============================================================================
// CDL_GAP RETURN CODE CONSTANTS (i8)
// ============================================================================
//
// Returned by `cdl_gap(prev, current)` to describe the gap relationship
// between two consecutive candles. Used by:
// - Runtime pattern calc functions to interpret cdl_gap results.
// - PatternMask::with_body_gap / with_wick_gap setters.
// - The pattern_template proc macro (via tulip_rs_macros).
/// No gap — bodies overlap.
pub const NO_GAP: i8 = 0;
/// Current body is entirely above prev body; wicks may still overlap.
pub const BODY_GAP_UP: i8 = 1;
/// Current body is entirely below prev body; wicks may still overlap.
pub const BODY_GAP_DOWN: i8 = -1;
/// Entire current candle (including wicks) is above prev candle.
pub const WICK_GAP_UP: i8 = 2;
/// Entire current candle (including wicks) is below prev candle.
pub const WICK_GAP_DOWN: i8 = -2;
// ============================================================================
// ENGULF KIND CONSTANTS (i8)
// ============================================================================
//
// Used by `PatternMask::with_engulf_prev` / `with_inside_prev` and the
// `pattern_template` proc macro to specify the engulf type.
/// Body-only engulf — current/previous body spans the target body.
pub const ENGULF_BODY: i8 = 1;
/// Full-line engulf — current/previous body spans the target body AND wicks.
pub const ENGULF_LINE: i8 = 2;