Skip to content

Commit 255ef31

Browse files
Create orders
1 parent 9e9e9ae commit 255ef31

File tree

2 files changed

+114
-10
lines changed

2 files changed

+114
-10
lines changed

automation.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,20 @@ def get_tomorrows_earnings():
222222
]
223223
return tickers
224224

225+
def get_todays_earnings():
226+
today = datetime.now().strftime('%Y-%m-%d')
227+
base_url = "https://www.dolthub.com/api/v1alpha1/post-no-preference/earnings/master"
228+
query = f"SELECT * FROM `earnings_calendar` where date = '{today}' ORDER BY `act_symbol` ASC, `date` ASC LIMIT 1000;"
229+
url = f"{base_url}?q={urllib.parse.quote(query)}"
230+
response = requests.get(url)
231+
data = response.json()
232+
# Return a list of dicts with act_symbol and when
233+
tickers = [
234+
{'act_symbol': row['act_symbol'], 'when': row.get('when')}
235+
for row in data.get('rows', []) if 'act_symbol' in row
236+
]
237+
return tickers
238+
225239
def main():
226240
tickers = get_tomorrows_earnings()
227241
results = []

trade_workflow.py

Lines changed: 100 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import requests
33
from dotenv import load_dotenv
44
from datetime import datetime, timedelta, time
5-
from automation import compute_recommendation, get_tomorrows_earnings
5+
from automation import compute_recommendation, get_tomorrows_earnings, get_todays_earnings
66
from alpaca_integration import place_calendar_spread_order, close_calendar_spread_order, get_portfolio_value
77
import yfinance as yf
88

@@ -150,25 +150,29 @@ def run_trade_workflow():
150150
except Exception as e:
151151
print(f"Error closing trade: {e}")
152152
# 2. Screen and open new trades
153-
ticker_dicts = get_tomorrows_earnings()
153+
# Fetch both today's and tomorrow's earnings
154+
todays_earnings = get_todays_earnings()
155+
tomorrows_earnings = get_tomorrows_earnings()
154156
portfolio_value = get_portfolio_value()
155157
if not portfolio_value:
156158
print("Could not fetch portfolio value. Skipping trade opening.")
157159
return
158-
for ticker_info in ticker_dicts:
160+
# Open BMO trades for tomorrow's earnings (open the day before)
161+
for ticker_info in tomorrows_earnings:
159162
ticker = ticker_info['act_symbol']
160163
when = ticker_info.get('when')
161164
if not when:
162165
print(f"Skipping {ticker}: no 'when' info available.")
163166
continue
164-
# Normalize 'when' to BMO/AMC
165167
when_norm = 'BMO' if 'before' in (when or '').lower() else 'AMC'
168+
if when_norm != 'BMO':
169+
continue # Only process BMO here
166170
try:
167171
rec = compute_recommendation(ticker)
168172
if isinstance(rec, dict) and rec.get('avg_volume') and rec.get('iv30_rv30') and rec.get('ts_slope_0_45'):
169173
earnings_date = datetime.now().date() + timedelta(days=1)
170174
if is_time_to_open(earnings_date, when_norm):
171-
print(f"Preparing trade for {ticker} ({when_norm})...")
175+
print(f"Preparing BMO trade for {ticker} ({when_norm})...")
172176
stock = yf.Ticker(ticker)
173177
expiry_short, expiry_long, strike = select_expiries_and_strike(stock, earnings_date)
174178
if not expiry_short or not expiry_long or not strike:
@@ -185,24 +189,110 @@ def run_trade_workflow():
185189
print(f"Kelly sizing yields 0 contracts for {ticker}. Skipping.")
186190
continue
187191
implied_move = rec.get('expected_move', '')
188-
print(f"Opening trade for {ticker}: {quantity}x {expiry_short}/{expiry_long} @ {strike}, cost/spread: ${spread_cost:.2f}, Kelly allocation: ${max_allocation:.2f}, Implied Move: {implied_move}")
192+
print(f"Opening BMO trade for {ticker}: {quantity}x {expiry_short}/{expiry_long} @ {strike}, cost/spread: ${spread_cost:.2f}, Kelly allocation: ${max_allocation:.2f}, Implied Move: {implied_move}")
193+
order = place_calendar_spread_order(
194+
ticker,
195+
quantity,
196+
expiry_short,
197+
expiry_long,
198+
strike
199+
)
200+
if order is None:
201+
print(f"Order placement failed for {ticker}. Skipping posting to Google Sheets.")
202+
continue
203+
open_price = spread_cost
204+
open_comm = 0
205+
if hasattr(order, 'legs'):
206+
try:
207+
open_price = sum([float(getattr(leg, 'filled_avg_price', 0) or 0) for leg in order.legs])
208+
open_comm = getattr(order, 'commission', 0) or 0
209+
except Exception:
210+
pass
211+
post_trade({
212+
'Ticker': ticker,
213+
'Implied Move': implied_move,
214+
'Structure': 'Calendar Spread',
215+
'Side': 'Long',
216+
'Size': quantity,
217+
'Open Date': datetime.now().strftime('%Y-%m-%d'),
218+
'Open Price': open_price,
219+
'Open Comm.': open_comm,
220+
'Close Date': '',
221+
'Close Price': '',
222+
'Close Comm.': ''
223+
})
224+
else:
225+
print(f"Skipping {ticker}: not in correct time window to open BMO trade.")
226+
except Exception as e:
227+
print(f"Error screening/opening BMO trade for {ticker}: {e}")
228+
# Open AMC trades for today's earnings (open the day of)
229+
for ticker_info in todays_earnings:
230+
ticker = ticker_info['act_symbol']
231+
when = ticker_info.get('when')
232+
if not when:
233+
print(f"Skipping {ticker}: no 'when' info available.")
234+
continue
235+
when_norm = 'BMO' if 'before' in (when or '').lower() else 'AMC'
236+
if when_norm != 'AMC':
237+
continue # Only process AMC here
238+
try:
239+
rec = compute_recommendation(ticker)
240+
if isinstance(rec, dict) and rec.get('avg_volume') and rec.get('iv30_rv30') and rec.get('ts_slope_0_45'):
241+
earnings_date = datetime.now().date()
242+
if is_time_to_open(earnings_date, when_norm):
243+
print(f"Preparing AMC trade for {ticker} ({when_norm})...")
244+
stock = yf.Ticker(ticker)
245+
expiry_short, expiry_long, strike = select_expiries_and_strike(stock, earnings_date)
246+
if not expiry_short or not expiry_long or not strike:
247+
print(f"Could not determine expiries/strike for {ticker}. Skipping.")
248+
continue
249+
spread_cost = calculate_calendar_spread_cost(stock, expiry_short, expiry_long, strike)
250+
if not spread_cost or spread_cost <= 0:
251+
print(f"Invalid spread cost for {ticker}. Skipping.")
252+
continue
253+
kelly_fraction = 0.10
254+
max_allocation = portfolio_value * kelly_fraction
255+
quantity = int(max_allocation // (spread_cost * 100)) # 1 contract = 100 shares
256+
if quantity < 1:
257+
print(f"Kelly sizing yields 0 contracts for {ticker}. Skipping.")
258+
continue
259+
implied_move = rec.get('expected_move', '')
260+
print(f"Opening AMC trade for {ticker}: {quantity}x {expiry_short}/{expiry_long} @ {strike}, cost/spread: ${spread_cost:.2f}, Kelly allocation: ${max_allocation:.2f}, Implied Move: {implied_move}")
261+
order = place_calendar_spread_order(
262+
ticker,
263+
quantity,
264+
expiry_short,
265+
expiry_long,
266+
strike
267+
)
268+
if order is None:
269+
print(f"Order placement failed for {ticker}. Skipping posting to Google Sheets.")
270+
continue
271+
open_price = spread_cost
272+
open_comm = 0
273+
if hasattr(order, 'legs'):
274+
try:
275+
open_price = sum([float(getattr(leg, 'filled_avg_price', 0) or 0) for leg in order.legs])
276+
open_comm = getattr(order, 'commission', 0) or 0
277+
except Exception:
278+
pass
189279
post_trade({
190280
'Ticker': ticker,
191281
'Implied Move': implied_move,
192282
'Structure': 'Calendar Spread',
193283
'Side': 'Long',
194284
'Size': quantity,
195285
'Open Date': datetime.now().strftime('%Y-%m-%d'),
196-
'Open Price': spread_cost, # or actual fill price if available
197-
'Open Comm.': 0,
286+
'Open Price': open_price,
287+
'Open Comm.': open_comm,
198288
'Close Date': '',
199289
'Close Price': '',
200290
'Close Comm.': ''
201291
})
202292
else:
203-
print(f"Skipping {ticker}: not in correct time window to open trade ({when_norm}).")
293+
print(f"Skipping {ticker}: not in correct time window to open AMC trade.")
204294
except Exception as e:
205-
print(f"Error screening/opening trade for {ticker}: {e}")
295+
print(f"Error screening/opening AMC trade for {ticker}: {e}")
206296

207297
if __name__ == "__main__":
208298
run_trade_workflow()

0 commit comments

Comments
 (0)