indicator validation looks for all NaN's and all zeroes
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
# Development Plan
|
# Development Plan
|
||||||
|
|
||||||
* Wiki memory
|
|
||||||
* Agent unification & spawn tool
|
|
||||||
* Realtime data
|
* Realtime data
|
||||||
* Triggers
|
* Triggers
|
||||||
* Strategy UI
|
* Strategy UI
|
||||||
@@ -13,6 +11,5 @@
|
|||||||
* Chat channels
|
* Chat channels
|
||||||
* MCP channel (with or without images)
|
* MCP channel (with or without images)
|
||||||
* TradingView indicator import tool
|
* TradingView indicator import tool
|
||||||
* Trader preferences tool
|
* Results persistence: ~~research analysis~~, backtests, strategy performance metrics, etc.
|
||||||
* Results persistence: research analysis, backtests, strategy performance metrics, etc.
|
* Free tier with token limits and sandbox shutdown
|
||||||
*
|
|
||||||
|
|||||||
@@ -61,14 +61,33 @@ def make_synthetic_ohlcv(n: int = 200):
|
|||||||
def summarize(result, n: int) -> str:
|
def summarize(result, n: int) -> str:
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
warnings = []
|
||||||
|
|
||||||
|
def _check_series_warnings(s: "pd.Series", label: str = "") -> None:
|
||||||
|
valid = s.dropna()
|
||||||
|
prefix = f"Column '{label}': " if label else ""
|
||||||
|
if len(valid) == 0:
|
||||||
|
warnings.append(
|
||||||
|
f"⚠ WARNING: {prefix}All {n} output values are NaN. "
|
||||||
|
"The indicator produced no usable data — check for NaN propagation bugs "
|
||||||
|
"(e.g. uninitialized recursive filter, division by zero, insufficient warmup)."
|
||||||
|
)
|
||||||
|
elif (valid == 0).all():
|
||||||
|
warnings.append(
|
||||||
|
f"⚠ WARNING: {prefix}All non-NaN output values are zero. "
|
||||||
|
"The indicator may have a computation bug "
|
||||||
|
"(e.g. log(1) always being 0, constant input, or a formula that cancels out)."
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(result, pd.Series):
|
if isinstance(result, pd.Series):
|
||||||
nan_count = int(result.isna().sum())
|
nan_count = int(result.isna().sum())
|
||||||
valid = result.dropna()
|
valid = result.dropna()
|
||||||
sample = [round(float(v), 4) for v in valid.tail(5).values] if len(valid) else []
|
sample = [round(float(v), 4) for v in valid.tail(5).values] if len(valid) else []
|
||||||
return (
|
summary = (
|
||||||
f"Series({n} bars), NaN: {nan_count}/{n}, "
|
f"Series({n} bars), NaN: {nan_count}/{n}, "
|
||||||
f"last 5 valid values: {sample}"
|
f"last 5 valid values: {sample}"
|
||||||
)
|
)
|
||||||
|
_check_series_warnings(result)
|
||||||
elif isinstance(result, pd.DataFrame):
|
elif isinstance(result, pd.DataFrame):
|
||||||
cols = list(result.columns)
|
cols = list(result.columns)
|
||||||
nan_counts = {c: int(result[c].isna().sum()) for c in cols}
|
nan_counts = {c: int(result[c].isna().sum()) for c in cols}
|
||||||
@@ -77,13 +96,18 @@ def summarize(result, n: int) -> str:
|
|||||||
valid = result[col].dropna()
|
valid = result[col].dropna()
|
||||||
if len(valid):
|
if len(valid):
|
||||||
sample[col] = [round(float(v), 4) for v in valid.tail(3).values]
|
sample[col] = [round(float(v), 4) for v in valid.tail(3).values]
|
||||||
return (
|
_check_series_warnings(result[col], label=col)
|
||||||
|
summary = (
|
||||||
f"DataFrame({n} bars × {len(cols)} cols {cols}), "
|
f"DataFrame({n} bars × {len(cols)} cols {cols}), "
|
||||||
f"NaN counts: {nan_counts}, last 3 valid per col: {sample}"
|
f"NaN counts: {nan_counts}, last 3 valid per col: {sample}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return f"Unexpected return type: {type(result).__name__}"
|
return f"Unexpected return type: {type(result).__name__}"
|
||||||
|
|
||||||
|
if warnings:
|
||||||
|
return summary + "\n" + "\n".join(warnings)
|
||||||
|
return summary
|
||||||
|
|
||||||
|
|
||||||
def run(impl_path: Path, metadata_path: Path) -> dict:
|
def run(impl_path: Path, metadata_path: Path) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user