Compare commits

..

4 Commits

Author SHA1 Message Date
shokollm
d81464b869 fix: flatten strategy config schema to match engine expectations
LLM was outputting nested params structure but engines expect flat fields.
This caused backtesting and simulation to never trigger any trades.

Changes:
- llm_connector.py: Update prompt to output flat condition structure
- crew.py: Update StrategyValidator to validate flat structure
- crew.py: Update StrategyExplainer to read flat structure

Fixes #25
2026-04-09 07:31:09 +00:00
55b008d4e8 Merge pull request 'fix: validate chain is 'bsc' for Phase 1' (#34) from fix/issue-31 into main 2026-04-09 09:10:55 +02:00
shokollm
04e4c1a487 fix: validate chain is 'bsc' for BacktestCreate and SimulationCreate 2026-04-09 06:58:16 +00:00
feb65131fa Merge pull request 'fix: populate config endpoints with chain and token data' (#33) from fix/issue-27 into main 2026-04-09 08:23:43 +02:00
3 changed files with 43 additions and 36 deletions

View File

@@ -1,5 +1,5 @@
from pydantic import BaseModel, EmailStr
from typing import Optional, List, Any
from pydantic import BaseModel, EmailStr, field_validator
from typing import Optional, List, Any, Dict
from datetime import datetime
@@ -69,6 +69,13 @@ class BacktestCreate(BaseModel):
start_date: str
end_date: str
@field_validator("chain")
@classmethod
def chain_must_be_bsc(cls, v: str) -> str:
if v != "bsc":
raise ValueError("Phase 1 only supports BSC (bnb chain)")
return v
class BacktestResponse(BaseModel):
id: str
@@ -90,6 +97,13 @@ class SimulationCreate(BaseModel):
check_interval: int = 60
auto_execute: bool = False
@field_validator("chain")
@classmethod
def chain_must_be_bsc(cls, v: str) -> str:
if v != "bsc":
raise ValueError("Phase 1 only supports BSC (bnb chain)")
return v
class SimulationResponse(BaseModel):
id: str

View File

@@ -33,29 +33,24 @@ class StrategyValidator:
errors.append(f"Condition {i}: unsupported type '{cond_type}'")
continue
params = condition.get("params", {})
if cond_type in ["price_drop", "price_rise", "volume_spike"]:
if "token" not in params:
if "token" not in condition:
errors.append(f"Condition {i}: missing 'token'")
if "threshold_percent" not in params:
errors.append(f"Condition {i}: missing 'threshold_percent'")
elif not isinstance(params["threshold_percent"], (int, float)):
errors.append(
f"Condition {i}: 'threshold_percent' must be a number"
)
elif params["threshold_percent"] <= 0:
errors.append(
f"Condition {i}: 'threshold_percent' must be positive"
)
if "threshold" not in condition:
errors.append(f"Condition {i}: missing 'threshold'")
elif not isinstance(condition["threshold"], (int, float)):
errors.append(f"Condition {i}: 'threshold' must be a number")
elif condition["threshold"] <= 0:
errors.append(f"Condition {i}: 'threshold' must be positive")
elif cond_type == "price_level":
if "token" not in params:
if "token" not in condition:
errors.append(f"Condition {i}: missing 'token'")
if "price" not in params:
if "price" not in condition:
errors.append(f"Condition {i}: missing 'price'")
if "direction" not in params:
if "direction" not in condition:
errors.append(f"Condition {i}: missing 'direction'")
elif params["direction"] not in ["above", "below"]:
elif condition["direction"] not in ["above", "below"]:
errors.append(
f"Condition {i}: direction must be 'above' or 'below'"
)
@@ -85,23 +80,22 @@ class StrategyExplainer:
explanations.append("This strategy will trigger when:")
for cond in cond_list:
cond_type = cond.get("type")
params = cond.get("params", {})
token = params.get("token", "the token")
token = cond.get("token", "the token")
if cond_type == "price_drop":
pct = params.get("threshold_percent", 0)
pct = cond.get("threshold", 0)
explanations.append(f" - {token} price drops by {pct}%")
elif cond_type == "price_rise":
pct = params.get("threshold_percent", 0)
pct = cond.get("threshold", 0)
explanations.append(f" - {token} price rises by {pct}%")
elif cond_type == "volume_spike":
pct = params.get("threshold_percent", 0)
pct = cond.get("threshold", 0)
explanations.append(
f" - {token} trading volume increases by {pct}%"
)
elif cond_type == "price_level":
price = params.get("price", 0)
direction = params.get("direction", "unknown")
price = cond.get("price", 0)
direction = cond.get("direction", "unknown")
explanations.append(
f" - {token} price crosses {direction} ${price}"
)

View File

@@ -61,9 +61,9 @@ class MiniMaxConnector:
system_prompt = """You are a trading strategy designer. Parse the user's natural language request into a JSON strategy_config object.
Supported conditions (MVP):
- price_drop: Token price drops by X% (requires: token, threshold_percent)
- price_rise: Token price rises by X% (requires: token, threshold_percent)
- volume_spike: Trading volume increases X% (requires: token, threshold_percent)
- price_drop: Token price drops by X% (requires: token, threshold)
- price_rise: Token price rises by X% (requires: token, threshold)
- volume_spike: Trading volume increases X% (requires: token, threshold)
- price_level: Price crosses above/below X (requires: token, price, direction)
Output ONLY valid JSON with this schema:
@@ -71,18 +71,17 @@ Output ONLY valid JSON with this schema:
"conditions": [
{
"type": "price_drop|price_rise|volume_spike|price_level",
"params": {
"token": "TOKEN_SYMBOL",
"threshold_percent": number, // for price_drop, price_rise, volume_spike
"chain": "bsc",
"threshold": number, // for price_drop, price_rise, volume_spike
"price": number, // for price_level
"direction": "above|below" // for price_level
}
"direction": "above|below", // for price_level
"timeframe": "1h"
}
],
"actions": [
{
"type": "buy|sell|notify",
"params": {}
"type": "buy|sell|notify"
}
]
}