| |
| |
| |
| |
|
|
| import os |
| import json |
| import time |
| from datetime import datetime, timezone |
|
|
| |
| STATE_FILE = "/app/capital_state.json" |
| ORIGINAL_DEPOSIT_FILE = "/app/original_deposit.json" |
| BALANCE_FILE = "/app/balance.txt" |
| VIRTUAL_STATE_FILE = "/app/virtual_capital_state.json" |
|
|
| |
| DEFAULT_RISK_POOL = 5.0 |
| DEFAULT_TOTAL_CAPITAL = 20.0 |
| HALT_FILE = "/app/halt_trading.txt" |
| UNHALT_FILE = "/app/unhalt_trading.txt" |
|
|
| |
| TIERS = [ |
| (0.25, 10, "L1"), |
| (0.50, 20, "L2"), |
| (0.75, 30, "L3"), |
| (0.90, 70, "L4"), |
| ] |
|
|
| def read_json(path, default=None): |
| try: |
| if os.path.exists(path): |
| with open(path, "r") as f: |
| return json.load(f) |
| except: |
| pass |
| return default |
|
|
| def write_json(path, data): |
| try: |
| with open(path, "w") as f: |
| json.dump(data, f, indent=2) |
| except Exception as e: |
| print(f"[Capital] Failed to write {path}: {e}", flush=True) |
|
|
| def read_file(path, default=""): |
| try: |
| if os.path.exists(path): |
| with open(path, "r") as f: |
| return f.read().strip() |
| except: |
| pass |
| return default |
|
|
| def load_state(filepath=STATE_FILE): |
| state = read_json(filepath) |
| if state is None: |
| state = { |
| "tier_index": 0, |
| "consecutive_wins": 0, |
| "consecutive_losses": 0, |
| "risk_pool": DEFAULT_RISK_POOL, |
| "total_capital": DEFAULT_TOTAL_CAPITAL, |
| "max_bet": DEFAULT_RISK_POOL * 0.20, |
| "reinvest_rate": TIERS[0][0], |
| "halted": False, |
| "original_deposit": None, |
| "deposit_date": None, |
| } |
| return state |
|
|
| def save_state(state, filepath=STATE_FILE): |
| write_json(filepath, state) |
|
|
| def get_risk_ratio(total_capital): |
| if total_capital <= 50: |
| return 0.25 |
| elif total_capital <= 200: |
| return 0.20 |
| elif total_capital <= 1000: |
| return 0.15 |
| elif total_capital <= 5000: |
| return 0.10 |
| else: |
| return 0.08 |
|
|
| def update_after_trade(won: bool, profit_amount: float, filepath=STATE_FILE): |
| state = load_state(filepath) |
|
|
| if state["original_deposit"] is None and filepath != VIRTUAL_STATE_FILE: |
| bal = read_file(BALANCE_FILE, "0") |
| try: |
| bal = float(bal) |
| except: |
| bal = 0.0 |
| if bal > 0: |
| state["original_deposit"] = bal |
| state["deposit_date"] = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC") |
|
|
| state["total_capital"] += profit_amount |
|
|
| if won: |
| state["consecutive_wins"] += 1 |
| state["consecutive_losses"] = 0 |
| tier_idx = state["tier_index"] |
| required = TIERS[tier_idx][1] |
| if state["consecutive_wins"] >= required and tier_idx < len(TIERS) - 1: |
| state["tier_index"] = tier_idx + 1 |
| state["consecutive_wins"] = 0 |
| state["reinvest_rate"] = TIERS[state["tier_index"]][0] |
| print(f"[Capital] 🟢 Upgraded to {TIERS[state['tier_index']][2]} " |
| f"({state['reinvest_rate']:.0%} reinvest)", flush=True) |
| reinvest = profit_amount * state["reinvest_rate"] |
| state["risk_pool"] += reinvest |
| else: |
| state["consecutive_losses"] += 1 |
| state["consecutive_wins"] = 0 |
| state["risk_pool"] = max(0.0, state["risk_pool"] + profit_amount) |
| if state["consecutive_losses"] >= 2 and state["tier_index"] > 0: |
| state["tier_index"] -= 1 |
| state["consecutive_wins"] = 0 |
| state["consecutive_losses"] = 0 |
| state["reinvest_rate"] = TIERS[state["tier_index"]][0] |
| print(f"[Capital] 🔴 Downgraded to {TIERS[state['tier_index']][2]} " |
| f"({state['reinvest_rate']:.0%} reinvest)", flush=True) |
|
|
| total = state["total_capital"] |
| ratio = get_risk_ratio(total) |
| target_risk_pool = total * ratio |
| if target_risk_pool > state["risk_pool"]: |
| state["risk_pool"] = target_risk_pool |
| elif state["risk_pool"] > target_risk_pool: |
| state["risk_pool"] = target_risk_pool |
|
|
| initial_risk = state["original_deposit"] * 0.25 if state["original_deposit"] else DEFAULT_RISK_POOL |
| if state["risk_pool"] < initial_risk * 0.5: |
| state["halted"] = True |
| print("[Capital] ⚠️ CIRCUIT BREAKER TRIGGERED: risk pool < 50% of initial", flush=True) |
|
|
| if os.path.exists(HALT_FILE): |
| state["halted"] = True |
| os.remove(HALT_FILE) |
| print("[Capital] ⚠️ Manual HALT triggered", flush=True) |
| if os.path.exists(UNHALT_FILE): |
| state["halted"] = False |
| os.remove(UNHALT_FILE) |
| print("[Capital] ✅ Manual UNHALT — trading resumed", flush=True) |
|
|
| state["max_bet"] = round(state["risk_pool"] * 0.20, 2) |
| save_state(state, filepath) |
| return state |
|
|
| def get_summary(): |
| state = load_state() |
| tier = TIERS[state["tier_index"]] |
| return { |
| "tier": tier[2], |
| "tier_index": state["tier_index"], |
| "reinvest_rate": state["reinvest_rate"], |
| "consecutive_wins": state["consecutive_wins"], |
| "wins_needed": tier[1], |
| "consecutive_losses": state["consecutive_losses"], |
| "risk_pool": round(state["risk_pool"], 2), |
| "max_bet": round(state["max_bet"], 2), |
| "total_capital": round(state["total_capital"], 2), |
| "halted": state["halted"], |
| "original_deposit": state["original_deposit"], |
| "deposit_date": state["deposit_date"], |
| } |