shape-runtime 0.3.2

Bytecode compiler, builtins, and runtime infrastructure for Shape
Documentation
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
// Shape Standard Library - Candlestick Patterns
// This module provides common candlestick pattern definitions

module patterns {
    // Import types for pattern analysis
    from std::finance::types use { Candle };

    // Single candle patterns

    // Note: hammer pattern is defined in patterns/hammer.shape

    // Note: shooting_star pattern is defined in patterns/shooting_star.shape

    // Note: doji pattern is defined in patterns/doji.shape

    pub fn dragonfly_doji(candle: Candle) -> boolean {
        let body = abs(candle.close - candle.open);
        let range = candle.high - candle.low;
        let lower_shadow = min(candle.open, candle.close) - candle.low;
        let upper_shadow = candle.high - max(candle.open, candle.close);

        return body < range * 0.1 and
               lower_shadow > range * 0.7 and
               upper_shadow < range * 0.1;
    }

    pub fn gravestone_doji(candle: Candle) -> boolean {
        let body = abs(candle.close - candle.open);
        let range = candle.high - candle.low;
        let upper_shadow = candle.high - max(candle.open, candle.close);
        let lower_shadow = min(candle.open, candle.close) - candle.low;

        return body < range * 0.1 and
               upper_shadow > range * 0.7 and
               lower_shadow < range * 0.1;
    }

    pub fn marubozu(candle: Candle) -> boolean {
        let body = abs(candle.close - candle.open);
        let range = candle.high - candle.low;

        return body > range * 0.95;
    }

    pub fn spinning_top(candle: Candle) -> boolean {
        let body = abs(candle.close - candle.open);
        let range = candle.high - candle.low;
        let upper_shadow = candle.high - max(candle.open, candle.close);
        let lower_shadow = min(candle.open, candle.close) - candle.low;

        return body < range * 0.4 and
               upper_shadow > body * 0.5 and
               lower_shadow > body * 0.5;
    }

    // Two candle patterns

    // Note: bullish_engulfing pattern is defined in patterns/bullish_engulfing.shape

    // Note: bearish_engulfing pattern is defined in patterns/bearish_engulfing.shape

    pub fn tweezer_top(candle: Candle) -> boolean {
        return candle[-1].high ~= candle[0].high and    // Same highs (fuzzy match)
               candle[-1].close > candle[-1].open and   // First is bullish
               candle[0].close < candle[0].open;        // Second is bearish
    }

    pub fn tweezer_bottom(candle: Candle) -> boolean {
        return candle[-1].low ~= candle[0].low and      // Same lows (fuzzy match)
               candle[-1].close < candle[-1].open and   // First is bearish
               candle[0].close > candle[0].open;        // Second is bullish
    }

    pub fn piercing_line(candle: Candle) -> boolean {
        return candle[-1].close < candle[-1].open and   // Previous is bearish
               candle[0].close > candle[0].open and     // Current is bullish
               candle[0].open < candle[-1].low and      // Opens below previous low
               candle[0].close > candle[-1].open - ((candle[-1].open - candle[-1].close) * 0.5) and
               candle[0].close < candle[-1].open;       // Closes within previous body
    }

    pub fn dark_cloud_cover(candle: Candle) -> boolean {
        return candle[-1].close > candle[-1].open and   // Previous is bullish
               candle[0].close < candle[0].open and     // Current is bearish
               candle[0].open > candle[-1].high and     // Opens above previous high
               candle[0].close < candle[-1].close + ((candle[-1].close - candle[-1].open) * 0.5) and
               candle[0].close > candle[-1].open;       // Closes within previous body
    }

    // Three candle patterns

    pub fn morning_star(candle: Candle) -> boolean {
        let first_range = candle[-2].high - candle[-2].low;
        let first_body = abs(candle[-2].close - candle[-2].open);
        let star_body = abs(candle[-1].close - candle[-1].open);
        let third_body = abs(candle[0].close - candle[0].open);
        let midpoint = candle[-2].close + (candle[-2].open - candle[-2].close) / 2.0;

        // First candle: long bearish
        return candle[-2].close < candle[-2].open and
               first_body > first_range * 0.5 and
               // Second candle: small body (star)
               star_body < first_body * 0.3 and
               candle[-1].high < candle[-2].low and     // Gap down
               // Third candle: long bullish
               candle[0].close > candle[0].open and
               third_body > first_range * 0.5 and
               candle[0].close > midpoint;              // Closes at least halfway up first candle
    }

    pub fn evening_star(candle: Candle) -> boolean {
        let first_range = candle[-2].high - candle[-2].low;
        let first_body = abs(candle[-2].close - candle[-2].open);
        let star_body = abs(candle[-1].close - candle[-1].open);
        let third_body = abs(candle[0].close - candle[0].open);
        let midpoint = candle[-2].open + (candle[-2].close - candle[-2].open) / 2.0;

        // First candle: long bullish
        return candle[-2].close > candle[-2].open and
               first_body > first_range * 0.5 and
               // Second candle: small body (star)
               star_body < first_body * 0.3 and
               candle[-1].low > candle[-2].high and     // Gap up
               // Third candle: long bearish
               candle[0].close < candle[0].open and
               third_body > first_range * 0.5 and
               candle[0].close < midpoint;              // Closes at least halfway down first candle
    }

    pub fn three_white_soldiers(candle: Candle) -> boolean {
        // Three consecutive bullish candles
        return candle[-2].close > candle[-2].open and
               candle[-1].close > candle[-1].open and
               candle[0].close > candle[0].open and
               // Each opens within previous body
               candle[-1].open > candle[-2].open and
               candle[-1].open < candle[-2].close and
               candle[0].open > candle[-1].open and
               candle[0].open < candle[-1].close and
               // Progressive higher closes
               candle[-1].close > candle[-2].close and
               candle[0].close > candle[-1].close;
    }

    pub fn three_black_crows(candle: Candle) -> boolean {
        // Three consecutive bearish candles
        return candle[-2].close < candle[-2].open and
               candle[-1].close < candle[-1].open and
               candle[0].close < candle[0].open and
               // Each opens within previous body
               candle[-1].open < candle[-2].open and
               candle[-1].open > candle[-2].close and
               candle[0].open < candle[-1].open and
               candle[0].open > candle[-1].close and
               // Progressive lower closes
               candle[-1].close < candle[-2].close and
               candle[0].close < candle[-1].close;
    }

    // Additional single candle patterns

    pub fn bullish_marubozu(candle: Candle) -> boolean {
        let body = candle.close - candle.open;
        let range = candle.high - candle.low;

        return body > 0 and                    // Bullish
               body > range * 0.95 and         // Almost no wicks
               candle.open ~= candle.low and   // Opens at low
               candle.close ~= candle.high;    // Closes at high
    }

    pub fn bearish_marubozu(candle: Candle) -> boolean {
        let body = candle.open - candle.close;
        let range = candle.high - candle.low;

        return body > 0 and                    // Bearish
               body > range * 0.95 and         // Almost no wicks
               candle.open ~= candle.high and  // Opens at high
               candle.close ~= candle.low;     // Closes at low
    }

    pub fn long_legged_doji(candle: Candle) -> boolean {
        let body = abs(candle.close - candle.open);
        let range = candle.high - candle.low;
        let upper_shadow = candle.high - max(candle.open, candle.close);
        let lower_shadow = min(candle.open, candle.close) - candle.low;

        return body < range * 0.1 and          // Very small body
               upper_shadow > range * 0.4 and  // Long upper shadow
               lower_shadow > range * 0.4;     // Long lower shadow
    }

    pub fn bullish_belt_hold(candle: Candle) -> boolean {
        let body = candle.close - candle.open;
        let range = candle.high - candle.low;

        return body > range * 0.7 and          // Large bullish body
               candle.open ~= candle.low and   // Opens at low
               candle[-1].close < candle[-1].open;  // Previous bearish
    }

    pub fn bearish_belt_hold(candle: Candle) -> boolean {
        let body = candle.open - candle.close;
        let range = candle.high - candle.low;

        return body > range * 0.7 and          // Large bearish body
               candle.open ~= candle.high and  // Opens at high
               candle[-1].close > candle[-1].open;  // Previous bullish
    }

    // Additional two candle patterns

    pub fn harami(candle: Candle) -> boolean {
        let prev_body = abs(candle[-1].close - candle[-1].open);
        let curr_body = abs(candle[0].close - candle[0].open);

        // Current candle body is inside previous candle body
        return max(candle[0].open, candle[0].close) < max(candle[-1].open, candle[-1].close) and
               min(candle[0].open, candle[0].close) > min(candle[-1].open, candle[-1].close) and
               curr_body < prev_body * 0.5;    // Current body is small
    }

    pub fn bullish_harami(candle: Candle) -> boolean {
        // Harami pattern with bullish implications
        return harami(candle) and
               candle[-1].close < candle[-1].open and  // Previous bearish
               candle[0].close > candle[0].open;       // Current bullish
    }

    pub fn bearish_harami(candle: Candle) -> boolean {
        // Harami pattern with bearish implications
        return harami(candle) and
               candle[-1].close > candle[-1].open and  // Previous bullish
               candle[0].close < candle[0].open;       // Current bearish
    }

    pub fn on_neck_line(candle: Candle) -> boolean {
        return candle[-1].close < candle[-1].open and   // Previous bearish
               candle[0].close > candle[0].open and     // Current bullish
               candle[0].open < candle[-1].low and      // Opens below previous low
               candle[0].close ~= candle[-1].low;       // Closes near previous low
    }

    pub fn in_neck_line(candle: Candle) -> boolean {
        return candle[-1].close < candle[-1].open and   // Previous bearish
               candle[0].close > candle[0].open and     // Current bullish
               candle[0].open < candle[-1].low and      // Opens below previous low
               candle[0].close ~= candle[-1].close;     // Closes near previous close
    }

    pub fn thrusting_pattern(candle: Candle) -> boolean {
        return candle[-1].close < candle[-1].open and   // Previous bearish
               candle[0].close > candle[0].open and     // Current bullish
               candle[0].open < candle[-1].low and      // Opens below previous low
               candle[0].close > candle[-1].close and   // Closes above previous close
               candle[0].close < candle[-1].open - (candle[-1].open - candle[-1].close) * 0.5;
    }

    // Additional three candle patterns

    pub fn abandoned_baby_bullish(candle: Candle) -> boolean {
        // First candle: bearish
        return candle[-2].close < candle[-2].open and
               // Second candle: doji with gap down
               abs(candle[-1].close - candle[-1].open) < (candle[-1].high - candle[-1].low) * 0.1 and
               candle[-1].high < candle[-2].low and
               // Third candle: bullish with gap up
               candle[0].close > candle[0].open and
               candle[0].low > candle[-1].high;
    }

    pub fn abandoned_baby_bearish(candle: Candle) -> boolean {
        // First candle: bullish
        return candle[-2].close > candle[-2].open and
               // Second candle: doji with gap up
               abs(candle[-1].close - candle[-1].open) < (candle[-1].high - candle[-1].low) * 0.1 and
               candle[-1].low > candle[-2].high and
               // Third candle: bearish with gap down
               candle[0].close < candle[0].open and
               candle[0].high < candle[-1].low;
    }

    pub fn three_inside_up(candle: Candle) -> boolean {
        // Bullish harami followed by higher close
        return candle[-2].close < candle[-2].open and                         // First bearish
               max(candle[-1].open, candle[-1].close) < candle[-2].open and  // Second inside first
               min(candle[-1].open, candle[-1].close) > candle[-2].close and
               candle[-1].close > candle[-1].open and                        // Second bullish
               candle[0].close > candle[0].open and                          // Third bullish
               candle[0].close > candle[-1].close;                           // Third closes higher
    }

    pub fn three_inside_down(candle: Candle) -> boolean {
        // Bearish harami followed by lower close
        return candle[-2].close > candle[-2].open and                         // First bullish
               max(candle[-1].open, candle[-1].close) < candle[-2].close and // Second inside first
               min(candle[-1].open, candle[-1].close) > candle[-2].open and
               candle[-1].close < candle[-1].open and                        // Second bearish
               candle[0].close < candle[0].open and                          // Third bearish
               candle[0].close < candle[-1].close;                           // Third closes lower
    }

    pub fn three_outside_up(candle: Candle) -> boolean {
        // Bullish engulfing followed by higher close
        return candle[-2].close < candle[-2].open and   // First bearish
               candle[-1].close > candle[-1].open and   // Second bullish
               candle[-1].open < candle[-2].close and   // Engulfs first
               candle[-1].close > candle[-2].open and
               candle[0].close > candle[0].open and     // Third bullish
               candle[0].close > candle[-1].close;      // Third closes higher
    }

    pub fn three_outside_down(candle: Candle) -> boolean {
        // Bearish engulfing followed by lower close
        return candle[-2].close > candle[-2].open and   // First bullish
               candle[-1].close < candle[-1].open and   // Second bearish
               candle[-1].open > candle[-2].close and   // Engulfs first
               candle[-1].close < candle[-2].open and
               candle[0].close < candle[0].open and     // Third bearish
               candle[0].close < candle[-1].close;      // Third closes lower
    }

    pub fn bullish_tri_star(candle: Candle) -> boolean {
        // Three dojis with the middle one gapped
        return abs(candle[-2].close - candle[-2].open) < (candle[-2].high - candle[-2].low) * 0.1 and
               abs(candle[-1].close - candle[-1].open) < (candle[-1].high - candle[-1].low) * 0.1 and
               abs(candle[0].close - candle[0].open) < (candle[0].high - candle[0].low) * 0.1 and
               candle[-1].low > candle[-2].high and    // Middle gaps up
               candle[0].low > candle[-1].high;        // Third gaps up
    }

    pub fn bearish_tri_star(candle: Candle) -> boolean {
        // Three dojis with the middle one gapped
        return abs(candle[-2].close - candle[-2].open) < (candle[-2].high - candle[-2].low) * 0.1 and
               abs(candle[-1].close - candle[-1].open) < (candle[-1].high - candle[-1].low) * 0.1 and
               abs(candle[0].close - candle[0].open) < (candle[0].high - candle[0].low) * 0.1 and
               candle[-1].high < candle[-2].low and    // Middle gaps down
               candle[0].high < candle[-1].low;        // Third gaps down
    }

    // Pattern helper functions

    pub fn is_bullish(candle: Candle) -> boolean {
        return candle[0].close > candle[0].open;
    }

    pub fn is_bearish(candle: Candle) -> boolean {
        return candle[0].close < candle[0].open;
    }

    pub fn body_size(candle: Candle) -> number {
        return abs(candle[0].close - candle[0].open);
    }

    pub fn upper_shadow_size(candle: Candle) -> number {
        return candle[0].high - max(candle[0].open, candle[0].close);
    }

    pub fn lower_shadow_size(candle: Candle) -> number {
        return min(candle[0].open, candle[0].close) - candle[0].low;
    }

    pub fn is_gap_up(candle: Candle) -> boolean {
        return candle[0].low > candle[-1].high;
    }

    pub fn is_gap_down(candle: Candle) -> boolean {
        return candle[0].high < candle[-1].low;
    }

    // Pattern strength assessment

    pub fn pattern_strength(candle: Candle, pattern_name: string) -> number {
        // Returns a strength score 0-100 for the pattern
        let strength = 0;

        // Add volume confirmation
        let avg_volume = sma_volume(candle, 20);
        if (candle[0].volume > avg_volume * 1.5) {
            strength = strength + 20;
        }

        // Add trend confirmation using short vs long lookback averages
        let short_avg = sma_close(candle, 20);
        let long_avg = sma_close(candle, 50);

        if (pattern_name == "hammer" or pattern_name == "bullish_engulfing" or pattern_name == "morning_star") {
            // Bullish patterns stronger in downtrend
            if (short_avg < long_avg) {
                strength = strength + 30;
            }
        } else if (pattern_name == "shooting_star" or pattern_name == "bearish_engulfing" or pattern_name == "evening_star") {
            // Bearish patterns stronger in uptrend
            if (short_avg > long_avg) {
                strength = strength + 30;
            }
        }

        // Add location confirmation (support/resistance)
        // This would need more complex logic in practice
        strength = strength + 50;

        return min(strength, 100);
    }

    // Private helper for close price SMA over candle lookback
    fn sma_close(candle: Candle, period: number) -> number {
        let sum = 0;
        for i in range(period) {
            sum = sum + candle[-i].close;
        }
        return sum / period;
    }

    // Private helper for volume SMA over candle lookback
    fn sma_volume(candle: Candle, period: number) -> number {
        let sum = 0;
        for i in range(period) {
            sum = sum + candle[-i].volume;
        }
        return sum / period;
    }
}