Module 2 · Financial Arithmetic, Time Value, and Returns Lesson 17 of 120
Log Return
Adding changes in log space without confusing wealth.
Transcript
21 sentences · select one to jump thereCheck your understanding
How do you convert total log return back to simple return?
Code lab
Run it yourself
The lesson source in 7 languages. Edit it, run TypeScript and Python right here, and compare with the expected output.
/**
* Fintech Math Bootcamp · Lesson 017 of 120
* Log Return
* Module 02: Financial Arithmetic, Time Value, and Returns
*
* Scenario: Adding changes in log space without confusing wealth
* Rule: r = ln(P₁/P₀)
*
* Try it: How do you convert total log return back to simple return?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*/
export function lesson017() {
const prices = [100, 110, 99];
if (!prices.every(p => Number.isFinite(p) && p > 0))
throw new Error("Positive prices required");
const logs = prices.slice(1).map((p, i) =>
Math.log(p / prices[i]));
const totalLog = logs.reduce((a, x) => a + x, 0);
const result = {totalLog, simple: Math.expm1(totalLog)};
return result;
}
export const checkedResult = {"totalLog":-0.010050335853501347,"simple":-0.009999999999999907};
// Run this file directly: npx tsx lessons/02-financial-arithmetic-time-value-and-returns/017-log-return.ts
if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/").split("/").pop()!)) {
console.log(JSON.stringify(lesson017(), null, 2));
}
Your output
Press Run to execute the code in your browser.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}"""
Fintech Math Bootcamp · Lesson 017 of 120
Log Return
Module 02: Financial Arithmetic, Time Value, and Returns
Scenario: Adding changes in log space without confusing wealth
Rule: r = ln(P₁/P₀)
Try it: How do you convert total log return back to simple return?
Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
Free course: https://courses.thefintechbuilder.com
Synthetic teaching example, not financial advice or a production library.
Run it: python main.py
"""
import json
import math
def lesson017() -> dict:
prices = [100, 110, 99]
if not all(math.isfinite(p) and p > 0 for p in prices):
raise ValueError("Positive prices required")
logs = [math.log(p / prices[i]) for i, p in enumerate(prices[1:])]
total_log = sum(logs)
result = {"totalLog": total_log, "simple": math.expm1(total_log)}
return result
if __name__ == "__main__":
print(json.dumps(lesson017(), indent=2))
Your output
Press Run to execute the code in your browser.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}/**
* Fintech Math Bootcamp · Lesson 017 of 120
* Log Return
* Module 02: Financial Arithmetic, Time Value, and Returns
*
* Scenario: Adding changes in log space without confusing wealth
* Rule: r = ln(P1/P0)
*
* Try it: How do you convert total log return back to simple return?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*
* Run it: javac Main.java && java Main
*/
import java.util.Arrays;
public class Main {
static final class LogReturns {
final double totalLog;
final double simple;
LogReturns(double totalLog, double simple) {
this.totalLog = totalLog;
this.simple = simple;
}
}
static LogReturns lesson017() {
double[] prices = {100, 110, 99};
if (!Arrays.stream(prices).allMatch(p -> Double.isFinite(p) && p > 0))
throw new IllegalArgumentException("Positive prices required");
double[] logs = new double[prices.length - 1];
for (int i = 0; i < logs.length; i++) {
logs[i] = Math.log(prices[i + 1] / prices[i]);
}
double totalLog = Arrays.stream(logs).reduce(0, (a, x) -> a + x);
LogReturns result = new LogReturns(totalLog, Math.expm1(totalLog));
return result;
}
public static void main(String[] args) {
LogReturns result = lesson017();
System.out.println(object("totalLog", num(result.totalLog), "simple", num(result.simple)));
}
// Formats a double the way JSON.stringify does: whole numbers without ".0", null for NaN or infinity.
static String num(double x) {
if (Double.isNaN(x) || Double.isInfinite(x)) return "null";
if (x == Math.rint(x) && Math.abs(x) < 1e15) return String.valueOf((long) x);
return String.valueOf(x);
}
static String quote(String text) {
return "\"" + text.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}
// Indents an already formatted JSON value by one level.
static String nested(String json) {
return json.replace("\n", "\n ");
}
static String object(String... keysAndValues) {
if (keysAndValues.length == 0) return "{}";
StringBuilder out = new StringBuilder("{");
for (int i = 0; i < keysAndValues.length; i += 2) {
out.append(i == 0 ? "\n " : ",\n ")
.append(quote(keysAndValues[i])).append(": ").append(nested(keysAndValues[i + 1]));
}
return out.append("\n}").toString();
}
}
No browser runner for Java yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}// Fintech Math Bootcamp · Lesson 017 of 120
// Log Return
// Module 02: Financial Arithmetic, Time Value, and Returns
//
// Scenario: Adding changes in log space without confusing wealth
// Rule: r = ln(P₁/P₀)
//
// Try it: How do you convert total log return back to simple return?
//
// Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
// Free course: https://courses.thefintechbuilder.com
// Synthetic teaching example, not financial advice or a production library.
//
// Run it: go run main.go
package main
import (
"encoding/json"
"errors"
"fmt"
"math"
"os"
)
type LogReturns struct {
TotalLog float64 `json:"totalLog"`
Simple float64 `json:"simple"`
}
func lesson017() (LogReturns, error) {
prices := []float64{100, 110, 99}
for _, p := range prices {
if math.IsNaN(p) || math.IsInf(p, 0) || p <= 0 {
return LogReturns{}, errors.New("Positive prices required")
}
}
logs := make([]float64, 0, len(prices)-1)
for i, p := range prices[1:] {
logs = append(logs, math.Log(p/prices[i]))
}
totalLog := 0.0
for _, x := range logs {
totalLog += x
}
result := LogReturns{TotalLog: totalLog, Simple: math.Expm1(totalLog)}
return result, nil
}
func main() {
result, err := lesson017()
if err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(1)
}
printJSON(result)
}
// printJSON prints a value as JSON indented with two spaces, like JSON.stringify(value, null, 2).
func printJSON(value any) {
out, err := json.MarshalIndent(value, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
No browser runner for Go yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}/**
* Fintech Math Bootcamp · Lesson 017 of 120
* Log Return
* Module 02: Financial Arithmetic, Time Value, and Returns
*
* Scenario: Adding changes in log space without confusing wealth
* Rule: r = ln(P₁/P₀)
*
* Try it: How do you convert total log return back to simple return?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*
* Run it: g++ -std=c++17 -o main main.cpp && ./main
*/
#include <algorithm>
#include <charconv>
#include <cmath>
#include <iostream>
#include <numeric>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
// Formats a double the way JSON.stringify does: shortest round-trip form, null for NaN or infinity.
std::string num(double x) {
if (!std::isfinite(x)) return "null";
char buffer[32];
auto [end, error] = std::to_chars(buffer, buffer + sizeof buffer, x);
(void)error;
return std::string(buffer, end);
}
std::string quote(const std::string& text) {
std::string out = "\"";
for (char c : text) {
if (c == '"' || c == '\\') out += '\\';
out += c;
}
return out + "\"";
}
// Indents an already formatted JSON value by one level.
std::string nested(const std::string& json) {
std::string out;
for (char c : json) {
out += c;
if (c == '\n') out += " ";
}
return out;
}
std::string object(const std::vector<std::pair<std::string, std::string>>& fields) {
if (fields.empty()) return "{}";
std::string out = "{";
for (std::size_t i = 0; i < fields.size(); ++i) {
out += i == 0 ? "\n " : ",\n ";
out += quote(fields[i].first) + ": " + nested(fields[i].second);
}
return out + "\n}";
}
struct LogReturns {
double totalLog;
double simple;
};
LogReturns lesson017() {
const std::vector<double> prices{100, 110, 99};
if (!std::all_of(prices.begin(), prices.end(),
[](double p) { return std::isfinite(p) && p > 0; }))
throw std::invalid_argument("Positive prices required");
std::vector<double> logs;
for (std::size_t i = 0; i + 1 < prices.size(); ++i) {
logs.push_back(std::log(prices[i + 1] / prices[i]));
}
const double totalLog = std::accumulate(logs.begin(), logs.end(), 0.0);
const LogReturns result{totalLog, std::expm1(totalLog)};
return result;
}
int main() {
const LogReturns result = lesson017();
std::cout << object({{"totalLog", num(result.totalLog)}, {"simple", num(result.simple)}}) << "\n";
}
No browser runner for C++ yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}//! Fintech Math Bootcamp · Lesson 017 of 120
//! Log Return
//! Module 02: Financial Arithmetic, Time Value, and Returns
//!
//! Scenario: Adding changes in log space without confusing wealth
//! Rule: r = ln(P₁/P₀)
//!
//! Try it: How do you convert total log return back to simple return?
//!
//! Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
//! Free course: https://courses.thefintechbuilder.com
//! Synthetic teaching example, not financial advice or a production library.
//!
//! Run it: rustc main.rs && ./main
struct LogReturns {
total_log: f64,
simple: f64,
}
fn lesson017() -> Result<LogReturns, String> {
let prices = [100.0_f64, 110.0, 99.0];
if !prices.iter().all(|&p| p.is_finite() && p > 0.0) {
return Err("Positive prices required".to_string());
}
let logs: Vec<f64> = prices.windows(2).map(|pair| (pair[1] / pair[0]).ln()).collect();
let total_log = logs.iter().fold(0.0, |a, &x| a + x);
let result = LogReturns { total_log, simple: total_log.exp_m1() };
Ok(result)
}
fn main() {
match lesson017() {
Ok(result) => println!("{}", object(&[
("totalLog", num(result.total_log)),
("simple", num(result.simple)),
])),
Err(message) => {
eprintln!("Error: {}", message);
std::process::exit(1);
}
}
}
/// Formats a number the way JSON.stringify does: shortest round-trip form, null for NaN or infinity.
fn num(x: f64) -> String {
if x.is_finite() {
format!("{}", x)
} else {
"null".to_string()
}
}
fn quote(text: &str) -> String {
format!("\"{}\"", text.replace('\\', "\\\\").replace('"', "\\\""))
}
/// Indents an already formatted JSON value by one level.
fn nested(json: &str) -> String {
json.replace('\n', "\n ")
}
fn object(fields: &[(&str, String)]) -> String {
if fields.is_empty() {
return "{}".to_string();
}
let lines: Vec<String> = fields
.iter()
.map(|(key, value)| format!(" {}: {}", quote(key), nested(value)))
.collect();
format!("{{\n{}\n}}", lines.join(",\n"))
}
No browser runner for Rust yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}/**
* Fintech Math Bootcamp · Lesson 017 of 120
* Log Return
* Module 02: Financial Arithmetic, Time Value, and Returns
*
* Scenario: Adding changes in log space without confusing wealth
* Rule: r = ln(P₁/P₀)
*
* Try it: How do you convert total log return back to simple return?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/financial-arithmetic-time-value-and-returns/log-return/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*
* Run it: dotnet run (inside a console project that holds this Program.cs)
*/
using System.Text.Json;
var jsonOptions = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
Console.WriteLine(JsonSerializer.Serialize(Lesson017(), jsonOptions));
static LogReturns Lesson017()
{
double[] prices = { 100, 110, 99 };
if (!prices.All(p => double.IsFinite(p) && p > 0))
throw new ArgumentException("Positive prices required");
var logs = prices.Skip(1).Select((p, i) => Math.Log(p / prices[i]));
var totalLog = logs.Aggregate(0.0, (a, x) => a + x);
var result = new LogReturns(totalLog, double.ExpM1(totalLog));
return result;
}
record LogReturns(double TotalLog, double Simple);
No browser runner for C# yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"totalLog": -0.010050335853501347,
"simple": -0.009999999999999907
}Prefer your own machine? Every file is in the course repository · open it in Codespaces.
Lesson notes
The rule
r = ln(P₁/P₀)