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:
@@ -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
|
||||
# 发送买入订单
|
||||
|
||||
@@ -179,6 +179,11 @@ python test_trading_service.py
|
||||
]
|
||||
```
|
||||
|
||||
**新规则**:若同时指定 `sl` 和 `tp`,
|
||||
- 买入指令要求 `sl < price < tp`;
|
||||
- 卖出指令要求 `tp < price < sl`。
|
||||
不满足规则的指令将被服务器拒绝并忽略。
|
||||
|
||||
**响应**:
|
||||
```json
|
||||
{
|
||||
|
||||
+31
-1
@@ -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
@@ -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")
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user