Better logic on fund splitting

This commit is contained in:
2026-05-04 09:37:07 -04:00
parent fcc64d6573
commit 9285b303d0
2 changed files with 22 additions and 14 deletions

View File

@@ -4,4 +4,6 @@ KRAKEN_API_KEY=your_api_key_here
KRAKEN_API_SECRET=your_api_secret_here KRAKEN_API_SECRET=your_api_secret_here
# Starting balance for paper trading simulation # Starting balance for paper trading simulation
# Needs to be at least (max_positions * min_order_usd) to fill all slots.
# Default config: 5 positions * $15 min = $75 minimum, $500+ recommended.
PAPER_BALANCE_USD=1000 PAPER_BALANCE_USD=1000

34
bot.py
View File

@@ -45,9 +45,11 @@ def setup_logging(log_file: str) -> None:
) )
def available_usd(client: KrakenClient, config: Config) -> float: def available_usd(client: KrakenClient, config: Config, portfolio: Portfolio) -> float:
if config.paper_trading: if config.paper_trading:
return float(os.getenv("PAPER_BALANCE_USD", "1000")) total = float(os.getenv("PAPER_BALANCE_USD", "1000"))
invested = sum(pos.cost_usd for pos in portfolio.all())
return max(0.0, total - invested)
try: try:
return client.get_usd_balance() return client.get_usd_balance()
except KrakenError as exc: except KrakenError as exc:
@@ -129,7 +131,7 @@ def run_cycle(config: Config) -> bool:
log.info("Portfolio full — no new positions to open.") log.info("Portfolio full — no new positions to open.")
return bool(closed) return bool(closed)
balance = available_usd(client, config) balance = available_usd(client, config, portfolio)
log.info("Available balance: $%.2f | Open slots: %d", balance, open_slots) log.info("Available balance: $%.2f | Open slots: %d", balance, open_slots)
if balance < config.min_order_usd: if balance < config.min_order_usd:
@@ -146,22 +148,26 @@ def run_cycle(config: Config) -> bool:
log.info("No opportunities matching criteria this cycle.") log.info("No opportunities matching criteria this cycle.")
return bool(closed) return bool(closed)
# Cap to available slots and allocate equally # Sort by 24h change descending — strongest movers get priority when funds are limited
to_buy = opportunities[:open_slots] opportunities.sort(key=lambda o: o.change_pct, reverse=True)
alloc_per_asset = balance / len(to_buy)
# How many can we actually afford at the minimum order size?
affordable = int(balance // config.min_order_usd)
num_to_buy = min(open_slots, affordable, len(opportunities))
if num_to_buy == 0:
log.info("Balance $%.2f too low for even one minimum order ($%.2f).", balance, config.min_order_usd)
return bool(closed)
to_buy = opportunities[:num_to_buy]
# Split available balance equally across the chosen assets (always >= min_order_usd)
alloc_per_asset = balance / num_to_buy
log.info( log.info(
"Buying %d asset(s) @ $%.2f each (total $%.2f)", "Buying %d asset(s) @ $%.2f each (total $%.2f)",
len(to_buy), alloc_per_asset, alloc_per_asset * len(to_buy), num_to_buy, alloc_per_asset, alloc_per_asset * num_to_buy,
) )
for opp in to_buy: for opp in to_buy:
if alloc_per_asset < config.min_order_usd:
log.warning(
"Allocation $%.2f < minimum $%.2f — skipping %s",
alloc_per_asset, config.min_order_usd, opp.pair,
)
continue
quantity = round(alloc_per_asset / opp.last_price, opp.lot_decimals) quantity = round(alloc_per_asset / opp.last_price, opp.lot_decimals)
if opp.order_min > 0 and quantity < opp.order_min: if opp.order_min > 0 and quantity < opp.order_min: