forked from qql0812/polymarketBot
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsell_bot.py
More file actions
348 lines (294 loc) · 12.8 KB
/
sell_bot.py
File metadata and controls
348 lines (294 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# sell_bot.py
import time
import os
from datetime import datetime, timezone, timedelta
from py_clob_client.client import ClobClient
from py_clob_client.order_builder.constants import SELL
from py_clob_client.clob_types import MarketOrderArgs
import json
import ast
# 配置参数
BEIJING_TZ = timezone(timedelta(hours=8))
DATA_DIR = os.environ.get('DATA_DIR', '/app/data')
PURCHASED_TOKENS_FILE = os.path.join(DATA_DIR, "purchased_tokens.txt")
ORDER_HISTORY_FILE = os.path.join(DATA_DIR, "order_history.txt")
SOLD_ORDERS_FILE = os.path.join(DATA_DIR, "sold_orders.txt")
# Polymarket 配置
HOST = os.environ.get('POLYMARKET_HOST', "https://clob.polymarket.com")
PRIVATE_KEY = os.environ.get('POLYMARKET_PRIVATE_KEY')
CHAIN_ID = int(os.environ.get('POLYMARKET_CHAIN_ID', 137))
FUNDER = os.environ.get('POLYMARKET_FUNDER')
LARK_WEBHOOK_URL = os.environ.get('LARK_WEBHOOK_URL', "")
# 初始化客户端
client = ClobClient(
host=HOST,
key=PRIVATE_KEY,
chain_id=CHAIN_ID,
signature_type=2,
funder=FUNDER
)
client.set_api_creds(client.create_or_derive_api_creds())
def load_order_history():
"""
从order_history.txt文件加载订单ID(只取前5条)
支持Python字典格式和标准JSON格式
"""
if not os.path.exists(ORDER_HISTORY_FILE):
print(f"订单历史文件不存在: {ORDER_HISTORY_FILE}")
return []
try:
with open(ORDER_HISTORY_FILE, 'r') as f:
lines = [line.strip() for line in f.readlines() if line.strip()]
if len(lines) == 0:
print("文件为空")
return []
order_ids = []
for i, line in enumerate(lines[:5]): # 只取前5条
order_id = None
# 尝试Python字典解析(处理单引号格式)
try:
order_data = ast.literal_eval(line)
if isinstance(order_data, dict):
order_id = order_data.get('orderID')
if order_id:
order_ids.append(order_id)
continue
except (ValueError, SyntaxError) as e:
print(f" Python字典解析失败: {e}")
# 最后尝试作为纯订单ID处理
if line.startswith('0x') and len(line) > 10:
order_ids.append(line)
print(f" 作为纯订单ID处理: {line}")
else:
print(f" 无法识别的行格式")
return order_ids
except Exception as e:
print(f"读取订单历史文件失败: {e}")
import traceback
traceback.print_exc()
return []
def log_sold_order(order_details, sell_price_info):
"""
记录卖出的订单到sold_orders.txt文件
"""
try:
sold_record = {
"order_id": order_details.get('id'),
"asset_id": order_details.get('asset_id'),
"original_size": order_details.get('original_size'),
"price": order_details.get('price'),
"side": order_details.get('side'),
"status": order_details.get('status'),
"sell_time": datetime.now().astimezone(BEIJING_TZ).strftime('%Y-%m-%d %H:%M:%S'),
"sell_price_info": sell_price_info
}
with open(SOLD_ORDERS_FILE, 'a', encoding='utf-8') as f:
f.write(json.dumps(sold_record, ensure_ascii=False) + '\n')
print(f"已记录卖出订单: {order_details.get('id')}")
except Exception as e:
print(f"记录卖出订单失败: {e}")
def remove_order_from_history(order_id):
"""
从order_history.txt文件中移除已处理的订单ID
"""
try:
if not os.path.exists(ORDER_HISTORY_FILE):
print("订单历史文件不存在")
return
# 读取所有订单
with open(ORDER_HISTORY_FILE, 'r') as f:
lines = [line.strip() for line in f.readlines() if line.strip()]
print(f"移除前文件行数: {len(lines)}")
print(f"要移除的订单ID: {order_id}")
# 过滤掉要移除的订单ID
filtered_lines = []
removed = False
for i, line in enumerate(lines):
found = False
# 尝试Python字典解析
try:
order_data = ast.literal_eval(line)
if isinstance(order_data, dict):
current_order_id = order_data.get('orderID')
if current_order_id == order_id:
found = True
except (ValueError, SyntaxError):
pass
# 关键修复:如果找到匹配的订单,标记为已移除但不添加到filtered_lines
# 如果没有找到匹配的订单,则保留该行
if found:
print(f" 在第 {i+1} 行找到匹配的订单,移除")
removed = True
# 注意:这里不添加到filtered_lines,相当于移除了这一行
else:
# 保留不匹配的行
filtered_lines.append(line)
if not removed:
print(f" 未找到要移除的订单ID: {order_id}")
print(f"移除后文件行数: {len(filtered_lines)}")
# 写回文件(只写入未被移除的行)
with open(ORDER_HISTORY_FILE, 'w') as f:
for line in filtered_lines:
f.write(f"{line}\n")
print(f"已从订单历史文件中移除订单: {order_id}")
except Exception as e:
print(f"从订单历史文件移除订单时出错: {e}")
import traceback
traceback.print_exc()
def should_sell_order(order_details):
"""
判断是否应该卖出订单
条件:当前买方价格或卖方价格比订单价格低0.2以上
"""
try:
asset_id = order_details.get('asset_id')
order_price = float(order_details.get('price', 0))
side = order_details.get('side')
status = order_details.get('status')
original_size = float(order_details.get('original_size', 0))
print(f"\n检查订单卖出条件:")
print(f" Asset ID: {asset_id}")
print(f" 订单价格: {order_price}")
print(f" 订单方向: {side}")
print(f" 订单状态: {status}")
print(f" 原始数量: {original_size}")
# 获取当前市场价格
try:
buy_price_response = client.get_price(asset_id, "BUY")
sell_price_response = client.get_price(asset_id, "SELL")
# 从返回的字典中提取价格
current_buy_price = None
current_sell_price = None
if isinstance(buy_price_response, dict):
current_buy_price = buy_price_response.get('price')
else:
current_buy_price = buy_price_response
if isinstance(sell_price_response, dict):
current_sell_price = sell_price_response.get('price')
else:
current_sell_price = sell_price_response
print(f" 当前买价: {current_buy_price}")
print(f" 当前卖价: {current_sell_price}")
if current_buy_price is not None:
current_buy_price = float(current_buy_price)
# 如果当前买价比订单价格低0.2以上,考虑卖出
price_diff_buy = order_price - current_buy_price
if price_diff_buy >= 0.2:
return True, f"当前买价({current_buy_price})比订单价({order_price})低{price_diff_buy:.3f}"
if current_sell_price is not None:
current_sell_price = float(current_sell_price)
# 如果当前卖价比订单价格低0.2以上,考虑卖出
price_diff_sell = order_price - current_sell_price
if price_diff_sell >= 0.2:
return True, f"当前卖价({current_sell_price})比订单价({order_price})低{price_diff_sell:.3f}"
except Exception as e:
# 检查是否是404错误(市场不存在)
error_str = str(e)
if "404" in error_str and "No orderbook exists" in error_str:
print(f" 市场不存在(404错误),标记为无效订单")
return False, "市场不存在,清除订单记录"
else:
print(f" 获取市场价格失败: {e}")
return False, f"获取市场价格失败: {e}"
return False, "价格差异未达到0.3的卖出条件"
except Exception as e:
print(f"判断卖出条件时出错: {e}")
return False, f"错误: {e}"
def create_sell_order(token_id, amount):
"""
创建卖单
"""
try:
print(f"创建卖单 - Token: {token_id}, 数量: {amount}")
order_args = MarketOrderArgs(
amount=amount,
side=SELL,
token_id=token_id
)
signed_order = client.create_market_order(order_args)
order_response = client.post_order(signed_order, "GTC") # 使用FOK确保立即成交
order_id = order_response.get('orderID', 'N/A')
if order_id != 'N/A':
print(f"卖单创建成功: {order_id}")
return order_id
else:
print("卖单创建失败")
return None
except Exception as e:
print(f"创建卖单失败: {e}")
return None
def process_batch_orders():
"""
处理一批订单(最多5条)
"""
print(f"\n=== 开始处理订单批次 ({datetime.now().astimezone(BEIJING_TZ).strftime('%Y-%m-%d %H:%M:%S')} 北京时间) ===")
# 加载订单ID(最多5条)
order_ids = load_order_history()
if not order_ids:
print("没有找到订单记录")
return 0
print(f"找到 {len(order_ids)} 个订单待处理")
processed_count = 0
# 处理每个订单
for i, order_id in enumerate(order_ids, 1):
print(f"\n--- 处理第 {i}/{len(order_ids)} 个订单: {order_id} ---")
try:
# 获取订单详情
order_details = client.get_order(order_id)
if not order_details:
print(f" 未找到订单详情: {order_id}")
# 从历史记录中移除无效订单
remove_order_from_history(order_id)
processed_count += 1
continue
# 检查是否应该卖出
should_sell, reason = should_sell_order(order_details)
if should_sell:
print(f" 决定卖出: {reason}")
# 计算剩余数量
original_size = float(order_details.get('original_size', 0))
asset_id = order_details.get('asset_id')
print(f" 卖出数量: {original_size}")
# 创建卖单
sell_order_id = create_sell_order(asset_id, original_size)
if sell_order_id:
# 从历史记录中移除已卖出的订单
remove_order_from_history(order_id)
print(f" 成功卖出并从历史记录中移除订单")
processed_count += 1
else:
print(f" 卖出失败,保留订单记录")
elif "市场不存在" in reason:
# 如果是市场不存在的错误,清除订单记录
print(f" 市场不存在,清除订单记录: {order_id}")
remove_order_from_history(order_id)
processed_count += 1
except Exception as e:
print(f"处理订单 {order_id} 时出错: {e}")
# 出错时暂时保留订单记录,下次再试
continue
print(f"\n=== 本批次处理完成,共处理 {processed_count} 个订单 ===")
return processed_count
def main():
"""
主函数 - 持续处理订单
"""
print("订单卖出机器人已启动...")
while True:
try:
processed = process_batch_orders()
if processed == 0:
print("没有订单需要处理,等待 10 秒后进行下一次检查...")
time.sleep(5) # 10s检查一次
else:
print("继续处理下一批订单...")
time.sleep(2) # 如果有处理过的订单,2秒后继续
except KeyboardInterrupt:
print("程序被用户中断")
break
except Exception as e:
print(f"执行时发生错误: {e}")
print("等待60秒后重试...")
time.sleep(60)
if __name__ == "__main__":
main()