Install
openclaw skills install futu-trading-botUse Futu Trade Bot Skills to run account, quote, and trade workflows with real HK market data.
openclaw skills install futu-trading-botEnglish Version: A trading bot skill based on Futu OpenAPI that enables natural language trading. This skill encapsulates Futu's market quote and order execution APIs, allowing agents to perform real-time trading operations through simple commands or scripts. Perfect for implementing natural language trading strategies and automated workflows.
Important: Always use the encapsulated functions provided in this skill (e.g., submit_order, get_market_snapshot). Never call Futu SDK functions directly (ctx.place_order, ctx.get_market_snapshot), as this will bypass connection management, parameter validation, and error handling, leading to unpredictable failures and resource leaks.
中文版本: 基于富途牛牛API接口的交易机器人技能,帮助用户用自然语言进行交易。本技能已将富途牛牛的行情报价、下单交易等功能做了完整封装,可供智能助手随时调用。建议通过命令行或脚本来实现自然语言的策略生成和交易执行。
重要提示:请始终使用本技能提供的封装函数(如 submit_order、get_market_snapshot)。切勿直接调用富途SDK的原始函数(例如 ctx.place_order),否则会绕过连接管理、参数校验和错误处理,导致不可预料的失败和资源泄漏。
Use this skill when the user asks in natural language, for example:
Note to agent: When the user expresses any of these intents, you should use the encapsulated functions provided in this skill (e.g., get_account_info, get_market_snapshot, submit_order, etc.). Never call Futu SDK functions directly – always go through the skill's API.
Prerequisites / 前提条件:
host / elevated mode.~/.com.futunn.FutuOpenD/Log, so restricted sandboxes may fail before business functions are called.host / elevated 模式。~/.com.futunn.FutuOpenD/Log 下的日志目录;因此受限沙箱可能会在业务函数执行前就失败。Setup Steps / 安装步骤:
Install this skill via ClawHub (if not installed yet):
clawhub install futu-trading-bot
Enter the skill folder (default OpenClaw workspace path):
cd ~/.openclaw/workspace/skills/futu-trading-bot
If you installed to a different location, cd into that folder instead.
Create virtual environment (recommended):
python3 -m venv .venv
source .venv/bin/activate
Install package:
pip install -e .
Configure credentials:
cp json/config_example.json json/config.json
# Edit json/config.json with your Futu credentials
# 编辑json/config.json填写你的富途账户信息
本技能通过 pip install -e . 自动安装以下核心 Python 包:
futu-api(富途 SDK)pydantic(数据校验)更多依赖请以 pyproject.toml / requirements.txt 为准。
account_manager
get_account_info()unlock_trade(password=None, password_md5=None)lock_trade()quote_service
get_stock_basicinfo, get_market_statesubscribe, unsubscribe, unsubscribe_all, query_subscription, callbacksget_market_snapshot, get_cur_kline, request_history_kline, get_rt_tickerstart_quote_stream, start_orderbook_streamtrade_service
submit_order, modify_order, cancel_order, cancel_all_ordersstrategy_runtime
run_strategystrategy
preflight_check first to verify config, OpenD connectivity, and sandbox/runtime readiness.get_account_info() and select target account (get acc_id).HK.00700).unlock_trade(...) (password from config or input).acc_id and trd_env.lock_trade() if needed.get_market_snapshot, get_stock_basicinfo, get_market_state, get_cur_kline, request_history_kline, and get_rt_ticker now close their quote context automatically after returning.submit_order, modify_order, and cancel_all_orders now close their trade/quote contexts automatically after returning.get_account_info, unlock_trade, and lock_trade now close their contexts automatically after returning.subscribe, unsubscribe, unsubscribe_all, query_subscription, set_quote_callback, and set_orderbook_callback, call close_quote_service() explicitly when you are done with the session.# Always use these import paths – do not import from futu directly
from preflight_check import run_preflight
from strategy import (
StrategyState, TradeGuard, in_trading_window,
trading_window_status, cooldown_elapsed, holding_timeout_exceeded
)
from strategy_runtime import run_strategy
from account_manager import get_account_info, unlock_trade, lock_trade
from quote_service import (
get_stock_basicinfo, get_market_state, get_market_snapshot,
get_cur_kline, request_history_kline, get_rt_ticker,
subscribe, unsubscribe, unsubscribe_all, query_subscription,
set_quote_callback, set_orderbook_callback,
start_quote_stream, start_orderbook_stream
)
from trade_service import submit_order, modify_order, cancel_order, cancel_all_orders
preflight = run_preflight()
if not preflight["success"]:
print(preflight)
raise SystemExit("Preflight failed")
# Get list of accounts
info = get_account_info()
if info['success']:
accounts = info['accounts']
print(accounts)
# Unlock trade (uses password from config or provided)
unlock_trade() # will prompt for password if not configured
# Or with explicit password:
# unlock_trade(password="your_password")
# Lock trade
lock_trade()
get_stock_basicinfo(market="HK", sec_type="STOCK", code_list=["HK.00700"])
get_market_state(["HK.00700"])
snap = get_market_snapshot(["HK.00700"])
if snap['success']:
price = snap['data'][0]['last_price']
# Current K-line (requires subscription, will auto-subscribe if needed)
kline = get_cur_kline(code="HK.00700", num=5, ktype="K_DAY", autype="QFQ")
# Historical K-line
hist = request_history_kline(
code="HK.00700",
start="2026-02-20",
end="2026-03-06",
ktype="K_DAY"
)
tickers = get_rt_ticker(code="HK.00700", num=10)
def on_quote(payload):
print(payload)
set_quote_callback(on_quote)
subscribe(["HK.00700"], ["QUOTE"], is_first_push=True, subscribe_push=True)
query_subscription()
unsubscribe(["HK.00700"], ["QUOTE"])
unsubscribe_all()
close_quote_service()
def on_quote(payload):
print(payload)
start_quote_stream(["HK.00700"], on_quote)
state = StrategyState()
guard = TradeGuard()
if in_trading_window(start_time="09:30", end_time="16:00"):
with guard.locked():
pass
# Submit an order
result = submit_order(
code="HK.00700",
side="BUY",
qty=200,
acc_id=6017237, # from get_account_info
trd_env="SIMULATE", # or "REAL"
price=150, # required for LIMIT order
order_type="NORMAL",
)
# Modify order (change price/quantity)
modify_order(
op="NORMAL",
order_id="123456789",
trd_env="SIMULATE",
price=151,
qty=200,
acc_id=6017237
)
# Cancel a single order
cancel_order(order_id="123456789", trd_env="SIMULATE", acc_id=6017237)
# Cancel all orders
cancel_all_orders(trd_env="SIMULATE", acc_id=6017237)
This skill does not manage long-running processes internally. Instead, you (the agent) should use system tools (e.g., exec, write, kill) to run strategy scripts in the background. This keeps the skill simple and leverages the platform's process management.
PYTHONPATH=src python -m preflight_check.host / elevated mode before using quote/trade functions.start_quote_stream, submit_order, run_strategy).write tool or similar).exec tool, redirecting output to a log file.ps, kill, cat).| User Request | Agent Action |
|---|---|
| “Start a strategy to monitor Tencent, buy below 540, sell above 550” | Generate script → save → launch with exec → return PID and log path |
| “How is my strategy doing?” | Read log file (e.g., tail -n 20 logfile) → summarize |
| “Stop my strategy” | Kill process using PID via kill tool |
When generating a strategy script, use the following template. It handles signals, logging, and state persistence correctly.
#!/usr/bin/env python3
import sys
import time
import json
import os
import signal
import logging
from pathlib import Path
# If you installed the skill with `pip install -e .`, you can import modules directly.
# Only use sys.path/PYTHONPATH hacks when you didn't install the package.
from trade_service import submit_order
from quote_service import get_market_snapshot
# ===== Strategy parameters – fill by agent =====
# Replace these placeholders with your own strategy settings.
SYMBOL = "HK.00700"
ACC_ID = 0 # fill from get_account_info()
TRD_ENV = "SIMULATE" # default to SIMULATE; use REAL only with explicit confirmation
QTY = 0 # position sizing / order quantity
LOG_FILE = Path("strategy.log")
PID_FILE = Path("strategy.pid")
# ===============================================
logging.basicConfig(
filename=LOG_FILE,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Handle termination signals
def handle_exit(signum, frame):
logging.info("Received signal, stopping strategy")
sys.exit(0)
signal.signal(signal.SIGTERM, handle_exit)
signal.signal(signal.SIGINT, handle_exit)
# Write PID file
with open(PID_FILE, "w") as f:
f.write(str(os.getpid()))
logging.info(f"Strategy started: {SYMBOL}")
try:
while True:
snap = get_market_snapshot([SYMBOL])
if not snap["success"]:
logging.error(f"Quote failed: {snap['message']}")
time.sleep(60)
continue
price = snap["data"][0]["last_price"]
logging.info(f"Current price: {price}")
# --- Insert your strategy logic here ---
# Decide whether to trade based on your own signals/logic, then call submit_order(...).
time.sleep(60) # check every minute
except Exception as e:
logging.exception("Strategy crashed")
finally:
if PID_FILE.exists():
PID_FILE.unlink()
User: “Start a range strategy for Tencent, buy below 540, sell above 550.”
Agent:
get_account_info().range_00700.py, using write tool.exec:
cd /path/to/workspace
nohup .venv/bin/python range_00700.py > strategy.out 2>&1 &
(Capture the PID from output.)User: “How is my strategy doing?”
Agent:
tail -n 20 strategy_00700.log.ps -p 12345.User: “Stop my strategy.”
Agent:
kill 12345.success (bool) and message (str).data or order_id may be present.success first before using other fields.Example:
result = submit_order(...)
if result["success"]:
print(f"Order ID: {result['order_id']}")
else:
print(f"Error: {result['message']}")
If OpenD connection fails, recheck:
lsof -i :11111)config.json matches OpenDIf the skill fails before quote/trade functions are even called, recheck:
host / elevated mode~/.com.futunn.FutuOpenD/LogPYTHONPATH=src python -m preflight_check first and follow its suggestionsjson/config.jsonfutu_api.host (default: 127.0.0.1)futu_api.port (default: 11111)futu_api.security_firm (e.g., FUTUSECURITIES)trade_password_md5 (32‑char lowercase MD5)trade_password (will be MD5‑ed at runtime)json/account_info.json (auto‑generated after get_account_info)This skill is licensed under MIT-0 (MIT No Attribution).
Copyright © 2026 jeffersonling1217-png