1
0

mx_moni.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. #!/usr/bin/env python3
  2. # mx_moni - 妙想模拟组合管理技能
  3. import os
  4. import sys
  5. import json
  6. import re
  7. import requests
  8. from typing import Dict, Any, Optional, Tuple
  9. # 加载环境变量
  10. MX_APIKEY = os.environ.get('MX_APIKEY')
  11. MX_API_URL = os.environ.get('MX_API_URL', 'https://mkapi2.dfcfs.com/finskillshub')
  12. OUTPUT_DIR = '/root/.openclaw/workspace/mx_data/output'
  13. os.makedirs(OUTPUT_DIR, exist_ok=True)
  14. def check_apikey() -> None:
  15. """检查API密钥是否配置"""
  16. if not MX_APIKEY:
  17. print("错误: 未配置MX_APIKEY环境变量,请先配置API密钥")
  18. print("示例: export MX_APIKEY=your_api_key_here")
  19. sys.exit(1)
  20. def make_request(endpoint: str, body: Dict[str, Any], output_prefix: str) -> None:
  21. """发送POST请求并保存结果"""
  22. check_apikey()
  23. full_url = f"{MX_API_URL}{endpoint}"
  24. headers = {
  25. 'apikey': MX_APIKEY,
  26. 'Content-Type': 'application/json'
  27. }
  28. try:
  29. response = requests.post(full_url, headers=headers, json=body)
  30. response.raise_for_status()
  31. result = response.json()
  32. output_path = os.path.join(OUTPUT_DIR, f"{output_prefix}_raw.json")
  33. with open(output_path, 'w', encoding='utf-8') as f:
  34. json.dump(result, f, ensure_ascii=False, indent=2)
  35. print(f"请求完成,结果保存在 {output_path}")
  36. # 打印结果摘要
  37. if result.get('success') or str(result.get('code')) == '200':
  38. print("\n操作结果: 成功")
  39. if 'message' in result:
  40. print(f"提示信息: {result['message']}")
  41. if 'data' in result and isinstance(result['data'], dict):
  42. data = result['data']
  43. if 'totalAssets' in data:
  44. print(f"\n账户资金:")
  45. print(f" 总资产: {data['totalAssets']:.2f} 元")
  46. print(f" 可用资金: {data['availBalance']:.2f} 元")
  47. if 'orderId' in data:
  48. print(f"\n委托成功:")
  49. print(f" 委托编号: {data['orderId']}")
  50. else:
  51. print(f"\n操作结果: 失败")
  52. print(f"错误码: {result.get('code')}")
  53. print(f"错误信息: {result.get('message')}")
  54. except Exception as e:
  55. print(f"网络请求失败: {str(e)}")
  56. sys.exit(1)
  57. def parse_buy_sell(query: str) -> Tuple[Optional[str], Optional[float], Optional[int], bool]:
  58. """解析买入卖出命令,返回(股票代码, 价格, 数量, 是否市价)"""
  59. # 提取6位股票代码
  60. code_match = re.search(r'(\d{6})', query)
  61. if not code_match:
  62. return None, None, None, False
  63. stock_code = code_match.group(1)
  64. # 提取数量(单位:股,必须是100倍数)
  65. quantity_match = re.search(r'(\d+)\s*(股|手)', query)
  66. quantity = None
  67. if quantity_match:
  68. qty = int(quantity_match.group(1))
  69. if quantity_match.group(2) == '手':
  70. qty = qty * 100
  71. quantity = qty
  72. # 检查是否市价委托
  73. is_market = any(word in query for word in ['市价', '市价买入', '市价卖出', '现价买入', '现价卖出'])
  74. # 提取价格
  75. price_match = re.search(r'(\d+\.?\d*)\s*元', query) if not is_market else None
  76. price = None
  77. if price_match and not is_market:
  78. price = float(price_match.group(1))
  79. elif not is_market and quantity:
  80. # 尝试找任意数字作为价格
  81. price_candidates = re.findall(r'\d+\.?\d*', query)
  82. for candidate in price_candidates:
  83. if len(candidate) != 6: # 排除股票代码
  84. price = float(candidate)
  85. break
  86. return stock_code, price, quantity, is_market
  87. def parse_cancel(query: str) -> Tuple[Optional[str], Optional[str], bool]:
  88. """解析撤单命令,返回(委托编号, 股票代码, 是否全部撤单)"""
  89. if any(word in query for word in ['全部', '所有', '一键撤单']):
  90. return None, None, True
  91. # 提取委托编号
  92. order_id_match = re.search(r'(\d{16,20})', query)
  93. order_id = order_id_match.group(1) if order_id_match else None
  94. # 提取股票代码
  95. code_match = re.search(r'(\d{6})', query)
  96. stock_code = code_match.group(1) if code_match else None
  97. return order_id, stock_code, False
  98. def main():
  99. if len(sys.argv) < 2:
  100. print("请提供操作指令,例如:")
  101. print(" python mx_moni.py 我的持仓 # 查询持仓")
  102. print(" python mx_moni.py 我的资金 # 查询资金")
  103. print(" python mx_moni.py 我的委托 # 查询委托订单")
  104. print(" python mx_moni.py 买入 600519 价格 1700 数量 100 股")
  105. print(" python mx_moni.py 市价买入 600519 100 股")
  106. print(" python mx_moni.py 卖出 600519 价格 1750 数量 100 股")
  107. print(" python mx_moni.py 撤单 123456789012345678")
  108. print(" python mx_moni.py 一键撤单")
  109. sys.exit(1)
  110. query = ' '.join(sys.argv[1:])
  111. output_prefix = f"mx_moni_{query.replace(' ', '_')}"
  112. # 根据意图识别调用不同接口
  113. if any(word in query for word in ['持仓', '我的持仓', '持仓情况']):
  114. make_request('/api/claw/mockTrading/positions', {'moneyUnit': 1}, output_prefix)
  115. elif any(word in query for word in ['资金', '我的资金', '账户余额', '资金情况']):
  116. make_request('/api/claw/mockTrading/balance', {'moneyUnit': 1}, output_prefix)
  117. elif any(word in query for word in ['委托', '我的委托', '订单', '委托记录']):
  118. make_request('/api/claw/mockTrading/orders', {'fltOrderDrt': 0, 'fltOrderStatus': 0}, output_prefix)
  119. elif any(word in query for word in ['买入', '买进', '建仓']):
  120. stock_code, price, quantity, is_market = parse_buy_sell(query)
  121. if not stock_code or not quantity:
  122. print("错误: 无法解析买入指令,请确保包含股票代码(6位)和数量(100的整数倍)")
  123. print("示例: python mx_moni.py 买入 600519 价格 1700 数量 100 股")
  124. print("示例: python mx_moni.py 市价买入 600519 100 股")
  125. sys.exit(1)
  126. if not is_market and price is None:
  127. print("错误: 限价买入需要提供价格,或使用市价买入")
  128. sys.exit(1)
  129. if quantity % 100 != 0:
  130. print("错误: 委托数量必须为100的整数倍")
  131. sys.exit(1)
  132. body = {
  133. 'type': 'buy',
  134. 'stockCode': stock_code,
  135. 'quantity': quantity,
  136. 'useMarketPrice': is_market
  137. }
  138. if not is_market:
  139. body['price'] = price
  140. make_request('/api/claw/mockTrading/trade', body, output_prefix)
  141. elif any(word in query for word in ['卖出', '抛售', '减仓']):
  142. stock_code, price, quantity, is_market = parse_buy_sell(query)
  143. if not stock_code or not quantity:
  144. print("错误: 无法解析卖出指令,请确保包含股票代码(6位)和数量(100的整数倍)")
  145. print("示例: python mx_moni.py 卖出 600519 价格 1750 数量 100 股")
  146. print("示例: python mx_moni.py 市价卖出 600519 100 股")
  147. sys.exit(1)
  148. if not is_market and price is None:
  149. print("错误: 限价卖出需要提供价格,或使用市价卖出")
  150. sys.exit(1)
  151. if quantity % 100 != 0:
  152. print("错误: 委托数量必须为100的整数倍")
  153. sys.exit(1)
  154. body = {
  155. 'type': 'sell',
  156. 'stockCode': stock_code,
  157. 'quantity': quantity,
  158. 'useMarketPrice': is_market
  159. }
  160. if not is_market:
  161. body['price'] = price
  162. make_request('/api/claw/mockTrading/trade', body, output_prefix)
  163. elif any(word in query for word in ['撤单', '撤销', '撤单']):
  164. order_id, stock_code, is_all = parse_cancel(query)
  165. if is_all:
  166. body = {'type': 'all'}
  167. make_request('/api/claw/mockTrading/cancel', body, output_prefix)
  168. else:
  169. if not order_id:
  170. print("错误: 请提供委托编号,或使用一键撤单撤销所有未成交委托")
  171. print("示例: python mx_moni.py 撤单 260854300000078983")
  172. print("示例: python mx_moni.py 一键撤单")
  173. sys.exit(1)
  174. body = {
  175. 'type': 'order',
  176. 'orderId': order_id
  177. }
  178. if stock_code:
  179. body['stockCode'] = stock_code
  180. make_request('/api/claw/mockTrading/cancel', body, output_prefix)
  181. else:
  182. print("无法识别意图,请使用以下操作之一:")
  183. print(" 持仓查询: 我的持仓 / 查询持仓")
  184. print(" 资金查询: 我的资金 / 查询资金")
  185. print(" 委托查询: 我的委托 / 查询委托")
  186. print(" 买入操作: 买入 [股票代码] [价格] [数量] 股 / 市价买入 [股票代码] [数量] 股")
  187. print(" 卖出操作: 卖出 [股票代码] [价格] [数量] 股 / 市价卖出 [股票代码] [数量] 股")
  188. print(" 撤单操作: 撤单 [委托编号] / 一键撤单")
  189. sys.exit(1)
  190. if __name__ == '__main__':
  191. main()