Mesa Sine Wave (MSW)
period = 5 Overview
The Mesa Sine Wave indicator transforms price data into two oscillating waves that reveal hidden market cycles by projecting price movements onto sine and cosine components, creating a bounded oscillator that swings smoothly between -1 and 1. The primary sine wave captures the dominant cycle in price action, while the companion lead wave advances 45 degrees ahead in phase, providing early warning of potential turning points before they occur in the main oscillator. When the lead wave crosses above the sine wave near cycle lows, it signals an impending upturn, whereas crosses below near peaks warn of coming declines. This indicator excels in ranging markets where prices exhibit regular cyclical behavior, helping traders time entries and exits at cycle extremes rather than chasing moves after they've begun. Unlike traditional oscillators that simply measure momentum, MSW actively extracts the periodic component from price data, making it particularly valuable for identifying the rhythm and frequency of market swings.
Implementation Examples
Compute MSW in a few lines:
use vector_ta::indicators::msw::{msw, MswInput, MswParams};
use vector_ta::utilities::data_loader::{Candles, read_candles_from_csv};
// From a price slice (default: period=5)
let prices = vec![100.0, 101.0, 102.0, 101.5, 103.0, 104.0];
let input = MswInput::from_slice(&prices, MswParams { period: Some(5) });
let out = msw(&input)?; // out.sine, out.lead
// From Candles with default params (source="close", period=5)
let candles: Candles = read_candles_from_csv("data/sample.csv")?;
let input = MswInput::with_default_candles(&candles);
let out = msw(&input)?;
// Access the values
for (s, l) in out.sine.iter().zip(out.lead.iter()) {
println!("sine={}, lead={}", s, l);
} API Reference
Input Methods ▼
// From price slice
MswInput::from_slice(&[f64], MswParams) -> MswInput
// From candles with custom source
MswInput::from_candles(&Candles, &str, MswParams) -> MswInput
// With defaults from candles (source = "close", period=5)
MswInput::with_default_candles(&Candles) -> MswInput Parameters Structure ▼
pub struct MswParams {
pub period: Option<usize>, // Default: 5
} Output Structure ▼
pub struct MswOutput {
pub sine: Vec<f64>, // bounded in [-1, 1]; NaN during warmup
pub lead: Vec<f64>, // phase-advanced by 45°; NaN during warmup
} Validation, Warmup & NaNs ▼
period > 0; otherwiseMswError::InvalidPeriod.period ≤ data.len(); otherwiseMswError::InvalidPeriod.- There must be at least
periodvalid points after the first finite value; elseMswError::NotEnoughValidData. - If all inputs are
NaN, returnsMswError::AllValuesNaN. - Warmup: outputs are
NaNuntil indexfirst + period − 1(first defined values).
Error Handling ▼
use vector_ta::indicators::msw::{msw, MswError};
match msw(&input) {
Ok(out) => process(out.sine, out.lead),
Err(MswError::EmptyData) => eprintln!("Input data is empty"),
Err(MswError::InvalidPeriod { period, data_len }) =>
eprintln!("Invalid period {} for length {}", period, data_len),
Err(MswError::NotEnoughValidData { needed, valid }) =>
eprintln!("Need {} valid points, only {} found", needed, valid),
Err(MswError::AllValuesNaN) => eprintln!("All input values are NaN"),
} Python Bindings
Basic Usage ▼
Calculate MSW using NumPy arrays (default: period=5):
import numpy as np
from vector_ta import msw
prices = np.array([100.0, 101.0, 102.0, 101.5, 103.0, 104.0], dtype=float)
# Returns two arrays: (sine, lead)
sine, lead = msw(prices, period=5)
print(sine.shape, lead.shape) Streaming Real-time Updates ▼
Process ticks incrementally with the streaming API:
from vector_ta import MswStream
stream = MswStream(period=5)
for price in price_feed:
res = stream.update(price)
if res is not None:
sine, lead = res
handle_cycle(sine, lead) Batch Parameter Optimization ▼
Sweep period values efficiently:
import numpy as np
from vector_ta import msw_batch
prices = np.array([...], dtype=float)
res = msw_batch(prices, period_range=(5, 30, 5), kernel="auto")
print(res["sine"].shape, res["lead"].shape) # (num_periods, len(prices))
print(res["periods"]) # tested periods CUDA Acceleration ▼
CUDA helpers are available when the Python package is built with CUDA support. Inputs must be float32; outputs are device arrays (DLPack / __cuda_array_interface__ compatible).
import numpy as np
from vector_ta import msw_cuda_batch_dev, msw_cuda_many_series_one_param_dev
# One series (float32)
close_f32 = np.asarray(load_close(), dtype=np.float32)
dev = msw_cuda_batch_dev(
close_f32=close_f32,
period_range=(5, 30, 5),
device_id=0,
)
# Many series (time-major)
data_tm_f32 = np.asarray(load_data_time_major_matrix(), dtype=np.float32)
dev_tm = msw_cuda_many_series_one_param_dev(
data_tm_f32=data_tm_f32,
period=14,
device_id=0,
) JavaScript/WASM Bindings
Basic Usage ▼
Calculate MSW in JavaScript/TypeScript:
import { msw_js } from 'vectorta-wasm';
const prices = new Float64Array([100, 101, 102, 101.5, 103, 104]);
// Returns object { values: Float64Array(2*n), rows: 2, cols: n }
const res = msw_js(prices, 5);
const { values, rows, cols } = res;
// First row is sine, second row is lead
const sine = values.slice(0, cols);
const lead = values.slice(cols, 2 * cols);
console.log('MSW sine:', sine);
console.log('MSW lead:', lead); Memory‑Efficient Operations ▼
Use zero‑copy helpers for large arrays:
import { msw_alloc, msw_free, msw_into, memory } from 'vectorta-wasm';
const prices = new Float64Array([/* your data */]);
const n = prices.length;
// Allocate WASM memory
const inPtr = msw_alloc(n);
const sinePtr = msw_alloc(n);
const leadPtr = msw_alloc(n);
// Copy input into WASM memory
new Float64Array(memory.buffer, inPtr, n).set(prices);
// Compute directly into output buffers: (in_ptr, sine_ptr, lead_ptr, len, period)
msw_into(inPtr, sinePtr, leadPtr, n, 5);
// Read results (slice to copy out)
const sine = new Float64Array(memory.buffer, sinePtr, n).slice();
const lead = new Float64Array(memory.buffer, leadPtr, n).slice();
// Free WASM buffers
msw_free(inPtr, n);
msw_free(sinePtr, n);
msw_free(leadPtr, n); Batch Processing ▼
Sweep periods efficiently with a config or explicit ranges:
import { msw_batch, msw_batch_js, msw_batch_metadata_js } from 'vectorta-wasm';
const prices = new Float64Array([/* historical prices */]);
// Config object API
const config = { period_range: [5, 30, 5] };
const flat = msw_batch(prices, config); // { values, rows: 2*combos, cols }
// Explicit ranges API
const meta = msw_batch_metadata_js(5, 30, 5); // periods tested
const structured = msw_batch_js(prices, 5, 30, 5); // { sine, lead, combos, rows, cols } CUDA Bindings (Rust)
use vector_ta::cuda::CudaMsw;
use vector_ta::indicators::msw::MswBatchRange;
let cuda = CudaMsw::new(0)?;
let prices_f32: [f32] = /* ... */;
let sweep = MswBatchRange::default();
let out = cuda.msw_batch_dev(&prices_f32, &sweep)?;
let _ = out; Performance Analysis
Across sizes, Rust CPU runs about 5.11× faster than Tulip C in this benchmark.
AMD Ryzen 9 9950X (CPU) | NVIDIA RTX 4090 (GPU) | Benchmarks: 2026-02-28