diff --git a/QUICKSTART.md b/QUICKSTART.md index 48fee6f..f0dce33 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -91,6 +91,11 @@ stats = client.get_statistics(count=5) ### 方式 C: HTTP请求 > **说明**: 对于 `POST /send_trade_instructions` 接口,若指令中未提供 `tp` 或 `tp<=0`,服务端会自动设置为 `0.005`;`sl`缺失则保留为`0.0`。 +> +> **价格验证规则**: +> - 买入指令必须满足 `sl < price < tp`。 +> - 卖出指令必须满足 `tp < price < sl`。 +> 若同时指定了 sl 与 tp 但不满足条件,该条指令会被拒绝。 ```bash # 发送买入订单 diff --git a/README.md b/README.md index 3fb1b3b..5babb86 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,11 @@ python test_trading_service.py ] ``` +**新规则**:若同时指定 `sl` 和 `tp`, +- 买入指令要求 `sl < price < tp`; +- 卖出指令要求 `tp < price < sl`。 +不满足规则的指令将被服务器拒绝并忽略。 + **响应**: ```json { diff --git a/api_examples.py b/api_examples.py index 9efb9a0..698bc1f 100644 --- a/api_examples.py +++ b/api_examples.py @@ -39,6 +39,35 @@ def example_1_basic_trade(): return result +def example_2a_invalid_order(): + """示例 2a: 包含无效指令的下单示例""" + print("\n" + "="*60) + print("示例 2a: 发送包含错误价格的订单") + print("="*60) + + url = "http://localhost:8000/send_trade_instructions" + + trades = [ + { + "symbol": "eurusd", + "action": "b", + "mount": 0.01, + # 指令价格低于止损,应该被拒绝 + "price": 1.0000, + "sl": 1.0050, + "tp": 1.0100 + } + ] + + response = requests.post(url, json=trades) + result = response.json() + + print(f"请求: {json.dumps(trades, ensure_ascii=False)}") + print(f"响应: {json.dumps(result, indent=2, ensure_ascii=False)}") + + return result + + # ==================== 示例 2: 批量下单 ==================== def example_2_batch_orders(): @@ -142,7 +171,7 @@ def example_4_query_statistics(): if positions: print(f" 持仓数: {len(positions)}") for pos in positions: - print(f" - 票证: {pos.get('ticket')}, 浮盈: ${pos.get('profit'):.2f}}") + print(f" - 票证: {pos.get('ticket')}, 浮盈: ${pos.get('profit'):.2f}") trades = stat.get('trades', []) if trades: @@ -391,6 +420,7 @@ def main(): examples = [ ("基础交易", example_1_basic_trade), ("批量下单", example_2_batch_orders), + ("错误指令示例", example_2a_invalid_order), ("查询指令", example_3_query_pending), ("查询统计", example_4_query_statistics), ("清空指令", example_5_clear_orders), diff --git a/routes_trader.py b/routes_trader.py index 9629cd9..095553a 100644 --- a/routes_trader.py +++ b/routes_trader.py @@ -58,10 +58,15 @@ def create_trader_routes(server: TradingServer) -> APIRouter: } ``` """ - count = server.add_trade_instruction(instructions) + result = server.add_trade_instruction(instructions) + added = result.get("added", 0) + rejected = result.get("rejected", 0) + msg = f"已添加 {added} 条交易指令" + if rejected > 0: + msg += f",{rejected} 条因价格不合法被拒绝" return { "status": "ok", - "message": f"已添加 {count} 条交易指令" + "message": msg } @router.get("/query_pending_trades") diff --git a/server.py b/server.py index 55ed668..c2b53a6 100644 --- a/server.py +++ b/server.py @@ -28,16 +28,23 @@ class TradingServer: print("[信息] 交易服务已初始化") - def add_trade_instruction(self, instructions: List[TradeInstruction]) -> int: + def add_trade_instruction(self, instructions: List[TradeInstruction]) -> dict: """ 添加交易指令 - 返回添加的指令数量 + 返回一个字典,包含添加和拒绝的数量。 + 在此处对缺失的 sl/tp 值进行补全: - sl 若未设置保持0.0 - tp 若未设置则默认 0.005 + + 同时按照规则进行价格检查: + * 买入指令: price 需大于 sl 且小于 tp + * 卖出指令: price 需小于 sl 且大于 tp + 若 sl 或 tp 未设置(<=0),则忽略检查。 """ with self.lock: - count = 0 + added = 0 + rejected = 0 for instruction in instructions: # 填充默认值 if instruction.sl is None: @@ -45,15 +52,29 @@ class TradingServer: if instruction.tp is None or instruction.tp <= 0.0: instruction.tp = 0.005 + # 验证价格与 SL/TP 关系 + if instruction.sl > 0 and instruction.tp > 0: + if instruction.action.lower() == 'b': + if not (instruction.price > instruction.sl and instruction.price < instruction.tp): + print(f"[警告] 忽略无效买入指令: {instruction}") + rejected += 1 + continue + elif instruction.action.lower() == 's': + if not (instruction.price < instruction.sl and instruction.price > instruction.tp): + print(f"[警告] 忽略无效卖出指令: {instruction}") + rejected += 1 + continue + symbol = instruction.symbol.upper() self.trade_instructions[symbol].append(instruction) - count += 1 - - print(f"[信息] 已添加 {count} 条交易指令") + added += 1 + + print(f"[信息] 已添加 {added} 条交易指令, 拒绝 {rejected} 条") for symbol, trades in self.trade_instructions.items(): print(f" {symbol}: {len(trades)} 条待执行") - - return count + + return {"added": added, "rejected": rejected} + def get_trades_by_symbol(self, symbol: str, price: Optional[float] = None) -> List[Dict]: """ diff --git a/test_trading_service.py b/test_trading_service.py index 6ae8f26..6fbf570 100644 --- a/test_trading_service.py +++ b/test_trading_service.py @@ -85,13 +85,30 @@ class TradingServiceTester: print(f"指令内容: {json.dumps(instructions, indent=2, ensure_ascii=False)}") try: + # 首先发送几条合法指令 response = self.session.post( f"{self.base_url}/send_trade_instructions", json=instructions ) print(f"状态码: {response.status_code}") print(f"响应: {json.dumps(response.json(), indent=2, ensure_ascii=False)}") - return response.status_code == 200 + if response.status_code != 200: + return False + # 再发送一条无效指令,以验证检查逻辑 + bad = [{ + "symbol": "eurusd", + "action": "b", + "mount": 0.01, + "price": 1.0000, + "sl": 1.0050, # 价格低于SL + "tp": 1.0100 + }] + bad_resp = self.session.post( + f"{self.base_url}/send_trade_instructions", + json=bad + ) + print("发送无效指令后响应:", bad_resp.status_code, bad_resp.json()) + return bad_resp.status_code == 200 and "拒绝" in bad_resp.json().get("message", "") except Exception as e: print(f"错误: {str(e)}") return False