{
 "$schema": "https://json-schema.org/draft/2020-12/schema",
 "title": "Chartroom Signals results",
 "type": "object",
 "required": [
  "schema_version",
  "generated_at",
  "source",
  "group",
  "methodology",
  "summary",
  "open_signals",
  "periods",
  "equity_curve",
  "signals"
 ],
 "$defs": {
  "tradeRef": {
   "type": [
    "object",
    "null"
   ],
   "properties": {
    "id": {
     "type": "integer"
    },
    "symbol": {
     "type": "string"
    },
    "pair": {
     "type": "string"
    },
    "side": {
     "enum": [
      "LONG",
      "SHORT"
     ]
    },
    "result_pct": {
     "type": "number"
    },
    "result_r": {
     "type": "number"
    }
   }
  },
  "statsLite": {
   "type": "object",
   "required": [
    "signals",
    "wins",
    "losses",
    "breakeven",
    "win_rate",
    "net_pct",
    "net_r",
    "avg_r",
    "profit_factor",
    "account_growth_pct",
    "max_drawdown_pct",
    "best",
    "worst",
    "exits"
   ],
   "properties": {
    "signals": {
     "type": "integer"
    },
    "wins": {
     "type": "integer"
    },
    "losses": {
     "type": "integer"
    },
    "breakeven": {
     "type": "integer"
    },
    "win_rate": {
     "type": [
      "number",
      "null"
     ]
    },
    "net_pct": {
     "type": "number"
    },
    "net_r": {
     "type": "number"
    },
    "avg_r": {
     "type": [
      "number",
      "null"
     ]
    },
    "profit_factor": {
     "type": [
      "number",
      "null"
     ]
    },
    "account_growth_pct": {
     "type": "number"
    },
    "max_drawdown_pct": {
     "type": "number"
    },
    "best": {
     "$ref": "#/$defs/tradeRef"
    },
    "worst": {
     "$ref": "#/$defs/tradeRef"
    },
    "exits": {
     "type": "object",
     "properties": {
      "tp3": {
       "type": "integer"
      },
      "trail": {
       "type": "integer"
      },
      "breakeven": {
       "type": "integer"
      },
      "sl": {
       "type": "integer"
      },
      "expired": {
       "type": "integer"
      },
      "time": {
       "type": "integer"
      }
     }
    },
    "by_strategy": {
     "type": "object",
     "additionalProperties": {
      "type": "object",
      "properties": {
       "signals": {
        "type": "integer"
       },
       "wins": {
        "type": "integer"
       },
       "net_pct": {
        "type": "number"
       },
       "net_r": {
        "type": "number"
       }
      }
     }
    },
    "costs": {
     "type": "object",
     "properties": {
      "gross_pct": {
       "type": "number"
      },
      "fees_pct": {
       "type": "number"
      },
      "funding_pct": {
       "type": "number"
      }
     }
    }
   }
  },
  "stats": {
   "allOf": [
    {
     "$ref": "#/$defs/statsLite"
    }
   ],
   "properties": {
    "per_pair": {
     "type": "array",
     "items": {
      "type": "object",
      "properties": {
       "symbol": {
        "type": "string"
       },
       "pair": {
        "type": "string"
       },
       "signals": {
        "type": "integer"
       },
       "wins": {
        "type": "integer"
       },
       "net_pct": {
        "type": "number"
       },
       "net_r": {
        "type": "number"
       }
      }
     }
    }
   }
  },
  "period": {
   "type": "object",
   "required": [
    "start",
    "end",
    "complete",
    "stats"
   ],
   "properties": {
    "start": {
     "type": "string"
    },
    "end": {
     "type": "string"
    },
    "complete": {
     "type": "boolean"
    },
    "stats": {
     "$ref": "#/$defs/statsLite"
    }
   }
  }
 },
 "properties": {
  "schema_version": {
   "const": "1.1"
  },
  "generated_at": {
   "type": "string",
   "format": "date-time"
  },
  "source": {
   "enum": [
    "live",
    "backtest"
   ]
  },
  "group": {
   "type": "object",
   "properties": {
    "name": {
     "type": "string"
    },
    "telegram": {
     "type": "string"
    },
    "website": {
     "type": "string",
     "description": "Public link of the group (the free Telegram channel). A placeholder until the channel exists."
    }
   }
  },
  "methodology": {
   "type": "object"
  },
  "summary": {
   "type": "object",
   "properties": {
    "all_time": {
     "$ref": "#/$defs/stats"
    },
    "last_7d": {
     "$ref": "#/$defs/stats"
    },
    "last_30d": {
     "$ref": "#/$defs/stats"
    }
   }
  },
  "open_signals": {
   "type": "integer"
  },
  "periods": {
   "type": "object",
   "properties": {
    "weekly": {
     "type": "array",
     "items": {
      "$ref": "#/$defs/period"
     }
    },
    "monthly": {
     "type": "array",
     "items": {
      "$ref": "#/$defs/period"
     }
    }
   }
  },
  "equity_curve": {
   "type": "array",
   "items": {
    "type": "object",
    "properties": {
     "after_signal_id": {
      "type": [
       "integer",
       "null"
      ]
     },
     "closed_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "equity": {
      "type": "number"
     }
    }
   }
  },
  "signals": {
   "type": "array",
   "items": {
    "type": "object",
    "required": [
     "id",
     "symbol",
     "pair",
     "side",
     "status",
     "opened_at",
     "entry",
     "stop_initial",
     "targets",
     "strategy"
    ],
    "properties": {
     "id": {
      "type": "integer"
     },
     "symbol": {
      "type": "string"
     },
     "pair": {
      "type": "string"
     },
     "side": {
      "enum": [
       "LONG",
       "SHORT"
      ]
     },
     "setup": {
      "type": [
       "string",
       "null"
      ]
     },
     "status": {
      "enum": [
       "open",
       "closed"
      ]
     },
     "opened_at": {
      "type": "string"
     },
     "closed_at": {
      "type": [
       "string",
       "null"
      ]
     },
     "entry": {
      "type": "number"
     },
     "entry_zone": {
      "type": "array",
      "items": {
       "type": "number"
      },
      "minItems": 2,
      "maxItems": 2
     },
     "stop_initial": {
      "type": "number"
     },
     "targets": {
      "type": "array",
      "items": {
       "type": "number"
      },
      "maxItems": 3,
      "description": "[tp1,tp2,tp3]; empty for rotation (time exit)"
     },
     "leverage_max": {
      "type": "integer"
     },
     "risk_pct": {
      "type": "number"
     },
     "tp_hits": {
      "type": "integer"
     },
     "close_reason": {
      "enum": [
       null,
       "tp3",
       "trail",
       "breakeven",
       "sl",
       "expired",
       "time"
      ]
     },
     "result_pct": {
      "type": [
       "number",
       "null"
      ]
     },
     "result_r": {
      "type": [
       "number",
       "null"
      ]
     },
     "telegram_message_id": {
      "type": [
       "integer",
       "null"
      ]
     },
     "strategy": {
      "enum": [
       "pullback",
       "rotation"
      ]
     },
     "hold_until": {
      "type": [
       "string",
       "null"
      ],
      "description": "planned time exit (rotation)"
     },
     "gross_pct": {
      "type": [
       "number",
       "null"
      ]
     },
     "fees_pct": {
      "type": [
       "number",
       "null"
      ]
     },
     "funding_pct": {
      "type": [
       "number",
       "null"
      ]
     },
     "tier": {
      "type": "string",
      "enum": [
       "core",
       "intraday",
       "rotation"
      ],
      "description": "Signal tier (added in v1.1 VIP build, optional). core = 1h/4h trend pullback, intraday = 30m/1h, half risk, rotation = weekly momentum."
     },
     "free": {
      "type": "boolean",
      "description": "Optional. True if the signal was also posted in the free channel (first signal of its UTC day)."
     },
     "simulated": {
      "type": "boolean",
      "description": "Optional. True for historical-simulation trades (results.backtest.json). Never true in live results.json."
     }
    }
   }
  },
  "backtest": {
   "type": [
    "object",
    "null"
   ],
   "properties": {
    "note": {
     "type": "string"
    },
    "in_sample": {
     "type": "array",
     "items": {
      "type": "string"
     }
    },
    "out_of_sample_from": {
     "type": "string"
    },
    "label": {
     "type": "string",
     "description": "Same label as the top-level field, for historical simulations."
    }
   }
  },
  "label": {
   "type": [
    "string",
    "null"
   ],
   "description": "Optional. Human-readable label that must be shown with the data, e.g. 'Historical simulation of the strategy, Aug 2025 – Oct 2026, not live trades'. Null for live results."
  }
 }
}
