Understanding the Trading Bot Code, Logic, and Execution Flow

This Python code defines a modular trading bot capable of executing buy/sell operations based on trading signals from a strategy. It manages balance, inventory, stop-loss, take-profit, and logs detailed history for analysis.

High-Level Purpose

This trading bot:

  • Uses signals (BUY, SELL, HOLD) from a strategy.
  • Manages an account balance and inventory.
  • Executes trades while considering feesminimum purchase quantitystop-loss, and take-profit mechanisms.
  • Keeps a detailed log of each trade and overall performance.

Key Components

1. BotAction Enum

Defines constant labels for different actions the bot can perform. This makes the logic clear and avoids “magic strings” in the code.

class BotAction(Enum):
BUY = "Buy"
SELL = "Sell"
HOLD = "Hold"
InsufficientBalance = "Insufficient Balance"
NothingToSell = "Nothing to Sell"
StopLoss = "Stop Loss"
TakeProfit = "Take Profit"
CannotBuyMultiple = "Cannot Buy Multiple"

2. Inventory Class

Manages the quantity and cost of the assets held.

Logic:

  • Tracks total quantity and buy cost.
  • Computes average cost per unit.
  • Updates inventory after buys and sells.
class Inventory:
def __init__(self):
self.quantity = 0
self.buy_cost = 0

@property
def average_cost(self):
return self.buy_cost / self.quantity

def add_buy(self, buy_cost, quantiy):
self.quantity += quantiy
self.buy_cost += buy_cost

def add_sell(self, quantiy):
self.quantity -= quantiy
if self.quantity > 0:
self.buy_cost = self.average_cost * self.quantity
else:
self.buy_cost = 0

3. TradingBot Class

Handles:

  • Account balance.
  • Trading strategy.
  • Trade execution.
  • Profit/Loss tracking.
  • Logging all activities.
class TradingBot:
"""Trading Bot that executes orders based on a given strategy."""

def __init__(
self,
initial_balance: int,
strategy: TradingStrategy,
trading_fees: float = 0.001,
minimum_quantity_purchaseable: float = 0.01,
maximum_quantity_purchaseable: str = "all",
stop_loss_percentage=10,
take_profit_percentage=10,
allow_multiple_buy=False,
):
self.strategy = strategy

self.inventory = Inventory()

self.trading_fees = trading_fees

self.minimum_quantity_purchaseable = minimum_quantity_purchaseable

self.initial_balance = initial_balance
self.balance = initial_balance

self.stop_loss_percentage = stop_loss_percentage / 100
self.take_profit_percentage = take_profit_percentage / 100

self.dynamic_stop_loss_value = None

self.allow_multiple_buy = allow_multiple_buy

self.net_profit_loss = 0

self.order_history = []
self.history = {
"Datetime": [],
"Close": [],
"Balance": [],
"worth": [],
"Inventory_qty": [],
"Inventory_amount": [],
"Stop_Loss": [],
"Signal": [],
"Action": [],
"Profit_Loss": [],
"Profit_Loss_Percentage": [],
"Net Profit_Loss": [],
"ActionBuy": [],
"ActionSell": [],
}

def set_strategy(self, strategy: TradingStrategy):
"""Allows switching trading strategies dynamically."""
self.strategy = strategy

def execute_trade(self, data):
"""Executes trade based on strategy signal."""

for index, row in data.iterrows():
signal = self.strategy.generate_signal(row)

self.history["Datetime"].append(row["Datetime"])
self.history["Close"].append(row["Close"])
self.history["Balance"].append(round(self.balance, 2))
self.history["Signal"].append(signal.name)

self.place_order(signal, row["Close"])

def buy_cost(self, price, quantity):
buy_cost = (quantity * price) * (1 + self.trading_fees)
return buy_cost

def sell_cost(self, price, quantity):
sell_cost = (quantity * price) * (1 - self.trading_fees)
return sell_cost

def purchaseable_quantity(self, price):
return self.balance / price

def buy(self, price, quantity):
buy_cost = self.buy_cost(price, quantity)

self.balance -= buy_cost

self.inventory.add_buy(buy_cost, quantity)

self.history["Inventory_qty"].append(self.inventory.quantity)
self.history["Inventory_amount"].append(self.inventory.buy_cost)
self.history["Action"].append(BotAction.BUY.value)
self.history["Profit_Loss"].append(0)
self.history["Profit_Loss_Percentage"].append(0)
self.history["Net Profit_Loss"].append(self.net_profit_loss)
self.history["ActionBuy"].append(price)
self.history["ActionSell"].append(0)

stop_loss_value = self.inventory.buy_cost * (1 - self.stop_loss_percentage)
self.dynamic_stop_loss_value = stop_loss_value

def sell(self, price, quantity, reason):
sell_cost = self.sell_cost(price, quantity)
profit_loss = sell_cost - self.inventory.buy_cost
profit_loss_percentage = (profit_loss / self.inventory.buy_cost) * 100
self.net_profit_loss += profit_loss

self.balance += sell_cost
self.inventory.add_sell(quantity)

self.dynamic_stop_loss_value = None

self.history["Inventory_qty"].append(self.inventory.quantity)
self.history["Inventory_amount"].append(self.inventory.buy_cost)
self.history["Action"].append(reason)
self.history["Profit_Loss"].append(profit_loss)
self.history["Profit_Loss_Percentage"].append(profit_loss_percentage)
self.history["Net Profit_Loss"].append(self.net_profit_loss)
self.history["ActionBuy"].append(0)
self.history["ActionSell"].append(price)

def do_nothing(self, action):
self.history["Inventory_qty"].append(self.inventory.quantity)
self.history["Inventory_amount"].append(self.inventory.buy_cost)
self.history["Action"].append(action)
self.history["Profit_Loss"].append(0)
self.history["Profit_Loss_Percentage"].append(0)
self.history["Net Profit_Loss"].append(self.net_profit_loss)
self.history["ActionBuy"].append(0)
self.history["ActionSell"].append(0)

def place_order(self, order_type: str, price: float):
"""Simulates placing an order."""

buy_quantity = self.minimum_quantity_purchaseable
minimum_buy_cost = self.buy_cost(
price,
buy_quantity
)

sell_quantity = self.inventory.quantity
sell_cost = self.sell_cost(price, sell_quantity)

self.history["worth"].append(sell_cost)

profit_loss = sell_cost - self.inventory.buy_cost

if self.inventory.quantity > 0:
stop_loss_value = sell_cost * (1 - self.stop_loss_percentage)
if stop_loss_value > self.dynamic_stop_loss_value:
self.dynamic_stop_loss_value = stop_loss_value

self.history["Stop_Loss"].append(f"{self.dynamic_stop_loss_value}")

# Stop Loss
if (self.stop_loss_percentage != 0) and (
sell_cost < self.dynamic_stop_loss_value
) and (self.inventory.quantity > 0):
self.sell(price, sell_quantity, BotAction.StopLoss.value)
return

# Take Profit
if (self.take_profit_percentage != 0) and (
profit_loss > (self.inventory.buy_cost * self.take_profit_percentage)
) and (self.inventory.quantity > 0):
self.sell(price, sell_quantity, BotAction.TakeProfit.value)
return
else:
self.history["Stop_Loss"].append(0)

if order_type == TradingSignal.HOLD:
self.do_nothing(BotAction.HOLD.value)

elif order_type == TradingSignal.BUY:
if self.balance < minimum_buy_cost:
self.do_nothing(BotAction.InsufficientBalance.value)
elif not self.allow_multiple_buy:
if self.inventory.quantity == 0:
self.buy(price, buy_quantity)
else:
self.do_nothing(BotAction.CannotBuyMultiple.value)
else:
self.buy(price, buy_quantity)
elif order_type == TradingSignal.SELL:
if self.inventory.quantity > 0:
self.sell(price, sell_quantity, BotAction.SELL.value)
else:
self.do_nothing(BotAction.SELL.NothingToSell.value)

Key Parameters of TradingBot:

Parameter Purpose initial_balance Starting cash balance. strategy External strategy that provides signals. trading_fees Fee percentage per trade. minimum_quantity_purchaseable Smallest allowable purchase. stop_loss_percentage Auto-sell to limit losses. take_profit_percentage Auto-sell to secure profits. allow_multiple_buy If False, prevents buying when already holding stock.

How the Bot Works (Step-by-Step):

1. Signal Execution

In execute_trade(), the bot processes each row (candlestick or price data):

for index, row in data.iterrows():
signal = self.strategy.generate_signal(row)
self.place_order(signal, row["Close"])

2. Order Placement

In place_order():

  • The bot checks:
  • Should we BUY, SELL, or HOLD?
  • Are we in a stop-loss or take-profit scenario?
  • Do we have enough balance or stock?

Stop-Loss Logic:

If the total value of the held stock drops below the dynamic stop-loss value, the bot sells to minimize losses.

if sell_cost < self.dynamic_stop_loss_value:
self.sell(price, sell_quantity, BotAction.StopLoss.value)

Take-Profit Logic:

If the profit exceeds a defined percentage, the bot locks in profits by selling.

if profit_loss > (self.inventory.buy_cost * self.take_profit_percentage):
self.sell(price, sell_quantity, BotAction.TakeProfit.value)

BUY Logic:

  • Check balance.
  • Check whether multiple buys are allowed.
  • Execute the purchase if valid.
            if self.balance < minimum_buy_cost:
self.do_nothing(BotAction.InsufficientBalance.value)
elif not self.allow_multiple_buy:
if self.inventory.quantity == 0:
self.buy(price, buy_quantity)
else:
self.do_nothing(BotAction.CannotBuyMultiple.value)
else:
self.buy(price, buy_quantity)

SELL Logic:

  • If we have stock, sell all.
  • If we don’t, log “Nothing to Sell”.
            if self.inventory.quantity > 0:
self.sell(price, sell_quantity, BotAction.SELL.value)
else:
self.do_nothing(BotAction.SELL.NothingToSell.value)

3. Cost Calculations

Includes trading fees:

def buy_cost(self, price, quantity):
return (quantity * price) * (1 + self.trading_fees)

Similarly for sell costs.

    def sell_cost(self, price, quantity):
return (quantity * price) * (1 - self.trading_fees)

4. History Logging

Everything (actions, profits, inventory levels) is logged in self.history for further analysis or visualization.

5. Dynamic Stop-Loss Update

After buying or during price increases, the stop-loss value is adjusted upwards to protect profits:

if stop_loss_value > self.dynamic_stop_loss_value:
self.dynamic_stop_loss_value = stop_loss_value

Why This Design?

Feature Reasoning Enums for Actions Clean and readable code. Separate Inventory Class Isolates stock handling logic. Dynamic Stop-Loss Protects against large losses while allowing gains. Strategy Injection Supports different strategies without modifying the bot. Full History Logging For audit, debugging, and performance analysis.

Summary of Flow:

  1. For each market tick: Get signal → place corresponding order.
  2. Before acting: Check for stop-loss or take-profit conditions.
  3. Execute BUY/SELL/HOLD as appropriate.
  4. Update balance, inventory, profits.
  5. Log all actions and results.

What Makes This Bot Solid?

  • Separation of concerns (strategy vs. execution).
  • Extendable (plug different strategies easily).
  • Protects capital (stop-loss, take-profit).
  • Balances multiple conditions (balance check, multiple buys).
  • Performance tracking (history dictionary).

Final Thoughts

This is a well-structured bot for backtesting and simulation purposes. With minor tweaks, it could integrate with real trading APIs like Binance or KuCoin.

It ensures:

  • Smart decision-making.
  • Risk management.
  • Data tracking.

Would you like me to show how to plug this bot into a real exchange or backtest on a dataset?

No comments:

Post a Comment

Building a CLI-Based People Tracking and Dwell Time Analytics System Using YOLOv8 and DeepSORT

  Introduction Tracking people across video frames and analyzing their behavior (like  dwell time ) is a crucial task for many real-world ap...