This commit is contained in:
薇薇安 2026-01-15 10:06:32 +08:00
parent 54430a0e06
commit 209a5cd376
5 changed files with 48 additions and 7 deletions

View File

@ -50,6 +50,8 @@ async def get_trades(
end_date: Optional[str] = Query(None, description="结束日期 (YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)"),
period: Optional[str] = Query(None, description="快速时间段筛选: '1d'(最近1天), '7d'(最近7天), '30d'(最近30天)"),
symbol: Optional[str] = Query(None, description="交易对筛选"),
trade_type: Optional[str] = Query(None, description="交易类型筛选: 'buy', 'sell'"),
exit_reason: Optional[str] = Query(None, description="平仓原因筛选: 'stop_loss', 'take_profit', 'trailing_stop', 'manual', 'sync'"),
status: Optional[str] = Query(None, description="状态筛选: 'open', 'closed', 'cancelled'"),
limit: int = Query(100, ge=1, le=1000, description="返回记录数限制")
):

View File

@ -112,7 +112,7 @@ class Trade:
)
@staticmethod
def get_all(start_date=None, end_date=None, symbol=None, status=None):
def get_all(start_date=None, end_date=None, symbol=None, status=None, trade_type=None, exit_reason=None):
"""获取交易记录"""
query = "SELECT * FROM trades WHERE 1=1"
params = []
@ -129,6 +129,12 @@ class Trade:
if status:
query += " AND status = %s"
params.append(status)
if trade_type:
query += " AND side = %s"
params.append(trade_type)
if exit_reason:
query += " AND exit_reason = %s"
params.append(exit_reason)
query += " ORDER BY entry_time DESC"
return db.execute_query(query, params)

View File

@ -30,12 +30,12 @@
gap: 1rem;
}
@media (min-width: 768px) {
/* @media (min-width: 768px) {
.dashboard-grid {
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 2rem;
}
}
} */
.dashboard-card {
background: #f8f9fa;
@ -128,6 +128,8 @@
border: 1px solid #e9ecef;
}
@media (min-width: 768px) {
.trade-item {
flex-direction: row;

View File

@ -145,6 +145,10 @@ const StatsDashboard = () => {
</div>
<div className="trade-info">
<div>数量: {parseFloat(trade.quantity || 0).toFixed(4)}</div>
<div>保证金: {margin.toFixed(2)} USDT</div>
{trade.entry_time && (
<div className="entry-time">开仓时间: {formatEntryTime(trade.entry_time)}</div>
)}
<div>入场价: {parseFloat(trade.entry_price || 0).toFixed(4)}</div>
{trade.mark_price && (
<div>标记价: {parseFloat(trade.mark_price).toFixed(4)}</div>
@ -152,10 +156,6 @@ const StatsDashboard = () => {
{trade.leverage && (
<div>杠杆: {trade.leverage}x</div>
)}
<div>保证金: {margin.toFixed(2)} USDT</div>
{trade.entry_time && (
<div className="entry-time">开仓时间: {formatEntryTime(trade.entry_time)}</div>
)}
</div>
<div className="trade-actions">
<div className={`trade-pnl ${parseFloat(trade.pnl || 0) >= 0 ? 'positive' : 'negative'}`}>

View File

@ -14,6 +14,8 @@ const TradeList = () => {
const [symbol, setSymbol] = useState('')
const [status, setStatus] = useState('')
const [useCustomDate, setUseCustomDate] = useState(false)
const [tradeType, setTradeType] = useState('')
const [exitReason, setExitReason] = useState('')
useEffect(() => {
loadData()
@ -37,6 +39,8 @@ const TradeList = () => {
if (symbol) params.symbol = symbol
if (status) params.status = status
if (tradeType) params.trade_type = tradeType
if (exitReason) params.exit_reason = exitReason
const [tradesData, statsData] = await Promise.all([
api.getTrades(params),
@ -151,6 +155,33 @@ const TradeList = () => {
style={{ width: '150px' }}
/>
</div>
<div className="filter-section">
<label>交易类型</label>
<select
value={tradeType}
onChange={(e) => setTradeType(e.target.value)}
style={{ width: '120px' }}
>
<option value="">全部</option>
<option value="buy">买入</option>
<option value="sell">卖出</option>
</select>
</div>
<div className="filter-section">
<label>平仓原因</label>
<select
value={exitReason}
onChange={(e) => setExitReason(e.target.value)}
style={{ width: '120px' }}
>
<option value="">全部</option>
<option value="stop_loss">止损</option>
<option value="take_profit">止盈</option>
<option value="trailing_stop">移动止损</option>
<option value="manual">手动平仓</option>
<option value="sync">同步平仓</option>
</select>
</div>
<div className="filter-section">
<label>状态</label>