Skip to content

Commit

Permalink
math
Browse files Browse the repository at this point in the history
  • Loading branch information
scab24 committed Sep 16, 2024
1 parent 6e112ef commit d0bb90e
Show file tree
Hide file tree
Showing 3 changed files with 395 additions and 0 deletions.
69 changes: 69 additions & 0 deletions send_logreturns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import json
from web3 import Web3
import math
import os
from dotenv import load_dotenv

# Cargar variables de entorno desde .env
load_dotenv()

# Configurar conexión a Anvil (nodo local)
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

# Verificar conexión
assert w3.isConnected(), "No se pudo conectar al nodo Ethereum"

# Dirección del contrato desplegado (cambiando según tu despliegue)
contrato_direccion = "0xYourContractAddressHere"

# Cargar ABI del contrato
with open("out/VolatilityCalculator.sol/VolatilityCalculator.json", "r") as f:
contrato_abi = json.load(f)["abi"]

# Crear instancia del contrato
contrato = w3.eth.contract(address=contrato_direccion, abi=contrato_abi)

# Dirección de la cuenta que enviará las transacciones (usar cuenta #0 de Anvil)
account = w3.eth.account.from_key(os.getenv("PRIVATE_KEY"))

# Precios históricos
precios = [100, 105, 102, 108]

def calculate_log_returns(prices):
log_returns = []
for i in range(1, len(prices)):
log_return = math.log(prices[i] / prices[i - 1])
log_returns.append(log_return)
return log_returns

def to_64x64(value):
return int(value * (2**64))

if __name__ == "__main__":
# Calcular retornos logarítmicos
log_returns = calculate_log_returns(precios)
print("Retornos Logarítmicos:", log_returns)

# Convertir a 64.64 fija
log_returns_64x64 = [to_64x64(r) for r in log_returns]
print("Retornos Logarítmicos (64.64 fija):", log_returns_64x64)

# Preparar la transacción
# Para optimizar, se enviarán todos los retornos en una sola transacción
tx = contrato.functions.addLogReturns(log_returns_64x64).buildTransaction({
'from': account.address,
'nonce': w3.eth.get_transaction_count(account.address),
'gas': 500000, # Ajusta según sea necesario
'gasPrice': w3.toWei('1', 'gwei') # Ajusta según sea necesario
})

# Firmar la transacción
signed_tx = account.sign_transaction(tx)

# Enviar la transacción
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print(f"Transacción enviada con hash: {tx_hash.hex()}")

# Esperar a que la transacción sea minada
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Transacción minada en bloque {receipt.blockNumber}")
File renamed without changes.
326 changes: 326 additions & 0 deletions src/math_solidity.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

// Importa la biblioteca ABDKMath64x64 desde GitHub
import "abdk-libraries-solidity/ABDKMath64x64.sol";

/**
* @title VolatilityCalculator
* @notice Contrato para calcular la volatilidad implícita y drift basado en retornos logarítmicos.
* Incluye el cálculo de logReturn utilizando una aproximación de la serie de Taylor.
*/
contract VolatilityCalculator {
using ABDKMath64x64 for int128;
using ABDKMath64x64 for uint256;

// Número de retornos logarítmicos ingresados
uint256 public count;

// Media acumulada en formato 64.64 fija
int128 public mean;

// Varianza acumulada (M2) en formato 64.64 fija
int128 public M2;

// Máximo número de retornos permitidos
uint256 public constant MAX_RETURNS = 1000;

// Array para almacenar precios del activo
uint256[] public prices;

// Array para almacenar retornos logarítmicos en formato 64.64 fija
int128[] public logReturns;

// Eventos para monitorear acciones
event PriceAdded(uint256 price, uint256 newCount);
event LogReturnAdded(int128 logReturn, uint256 newCount);
event VolatilityAndDriftCalculated(int128 sigma, int128 drift);

/**
* @notice Calcula el coseno hiperbólico de x.
* @param x Valor en formato 64.64 fija.
* @return cosh_x Coseno hiperbólico de x en formato 64.64 fija.
*/
function cosh(int128 x) internal pure returns (int128) {
// e^x
int128 expx = ABDKMath64x64.exp(x);
// e^-x
int128 expNegx = ABDKMath64x64.exp(ABDKMath64x64.neg(x));
// (e^x + e^-x) / 2
return ABDKMath64x64.div(ABDKMath64x64.add(expx, expNegx), ABDKMath64x64.fromUInt(2));
}

/**
* @notice Calcula el logaritmo natural de x utilizando la biblioteca ABDKMath64x64.
* @param x Valor en formato 64.64 fija.
* @return ln_x Logaritmo natural de x en formato 64.64 fija.
*/
function naturalLog(int128 x) internal pure returns (int128) {
return ABDKMath64x64.ln(x);
}

/**
* @notice Calcula la raíz cuadrada de x utilizando la biblioteca ABDKMath64x64.
* @param x Valor en formato 64.64 fija.
* @return sqrt_x Raíz cuadrada de x en formato 64.64 fija.
*/
function sqrt(int128 x) internal pure returns (int128) {
return ABDKMath64x64.sqrt(x);
}

/**
* @notice Calcula el logaritmo natural de x utilizando una aproximación de la serie de Taylor.
* @param x Valor para el cual se calculará ln(x) en formato 64.64 fija.
* @return ln_x Logaritmo natural aproximado de x en formato 64.64 fija.
*/
function approximateLn(int128 x) internal pure returns (int128 ln_x) {
require(x > 0, "x debe ser positivo");

// Número de términos de la serie de Taylor
uint256 terms = 6;

// Normalización: encontrar k tal que x = y * 2^k, donde y está en [0.5, 1.5]
int256 k = 0; // Contador para el exponente de 2
int128 y = x;

// Límites para la normalización
int128 onePointFive = ABDKMath64x64.divu(3, 2); // 1.5 en formato 64.64 fija
int128 zeroPointFive = ABDKMath64x64.divu(1, 2); // 0.5 en formato 64.64 fija

// Ajustar y y k para que y esté en [0.5, 1.5]
while (y > onePointFive) {
y = y.div(ABDKMath64x64.fromUInt(2)); // Dividir y por 2
k += 1;
}

while (y < zeroPointFive) {
y = y.mul(ABDKMath64x64.fromUInt(2)); // Multiplicar y por 2
k -= 1;
}

// Ahora, y está en [0.5, 1.5]
// Podemos escribir y = 1 + z, donde z está en [-0.5, 0.5]
int128 one = ABDKMath64x64.fromUInt(1);
int128 z = y.sub(one);

// Inicializar ln_x con el primer término de la serie de Taylor
ln_x = z;

// Variables para la expansión de la serie
int128 term = z; // Término actual inicializado a z^1 / 1
int128 z_power = z; // z elevado a la potencia n
int128 sign = ABDKMath64x64.fromInt(-1); // Signo alternante inicia en negativo

// Calcular la suma de la serie de Taylor
for (uint256 n = 2; n <= terms; n++) {
// Calcular z_power = z^n
z_power = z_power.mul(z);

// term = z^n / n
term = z_power.div(ABDKMath64x64.fromUInt(n));

// Alternar el signo para cada término
term = term.mul(sign);

// Agregar el término al resultado
ln_x = ln_x.add(term);

// Cambiar el signo para el siguiente término
sign = sign.neg();
}

// Agregar ln(2^k) = k * ln(2)
// ln(2) ≈ 0.69314718056 en decimal
int128 LN2 = 0xB17217F7D1CF79AB; // ln(2) en formato 64.64 fija
int128 kLn2 = ABDKMath64x64.fromInt(k).mul(LN2);

ln_x = ln_x.add(kLn2);
}

/**
* @notice Agrega un nuevo precio y calcula el retorno logarítmico respecto al precio anterior.
* @param newPrice Precio del activo en el nuevo periodo (sin decimales).
*/
function addPrice(uint256 newPrice) external /* onlyOwner */ {
require(prices.length - 1 < MAX_RETURNS, "Excede el maximo de retornos");

// Agregar el nuevo precio al array
prices.push(newPrice);
emit PriceAdded(newPrice, prices.length);

// Si es el primer precio, no hay retorno que calcular
if (prices.length == 1) {
return;
}

// Obtener el precio anterior y el actual
uint256 prevPrice = prices[prices.length - 2];
uint256 currentPrice = prices[prices.length - 1];

// Convertir los precios a formato 64.64 fija
int128 pi = ABDKMath64x64.fromUInt(currentPrice);
int128 pi_prev = ABDKMath64x64.fromUInt(prevPrice);

// Calcular la relación: Pi / P_{i-1}
int128 ratio = pi.div(pi_prev);

// Calcular ln(ratio) usando la aproximación de la serie de Taylor
int128 logReturn = approximateLn(ratio);

// Agregar el retorno al cálculo estadístico
_addLogReturn_internal(logReturn);
}

/**
* @notice Agrega un nuevo retorno logarítmico y actualiza la media y varianza.
* @param logReturn Retorno logarítmico en formato 64.64 fija.
*/
function addLogReturn(int128 logReturn) external /* onlyOwner */ {
require(count < MAX_RETURNS, "Se ha alcanzado el maximo de retornos");
_addLogReturn_internal(logReturn);
}

/**
* @notice Función interna para agregar un retorno logarítmico y actualizar estadísticas.
* @param logReturn Retorno logarítmico en formato 64.64 fija.
*/
function _addLogReturn_internal(int128 logReturn) internal {
logReturns.push(logReturn);
count += 1;

if (count == 1) {
mean = logReturn;
M2 = ABDKMath64x64.fromInt(0); // varianza no definida para 1 dato
emit LogReturnAdded(logReturn, count);
return;
}

// Algoritmo de Welford para actualizar la media y M2
int128 delta = logReturn.sub(mean);
mean = mean.add(delta.div(ABDKMath64x64.fromUInt(count)));
int128 delta2 = logReturn.sub(mean);
M2 = M2.add(delta.mul(delta2));

emit LogReturnAdded(logReturn, count);
}

/**
* @notice Calcula la volatilidad implícita sigma y el drift u.
* @return sigma Volatilidad implícita en formato 64.64 fija.
* @return drift Drift calculado en formato 64.64 fija.
*/
function calculateSigmaAndDrift() external /* onlyOwner */ returns (int128 sigma, int128 drift) {
require(count >= 2, "Se requieren al menos 2 retornos para calcular varianza");

// Calcular varianza: varianza = M2 / (n - 1)
int128 variance = M2.div(ABDKMath64x64.fromUInt(count - 1));

// Calcular la desviación estándar (std dev) = sqrt(varianza)
int128 stdDev = sqrt(variance);

// Anualizar la desviación estándar: sigma = stdDev * sqrt(252)
// sqrt(252) ≈ 15.87401
int128 sqrt252 = ABDKMath64x64.fromUInt(15).add(ABDKMath64x64.divu(87401, 100000)); // Aproximación

sigma = stdDev.mul(sqrt252);

// Calcular drift u = mean - (sigma^2 / 2)
int128 sigmaSquared = sigma.mul(sigma);
int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2));
drift = mean.sub(sigmaSquaredOver2);

emit VolatilityAndDriftCalculated(sigma, drift);
}

/**
* @notice Calcula el drift u usando la fórmula:
* u = muPool - (sigma^2 / 2)
* @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija.
* @param sigma Volatilidad implícita σ en formato 64.64 fija.
* @return u Drift calculado en formato 64.64 fija.
*/
function calculateDrift(int128 muPool, int128 sigma) public pure returns (int128 u) {
// Calcular sigma^2
int128 sigmaSquared = sigma.mul(sigma);

// Calcular sigma^2 / 2
int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2));

// Calcular u = muPool - (sigma^2 / 2)
u = muPool.sub(sigmaSquaredOver2);
}

/**
* @notice Calcula y devuelve la volatilidad implícita y drift sin almacenarlos.
* @param muPool Retorno medio en fees de la pool durante el tiempo t (μ_pool) en formato 64.64 fija.
* @param u Drift del activo subyacente (u) en formato 64.64 fija.
* @param t Tiempo en años (t), asumimos t = 1.
* @return sigma Volatilidad implícita en formato 64.64 fija.
* @return drift Drift calculado en formato 64.64 fija.
*/
function computeImpliedVolatilityAndDrift(int128 muPool, int128 u, uint256 t) external pure returns (int128 sigma, int128 drift) {
require(t > 0, "Tiempo t debe ser mayor que cero");

// Calcular u * t / 2
int128 ut = u.mul(ABDKMath64x64.fromUInt(t));
int128 utOver2 = ut.div(ABDKMath64x64.fromUInt(2));

// Calcular cosh(u * t / 2)
int128 coshUtOver2 = cosh(utOver2);

// Calcular ln(cosh(u * t / 2)) utilizando la aproximación de ln(x)
int128 lnCoshUtOver2 = approximateLn(coshUtOver2);

// Calcular [mu_pool * t - ln(cosh(u * t / 2))]
int128 muPoolTimesT = muPool.mul(ABDKMath64x64.fromUInt(t));
int128 innerExpression = muPoolTimesT.sub(lnCoshUtOver2);

// Calcular 8 / t
int128 eightOverT = ABDKMath64x64.fromUInt(8).div(ABDKMath64x64.fromUInt(t));

// Multiplicar 8/t * [mu_pool * t - ln(cosh(u * t / 2))]
int128 multiplicand = eightOverT.mul(innerExpression);

// Calcular la raíz cuadrada de multiplicand
sigma = sqrt(multiplicand);

// Calcular drift u = muPool - (sigma^2 / 2)
int128 sigmaSquared = sigma.mul(sigma);
int128 sigmaSquaredOver2 = sigmaSquared.div(ABDKMath64x64.fromUInt(2));
drift = muPool.sub(sigmaSquaredOver2);
}

/**
* @notice Obtiene la media de los retornos logarítmicos.
* @return mean_64x64 Media en formato 64.64 fija.
*/
function getMean() external view /* onlyOwner */ returns (int128) {
return mean;
}

/**
* @notice Obtiene la varianza acumulada (M2).
* @return M2_64x64 Varianza acumulada en formato 64.64 fija.
*/
function getM2() external view /* onlyOwner */ returns (int128) {
return M2;
}

/**
* @notice Obtiene un retorno logarítmico específico por su índice.
* @param index Índice del retorno logarítmico (empezando desde 0).
* @return logReturn Retorno logarítmico en formato 64.64 fija.
*/
function getLogReturn(uint256 index) external view /* onlyOwner */ returns (int128 logReturn) {
require(index < logReturns.length, "Indice fuera de rango");
return logReturns[index];
}

/**
* @notice Obtiene todos los retornos logarítmicos.
* @return allLogReturns Array de retornos logarítmicos en formato 64.64 fija.
*/
function getAllLogReturns() external view /* onlyOwner */ returns (int128[] memory allLogReturns) {
return logReturns;
}
}

0 comments on commit d0bb90e

Please sign in to comment.