Clean up unused files, organize structure, update docs
- 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
This commit is contained in:
137
list_latest_candles.py
Normal file
137
list_latest_candles.py
Normal file
@ -0,0 +1,137 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user