strategy status table
This commit is contained in:
109
main_app.py
109
main_app.py
@ -133,7 +133,7 @@ def run_strategy(strategy_name: str, config: dict):
|
||||
log_file = os.path.join(LOGS_DIR, f"strategy_{strategy_name}.log")
|
||||
script_name = config['script']
|
||||
params_str = json.dumps(config['parameters'])
|
||||
command = [sys.executable, script_name, "--name", strategy_name, "--params", params_str, "--log-level", "off"]
|
||||
command = [sys.executable, script_name, "--name", strategy_name, "--params", params_str, "--log-level", "normal"]
|
||||
while True:
|
||||
try:
|
||||
with open(log_file, 'a') as f:
|
||||
@ -147,14 +147,15 @@ def run_strategy(strategy_name: str, config: dict):
|
||||
|
||||
|
||||
class MainApp:
|
||||
def __init__(self, coins_to_watch: list, processes: dict):
|
||||
def __init__(self, coins_to_watch: list, processes: dict, strategy_configs: dict):
|
||||
self.watched_coins = coins_to_watch
|
||||
self.prices = {}
|
||||
self.market_caps = {}
|
||||
self.last_db_update_info = "Initializing..."
|
||||
self._lines_printed = 0
|
||||
self.background_processes = processes
|
||||
self.process_status = {}
|
||||
self.strategy_configs = strategy_configs
|
||||
self.strategy_statuses = {}
|
||||
|
||||
def read_prices(self):
|
||||
"""Reads the latest prices from the JSON file."""
|
||||
@ -172,7 +173,6 @@ class MainApp:
|
||||
with open(MARKET_CAP_SUMMARY_FILE, 'r', encoding='utf-8') as f:
|
||||
summary_data = json.load(f)
|
||||
|
||||
# Extract just the market cap value for each coin
|
||||
for coin in self.watched_coins:
|
||||
table_key = f"{coin}_market_cap"
|
||||
if table_key in summary_data:
|
||||
@ -180,6 +180,20 @@ class MainApp:
|
||||
except (json.JSONDecodeError, IOError):
|
||||
logging.debug("Could not read market cap summary file.")
|
||||
|
||||
def read_strategy_statuses(self):
|
||||
"""Reads the status JSON file for each enabled strategy."""
|
||||
for name in self.strategy_configs.keys():
|
||||
status_file = os.path.join("_data", f"strategy_status_{name}.json")
|
||||
if os.path.exists(status_file):
|
||||
try:
|
||||
with open(status_file, 'r', encoding='utf-8') as f:
|
||||
self.strategy_statuses[name] = json.load(f)
|
||||
except (IOError, json.JSONDecodeError):
|
||||
self.strategy_statuses[name] = {"error": "Could not read status file."}
|
||||
else:
|
||||
self.strategy_statuses[name] = {"current_signal": "Initializing..."}
|
||||
|
||||
|
||||
def get_overall_db_status(self):
|
||||
"""Reads the fetcher status from the status file."""
|
||||
if os.path.exists(STATUS_FILE):
|
||||
@ -210,37 +224,70 @@ class MainApp:
|
||||
self.process_status[name] = "Running" if process.is_alive() else "STOPPED"
|
||||
|
||||
def display_dashboard(self):
|
||||
"""Displays a formatted table without blinking by overwriting previous lines."""
|
||||
if self._lines_printed > 0:
|
||||
print(f"\x1b[{self._lines_printed}A", end="")
|
||||
|
||||
output_lines = ["--- Market Dashboard ---"]
|
||||
table_width = 44
|
||||
output_lines.append("-" * table_width)
|
||||
output_lines.append(f"{'#':<2} | {'Coin':<6} | {'Live Price':>10} | {'Market Cap':>15} |")
|
||||
output_lines.append("-" * table_width)
|
||||
"""Displays a formatted dashboard with side-by-side tables."""
|
||||
print("\x1b[H\x1b[J", end="") # Clear screen
|
||||
|
||||
# --- Build Left Table (Market Dashboard) ---
|
||||
left_table_lines = []
|
||||
left_table_width = 44
|
||||
left_table_lines.append("--- Market Dashboard ---\t\t")
|
||||
left_table_lines.append("-" * left_table_width)
|
||||
left_table_lines.append(f"{'#':^2} | {'Coin':^6} | {'Live Price':>10} | {'Market Cap':>15} |")
|
||||
left_table_lines.append("-" * left_table_width)
|
||||
for i, coin in enumerate(self.watched_coins, 1):
|
||||
price = self.prices.get(coin, "Loading...")
|
||||
market_cap = self.market_caps.get(coin)
|
||||
formatted_mc = format_market_cap(market_cap)
|
||||
output_lines.append(f"{i:<2} | {coin:<6} | {price:>10} | {formatted_mc:>15} |")
|
||||
output_lines.append("-" * table_width)
|
||||
left_table_lines.append(f"{i:<2} | {coin:^6} | {price:>10} | {formatted_mc:>15} |")
|
||||
left_table_lines.append("-" * left_table_width)
|
||||
|
||||
# --- Build Right Table (Strategy Status) ---
|
||||
right_table_lines = []
|
||||
right_table_width = 148
|
||||
right_table_lines.append("--- Strategy Status ---")
|
||||
right_table_lines.append("-" * right_table_width)
|
||||
right_table_lines.append(f"{'#':<2} | {'Strategy Name':<25} | {'Coin':^6} | {'Signal':<8} | {'Signal Price':>12} | {'Last Change (Local)':>22} | {'TF':^5} | {'Parameters':<45} |")
|
||||
right_table_lines.append("-" * right_table_width)
|
||||
for i, (name, status) in enumerate(self.strategy_statuses.items(), 1):
|
||||
signal = status.get('current_signal', 'N/A')
|
||||
price = status.get('signal_price')
|
||||
price_display = f"{price:.4f}" if isinstance(price, (int, float)) else "-"
|
||||
last_change = status.get('last_signal_change_utc')
|
||||
last_change_display = 'Never'
|
||||
if last_change:
|
||||
# Convert UTC timestamp from file to local time for display
|
||||
dt_utc = datetime.fromisoformat(last_change.replace('Z', '+00:00')).replace(tzinfo=timezone.utc)
|
||||
dt_local = dt_utc.astimezone(None)
|
||||
last_change_display = dt_local.strftime('%Y-%m-%d %H:%M')
|
||||
|
||||
config_params = self.strategy_configs.get(name, {}).get('parameters', {})
|
||||
coin = config_params.get('coin', 'N/A')
|
||||
timeframe = config_params.get('timeframe', 'N/A')
|
||||
|
||||
other_params = {k: v for k, v in config_params.items() if k not in ['coin', 'timeframe']}
|
||||
params_str = ", ".join([f"{k}={v}" for k, v in other_params.items()])
|
||||
|
||||
right_table_lines.append(f"{i:^2} | {name:<25} | {coin:^6} | {signal:<8} | {price_display:>12} | {last_change_display:>22} | {timeframe:^5} | {params_str:<45} |")
|
||||
right_table_lines.append("-" * right_table_width)
|
||||
|
||||
status_prefix = "DB Status: Last update -> "
|
||||
max_len = 80
|
||||
status_message = f"{status_prefix}{self.last_db_update_info}"
|
||||
if len(status_message) > max_len:
|
||||
status_message = status_message[:max_len-3] + "..."
|
||||
output_lines.append(status_message)
|
||||
|
||||
output_lines.append("--- Background Processes ---")
|
||||
# --- Combine Tables Side-by-Side ---
|
||||
output_lines = []
|
||||
max_rows = max(len(left_table_lines), len(right_table_lines))
|
||||
separator = " "
|
||||
indent = " " * 10
|
||||
for i in range(max_rows):
|
||||
left_part = left_table_lines[i] if i < len(left_table_lines) else " " * left_table_width
|
||||
right_part = indent + right_table_lines[i] if i < len(right_table_lines) else ""
|
||||
output_lines.append(f"{left_part}{separator}{right_part}")
|
||||
|
||||
# --- Add Bottom Sections ---
|
||||
output_lines.append(f"\nDB Status: Last update -> {self.last_db_update_info}")
|
||||
output_lines.append("\n--- Background Processes ---")
|
||||
for name, status in self.process_status.items():
|
||||
output_lines.append(f"{name:<25}: {status}")
|
||||
|
||||
final_output = "\n".join(output_lines) + "\n\x1b[J"
|
||||
print(final_output, end="")
|
||||
|
||||
self._lines_printed = len(output_lines)
|
||||
final_output = "\n".join(output_lines)
|
||||
print(final_output)
|
||||
sys.stdout.flush()
|
||||
|
||||
def run(self):
|
||||
@ -249,6 +296,7 @@ class MainApp:
|
||||
self.read_prices()
|
||||
self.read_market_caps()
|
||||
self.get_overall_db_status()
|
||||
self.read_strategy_statuses()
|
||||
self.check_process_status()
|
||||
self.display_dashboard()
|
||||
time.sleep(2)
|
||||
@ -268,6 +316,7 @@ if __name__ == "__main__":
|
||||
sys.exit(1)
|
||||
|
||||
processes = {}
|
||||
strategy_configs = {}
|
||||
|
||||
processes["Market Feeder"] = multiprocessing.Process(target=run_market_feeder, daemon=True)
|
||||
processes["Data Fetcher"] = multiprocessing.Process(target=data_fetcher_scheduler, daemon=True)
|
||||
@ -279,18 +328,22 @@ if __name__ == "__main__":
|
||||
strategy_configs = json.load(f)
|
||||
for name, config in strategy_configs.items():
|
||||
if config.get("enabled", False):
|
||||
if not os.path.exists(config['script']):
|
||||
logging.error(f"Strategy script '{config['script']}' for strategy '{name}' not found. Skipping.")
|
||||
continue
|
||||
proc = multiprocessing.Process(target=run_strategy, args=(name, config), daemon=True)
|
||||
processes[f"Strategy: {name}"] = proc
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
logging.error(f"Could not load strategies from '{STRATEGY_CONFIG_FILE}': {e}")
|
||||
|
||||
# Launch all processes
|
||||
for name, proc in processes.items():
|
||||
logging.info(f"Starting process '{name}'...")
|
||||
proc.start()
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
app = MainApp(coins_to_watch=WATCHED_COINS, processes=processes)
|
||||
app = MainApp(coins_to_watch=WATCHED_COINS, processes=processes, strategy_configs=strategy_configs)
|
||||
try:
|
||||
app.run()
|
||||
except KeyboardInterrupt:
|
||||
|
||||
Reference in New Issue
Block a user