Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions render.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
services:
- type: worker
name: allocation-engine-2.0
runtime: python
region: oregon
plan: starter
buildCommand: pip install -r requirements.txt
startCommand: python -m trading_system.main --publish --dashboard --continuous --interval 5 --ticker BTC SPY QQQ AMZN
envVars:
- key: NETLIFY_API_TOKEN
sync: false
- key: NETLIFY_SITE_ID
sync: false
- key: TWELVE_DATA_API_KEY
sync: false
- key: RH_USERNAME
sync: false
- key: RH_PASSWORD
sync: false
- key: RH_TOTP_CODE
sync: false
- key: RH_AUTOMATED_ACCOUNT_NUMBER
sync: false
- key: SLACK_WEBHOOK_URL
sync: false
- key: PYTHON_VERSION
value: "3.11"
112 changes: 66 additions & 46 deletions trading_system/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def __init__(self, twelve_data_api_key: str, symbols: List[str],
strategy_name: str = 'momentum_dca',
verbose: bool = False,
dashboard: bool = False,
recent_days: int = None):
recent_days: int = None,
publish: bool = False):
"""
Initialize trading system

Expand All @@ -48,50 +49,58 @@ def __init__(self, twelve_data_api_key: str, symbols: List[str],
verbose: If True, show detailed output (metrics, portfolio, etc.)
dashboard: If True, fetch market indicators and write dashboard data each cycle
recent_days: If set, limit backtest to last N daily bars
publish: If True, upload blobs even in dry-run mode (no trades, but site data updates)
"""
self.symbols = symbols
self.dry_run = dry_run
self.strategy_name = strategy_name
self.verbose = verbose
self.dashboard = dashboard
self.recent_days = recent_days
self.publish = publish

# Initialize components
self.data_provider = TwelveDataProvider(twelve_data_api_key)
self.metrics_calculator = MetricsCalculator()
self.state_manager = StateManager()
self.trading_bot = SafeCashBot()

# Initialize execution quality layer
# Skip broker initialization in publish-only mode (no trading needed)
self.trading_bot = None
self.fill_logger = None
try:
from trading_system.execution.fill_auditor import FillAuditor
from trading_system.execution.spread_checker import SpreadChecker
from trading_system.execution.price_optimizer import PriceOptimizer
from trading_system.execution.pdt_gate import PDTGate
from trading_system.execution.fill_logger import FillLogger

fill_auditor = FillAuditor(
alpaca_key=os.getenv('ALPACA_API_KEY', ''),
alpaca_secret=os.getenv('ALPACA_SECRET_KEY', ''),
twelve_data_provider=self.data_provider,
)
spread_checker = SpreadChecker(fill_auditor=fill_auditor)
price_optimizer = PriceOptimizer()
pdt_gate = PDTGate(trading_bot=self.trading_bot)
fill_logger = FillLogger()

self.trading_bot.init_execution_layer(
fill_auditor=fill_auditor,
spread_checker=spread_checker,
price_optimizer=price_optimizer,
pdt_gate=pdt_gate,
fill_logger=fill_logger,
)
if not (self.publish and self.dry_run):
self.trading_bot = SafeCashBot()

self.fill_logger = fill_logger
except Exception as e:
print(f" [exec-layer] Execution quality layer init failed (proceeding without): {e}")
# Initialize execution quality layer
try:
from trading_system.execution.fill_auditor import FillAuditor
from trading_system.execution.spread_checker import SpreadChecker
from trading_system.execution.price_optimizer import PriceOptimizer
from trading_system.execution.pdt_gate import PDTGate
from trading_system.execution.fill_logger import FillLogger

fill_auditor = FillAuditor(
alpaca_key=os.getenv('ALPACA_API_KEY', ''),
alpaca_secret=os.getenv('ALPACA_SECRET_KEY', ''),
twelve_data_provider=self.data_provider,
)
spread_checker = SpreadChecker(fill_auditor=fill_auditor)
price_optimizer = PriceOptimizer()
pdt_gate = PDTGate(trading_bot=self.trading_bot)
fill_logger = FillLogger()

self.trading_bot.init_execution_layer(
fill_auditor=fill_auditor,
spread_checker=spread_checker,
price_optimizer=price_optimizer,
pdt_gate=pdt_gate,
fill_logger=fill_logger,
)

self.fill_logger = fill_logger
except Exception as e:
print(f" [exec-layer] Execution quality layer init failed (proceeding without): {e}")
else:
print(" [publish-only] Skipping broker init — publish-only mode (no trades)")

if strategy_name == 'momentum_dca_long':
self.strategy = MomentumDcaLongStrategy(symbols)
Expand Down Expand Up @@ -607,15 +616,18 @@ def run_once(self):
print(f"{'='*70}\n")

# Print initial portfolio allocation
self.print_portfolio_allocation()
if self.trading_bot:
self.print_portfolio_allocation()

# Fetch open orders once (used by momentum_dca)
open_orders = []
if self.strategy_name == 'momentum_dca_long':
open_orders = self.trading_bot.get_open_orders()

recent_orders = self.trading_bot.get_recent_orders(days=7)
recent_option_orders = self.trading_bot.get_recent_option_orders(days=7)
recent_orders = []
recent_option_orders = []
if self.trading_bot:
if self.strategy_name == 'momentum_dca_long':
open_orders = self.trading_bot.get_open_orders()
recent_orders = self.trading_bot.get_recent_orders(days=7)
recent_option_orders = self.trading_bot.get_recent_option_orders(days=7)

# Print order book before processing through state manager
if open_orders:
Expand Down Expand Up @@ -668,11 +680,12 @@ def run_once(self):
if self.verbose:
print(self.metrics_calculator.format_metrics(symbol, metrics))

# 3. Execute strategy
signal = self.execute_strategy(symbol, metrics, open_orders)
# 3. Execute strategy (requires broker for positions)
if self.trading_bot:
signal = self.execute_strategy(symbol, metrics, open_orders)

# 4. Process signal
self.process_signal(symbol, signal, open_orders)
# 4. Process signal
self.process_signal(symbol, signal, open_orders)

except Exception as e:
print(f"Error processing {symbol}: {e}")
Expand All @@ -683,14 +696,14 @@ def run_once(self):

# Log state: local file in dry-run, Netlify Blobs when live
symbols_filter = None if self.verbose else self.symbols
portfolio_data = self.trading_bot.get_portfolio_summary(symbols=symbols_filter)
portfolio_data = self.trading_bot.get_portfolio_summary(symbols=symbols_filter) if self.trading_bot else None

# Compute drift metrics from cached daily bars (if available)
drift_metrics = self._compute_drift_metrics()

log_state_to_blob(
self.state_manager,
live=not self.dry_run,
live=not self.dry_run or self.publish,
order_book=open_orders,
portfolio=portfolio_data,
drift_metrics=drift_metrics,
Expand Down Expand Up @@ -737,8 +750,9 @@ def run_once(self):
self.state_manager.print_state_summary()

# Print final portfolio allocation
print("\nFinal Portfolio Allocation:")
self.print_portfolio_allocation()
if self.trading_bot:
print("\nFinal Portfolio Allocation:")
self.print_portfolio_allocation()

# Print fill quality stats
if hasattr(self, 'fill_logger') and self.fill_logger:
Expand Down Expand Up @@ -848,7 +862,7 @@ def _send_oncall_summary(self, open_orders: List[Dict], portfolio_data: Dict):
lines.append("=" * 40)

# PDT status
pdt_info = self.trading_bot.get_pdt_status()
pdt_info = self.trading_bot.get_pdt_status() if self.trading_bot else None
if pdt_info is not None:
count = pdt_info.get('day_trade_count', 0)
if pdt_info.get('flagged'):
Expand Down Expand Up @@ -1137,6 +1151,11 @@ def main():
action='store_true',
help='Fetch market indicators and write dashboard/market_data.json each cycle'
)
parser.add_argument(
'--publish',
action='store_true',
help='Upload blobs in dry-run mode (no trades placed, but site data updates)'
)
parser.add_argument(
'--backtest',
action='store_true',
Expand Down Expand Up @@ -1179,10 +1198,11 @@ def main():
verbose=args.verbose,
dashboard=args.dashboard,
recent_days=args.recent,
publish=args.publish,
)

# Notify Slack on deployment startup
mode = "LIVE" if args.live else "DRY RUN"
mode = "LIVE" if args.live else ("PUBLISH" if args.publish else "DRY RUN")
send_slack_alert(
f":rocket: Engine deployed — mode={mode}, strategy={args.strategy}, symbols={', '.join(symbols)}",
emoji=":rocket:",
Expand Down
Loading