Skip to content

Commit

Permalink
strategy balance tracker update
Browse files Browse the repository at this point in the history
  • Loading branch information
r0fls committed Jun 18, 2024
1 parent 8e70f5c commit 5c82810
Showing 1 changed file with 67 additions and 28 deletions.
95 changes: 67 additions & 28 deletions strategies/constant_percentage_strategy.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import asyncio
from datetime import timedelta
from strategies.base_strategy import BaseStrategy
from database.models import Balance, Position
from utils.logger import logger
from utils.utils import is_market_open
from datetime import datetime
from utils.utils import is_market_open
from utils.logger import logger
from database.models import Balance, Position
from strategies.base_strategy import BaseStrategy
import asyncio
RDFNfrom datetime import timedelta


class ConstantPercentageStrategy(BaseStrategy):
def __init__(self, broker, stock_allocations, cash_percentage, rebalance_interval_minutes, starting_capital):
Expand All @@ -15,34 +16,49 @@ def __init__(self, broker, stock_allocations, cash_percentage, rebalance_interva
self.starting_capital = starting_capital
self.strategy_name = 'constant_percentage'
super().__init__(broker)
logger.info(f"Initialized {self.strategy_name} strategy with starting capital {self.starting_capital}")
logger.info(
f"Initialized {self.strategy_name} strategy with starting capital {self.starting_capital}")

async def initialize(self):
await self.sync_positions_with_broker() # Ensure positions are synced on initialization
# Ensure positions are synced on initialization
await self.sync_positions_with_broker()

async def rebalance(self):
logger.debug("Starting rebalance process")
self.sync_positions_with_broker() # Ensure positions are synced before rebalancing

# Ensure positions are synced before rebalancing
self.sync_positions_with_broker()

account_info = self.broker.get_account_info()
cash_balance = account_info.get('cash_available')
logger.debug(f"Account info: {account_info}")

with self.broker.Session() as session:
balance = session.query(Balance).filter_by(
strategy=self.strategy_name,
broker=self.broker.broker_name,
type='cash'
).first()
if balance is None:
logger.error(f"Strategy balance not initialized for {self.strategy_name} strategy on {self.broker.broker_name}.")
raise ValueError(f"Strategy balance not initialized for {self.strategy_name} strategy on {self.broker.broker_name}.")
logger.error(
f"Strategy balance not initialized for {self.strategy_name} strategy on {self.broker.broker_name}.")
raise ValueError(
f"Strategy balance not initialized for {self.strategy_name} strategy on {self.broker.broker_name}.")
total_balance = balance.balance
logger.debug(f"Total balance retrieved: {total_balance}")

# Query for current positions in the database
current_db_positions = session.query(Position).filter_by(
strategy=self.strategy_name,
broker=self.broker.broker_name
).all()
current_db_positions_dict = {
pos.stock: pos.quantity for pos in current_db_positions}
logger.debug(f"Current DB positions: {current_db_positions_dict}")

target_cash_balance = total_balance * self.cash_percentage
target_investment_balance = total_balance - target_cash_balance
logger.debug(f"Target cash balance: {target_cash_balance}, Target investment balance: {target_investment_balance}")
logger.debug(
f"Target cash balance: {target_cash_balance}, Target investment balance: {target_investment_balance}")

current_positions = self.get_current_positions()
logger.debug(f"Current positions: {current_positions}")
Expand All @@ -56,42 +72,63 @@ async def rebalance(self):
else:
current_price = self.broker.get_current_price(stock)
target_quantity = target_balance // current_price
logger.debug(f"Stock: {stock}, Allocation: {allocation}, Target balance: {target_balance}, Current position: {current_position}, Current price: {current_price}, Target quantity: {target_quantity}")

logger.debug(
f"Stock: {stock}, Allocation: {allocation}, Target balance: {target_balance}, Current position: {current_position}, Current price: {current_price}, Target quantity: {target_quantity}")
if current_position < target_quantity:
if is_market_open():
if asyncio.iscoroutinefunction(self.broker.place_order):
await self.broker.place_order(stock, target_quantity - current_position, 'buy', self.strategy_name)
else:
self.broker.place_order(stock, target_quantity - current_position, 'buy', self.strategy_name)
logger.info(f"Placed buy order for {stock}: {target_quantity - current_position} shares")
self.broker.place_order(
stock, target_quantity - current_position, 'buy', self.strategy_name)
logger.info(
f"Placed buy order for {stock}: {target_quantity - current_position} shares")
else:
logger.info(f"Market is closed, not buying {stock}: {target_quantity - current_position} shares")
logger.info(
f"Market is closed, not buying {stock}: {target_quantity - current_position} shares")
elif current_position > target_quantity:
if is_market_open():
if asyncio.iscoroutinefunction(self.broker.place_order):
await self.broker.place_order(stock, current_position - target_quantity, 'sell', self.strategy_name)
else:
self.broker.place_order(stock, current_position - target_quantity, 'sell', self.strategy_name)
logger.info(f"Placed sell order for {stock}: {current_position - target_quantity} shares")
self.broker.place_order(
stock, current_position - target_quantity, 'sell', self.strategy_name)
logger.info(
f"Placed sell order for {stock}: {current_position - target_quantity} shares")
else:
logger.info(
f"Market is closed, not selling {stock}: {current_position - target_quantity} shares")

# Sell positions that aren't part of the current intended stock allocation
for stock, quantity in current_db_positions_dict.items():
if stock not in self.stock_allocations:
if is_market_open():
if asyncio.iscoroutinefunction(self.broker.place_order):
await self.broker.place_order(stock, quantity, 'sell', self.strategy_name)
else:
self.broker.place_order(
stock, quantity, 'sell', self.strategy_name)
logger.info(
f"Placed sell order for {stock}: {quantity} shares")
else:
logger.info(f"Market is closed, not selling {stock}: {target_quantity - current_position} shares")
logger.info(
f"Market is closed, not selling {stock}: {quantity} shares")

def get_current_positions(self):
positions = self.broker.get_positions()
positions_dict = {position: positions[position]['quantity'] for position in positions}
positions_dict = {
position: positions[position]['quantity'] for position in positions}
logger.debug(f"Retrieved current positions: {positions_dict}")
return positions_dict


# TODO: can we abstract this method across strategies?

async def sync_positions_with_broker(self):
logger.debug("Syncing positions with broker")

broker_positions = self.broker.get_positions()
logger.debug(f"Broker positions: {broker_positions}")


with self.broker.Session() as session:
# Get the actual positions from the broker
for symbol, data in broker_positions.items():
Expand All @@ -118,7 +155,8 @@ async def sync_positions_with_broker(self):
position.quantity = data['quantity']
position.latest_price = current_price
position.last_updated = datetime.utcnow()
logger.info(f"Updated uncategorized position for {symbol} to strategy {self.strategy_name} with quantity {data['quantity']} and price {current_price}")
logger.info(
f"Updated uncategorized position for {symbol} to strategy {self.strategy_name} with quantity {data['quantity']} and price {current_price}")
else:
# Create a new position
position = Position(
Expand All @@ -130,7 +168,8 @@ async def sync_positions_with_broker(self):
last_updated=datetime.utcnow()
)
session.add(position)
logger.info(f"Created new position for {symbol} with quantity {data['quantity']} and price {current_price}")
logger.info(
f"Created new position for {symbol} with quantity {data['quantity']} and price {current_price}")
session.commit()
logger.debug("Positions synced with broker")

Expand Down

0 comments on commit 5c82810

Please sign in to comment.