Math functions

The kScript (legacy) math namespace becomes AssemblyScript's Math: absolute value, rounding, powers and roots, logarithms, and the full trig and hyperbolic…

The kScript (legacy) math namespace becomes AssemblyScript's Math: absolute value, rounding, powers and roots, logarithms, and the full trig and hyperbolic families, on f64. Call them on any number in state() or finalize() and they evaluate per bar, exactly like their JavaScript counterparts, with one difference worth knowing: the compiler is typed, so an integer and a float never mix silently.

Every method lives under the Math. prefix (capital M). There is no global abs() or round() for floats; it is always Math.abs(...), Math.round(...). Use them to normalize a signal, scale a value into a range, or build a custom indicator from primitives.

const spread = close - average;
// How far is price from its average, regardless of direction?
distance = Math.abs(spread);

What is available

GroupMethods
Basicabs, sign
Roundinground, floor, ceil, trunc
Powers and rootspow, sqrt, cbrt, hypot
Exp and logexp, expm1, log, log1p, log2, log10
Comparisonmax, min
Trig (radians)sin, cos, tan, asin, acos, atan, atan2
Hyperbolicsinh, cosh, tanh, asinh, acosh, atanh

Constants: Math.PI, Math.E, Math.SQRT2, Math.SQRT1_2, Math.LN2, Math.LN10, Math.LOG2E, Math.LOG10E.

Each behaves like its JavaScript counterpart. Math.max and Math.min take two f64 arguments and return the larger or smaller; nest them for three. Math.round rounds to the nearest integer and returns an f64. Trig functions work in radians, so divide a bar count or an angle accordingly (Math.sin(f64(barIndex) / 10.0)).

Types. Math.* takes and returns f64. An i32 (a bar count, a loop index, a ring cursor) must be cast in (f64(count)) and a result used as an index must be cast out (i32(Math.floor(x))); the compiler refuses the implicit conversion with AS200. For integer work AssemblyScript has typed builtins that need no cast: abs<i32>(n), max<i32>(a, b), min<i32>(a, b), and integer division truncates on its own (7 / 2 is 3 for two i32 values, 3.5 for two f64).

There is no Math.avg. To average two values, write the arithmetic ((a + b) / 2.0); for a rolling average over a window, use Sma (Moving averages). Calling a method that does not exist is a compile error, not a runtime one: Math.unknownFn(x) stops the build with TS2339: Property 'unknownFn' does not exist on type '~lib/math/NativeMath', with the line and column.

Every method in one module

This module runs the full namespace at once. It builds a few helper values first (a centered series of price minus its average, a bounded value safe for asin and acos, and a positive value safe for log and sqrt), then writes each Math.* result to its own output.

import { input, line, lower, ohlcv, output, param } from "./sdk/declare";
import { in_close, in_high, in_low } from "./gen/inputs";
import {
  emitRow,
  out_abs,
  out_acos,
  out_acosh,
  out_asin,
  out_asinh,
  out_atan,
  out_atan2,
  out_atanh,
  out_cbrt,
  out_ceil,
  out_cos,
  out_cosh,
  out_exp,
  out_expm1,
  out_floor,
  out_hypot,
  out_log,
  out_log10,
  out_log1p,
  out_log2,
  out_max,
  out_min,
  out_pow,
  out_round,
  out_sign,
  out_sin,
  out_sinh,
  out_sqrt,
  out_tan,
  out_tanh,
  out_trunc,
} from "./gen/outputs";
import { p_period } from "./gen/params";
import { Sma } from "./sdk/ta";

param("period", 5, { min: 2, max: 200 });
input("close", ohlcv.close);
input("high", ohlcv.high);
input("low", ohlcv.low);
output("abs", line, lower, { description: "Math.abs of the centered close" });
output("round", line, lower, { description: "Math.round" });
output("max", line, lower, { description: "Math.max of high and close" });
output("min", line, lower, { description: "Math.min of low and close" });
output("pow", line, lower, { description: "Math.pow of the small value, squared" });
output("sqrt", line, lower, { description: "Math.sqrt of the positive value" });
output("log", line, lower, { description: "Math.log of the positive value" });
output("floor", line, lower, { description: "Math.floor" });
output("ceil", line, lower, { description: "Math.ceil" });
output("sign", line, lower, { description: "Math.sign: -1, 0, or 1" });
output("sin", line, lower, { description: "Math.sin of the bar index over 8" });
output("cos", line, lower, { description: "Math.cos of the bar index over 8" });
output("tan", line, lower, { description: "Math.tan of the bar index over 40" });
output("asin", line, lower, { description: "Math.asin of the bounded value" });
output("acos", line, lower, { description: "Math.acos of the bounded value" });
output("atan", line, lower, { description: "Math.atan of the centered close" });
output("atan2", line, lower, { description: "Math.atan2 of centered over positive" });
output("sinh", line, lower, { description: "Math.sinh of the small value" });
output("cosh", line, lower, { description: "Math.cosh of the small value" });
output("tanh", line, lower, { description: "Math.tanh of the small value" });
output("asinh", line, lower, { description: "Math.asinh of the small value" });
output("acosh", line, lower, { description: "Math.acosh of the positive value" });
output("atanh", line, lower, { description: "Math.atanh of half the bounded value" });
output("exp", line, lower, { description: "Math.exp of the small value" });
output("expm1", line, lower, { description: "Math.expm1 of the small value" });
output("log1p", line, lower, { description: "Math.log1p of the small value's magnitude" });
output("log2", line, lower, { description: "Math.log2 of the positive value" });
output("log10", line, lower, { description: "Math.log10 of the positive value" });
output("cbrt", line, lower, { description: "Math.cbrt of the centered close" });
output("hypot", line, lower, { description: "Math.hypot of centered and small" });
output("trunc", line, lower, { description: "Math.trunc of the centered close" });

let sma = new Sma(5);
let barIndex: i32 = 0;
let centered: f64 = NaN;
let bounded: f64 = NaN;
let positive: f64 = NaN;
let small: f64 = NaN;
let high: f64 = NaN;
let low: f64 = NaN;
let close: f64 = NaN;

export function init(): void {
  sma = new Sma(i32(p_period()));
}

export function state(): i32 {
  close = in_close();
  high = in_high();
  low = in_low();
  const average = sma.update(close);
  centered = close - average;
  // The bar index is an i32 counter; the trig helpers want an f64 in radians.
  bounded = Math.sin(f64(barIndex) / 10.0);
  positive = Math.abs(centered) + 1.0;
  small = centered / 10.0;
  barIndex += 1;
  return isNaN(average) ? 0 : 1;
}

export function finalize(): void {
  const angle = f64(barIndex) / 8.0;
  out_abs(Math.abs(centered));
  out_round(Math.round(centered));
  out_max(Math.max(high, close));
  out_min(Math.min(low, close));
  out_pow(Math.pow(small, 2.0));
  out_sqrt(Math.sqrt(positive));
  out_log(Math.log(positive));
  out_floor(Math.floor(centered));
  out_ceil(Math.ceil(centered));
  out_sign(Math.sign(centered));
  out_sin(Math.sin(angle));
  out_cos(Math.cos(angle));
  out_tan(Math.tan(f64(barIndex) / 40.0));
  out_asin(Math.asin(bounded));
  out_acos(Math.acos(bounded));
  out_atan(Math.atan(centered));
  out_atan2(Math.atan2(centered, positive));
  out_sinh(Math.sinh(small));
  out_cosh(Math.cosh(small));
  out_tanh(Math.tanh(small));
  out_asinh(Math.asinh(small));
  out_acosh(Math.acosh(positive));
  out_atanh(Math.atanh(bounded / 2.0));
  out_exp(Math.exp(small));
  out_expm1(Math.expm1(small));
  out_log1p(Math.log1p(Math.abs(small)));
  out_log2(Math.log2(positive));
  out_log10(Math.log10(positive));
  out_cbrt(Math.cbrt(centered));
  out_hypot(Math.hypot(centered, small));
  out_trunc(Math.trunc(centered));
  emitRow();
}

export function reset(): void {
  sma.reset();
  barIndex = 0;
  centered = NaN;
  bounded = NaN;
  positive = NaN;
  small = NaN;
  high = NaN;
  low = NaN;
  close = NaN;
}

Domain notes

A few methods are only defined for part of the number line, exactly as in standard math:

  • Math.sqrt and Math.log expect non-negative input. Guard with Math.abs(x) + 1.0 or a Math.max floor if your series can go negative or hit zero.
  • Math.asin and Math.acos only accept values in [-1, 1]. Feed them something already bounded (the module uses Math.sin(...)).
  • Math.acosh expects input >= 1.

Out-of-domain input produces NaN rather than trapping, so an output may simply show gaps where the input left the valid range, and NaN written to an output draws nothing and never trips an alert. A division by zero on f64 yields an infinity, not a trap; isFinite(x) catches both before the value reaches an output. Integer division by zero DOES trap and aborts the evaluation, so guard an i32 divisor.