Initial commit: Hermes Agent skills collection

- Trading skills (OKX, dividend, lottery, quantitative)
- Creative skills (ASCII art, diagrams, video)
- Development skills (GitHub, debugging, TDD)
- Research skills (arXiv, blog monitoring)
- Productivity skills (email, documents, notes)
- MCP integration skills
- Custom user skills
This commit is contained in:
Hermes Skills Manager
2026-07-05 02:31:15 -04:00
commit 6770bc9b9d
908 changed files with 239614 additions and 0 deletions
@@ -0,0 +1,147 @@
"""账户与持仓命令模块"""
import click
from longbridge.openapi import TradeContext
from longbridge_cli.config import get_config
from longbridge_cli.formatters import print_table, print_json, print_error
@click.command("balance")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def balance_cmd(ctx, output_json):
"""查看账户余额与净资产
示例:longbridge balance
"""
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.account_balance()
except Exception as e:
print_error(str(e))
if output_json:
data = [
{
"currency": b.currency,
"total_cash": float(b.total_cash),
"max_finance_amount": float(b.max_finance_amount),
"remaining_finance_amount": float(b.remaining_finance_amount),
"risk_level": b.risk_level,
"margin_call": float(b.margin_call),
"net_assets": float(b.net_assets),
"init_margin": float(b.init_margin),
"maintenance_margin": float(b.maintenance_margin),
}
for b in resp
]
print_json(data)
else:
headers = ["币种", "现金余额", "净资产", "最大融资额", "剩余融资额", "风险等级"]
rows = [
[
b.currency,
f"{float(b.total_cash):,.2f}",
f"{float(b.net_assets):,.2f}",
f"{float(b.max_finance_amount):,.2f}",
f"{float(b.remaining_finance_amount):,.2f}",
str(b.risk_level),
]
for b in resp
]
print_table(headers, rows, title="账户余额")
@click.command("positions")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def positions_cmd(ctx, output_json):
"""查看股票持仓
示例:longbridge positions
"""
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.stock_positions()
except Exception as e:
print_error(str(e))
channels = resp.channels if resp else []
if output_json:
data = []
for ch in channels:
for p in ch.positions:
data.append({
"symbol": p.symbol,
"symbol_name": p.symbol_name,
"quantity": p.quantity,
"available_quantity": p.available_quantity,
"currency": p.currency,
"cost_price": float(p.cost_price),
"init_quantity": p.init_quantity,
"market": str(p.market),
})
print_json(data)
else:
headers = ["标的", "名称", "持仓", "可卖", "成本价", "初始持仓", "市场", "币种"]
rows = []
for ch in channels:
for p in ch.positions:
rows.append([
p.symbol,
p.symbol_name,
str(p.quantity),
str(p.available_quantity),
f"{float(p.cost_price):.3f}",
str(p.init_quantity),
str(p.market),
p.currency,
])
print_table(headers, rows, title="股票持仓")
@click.command("funds")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def funds_cmd(ctx, output_json):
"""查看基金持仓
示例:longbridge funds
"""
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.fund_positions()
except Exception as e:
print_error(str(e))
channels = resp.channels if resp else []
if output_json:
data = []
for ch in channels:
for f in ch.positions:
data.append({
"symbol": f.symbol,
"symbol_name": f.symbol_name,
"holding_units": float(f.holding_units),
"current_net_asset_value": float(f.current_net_asset_value),
"cost_net_asset_value": float(f.cost_net_asset_value),
"net_asset_value_day": f.net_asset_value_day.isoformat() if f.net_asset_value_day else None,
"market_value": float(f.market_value) if hasattr(f, "market_value") else None,
})
print_json(data)
else:
headers = ["基金代码", "名称", "持有份额", "当前净值", "成本净值", "净值日期"]
rows = []
for ch in channels:
for f in ch.positions:
rows.append([
f.symbol,
f.symbol_name,
f"{float(f.holding_units):.4f}",
f"{float(f.current_net_asset_value):.4f}",
f"{float(f.cost_net_asset_value):.4f}",
f.net_asset_value_day.strftime("%Y-%m-%d") if f.net_asset_value_day else "-",
])
print_table(headers, rows, title="基金持仓")
@@ -0,0 +1,162 @@
"""市场数据命令模块"""
import click
from longbridge.openapi import QuoteContext, Market
from longbridge_cli.config import get_config
from longbridge_cli.formatters import print_table, print_json, print_kv, print_error
MARKET_MAP = {
"US": Market.US,
"HK": Market.HK,
"CN": Market.CN,
"SG": Market.SG,
}
@click.command("temperature")
@click.argument("market", type=click.Choice(["US", "HK", "CN", "SG"]))
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def temperature_cmd(ctx, market, output_json):
"""查看市场温度
MARKET 可选:US HK CN SG
示例:longbridge temperature US
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.market_temperature(MARKET_MAP[market])
except Exception as e:
print_error(str(e))
if output_json:
data = {
"market": market,
"temperature": resp.temperature,
"description": resp.description,
"valuation": float(resp.valuation) if hasattr(resp, "valuation") else None,
}
print_json(data)
else:
pairs = [
("市场", market),
("温度", resp.temperature),
("描述", resp.description),
]
if hasattr(resp, "valuation"):
pairs.append(("估值", float(resp.valuation)))
print_kv(pairs, title=f"市场温度 - {market}")
@click.command("capital-flow")
@click.argument("symbol")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def capital_flow_cmd(ctx, symbol, output_json):
"""查看资金流向
示例:longbridge capital-flow 700.HK
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.capital_flow(symbol)
except Exception as e:
print_error(str(e))
if output_json:
data = [
{
"timestamp": item.timestamp.isoformat() if item.timestamp else None,
"inflow": float(item.inflow),
}
for item in resp
]
print_json(data)
else:
headers = ["时间", "净流入"]
rows = [
[
item.timestamp.strftime("%Y-%m-%d %H:%M") if item.timestamp else "-",
f"{float(item.inflow):+,.0f}",
]
for item in resp
]
print_table(headers, rows, title=f"资金流向 - {symbol}")
@click.command("capital-dist")
@click.argument("symbol")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def capital_dist_cmd(ctx, symbol, output_json):
"""查看资金分布
示例:longbridge capital-dist 700.HK
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.capital_distribution(symbol)
except Exception as e:
print_error(str(e))
if output_json:
data = {
"symbol": symbol,
"timestamp": resp.timestamp.isoformat() if resp.timestamp else None,
"capital_in": {
"large": float(resp.capital_in.large),
"medium": float(resp.capital_in.medium),
"small": float(resp.capital_in.small),
},
"capital_out": {
"large": float(resp.capital_out.large),
"medium": float(resp.capital_out.medium),
"small": float(resp.capital_out.small),
},
}
print_json(data)
else:
headers = ["方向", "大单", "中单", "小单"]
rows = [
[
"流入",
f"{float(resp.capital_in.large):,.0f}",
f"{float(resp.capital_in.medium):,.0f}",
f"{float(resp.capital_in.small):,.0f}",
],
[
"流出",
f"{float(resp.capital_out.large):,.0f}",
f"{float(resp.capital_out.medium):,.0f}",
f"{float(resp.capital_out.small):,.0f}",
],
]
print_table(headers, rows, title=f"资金分布 - {symbol}")
@click.command("option-chain")
@click.argument("symbol")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def option_chain_cmd(ctx, symbol, output_json):
"""查看期权链到期日列表
示例:longbridge option-chain AAPL.US
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.option_chain_expiry_date_list(symbol)
except Exception as e:
print_error(str(e))
if output_json:
data = {
"symbol": symbol,
"expiry_dates": [d.isoformat() for d in resp],
}
print_json(data)
else:
headers = ["序号", "到期日"]
rows = [[str(i + 1), d.strftime("%Y-%m-%d")] for i, d in enumerate(resp)]
print_table(headers, rows, title=f"期权链到期日 - {symbol}")
@@ -0,0 +1,188 @@
"""订单管理命令模块"""
from datetime import datetime
from decimal import Decimal
import click
from longbridge.openapi import (
TradeContext,
OrderType,
OrderSide,
TimeInForceType,
)
from longbridge_cli.config import get_config, require_trade_enabled
from longbridge_cli.formatters import print_table, print_json, print_error
@click.command("orders")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def orders_cmd(ctx, output_json):
"""查看今日订单
示例:longbridge orders
"""
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.today_orders()
except Exception as e:
print_error(str(e))
_print_orders(resp, output_json, "今日订单")
@click.command("history-orders")
@click.option("--start", required=True, help="开始日期 (YYYY-MM-DD)")
@click.option("--end", required=True, help="结束日期 (YYYY-MM-DD)")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def history_orders_cmd(ctx, start, end, output_json):
"""查看历史订单
示例:longbridge history-orders --start 2026-01-01 --end 2026-03-14
"""
try:
start_dt = datetime.strptime(start, "%Y-%m-%d")
end_dt = datetime.strptime(end, "%Y-%m-%d")
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.history_orders(start_at=start_dt, end_at=end_dt)
except ValueError:
print_error("日期格式错误,请使用 YYYY-MM-DD")
return
except Exception as e:
print_error(str(e))
_print_orders(resp, output_json, f"历史订单 ({start} ~ {end})")
@click.command("buy")
@click.argument("symbol")
@click.option("--qty", required=True, type=int, help="买入数量")
@click.option("--price", required=True, type=float, help="限价价格")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.option("--yes", "-y", is_flag=True, help="跳过交互确认(程序化调用时使用)")
@click.pass_context
def buy_cmd(ctx, symbol, qty, price, output_json, yes):
"""限价买入
示例:longbridge buy AAPL.US --qty 100 --price 180.0
"""
require_trade_enabled()
if not yes:
click.confirm(f"确认买入 {symbol} 数量 {qty} 限价 {price}", abort=True)
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.submit_order(
symbol,
OrderType.LO,
OrderSide.Buy,
qty,
TimeInForceType.Day,
submitted_price=Decimal(str(price)),
)
except Exception as e:
print_error(str(e))
return
if output_json:
print_json({"order_id": resp.order_id})
else:
click.echo(f"下单成功,订单号:{resp.order_id}")
@click.command("sell")
@click.argument("symbol")
@click.option("--qty", required=True, type=int, help="卖出数量")
@click.option("--price", required=True, type=float, help="限价价格")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.option("--yes", "-y", is_flag=True, help="跳过交互确认(程序化调用时使用)")
@click.pass_context
def sell_cmd(ctx, symbol, qty, price, output_json, yes):
"""限价卖出
示例:longbridge sell 700.HK --qty 500 --price 320.0
"""
require_trade_enabled()
if not yes:
click.confirm(f"确认卖出 {symbol} 数量 {qty} 限价 {price}", abort=True)
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
resp = trade_ctx.submit_order(
symbol,
OrderType.LO,
OrderSide.Sell,
qty,
TimeInForceType.Day,
submitted_price=Decimal(str(price)),
)
except Exception as e:
print_error(str(e))
return
if output_json:
print_json({"order_id": resp.order_id})
else:
click.echo(f"下单成功,订单号:{resp.order_id}")
@click.command("cancel")
@click.argument("order_id")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def cancel_cmd(ctx, order_id, output_json):
"""撤销订单
示例:longbridge cancel 701234567890
"""
require_trade_enabled()
click.confirm(f"确认撤销订单 {order_id}", abort=True)
try:
trade_ctx = TradeContext(get_config(ctx.obj.get("profile")))
trade_ctx.cancel_order(order_id)
except Exception as e:
print_error(str(e))
if output_json:
print_json({"order_id": order_id, "status": "cancelled"})
else:
click.echo(f"订单 {order_id} 已撤销")
def _print_orders(orders, output_json: bool, title: str) -> None:
"""内部辅助:统一输出订单列表"""
if output_json:
data = [
{
"order_id": o.order_id,
"symbol": o.symbol,
"side": str(o.side),
"order_type": str(o.order_type),
"quantity": o.quantity,
"executed_quantity": o.executed_quantity,
"price": float(o.price) if o.price else None,
"executed_price": float(o.executed_price) if o.executed_price else None,
"status": str(o.status),
"submitted_at": o.submitted_at.isoformat() if o.submitted_at else None,
"updated_at": o.updated_at.isoformat() if o.updated_at else None,
}
for o in orders
]
print_json(data)
else:
headers = ["订单号", "标的", "方向", "类型", "数量", "已成交", "委托价", "成交价", "状态", "提交时间"]
rows = [
[
o.order_id,
o.symbol,
str(o.side),
str(o.order_type),
str(o.quantity),
str(o.executed_quantity),
f"{float(o.price):.3f}" if o.price else "-",
f"{float(o.executed_price):.3f}" if o.executed_price else "-",
str(o.status),
o.submitted_at.strftime("%Y-%m-%d %H:%M:%S") if o.submitted_at else "-",
]
for o in orders
]
print_table(headers, rows, title=title)
@@ -0,0 +1,239 @@
"""行情命令模块"""
import click
from longbridge.openapi import QuoteContext, Period, AdjustType
from longbridge_cli.config import get_config
from longbridge_cli.formatters import print_table, print_json, print_error
PERIOD_MAP = {
"1m": Period.Min_1,
"5m": Period.Min_5,
"15m": Period.Min_15,
"30m": Period.Min_30,
"60m": Period.Min_60,
"day": Period.Day,
"week": Period.Week,
"month": Period.Month,
"quarter": Period.Quarter,
"year": Period.Year,
}
@click.command("quote")
@click.argument("symbols", nargs=-1, required=True)
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def quote_cmd(ctx, symbols, output_json):
"""获取实时报价(支持多个标的)
示例:longbridge quote AAPL.US 700.HK
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.quote(list(symbols))
except Exception as e:
print_error(str(e))
if output_json:
data = [
{
"symbol": q.symbol,
"last_done": float(q.last_done),
"open": float(q.open),
"high": float(q.high),
"low": float(q.low),
"volume": q.volume,
"turnover": float(q.turnover),
"change_rate": round(float(q.last_done / q.prev_close - 1) * 100, 2) if q.prev_close else None,
"prev_close": float(q.prev_close),
}
for q in resp
]
print_json(data)
else:
headers = ["标的", "最新价", "涨跌幅", "开盘", "最高", "最低", "成交量", "成交额"]
rows = []
for q in resp:
change_rate = round(float(q.last_done / q.prev_close - 1) * 100, 2) if q.prev_close else "-"
change_str = f"{change_rate:+.2f}%" if isinstance(change_rate, float) else change_rate
rows.append([
q.symbol,
f"{float(q.last_done):.3f}",
change_str,
f"{float(q.open):.3f}",
f"{float(q.high):.3f}",
f"{float(q.low):.3f}",
f"{q.volume:,}",
f"{float(q.turnover):,.0f}",
])
print_table(headers, rows, title="实时报价")
@click.command("depth")
@click.argument("symbol")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def depth_cmd(ctx, symbol, output_json):
"""查看盘口(买5卖5
示例:longbridge depth 700.HK
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.depth(symbol)
except Exception as e:
print_error(str(e))
if output_json:
data = {
"symbol": symbol,
"asks": [{"position": a.position, "price": float(a.price), "volume": a.volume} for a in resp.asks],
"bids": [{"position": b.position, "price": float(b.price), "volume": b.volume} for b in resp.bids],
}
print_json(data)
else:
headers = ["方向", "档位", "价格", "数量"]
rows = []
for a in reversed(resp.asks):
rows.append(["", str(a.position), f"{float(a.price):.3f}", f"{a.volume:,}"])
for b in resp.bids:
rows.append(["", str(b.position), f"{float(b.price):.3f}", f"{b.volume:,}"])
print_table(headers, rows, title=f"盘口 - {symbol}")
@click.command("trades")
@click.argument("symbol")
@click.option("--count", default=20, show_default=True, help="返回条数(最多 1000")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def trades_cmd(ctx, symbol, count, output_json):
"""查看最近逐笔成交
示例:longbridge trades 700.HK --count 20
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.trades(symbol, count)
except Exception as e:
print_error(str(e))
if output_json:
data = [
{
"price": float(t.price),
"volume": t.volume,
"timestamp": t.timestamp.isoformat() if t.timestamp else None,
"direction": str(t.trade_type),
}
for t in resp
]
print_json(data)
else:
headers = ["时间", "价格", "数量", "类型"]
rows = [
[
t.timestamp.strftime("%H:%M:%S") if t.timestamp else "-",
f"{float(t.price):.3f}",
f"{t.volume:,}",
str(t.trade_type),
]
for t in resp
]
print_table(headers, rows, title=f"逐笔成交 - {symbol}")
@click.command("candlesticks")
@click.argument("symbol")
@click.argument("period", type=click.Choice(list(PERIOD_MAP.keys())))
@click.option("--count", default=30, show_default=True, help="返回K线条数(最多 1000")
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def candlesticks_cmd(ctx, symbol, period, count, output_json):
"""查看 K 线数据
PERIOD 可选:1m 5m 15m 30m 60m day week month quarter year
示例:longbridge candlesticks AAPL.US day --count 30
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.candlesticks(symbol, PERIOD_MAP[period], count, AdjustType.NoAdjust)
except Exception as e:
print_error(str(e))
if output_json:
data = [
{
"timestamp": c.timestamp.isoformat() if c.timestamp else None,
"open": float(c.open),
"high": float(c.high),
"low": float(c.low),
"close": float(c.close),
"volume": c.volume,
"turnover": float(c.turnover),
}
for c in resp
]
print_json(data)
else:
headers = ["时间", "开盘", "最高", "最低", "收盘", "成交量"]
rows = [
[
c.timestamp.strftime("%Y-%m-%d %H:%M") if c.timestamp else "-",
f"{float(c.open):.3f}",
f"{float(c.high):.3f}",
f"{float(c.low):.3f}",
f"{float(c.close):.3f}",
f"{c.volume:,}",
]
for c in resp
]
print_table(headers, rows, title=f"K线 - {symbol} ({period})")
@click.command("info")
@click.argument("symbols", nargs=-1, required=True)
@click.option("--json", "output_json", is_flag=True, help="以 JSON 格式输出")
@click.pass_context
def info_cmd(ctx, symbols, output_json):
"""查看标的静态基本信息(名称、交易所、类型等)
示例:longbridge info 700.HK AAPL.US
"""
try:
quote_ctx = QuoteContext(get_config(ctx.obj.get("profile")))
resp = quote_ctx.static_info(list(symbols))
except Exception as e:
print_error(str(e))
if output_json:
data = [
{
"symbol": s.symbol,
"name_cn": s.name_cn,
"name_en": s.name_en,
"exchange": s.exchange,
"currency": s.currency,
"lot_size": s.lot_size,
"total_shares": s.total_shares,
"circulating_shares": s.circulating_shares,
"board": str(s.board),
}
for s in resp
]
print_json(data)
else:
headers = ["标的", "中文名", "英文名", "交易所", "币种", "手数", "板块"]
rows = [
[
s.symbol,
s.name_cn,
s.name_en,
s.exchange,
s.currency,
str(s.lot_size),
str(s.board),
]
for s in resp
]
print_table(headers, rows, title="标的信息")