Add price/SL/TP validation for trade instructions

- Buy orders must satisfy: sl < price < tp
- Sell orders must satisfy: tp < price < sl
- Invalid instructions are rejected with message feedback
- Updated test suite with validation examples
- Updated documentation to clarify rules
This commit is contained in:
guaiwoluo2020
2026-03-04 23:14:41 +08:00
parent 2470283c22
commit 0c9cf048d2
6 changed files with 95 additions and 12 deletions
+5
View File
@@ -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
# 发送买入订单
+5
View File
@@ -179,6 +179,11 @@ python test_trading_service.py
]
```
**新规则**:若同时指定 `sl` 和 `tp`
- 买入指令要求 `sl < price < tp`
- 卖出指令要求 `tp < price < sl`。
不满足规则的指令将被服务器拒绝并忽略。
**响应**:
```json
{
+31 -1
View File
@@ -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),
+7 -2
View File
@@ -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")
+29 -8
View File
@@ -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]:
"""
+18 -1
View File
@@ -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