- Delete obsolete files: data_fetcher_old.py, market_old.py, base_strategy.py (root), strategy_sma_cross.py, and old architecture remnants (address_monitor.py, position_monitor.py, trade_log.py, wallet_data.py, whale_tracker.py) - Delete zero-byte Docker artifacts and runtime files (clp_hedger.log, clp_hedger/hedge_status.json) - Move one-off utility scripts to scripts/ directory - Move example/template files to .temp/ directory - Update .gitignore: add entries for clp_hedger.log, clp_hedger/hedge_status.json, Docker layer hash files, Using, Running, and backups/ - Update .dockerignore: add clp_hedger.log, clp_hedger/hedge_status.json, backups/ - Create example config files: _data/strategies.json.example, _data/backtesting_conf.json.example, _data/coin_precision.json.example - Update GEMINI.md: remove outdated session summaries and duplicate review section - Update review.md: add cleanup status section, update remaining recommendations - Update MIGRATION_PLAN.md: mark completed phases, update file references - Update DOCKER_MIGRATION_GUIDE.md: update import_csv.py path reference
138 lines
4.7 KiB
Python
138 lines
4.7 KiB
Python
import argparse
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from contextlib import closing
|
|
|
|
import psycopg2
|
|
|
|
from logging_utils import setup_logging
|
|
|
|
DEFAULT_DB_PATH = os.environ.get(
|
|
"PG_CONN_STR",
|
|
"postgresql://hyper:hyper@localhost:5432/hyper"
|
|
)
|
|
|
|
|
|
def load_coins():
|
|
"""Load the list of all coins from the local coin_precision.json file."""
|
|
coin_file = "_data/coin_precision.json"
|
|
try:
|
|
with open(coin_file, 'r') as f:
|
|
return list(json.load(f).keys())
|
|
except FileNotFoundError:
|
|
logging.error(f"'{coin_file}' not found. Please run list_coins.py first.")
|
|
sys.exit(1)
|
|
except (IOError, json.JSONDecodeError) as e:
|
|
logging.error(f"Failed to load or parse '{coin_file}': {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def get_latest_candle(conn, coin, interval="1m"):
|
|
"""
|
|
Query the database for the most recent candle for a given coin.
|
|
|
|
Returns a dict with keys: datetime_utc, timestamp_ms, open, high, low, close, volume, number_of_trades
|
|
or None if the table doesn't exist or has no rows.
|
|
"""
|
|
table_name = f"{coin.replace(':', '_')}_{interval}"
|
|
try:
|
|
with closing(conn.cursor()) as cur:
|
|
cur.execute(f'SELECT 1 FROM information_schema.tables WHERE table_name = %s', (table_name,))
|
|
if not cur.fetchone()[0]:
|
|
return None
|
|
|
|
cur.execute(
|
|
f'SELECT datetime_utc, timestamp_ms, open, high, low, close, volume, number_of_trades '
|
|
f'FROM "{table_name}" ORDER BY timestamp_ms DESC LIMIT 1'
|
|
)
|
|
row = cur.fetchone()
|
|
if row is None:
|
|
return None
|
|
|
|
return {
|
|
"datetime_utc": row[0],
|
|
"timestamp_ms": row[1],
|
|
"open": row[2],
|
|
"high": row[3],
|
|
"low": row[4],
|
|
"close": row[5],
|
|
"volume": row[6],
|
|
"number_of_trades": row[7],
|
|
}
|
|
except Exception as e:
|
|
logging.debug(f"Could not get latest candle for {coin} ({interval}): {e}")
|
|
return None
|
|
|
|
|
|
def list_latest_candles(coins, interval="1m", db_path=None):
|
|
"""
|
|
Fetch and display the newest candle for every coin in the list.
|
|
"""
|
|
if db_path is None:
|
|
db_path = DEFAULT_DB_PATH
|
|
|
|
conn = psycopg2.connect(db_path)
|
|
|
|
results = []
|
|
for coin in coins:
|
|
candle = get_latest_candle(conn, coin, interval)
|
|
if candle is not None:
|
|
results.append((coin, candle))
|
|
else:
|
|
results.append((coin, None))
|
|
|
|
conn.close()
|
|
|
|
print(f"\n--- Newest {interval} Candles for All Symbols ---")
|
|
print(f"Total symbols: {len(coins)} | Symbols with data: {sum(1 for _, c in results if c is not None)}")
|
|
print(f"Generated at: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}")
|
|
print("-" * 120)
|
|
print(f"{'Coin':<16} | {'Datetime (UTC)':<22} | {'Open':>12} | {'High':>12} | {'Low':>12} | {'Close':>12} | {'Volume':>12}")
|
|
print("-" * 120)
|
|
|
|
for coin, candle in results:
|
|
if candle is not None:
|
|
dt = candle["datetime_utc"].strftime('%Y-%m-%d %H:%M:%S') if candle["datetime_utc"] else "N/A"
|
|
o = f"{candle['open']:.4f}" if candle['open'] is not None else "N/A"
|
|
h = f"{candle['high']:.4f}" if candle['high'] is not None else "N/A"
|
|
l = f"{candle['low']:.4f}" if candle['low'] is not None else "N/A"
|
|
c = f"{candle['close']:.4f}" if candle['close'] is not None else "N/A"
|
|
v = f"{candle['volume']:.4f}" if candle['volume'] is not None else "N/A"
|
|
print(f"{coin:<16} | {dt:<22} | {o:>12} | {h:>12} | {l:>12} | {c:>12} | {v:>12}")
|
|
else:
|
|
print(f"{coin:<16} | {'(no data)':<22} | {'':>12} | {'':>12} | {'':>12} | {'':>12} | {'':>12}")
|
|
|
|
print("-" * 120)
|
|
print(f"Symbols without data: {sum(1 for _, c in results if c is None)}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(
|
|
description="List the newest 1-minute candle for all symbols from the database."
|
|
)
|
|
parser.add_argument(
|
|
"--interval",
|
|
default="1m",
|
|
help="Candle interval to query (default: 1m)."
|
|
)
|
|
parser.add_argument(
|
|
"--db",
|
|
default=None,
|
|
help="PostgreSQL connection string (default: from PG_CONN_STR env or localhost)."
|
|
)
|
|
parser.add_argument(
|
|
"--log-level",
|
|
default="off",
|
|
choices=['off', 'normal', 'debug'],
|
|
help="Set the logging level."
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
setup_logging(args.log_level, 'ListLatestCandles')
|
|
|
|
coins = load_coins()
|
|
list_latest_candles(coins, interval=args.interval, db_path=args.db)
|