This one's a little different. For a weekend project, I wanted to build a utility to generate Brownian time series and graph composable operations on them in real time without having to invent a DSL. Then I remembered I've always wanted to make a website you can control directly from the Developer Tools console. Enter phlebas.dev. It's not fully optimized and definitely not finished, but I put it together in two afternoons and it already lets you experiment with a good range of operators. Just open the console and give it a try:
➤ Define a new series in p:
p.mySeries = new BrownianGenerator(start, mu, sigma)
p.mySeries = new BrownianGenerator() // With default params
➤ Combine and transform series:
p.ma = p.mySeries.sma(20) // Simple Moving Average
p.ema = p.mySeries.ema(10) // Exponential Moving Average
p.emaCustom = p.mySeries.ema(10, 0.3) // EMA (specify alpha)
p.diff = p.mySeries.subtract(p.ma) // Difference
p.sum = p.mySeries.add(p.ma) // Sum
p.prod = p.mySeries.mul(p.ma) // Multiply
p.prod = p.mySeries.mul(8) // All arithmetic operations work with numbers
p.absdiff = p.diff.abs() // Absolute
➤ Chain series:
p.diff10 = p.mySeries.subtract(p.ma).mul(10)
➤ Render series in the chart:
show(p.mySeries, p.ma) // Show selected series
showAll() // Show all series in p
BONUS: you can create generators and operators and use them in console. Here's how sum is implemented:
class AddGenerator extends TimeseriesGenerator {
sourceA: TimeseriesGenerator;
sourceB: number | string | TimeseriesGenerator;
constructor(
sourceA: TimeseriesGenerator,
sourceB: number | string | TimeseriesGenerator
) {
super();
this.sourceA = sourceA;
this.sourceB = sourceB;
}
next(cache: Map<string, number>): number {
if (cache.has(this.name)) return cache.get(this.name)!;
const resolvedSourceB =
typeof this.sourceB === "string"
? this.context![this.sourceB]
: this.sourceB;
if (resolvedSourceB === undefined) {
throw new Error(
`Could not find timeseries '${this.sourceB}' in context.`
);
}
const valueA = this.sourceA.next(cache);
const valueB =
typeof resolvedSourceB === "number"
? resolvedSourceB
: resolvedSourceB.next(cache);
const result = valueA + valueB;
cache.set(this.name, result);
return result;
}
}