Skip to content

Commit bff6110

Browse files
authored
Merge pull request #128 from neuron7xLab/physics-engine-v1
feat(physics): GeoSync Physics Engine v2 — 7 derived modules + 94 tests
2 parents 777a6c3 + 92fb72f commit bff6110

31 files changed

Lines changed: 5218 additions & 0 deletions

core/physics/__init__.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,32 @@
2626
compute_market_momentum,
2727
)
2828
from .constants import PhysicsConstants
29+
30+
# Physics Engine v2 — T1-T7 modules
31+
from .coulomb import CoulombInteraction
32+
33+
# Research modules — T1-T7 v2
34+
from .diffusion_predictor import BacktestResult as DiffusionBacktestResult
35+
from .diffusion_predictor import DiffusionVolatilityPredictor, VolatilityFrontPrediction
36+
from .engine import GeoSyncPhysicsEngine, PhysicsEngineResult
37+
from .explosive_sync import ESCircuitBreaker, ESProximityResult, ExplosiveSyncDetector
38+
from .forman_ricci import DualTrackRicciMonitor, FormanRicciCurvature, FormanRicciResult
39+
from .free_energy_trading_gate import FreeEnergyTradeDecision, FreeEnergyTradingGate, GateStatistics
40+
from .gravitational_coupling import GravitationalCouplingMatrix
2941
from .gravity import (
3042
compute_market_gravity,
3143
gravitational_force,
3244
gravitational_potential,
3345
market_gravity_center,
3446
)
47+
from .higher_order_kuramoto import HigherOrderKuramotoEngine, HigherOrderKuramotoResult
48+
from .landauer import (
49+
K_BOLTZMANN,
50+
LANDAUER_ENERGY,
51+
ROOM_TEMPERATURE,
52+
LandauerInferenceProfiler,
53+
)
54+
from .liquidity_coupling import CouplingBenchmarkResult, LiquidityCouplingMatrix
3555
from .maxwell import (
3656
compute_market_field_curl,
3757
compute_market_field_divergence,
@@ -45,6 +65,8 @@
4565
compute_price_acceleration,
4666
compute_price_velocity,
4767
)
68+
from .newtonian_dynamics import FreeEnergyGate, NewtonianPriceDynamics
69+
from .portfolio_conservation import PortfolioEnergyConservation
4870
from .relativity import (
4971
compute_relative_time,
5072
lorentz_factor,
@@ -53,6 +75,7 @@
5375
time_dilation_factor,
5476
velocity_addition,
5577
)
78+
from .thermodynamic_risk import ThermodynamicRiskGate
5679
from .thermodynamics import (
5780
boltzmann_entropy,
5881
compute_free_energy,
@@ -61,6 +84,7 @@
6184
is_thermodynamic_equilibrium,
6285
thermal_equilibrium_distance,
6386
)
87+
from .tsallis_gate import TsallisGateResult, TsallisRegime, TsallisRiskGate
6488
from .uncertainty import (
6589
check_uncertainty_principle,
6690
heisenberg_uncertainty,
@@ -69,6 +93,7 @@
6993
optimal_measurement_tradeoff,
7094
position_momentum_uncertainty,
7195
)
96+
from .wave_propagation import GraphDiffusionEngine
7297

7398
__all__ = [
7499
# Constants
@@ -115,4 +140,38 @@
115140
"check_uncertainty_principle",
116141
"optimal_measurement_tradeoff",
117142
"information_limit",
143+
# Physics Engine v2 — T1-T7
144+
"GravitationalCouplingMatrix",
145+
"NewtonianPriceDynamics",
146+
"FreeEnergyGate",
147+
"PortfolioEnergyConservation",
148+
"ThermodynamicRiskGate",
149+
"CoulombInteraction",
150+
"GraphDiffusionEngine",
151+
"LandauerInferenceProfiler",
152+
"K_BOLTZMANN",
153+
"ROOM_TEMPERATURE",
154+
"LANDAUER_ENERGY",
155+
"GeoSyncPhysicsEngine",
156+
"PhysicsEngineResult",
157+
# Research modules — T1-T7 v2
158+
"FormanRicciCurvature",
159+
"FormanRicciResult",
160+
"DualTrackRicciMonitor",
161+
"LiquidityCouplingMatrix",
162+
"CouplingBenchmarkResult",
163+
"ExplosiveSyncDetector",
164+
"ESProximityResult",
165+
"ESCircuitBreaker",
166+
"TsallisRiskGate",
167+
"TsallisGateResult",
168+
"TsallisRegime",
169+
"FreeEnergyTradingGate",
170+
"FreeEnergyTradeDecision",
171+
"GateStatistics",
172+
"HigherOrderKuramotoEngine",
173+
"HigherOrderKuramotoResult",
174+
"DiffusionVolatilityPredictor",
175+
"VolatilityFrontPrediction",
176+
"DiffusionBacktestResult",
118177
]

core/physics/coulomb.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# SPDX-License-Identifier: MIT
2+
"""T5 — Coulomb Electrostatic Interaction for Data Ingestion.
3+
4+
Define charge:
5+
q_i(t) = OFI_i(t) / σ(OFI_i, 30) # normalised order flow imbalance
6+
positive charge = net buying pressure
7+
negative charge = net selling pressure
8+
9+
Coulomb force:
10+
F_ij = k · q_i · q_j / r_ij²
11+
r_ij = correlation distance (same metric as T1)
12+
k = normalisation s.t. |F_ij| ∈ [0, 1]
13+
14+
AUDIT RESOLUTION — sign convention:
15+
F_ij > 0 (same sign charges) → repulsion.
16+
In markets: same-direction OFI = momentum, which implies ATTRACTION.
17+
Therefore we FLIP the sign: F_market = -F_coulomb.
18+
19+
Attraction (opposite signs) → convergent behaviour.
20+
Repulsion (same signs in Coulomb) → but in market context same-direction
21+
flow means co-movement → attraction, so we invert.
22+
23+
Adjacency update:
24+
A_ij(t) = clip(A_ij(t-1) + α · F_market_ij, 0, 1)
25+
α = 0.1 (slow adaptation prevents overfitting to noise)
26+
"""
27+
28+
from __future__ import annotations
29+
30+
import numpy as np
31+
from numpy.typing import NDArray
32+
33+
34+
class CoulombInteraction:
35+
"""Coulomb-inspired electrostatic interaction for market networks.
36+
37+
Parameters
38+
----------
39+
alpha : float
40+
Learning rate for adjacency update (default 0.1).
41+
lookback : int
42+
Lookback for OFI normalisation std (default 30).
43+
"""
44+
45+
def __init__(self, alpha: float = 0.1, lookback: int = 30) -> None:
46+
if not 0 < alpha <= 1:
47+
raise ValueError(f"alpha must be in (0, 1], got {alpha}")
48+
if lookback < 1:
49+
raise ValueError(f"lookback must be ≥ 1, got {lookback}")
50+
self._alpha = alpha
51+
self._lookback = lookback
52+
53+
@property
54+
def alpha(self) -> float:
55+
return self._alpha
56+
57+
def compute_charges(
58+
self, ofi_matrix: NDArray[np.float64]
59+
) -> NDArray[np.float64]:
60+
"""Normalised charges q_i = OFI_i / σ(OFI_i).
61+
62+
Parameters
63+
----------
64+
ofi_matrix : (T, N) array
65+
Order flow imbalance time series for N assets.
66+
67+
Returns
68+
-------
69+
(N,) array of current charges.
70+
"""
71+
ofi = np.asarray(ofi_matrix, dtype=np.float64)
72+
if ofi.ndim != 2:
73+
raise ValueError(f"Expected 2-D array (T, N), got ndim={ofi.ndim}")
74+
75+
tail = ofi[-self._lookback:]
76+
current = ofi[-1]
77+
sigma = np.std(tail, axis=0)
78+
sigma = np.maximum(sigma, 1e-12)
79+
return current / sigma
80+
81+
@staticmethod
82+
def compute_forces(
83+
charges: NDArray[np.float64],
84+
correlation_distances: NDArray[np.float64],
85+
) -> NDArray[np.float64]:
86+
"""Compute Coulomb force matrix with market sign convention.
87+
88+
F_market_ij = -k · q_i · q_j / r_ij²
89+
90+
The negative sign flips Coulomb convention:
91+
same-sign OFI → negative F_market → attraction (co-movement).
92+
93+
Forces are normalised to [-1, 1].
94+
"""
95+
charges = np.asarray(charges, dtype=np.float64)
96+
distances = np.asarray(correlation_distances, dtype=np.float64)
97+
n = charges.shape[0]
98+
99+
if distances.shape != (n, n):
100+
raise ValueError(
101+
f"distances must be ({n}, {n}), got {distances.shape}"
102+
)
103+
104+
charge_product = np.outer(charges, charges)
105+
dist_safe = np.maximum(distances, 1e-6)
106+
F_coulomb = charge_product / (dist_safe ** 2)
107+
108+
# Flip sign: same-direction OFI = market attraction
109+
F_market = -F_coulomb
110+
np.fill_diagonal(F_market, 0.0)
111+
112+
# Normalise to [-1, 1]
113+
max_abs = np.max(np.abs(F_market))
114+
if max_abs > 0:
115+
F_market = F_market / max_abs
116+
117+
return F_market
118+
119+
def update_adjacency(
120+
self,
121+
A: NDArray[np.float64],
122+
forces: NDArray[np.float64],
123+
) -> NDArray[np.float64]:
124+
"""Update adjacency matrix with Coulomb forces.
125+
126+
A_ij(t) = clip(A_ij(t-1) + α · F_ij, 0, 1)
127+
128+
Parameters
129+
----------
130+
A : (N, N) current adjacency matrix.
131+
forces : (N, N) normalised force matrix from compute_forces.
132+
133+
Returns
134+
-------
135+
Updated (N, N) adjacency matrix.
136+
"""
137+
A = np.asarray(A, dtype=np.float64)
138+
forces = np.asarray(forces, dtype=np.float64)
139+
140+
if A.shape != forces.shape:
141+
raise ValueError(
142+
f"A and forces must match: {A.shape} vs {forces.shape}"
143+
)
144+
145+
A_new = A + self._alpha * forces
146+
A_new = np.clip(A_new, 0.0, 1.0)
147+
np.fill_diagonal(A_new, 0.0)
148+
return A_new
149+
150+
151+
__all__ = ["CoulombInteraction"]

0 commit comments

Comments
 (0)