Skip to content

Implement Binance-ready automated trading bot runtime - #17

Open
luaroncrew wants to merge 1 commit into
mainfrom
add-binance-trading-bot-c941e0
Open

Implement Binance-ready automated trading bot runtime#17
luaroncrew wants to merge 1 commit into
mainfrom
add-binance-trading-bot-c941e0

Conversation

@luaroncrew

@luaroncrew luaroncrew commented May 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • add an end-to-end automated trading runtime around the existing diffusion signal model
  • introduce typed signal, risk, execution, monitoring, persistence, and control-plane modules
  • default all execution to paper/testnet-safe behavior unless explicitly configured otherwise

Test plan

  • python3 -m compileall /home/user/vibetrader/bot /home/user/vibetrader/data/fetch_ohlcv.py
  • reviewed runtime wiring and README operating instructions

Open in Devin Review

Copy link
Copy Markdown
Owner Author

Task Deliverables

Generated by Ascii


Automated Binance-ready bot runtime

Implemented a full trading-bot path in vibetrader with safe defaults.

  • Added a machine-safe signal contract for model outputs
  • Added market ingestion, inference runtime, separate risk engine, and execution adapters
  • Added SQLite persistence for signals, orders, fills, positions, and bot events
  • Added monitoring hooks, manual pause/kill controls, and automatic kill switch behavior
  • Defaulted operation to paper trading or Binance testnet-safe behavior unless explicitly configured otherwise
  • Documented how to run, control, and configure the bot in the README
flowchart LR
    A[Binance/CCXT market data] --> B[Indicator enrichment]
    B --> C[Diffusion inference]
    C --> D[Signal contract]
    D --> E[Risk engine]
    E -->|approved| F[Paper or Binance execution]
    E -->|rejected| G[Event log]
    F --> H[SQLite persistence]
    G --> H
    H --> I[Monitoring hooks]
Loading
flowchart TD
    A[Operator] -->|pause| B[runtime/control/pause]
    A -->|kill| C[runtime/control/kill]
    D[Risk breach] --> C
    B --> E[Bot skips iteration]
    C --> F[Bot refuses to trade]
Loading

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment thread bot/runtime.py
Comment on lines +47 to +52
self.cash_usd = config.initial_balance_usd
self.start_equity_usd = config.initial_balance_usd
self.daily_start_equity_usd = config.initial_balance_usd
self.daily_key = self.risk.utc_day_key()
self.consecutive_losses = 0
self.position = self._load_or_create_position()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Cash balance not persisted, causing inflated equity after bot restart with open position

When the bot restarts, cash_usd is reset to config.initial_balance_usd (line 47) while self.position is loaded from the SQLite database at line 52 via _load_or_create_position. If the previous run had an open position (e.g., a long purchased for $2,000), the cash should have been reduced by that amount, but it resets to the full initial balance (e.g., $10,000). The bot then computes equity as cash_usd + unrealized (bot/runtime.py:89), which would be $10,000 + unrealized instead of the correct $8,000 + unrealized. This inflated equity flows into the risk engine's position sizing at bot/risk_engine.py:52-53, allowing oversized positions. Similarly, start_equity_usd, daily_start_equity_usd, and consecutive_losses are not persisted, meaning drawdown guards and loss streak limits are also reset on restart.

Prompt for agents
The core problem is that bot/runtime.py initializes cash_usd, start_equity_usd, daily_start_equity_usd, and consecutive_losses from config defaults at lines 47-51, but then loads the position from SQLite at line 52 via _load_or_create_position. If a position already exists in the DB (from a previous run), the cash and equity baselines are inconsistent with the loaded position.

To fix this, the bot's portfolio state (cash_usd, start_equity_usd, daily_start_equity_usd, consecutive_losses) should also be persisted in SQLite. This likely requires:

1. A new table in bot/persistence.py (e.g., portfolio_state) with columns for cash_usd, start_equity_usd, daily_start_equity_usd, daily_key, consecutive_losses.
2. A save_portfolio_state and get_portfolio_state method in Persistence.
3. In TradingBot.__init__, after loading the position, also load the portfolio state from DB. If none exists, use the config defaults.
4. In run_once and _update_position_from_fill, persist the portfolio state alongside the position.

Alternatively, a simpler approach would be to reconstruct cash_usd from the position and order history in the DB, but this is more complex. The persistence approach is cleaner.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread bot/runtime.py
Comment on lines +101 to +116
elif intent == "close_long":
realized_change = (price - self.position.entry_price) * self.position.quantity - fees
self.cash_usd += self.position.quantity * price - fees
self.position.realized_pnl += realized_change
self.position.quantity = 0.0
self.position.entry_price = 0.0
elif intent == "open_short":
self.cash_usd += qty * price - fees
self.position.quantity = -qty
self.position.entry_price = price
elif intent == "close_short":
realized_change = (self.position.entry_price - price) * abs(self.position.quantity) - fees
self.cash_usd -= abs(self.position.quantity) * price + fees
self.position.realized_pnl += realized_change
self.position.quantity = 0.0
self.position.entry_price = 0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Close handlers use self.position.quantity instead of actual fill quantity, ignoring partial fills

In _update_position_from_fill, the variable qty = fill.quantity is assigned at line 92 and correctly used in the open_long and open_short handlers. However, the close_long (lines 101-106) and close_short (lines 111-116) handlers use self.position.quantity instead of qty for cash and PnL calculations. If the exchange returns a partial fill (where fill.quantity < self.position.quantity), the bot credits/debits cash for the full position size, then zeroes out the position — creating an accounting mismatch with the actual exchange state. While partial fills are rare for market orders, this is architecturally incorrect and would cause real financial discrepancies in Binance live/testnet modes.

Suggested change
elif intent == "close_long":
realized_change = (price - self.position.entry_price) * self.position.quantity - fees
self.cash_usd += self.position.quantity * price - fees
self.position.realized_pnl += realized_change
self.position.quantity = 0.0
self.position.entry_price = 0.0
elif intent == "open_short":
self.cash_usd += qty * price - fees
self.position.quantity = -qty
self.position.entry_price = price
elif intent == "close_short":
realized_change = (self.position.entry_price - price) * abs(self.position.quantity) - fees
self.cash_usd -= abs(self.position.quantity) * price + fees
self.position.realized_pnl += realized_change
self.position.quantity = 0.0
self.position.entry_price = 0.0
elif intent == "close_long":
realized_change = (price - self.position.entry_price) * qty - fees
self.cash_usd += qty * price - fees
self.position.realized_pnl += realized_change
self.position.quantity -= qty
if self.position.quantity <= 0:
self.position.quantity = 0.0
self.position.entry_price = 0.0
elif intent == "open_short":
self.cash_usd += qty * price - fees
self.position.quantity = -qty
self.position.entry_price = price
elif intent == "close_short":
realized_change = (self.position.entry_price - price) * qty - fees
self.cash_usd -= qty * price + fees
self.position.realized_pnl += realized_change
self.position.quantity += qty
if self.position.quantity >= 0:
self.position.quantity = 0.0
self.position.entry_price = 0.0
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant