108 lines
3.6 KiB
Python
108 lines
3.6 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
"""
|
|||
|
|
工具脚本:手动执行交易操作
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import sys, os, argparse
|
|||
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|||
|
|
|
|||
|
|
from engine import TradingEngine, load_config
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
parser = argparse.ArgumentParser(description='交易工具')
|
|||
|
|
sub = parser.add_subparsers(dest='cmd')
|
|||
|
|
|
|||
|
|
# 状态
|
|||
|
|
sub.add_parser('status', help='查看状态')
|
|||
|
|
|
|||
|
|
# 初始化
|
|||
|
|
p_init = sub.add_parser('init', help='初始化(重置资金)')
|
|||
|
|
p_init.add_argument('--cash', type=float, default=100000)
|
|||
|
|
|
|||
|
|
# 手动买入
|
|||
|
|
p_buy = sub.add_parser('buy', help='手动买入')
|
|||
|
|
p_buy.add_argument('code', help='股票代码 如 600519.SH')
|
|||
|
|
p_buy.add_argument('--name', default='')
|
|||
|
|
p_buy.add_argument('--price', type=float, required=True)
|
|||
|
|
p_buy.add_argument('--qty', type=int, required=True)
|
|||
|
|
p_buy.add_argument('--reason', default='手动买入')
|
|||
|
|
|
|||
|
|
# 手动卖出
|
|||
|
|
p_sell = sub.add_parser('sell', help='手动卖出')
|
|||
|
|
p_sell.add_argument('code', help='股票代码')
|
|||
|
|
p_sell.add_argument('--price', type=float, required=True)
|
|||
|
|
p_sell.add_argument('--reason', default='手动卖出')
|
|||
|
|
|
|||
|
|
# 运行一天
|
|||
|
|
p_run = sub.add_parser('run', help='运行指定日期')
|
|||
|
|
p_run.add_argument('--date', default=None)
|
|||
|
|
|
|||
|
|
# 交易历史
|
|||
|
|
p_hist = sub.add_parser('history', help='交易历史')
|
|||
|
|
p_hist.add_argument('--limit', type=int, default=20)
|
|||
|
|
|
|||
|
|
# 净值曲线
|
|||
|
|
sub.add_parser('nav', help='净值历史')
|
|||
|
|
|
|||
|
|
args = parser.parse_args()
|
|||
|
|
cfg = load_config()
|
|||
|
|
engine = TradingEngine(cfg)
|
|||
|
|
|
|||
|
|
if args.cmd == 'status' or args.cmd is None:
|
|||
|
|
print(engine.get_report())
|
|||
|
|
|
|||
|
|
elif args.cmd == 'init':
|
|||
|
|
engine.state = {
|
|||
|
|
'cash': args.cash, 'positions': {},
|
|||
|
|
'last_scan_date': None, 'last_scan_idx': -engine.scan_interval,
|
|||
|
|
'trade_count': 0, 'created': engine.state.get('created'),
|
|||
|
|
'nav_history': [],
|
|||
|
|
}
|
|||
|
|
engine.save_state()
|
|||
|
|
print(f"✅ 已重置,初始资金: ¥{args.cash:,.0f}")
|
|||
|
|
|
|||
|
|
elif args.cmd == 'buy':
|
|||
|
|
name = args.name or args.code
|
|||
|
|
engine.buy(args.code, name, args.price, args.qty,
|
|||
|
|
datetime.now().strftime('%Y%m%d'), args.reason)
|
|||
|
|
engine.save_state()
|
|||
|
|
|
|||
|
|
elif args.cmd == 'sell':
|
|||
|
|
engine.sell(args.code, args.price,
|
|||
|
|
datetime.now().strftime('%Y%m%d'), args.reason)
|
|||
|
|
engine.save_state()
|
|||
|
|
|
|||
|
|
elif args.cmd == 'run':
|
|||
|
|
engine.run_daily(args.date)
|
|||
|
|
|
|||
|
|
elif args.cmd == 'history':
|
|||
|
|
import sqlite3
|
|||
|
|
db = sqlite3.connect(engine._db_file())
|
|||
|
|
rows = db.execute(
|
|||
|
|
'SELECT date,code,name,direction,qty,price,pnl,pnl_pct,reason '
|
|||
|
|
'FROM trades ORDER BY id DESC LIMIT ?', (args.limit,)
|
|||
|
|
).fetchall()
|
|||
|
|
db.close()
|
|||
|
|
print(f"最近 {args.limit} 笔交易:")
|
|||
|
|
for r in rows:
|
|||
|
|
d, code, name, direction, qty, price, pnl, pnl_pct, reason = r
|
|||
|
|
if direction == 'buy':
|
|||
|
|
print(f" 🟢 {d} {name}({code}) {qty}股@¥{price:.2f}")
|
|||
|
|
else:
|
|||
|
|
print(f" {'✅' if (pnl or 0) > 0 else '❌'} {d} {name}({code}) "
|
|||
|
|
f"{qty}股@¥{price:.2f} ¥{pnl or 0:+,.0f}({pnl_pct or 0:+.1f}%) [{reason}]")
|
|||
|
|
|
|||
|
|
elif args.cmd == 'nav':
|
|||
|
|
history = engine.state.get('nav_history', [])
|
|||
|
|
if not history:
|
|||
|
|
print("无净值历史")
|
|||
|
|
else:
|
|||
|
|
print("净值历史:")
|
|||
|
|
for h in history:
|
|||
|
|
bar = '█' * max(0, int(h['ret'] / 2)) if h['ret'] > 0 else '░' * max(0, int(-h['ret'] / 2))
|
|||
|
|
print(f" {h['date']}: ¥{h['nav']:>10,.2f} {h['ret']:>+6.2f}% {bar}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == '__main__':
|
|||
|
|
main()
|