feat: Add DB logging, price monitoring, and duplicate trade prevention

This commit is contained in:
crypto_predictor committed 2026-02-06 15:48:45 +08:00
1 parent fcf64f4a3d
commit db937fdcf9
2 files changed
+213 -8

No files matched your search

+104 -8
View File
@@ -34,6 +34,7 @@ import httpx
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from data.fetcher import BinanceDataFetcher from data.fetcher import BinanceDataFetcher
from data.trading_db import TradingDatabase
load_dotenv() load_dotenv()
@@ -186,6 +187,9 @@ class PolymarketBTCTrader:
# 数据获取器 # 数据获取器
self.fetcher = BinanceDataFetcher() self.fetcher = BinanceDataFetcher()
# 数据库
self.db = TradingDatabase()
# CLOB客户端(用于下单) # CLOB客户端(用于下单)
self.clob_client = None self.clob_client = None
if self.private_key and not self.dry_run: if self.private_key and not self.dry_run:
@@ -230,6 +234,42 @@ class PolymarketBTCTrader:
logger.warning(f"⚠️ CLOB客户端初始化失败: {e}") logger.warning(f"⚠️ CLOB客户端初始化失败: {e}")
self.clob_client = None self.clob_client = None
def _log_to_db(self, result: Dict, market: Optional[Dict]):
"""记录交易结果到数据库"""
try:
# 确定状态
status = 'failure'
if self.dry_run:
status = 'test'
elif result.get('success'):
status = 'success'
elif result.get('error'):
status = 'failure'
elif result.get('prediction'): # 有预测但未交易(如市场未找到)
status = 'in_progress' # 或者 failure? 未找到市场算 failure
if result.get('error'):
status = 'failure'
log_data = {
'timestamp': result.get('timestamp'),
'market_title': result.get('market'),
'market_slug': market.get('slug') if market else result.get('market_slug'),
'model_name': 'DLinear',
'prediction': result.get('prediction'),
'probability': result.get('probability'),
'action': result.get('side'),
'price': result.get('price'),
'amount': result.get('amount'),
'size': result.get('size'),
'status': status,
'error_message': result.get('error'),
'raw_data': result
}
self.db.log_trade(log_data)
logger.info(f"💾 交易记录已保存到数据库")
except Exception as e:
logger.error(f"❌ 保存数据库失败: {e}")
def generate_market_slug(self, target_time: Optional[datetime] = None) -> str: def generate_market_slug(self, target_time: Optional[datetime] = None) -> str:
""" """
生成市场slug 生成市场slug
@@ -598,7 +638,12 @@ class PolymarketBTCTrader:
return prediction, probability return prediction, probability
def run_at_hour_start(self, delay_seconds: float = 2.0) -> Dict: def run_once(self) -> Dict:
"""运行一次交易流程(不等待整点)"""
logger.info("运行单次交易...")
return self.run_at_hour_start(delay_seconds=0)
def run_at_hour_start(self, delay_seconds: float = 2.0, timeout_minutes: int = 50) -> Dict:
""" """
整点时执行的交易流程 整点时执行的交易流程
@@ -606,11 +651,13 @@ class PolymarketBTCTrader:
2. 获取最新K线 2. 获取最新K线
3. DLinear预测当前整点的收盘涨跌 3. DLinear预测当前整点的收盘涨跌
4. 获取对应的Polymarket市场 4. 获取对应的Polymarket市场
5. 检查价格是否≤max_price 5. 循环检查价格:
6. 下单 - 如果价格 <= max_price: 下单并结束
- 如果价格 > max_price: 等待并重试(直到超时)
Args: Args:
delay_seconds: 整点后延迟秒数 delay_seconds: 整点后延迟秒数
timeout_minutes: 价格等待超时时间(分钟)
Returns: Returns:
执行结果 执行结果
@@ -621,6 +668,7 @@ class PolymarketBTCTrader:
logger.info(f" 模式: {'🔸 模拟' if self.dry_run else '💰 真实交易'}") logger.info(f" 模式: {'🔸 模拟' if self.dry_run else '💰 真实交易'}")
logger.info(f" 最大下注: ${self.max_bet:.2f} USDC") logger.info(f" 最大下注: ${self.max_bet:.2f} USDC")
logger.info(f" 最高价格: ${self.max_price:.2f}") logger.info(f" 最高价格: ${self.max_price:.2f}")
logger.info(f" 等待超时: {timeout_minutes}分钟")
result = { result = {
'timestamp': datetime.now().isoformat(), 'timestamp': datetime.now().isoformat(),
@@ -633,24 +681,75 @@ class PolymarketBTCTrader:
result['prediction'] = 'UP' if prediction == 1 else 'DOWN' result['prediction'] = 'UP' if prediction == 1 else 'DOWN'
result['probability'] = probability if prediction == 1 else 1 - probability result['probability'] = probability if prediction == 1 else 1 - probability
# 2. 获取当前整点的市场(不是下一个整点) # 2. 获取初始市场
market = self.get_current_hourly_market() market = self.get_current_hourly_market()
if not market: if not market:
result['error'] = '未找到市场' result['error'] = '未找到市场'
logger.error(f"❌ {result['error']}") logger.error(f"❌ {result['error']}")
self._log_to_db(result, None)
return result return result
result['market'] = market['title'] result['market'] = market['title']
result['market_slug'] = market['slug']
if market['closed']: if market['closed']:
result['error'] = '市场已关闭' result['error'] = '市场已关闭'
logger.error(f"❌ {result['error']}") logger.error(f"❌ {result['error']}")
self._log_to_db(result, market)
return result return result
# 3. 执行交易 # 检查是否已交易过该市场
check_status = 'test' if self.dry_run else 'success'
if self.db.check_existing_trade(market['slug'], status=check_status):
result['error'] = '该市场已存在成功交易记录'
logger.warning(f"⚠️ {result['error']}, 跳过交易")
return result
# 3. 循环检查价格并交易
start_time = datetime.now()
end_time = start_time + timedelta(minutes=timeout_minutes)
logger.info(f"🔄 开始价格监控 (预测: {'UP' if prediction == 1 else 'DOWN'})...")
while datetime.now() < end_time:
# 刷新价格
market = self.refresh_prices()
if not market:
logger.warning("无法获取市场价格,重试中...")
time.sleep(10)
continue
# 检查价格
price_idx = 0 if prediction == 1 else 1
current_price = market['outcome_prices'][price_idx]
logger.info(f" 当前价格: ${current_price:.3f} (目标: <= ${self.max_price:.2f})")
if current_price <= self.max_price:
# 价格符合,执行交易
logger.info("✅ 价格满足条件,正在下单...")
trade_result = self.execute_trade(prediction, market) trade_result = self.execute_trade(prediction, market)
result.update(trade_result) result.update(trade_result)
# 记录数据库
self._log_to_db(result, market)
logger.info("=" * 60)
return result
else:
# 价格过高,等待
wait_time = 30 # 等待30秒
elapsed = int((datetime.now() - start_time).total_seconds())
logger.info(f"⏳ 价格过高,继续等待... (已耗时: {elapsed}s)")
time.sleep(wait_time)
# 超时
result['error'] = f'等待价格超时 ({timeout_minutes}分钟)'
result['price'] = market['outcome_prices'][0] if prediction == 1 else market['outcome_prices'][1]
result['side'] = 'UP' if prediction == 1 else 'DOWN'
logger.error(f"❌ {result['error']}")
self._log_to_db(result, market)
logger.info("=" * 60) logger.info("=" * 60)
return result return result
@@ -758,9 +857,6 @@ class PolymarketBTCTrader:
# 启动时进行一次预测(不下单,仅显示) # 启动时进行一次预测(不下单,仅显示)
self._show_initial_prediction() self._show_initial_prediction()
# 定时刷新价格
schedule.every(interval_minutes).minutes.do(self.refresh_prices)
# 每小时整点后执行交易 # 每小时整点后执行交易
def hourly_trade(): def hourly_trade():
self.run_at_hour_start(delay_seconds=delay_seconds) self.run_at_hour_start(delay_seconds=delay_seconds)
+109
View File
@@ -0,0 +1,109 @@
import sqlite3
from datetime import datetime
import json
import os
from contextlib import contextmanager
class TradingDatabase:
"""交易数据库管理类 - 用于记录交易历史和预测结果"""
def __init__(self, db_path="trading_history.db"):
self.db_path = db_path
self._init_db()
@contextmanager
def get_connection(self):
"""数据库连接上下文管理器"""
conn = sqlite3.connect(self.db_path)
try:
yield conn
finally:
conn.close()
def _init_db(self):
"""初始化数据库表结构"""
with self.get_connection() as conn:
cursor = conn.cursor()
# 创建交易记录表
cursor.execute('''
CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
market_title TEXT,
market_slug TEXT,
model_name TEXT,
prediction TEXT,
probability REAL,
action TEXT,
price REAL,
amount REAL,
size REAL,
status TEXT,
error_message TEXT,
raw_data TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
conn.commit()
def log_trade(self, trade_data: dict):
"""
记录交易信息
Args:
trade_data: 包含交易信息的字典,支持字段:
timestamp, market_title, market_slug, model_name,
prediction, probability, action, price, amount, size,
status, error_message, raw_data
"""
try:
with self.get_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT INTO trades (
timestamp, market_title, market_slug, model_name,
prediction, probability, action, price, amount, size,
status, error_message, raw_data
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
trade_data.get('timestamp', datetime.now().isoformat()),
trade_data.get('market_title'),
trade_data.get('market_slug'),
trade_data.get('model_name'),
trade_data.get('prediction'),
trade_data.get('probability'),
trade_data.get('action'),
trade_data.get('price'),
trade_data.get('amount'),
trade_data.get('size'),
trade_data.get('status'),
trade_data.get('error_message'),
json.dumps(trade_data.get('raw_data', {}), ensure_ascii=False)
))
conn.commit()
except Exception as e:
print(f"写入数据库失败: {e}")
def check_existing_trade(self, market_slug: str, status: str = 'success') -> bool:
"""
检查指定市场是否已有成功交易记录
Args:
market_slug: 市场slug
status: 交易状态 (默认 'success', 也可以是 'test')
Returns:
True 如果已有成功交易, 否则 False
"""
try:
with self.get_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
SELECT COUNT(*) FROM trades
WHERE market_slug = ? AND status = ?
''', (market_slug, status))
count = cursor.fetchone()[0]
return count > 0
except Exception as e:
print(f"检查已存在交易失败: {e}")
return False