feat: add trade activity dashboard
Shows what happened at each candle: - BUY/SELL/HOLD actions - Price at that time - Reason for action - Entry price for positions Trade log is stored in DB and displayed in frontend.
This commit is contained in:
@@ -52,6 +52,8 @@ def run_simulation_sync(
|
||||
{"time": k.get("time"), "close": k.get("close")}
|
||||
for k in engine.klines
|
||||
]
|
||||
# Save trade log for dashboard
|
||||
simulation.trade_log = engine.trade_log
|
||||
db.commit()
|
||||
|
||||
for signal in engine.signals:
|
||||
|
||||
@@ -94,6 +94,7 @@ class Simulation(Base):
|
||||
config = Column(JSON, nullable=False)
|
||||
signals = Column(JSON)
|
||||
klines = Column(JSON) # Price data for chart display
|
||||
trade_log = Column(JSON) # Trade activity log
|
||||
|
||||
bot = relationship("Bot", back_populates="simulations")
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ class SimulationResponse(BaseModel):
|
||||
config: dict
|
||||
signals: Optional[List[dict]]
|
||||
klines: Optional[List[dict]] = None # Price data for chart
|
||||
trade_log: Optional[List[dict]] = None # Trade activity log
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
@@ -58,6 +58,9 @@ class SimulateEngine:
|
||||
# Kline data
|
||||
self.klines: List[Dict[str, Any]] = []
|
||||
self.last_processed_time: Optional[int] = None
|
||||
|
||||
# Trade log - tracks what happened at each candle
|
||||
self.trade_log: List[Dict[str, Any]] = []
|
||||
|
||||
async def run(self) -> Dict[str, Any]:
|
||||
self.running = True
|
||||
@@ -130,6 +133,7 @@ class SimulateEngine:
|
||||
self.results["signals"] = self.signals
|
||||
self.results["candles_processed"] = candles_processed if self.running else 0
|
||||
self.results["klines"] = self.klines # Include klines for chart display
|
||||
self.results["trade_log"] = self.trade_log # Include trade log for dashboard
|
||||
self.results["started_at"] = self.started_at
|
||||
self.results["ended_at"] = datetime.utcnow()
|
||||
|
||||
@@ -165,19 +169,60 @@ class SimulateEngine:
|
||||
):
|
||||
"""Process a single candle - check conditions and risk management."""
|
||||
|
||||
action = "hold" # Default action
|
||||
reason = ""
|
||||
|
||||
# Check risk management first (for open positions)
|
||||
if self.position > 0 and self.entry_price is not None:
|
||||
exit_info = self._check_risk_management(close_price, timestamp)
|
||||
if exit_info:
|
||||
await self._execute_risk_exit(close_price, timestamp, exit_info)
|
||||
return # Skip condition check if we just exited
|
||||
action = "sell"
|
||||
reason = exit_info["reason"]
|
||||
# Log the action
|
||||
self.trade_log.append({
|
||||
"time": timestamp,
|
||||
"price": close_price,
|
||||
"action": action,
|
||||
"reason": reason,
|
||||
"position": self.position,
|
||||
"entry_price": self.entry_price,
|
||||
})
|
||||
return
|
||||
|
||||
# Check conditions (only if no open position)
|
||||
if self.position == 0:
|
||||
for condition in self.conditions:
|
||||
if self._check_condition(condition, close_price, volume):
|
||||
await self._execute_actions(close_price, timestamp, condition)
|
||||
action = "buy"
|
||||
reason = f"{condition.get('type')} {condition.get('threshold')}%".format(
|
||||
type=condition.get('type'),
|
||||
threshold=condition.get('threshold')
|
||||
)
|
||||
# Log the action
|
||||
self.trade_log.append({
|
||||
"time": timestamp,
|
||||
"price": close_price,
|
||||
"action": action,
|
||||
"reason": reason,
|
||||
"position": self.position,
|
||||
"entry_price": self.entry_price,
|
||||
})
|
||||
break
|
||||
|
||||
# Log hold action (no signal)
|
||||
if action == "hold":
|
||||
# Only log every 10th candle to reduce data
|
||||
if len(self.trade_log) == 0 or (len(self.klines) - len(self.trade_log) > 10):
|
||||
self.trade_log.append({
|
||||
"time": timestamp,
|
||||
"price": close_price,
|
||||
"action": "hold",
|
||||
"reason": "no_signal",
|
||||
"position": self.position,
|
||||
"entry_price": self.entry_price,
|
||||
})
|
||||
|
||||
def _check_risk_management(
|
||||
self, current_price: float, timestamp: int
|
||||
|
||||
Reference in New Issue
Block a user