ICT Propulsion Block

Parameters: swing_length = 3 | mitigation_price = close

Overview

ICT Propulsion Block tracks the most recent bullish and bearish order-block structure on every bar. For each side it emits the current block high and low, whether the current block is a regular order block or a propulsion block, whether that block is still active, whether it has been mitigated, and whether a new block was created on the current bar.

The implementation works on full OHLC input, validates bars with finite open, high, low, and close values, and resets its internal swing and block state when it encounters an invalid bar. Bullish and bearish block outputs are emitted as twelve parallel series, which makes the indicator suitable for chart overlays, screening, and batch parameter sweeps.

Defaults: swing_length = 3 and mitigation_price = "close".

Implementation Examples

Calculate bullish and bearish propulsion-block state from OHLC slices or from the shared Candles type:

use vector_ta::indicators::ict_propulsion_block::{
    ict_propulsion_block, IctPropulsionBlockInput, IctPropulsionBlockMitigationPrice,
    IctPropulsionBlockParams,
};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};

let open = vec![100.0, 101.0, 100.5, 102.0, 103.0];
let high = vec![101.0, 102.0, 101.5, 103.5, 104.0];
let low = vec![99.5, 100.0, 99.8, 101.5, 102.0];
let close = vec![100.8, 100.4, 101.2, 103.2, 103.6];

let input = IctPropulsionBlockInput::from_slices(
    &open,
    &high,
    &low,
    &close,
    IctPropulsionBlockParams {
        swing_length: Some(3),
        mitigation_price: Some(IctPropulsionBlockMitigationPrice::Close),
    },
);
let out = ict_propulsion_block(&input)?;

// Twelve aligned output series
println!("{:?}", out.bullish_high);
println!("{:?}", out.bullish_kind);
println!("{:?}", out.bearish_mitigated);

// Candles helper with default params
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let out = ict_propulsion_block(&IctPropulsionBlockInput::with_default_candles(&candles))?;

API Reference

Input Methods
IctPropulsionBlockInput::from_candles(&Candles, IctPropulsionBlockParams) -> IctPropulsionBlockInput
IctPropulsionBlockInput::from_slices(&[f64], &[f64], &[f64], &[f64], IctPropulsionBlockParams) -> IctPropulsionBlockInput
IctPropulsionBlockInput::with_default_candles(&Candles) -> IctPropulsionBlockInput
Parameters Structure
pub enum IctPropulsionBlockMitigationPrice {
    Close,
    Wick,
}

pub struct IctPropulsionBlockParams {
    pub swing_length: Option<usize>,
    pub mitigation_price: Option<IctPropulsionBlockMitigationPrice>,
}

// Defaults
// swing_length = 3
// mitigation_price = Close
Output Structure
pub struct IctPropulsionBlockOutput {
    pub bullish_high: Vec<f64>,
    pub bullish_low: Vec<f64>,
    pub bullish_kind: Vec<f64>,
    pub bullish_active: Vec<f64>,
    pub bullish_mitigated: Vec<f64>,
    pub bullish_new: Vec<f64>,
    pub bearish_high: Vec<f64>,
    pub bearish_low: Vec<f64>,
    pub bearish_kind: Vec<f64>,
    pub bearish_active: Vec<f64>,
    pub bearish_mitigated: Vec<f64>,
    pub bearish_new: Vec<f64>,
}

// kind values:
// 0.0 = no current block snapshot
// 1.0 = regular order block
// 2.0 = propulsion block

// active / mitigated / new values:
// 1.0 = true
// 0.0 = false
Builders, Batch Range, and Stream
  • IctPropulsionBlockBuilder::new() exposes swing_length(), mitigation_price(), kernel(), apply(), apply_slices(), and into_stream().
  • IctPropulsionBlockStream::update(open, high, low, close) returns Option<(f64, ..., f64)> with 12 values in the same order as the output struct.
  • IctPropulsionBlockBatchRange sweeps swing_length: (start, end, step) and mitigation_price: (include_close, include_wick).
  • IctPropulsionBlockBatchBuilder::new() exposes swing_length_range(), mitigation_price_toggle(), kernel(), apply(), and apply_slices().
  • Batch output adds combos, rows, and cols; each output vector is flattened row-major with rows * cols entries.
Validation, State, and NaNs
  • All four inputs must be non-empty and have matching lengths; otherwise the indicator returns IctPropulsionBlockError::EmptyInputData or DataLengthMismatch.
  • A valid bar requires finite open, high, low, and close values with high >= low.
  • swing_length must be greater than zero.
  • If every bar is invalid, the indicator returns IctPropulsionBlockError::AllValuesNaN.
  • When an invalid bar appears during calculation, that bar is written as NaN across all twelve outputs and the internal swing/block state is reset.
  • mitigation_price = close uses close-through-block checks for mitigation; wick uses high/low wick breaches.
Error Handling
use vector_ta::indicators::ict_propulsion_block::IctPropulsionBlockError;

match ict_propulsion_block(&input) {
    Ok(out) => {
        println!("{:?}", out.bullish_high);
    }
    Err(IctPropulsionBlockError::EmptyInputData) =>
        eprintln!("input slices cannot be empty"),
    Err(IctPropulsionBlockError::DataLengthMismatch { open, high, low, close }) =>
        eprintln!("length mismatch: open={open}, high={high}, low={low}, close={close}"),
    Err(IctPropulsionBlockError::InvalidSwingLength { swing_length }) =>
        eprintln!("invalid swing length: {}", swing_length),
    Err(IctPropulsionBlockError::InvalidMitigationPrice { mitigation_price }) =>
        eprintln!("invalid mitigation price: {}", mitigation_price),
    Err(IctPropulsionBlockError::AllValuesNaN) =>
        eprintln!("all bars were invalid"),
    Err(IctPropulsionBlockError::InvalidKernelForBatch(kernel)) =>
        eprintln!("invalid batch kernel: {:?}", kernel),
    Err(e) => eprintln!("ICT Propulsion Block error: {}", e),
}

Python Bindings

Basic Usage

Python exposes ict_propulsion_block(open, high, low, close, swing_length=3, mitigation_price="close", kernel=None) and returns twelve NumPy arrays in the same field order as the Rust output struct.

import numpy as np
from vector_ta import ict_propulsion_block

open_ = np.array([100.0, 101.0, 100.5, 102.0, 103.0], dtype=np.float64)
high = np.array([101.0, 102.0, 101.5, 103.5, 104.0], dtype=np.float64)
low = np.array([99.5, 100.0, 99.8, 101.5, 102.0], dtype=np.float64)
close = np.array([100.8, 100.4, 101.2, 103.2, 103.6], dtype=np.float64)

(
    bullish_high,
    bullish_low,
    bullish_kind,
    bullish_active,
    bullish_mitigated,
    bullish_new,
    bearish_high,
    bearish_low,
    bearish_kind,
    bearish_active,
    bearish_mitigated,
    bearish_new,
) = ict_propulsion_block(open_, high, low, close, swing_length=3, mitigation_price="close")
Streaming Real-time Updates

The Python stream class is IctPropulsionBlockStream. Its update() method returns the current twelve-field snapshot or None for an invalid bar.

from vector_ta import IctPropulsionBlockStream

stream = IctPropulsionBlockStream(swing_length=3, mitigation_price="wick")

for open_, high, low, close in feed:
    out = stream.update(open_, high, low, close)
    if out is not None:
        bullish_high = out[0]
        bearish_new = out[11]
        print(bullish_high, bearish_new)
Batch Processing

ict_propulsion_block_batch returns a dict with twelve 2D output arrays plus grid metadata.

import numpy as np
from vector_ta import ict_propulsion_block_batch

result = ict_propulsion_block_batch(
    open_,
    high,
    low,
    close,
    swing_length_range=(3, 7, 2),
    mitigation_price_toggle=(True, True),
)

bullish_high = result["bullish_high"]
bearish_kind = result["bearish_kind"]
rows = result["rows"]
cols = result["cols"]
swing_lengths = result["swing_lengths"]
mitigation_prices = result["mitigation_prices"]

JavaScript/WASM Bindings

Basic Usage

WASM exports ict_propulsion_block(open, high, low, close, swing_length, mitigation_price) and returns an object with the same twelve output keys as Rust.

import { ict_propulsion_block } from 'vectorta-wasm';

const open = new Float64Array([100.0, 101.0, 100.5, 102.0, 103.0]);
const high = new Float64Array([101.0, 102.0, 101.5, 103.5, 104.0]);
const low = new Float64Array([99.5, 100.0, 99.8, 101.5, 102.0]);
const close = new Float64Array([100.8, 100.4, 101.2, 103.2, 103.6]);

const out = ict_propulsion_block(open, high, low, close, 3, 'close');

console.log(out.bullish_high);
console.log(out.bullish_kind);
console.log(out.bearish_new);
Memory-Efficient Operations

For host-array input plus zero-copy output writes, use ict_propulsion_block_into_host with a buffer allocated by ict_propulsion_block_alloc. The output buffer stores 12 consecutive lanes of length len.

import {
  ict_propulsion_block_alloc,
  ict_propulsion_block_free,
  ict_propulsion_block_into_host,
  memory,
} from 'vectorta-wasm';

const len = close.length;
const outPtr = ict_propulsion_block_alloc(len);

ict_propulsion_block_into_host(open, high, low, close, outPtr, 3, 'wick');

const flat = new Float64Array(memory.buffer, outPtr, len * 12);
const bullishHigh = flat.slice(0, len);
const bullishLow = flat.slice(len, len * 2);
const bullishKind = flat.slice(len * 2, len * 3);
const bearishNew = flat.slice(len * 11, len * 12);

ict_propulsion_block_free(outPtr, len);
Batch Processing

Batch WASM uses ict_propulsion_block_batch with a config object containing swing_length_range and mitigation_price_toggle.

import { ict_propulsion_block_batch } from 'vectorta-wasm';

const batch = ict_propulsion_block_batch(open, high, low, close, {
  swing_length_range: [3, 7, 2],
  mitigation_price_toggle: [true, true],
});

console.log(batch.rows, batch.cols);
console.log(batch.combos);
console.log(batch.bullish_high);
console.log(batch.bearish_kind);

CUDA Bindings (Rust)

Additional details for the CUDA bindings can be found inside the VectorTA repository.

Performance Analysis

Comparison:
View:
Placeholder data (no recorded benchmarks for this indicator)

Across sizes, Rust CPU runs about 1.14× faster than Tulip C in this benchmark.

Loading chart...

AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU)

Related Indicators