-
Notifications
You must be signed in to change notification settings - Fork 0
/
TDExecutor.py
158 lines (140 loc) · 7.22 KB
/
TDExecutor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
from TDAccount import Trade, Position, Account
from TDRestAPI import Rest_Account
import datetime
import matplotlib.pyplot as plt
class TDExecutor():
@staticmethod
def choose_best(td_account, position):
surroundings = TDExecutor.get_surrounding(td_account, position)
min_symbol, min_spread = TDExecutor.find_min_spread(surroundings)
new_position = position.copy(strike=TDExecutor.get_strike_from_symbol(min_symbol), symbol=min_symbol)
new_surroundings = TDExecutor.get_surrounding(td_account, new_position, above_below=1)
if not (new_surroundings[0]['ask'] < new_surroundings[1]['ask'] and new_surroundings[2]['ask'] < new_surroundings[1]['ask']):
return new_position, min_symbol, min_spread
elif not (surroundings[0]['ask'] < surroundings[1]['ask'] and surroundings[2]['ask'] < surroundings[1]['ask']):
return position, position.symbol, (surroundings[1]['ask']-surroundings[1]['bid'])/surroundings[1]['ask']
else:
return 404, 'Not able to find fairly priced symbol'
@staticmethod
def get_strike_from_symbol(symbol):
data = symbol.split('_')[1]
if 'C' in data:
return float(data.split('C')[1])
return float(data.split('P')[1])
@staticmethod
def get_symbol_from_position(td_account, position):
strike = float(position.strike)
date = position.date
ticker = position.ticker
side = position.side
if strike.is_integer():
strike = int(strike)
return td_account.get_option_symbol(ticker, strike, '20', date.split('/')[0], date.split('/')[1], side)
@staticmethod
def find_min_spread(options):
spreads = []
for row in options:
bid = row['bid']*100
ask = row['ask']*100
spread = (ask-bid)/ask
spreads.append((row.name, spread))
return min(spreads, key = lambda i : i[1])
@staticmethod
def get_surrounding(td_account, position, strike_count=10, above_below=4):
date = position.date
side = position.side
date_string = '2020-'+date.split('/')[0]+'-'+date.split('/')[1]
symbol = TDExecutor.get_symbol_from_position(td_account, position)
delta = datetime.timedelta(weeks=2)
date_time = datetime.datetime.strptime(date_string, '%Y-%m-%d')
from_date = datetime.datetime.strftime(date_time-delta, '%Y-%m-%d')
to_date = datetime.datetime.strftime(date_time+delta, '%Y-%m-%d')
chain = td_account.get_options_chain(position.ticker.upper(), strike_count=strike_count, from_date=from_date, to_date=to_date, contract_type=side.replace('S', ''))
for epoch_time in chain['expirationDate']:
time = int(epoch_time)
time = datetime.datetime.fromtimestamp(time/1000).strftime('%m/%d/%Y')
if datetime.datetime.strftime(date_time, '%m/%d/%Y') == time:
expiration_time = epoch_time
break
symbols = [index for (index, item) in chain['expirationDate'].iteritems() if item == expiration_time]
for index, item in enumerate(symbols):
if item == symbol:
if (index-(above_below+1) < 0 or index+(above_below+1) > len(symbols)) or type(symbols) == None:
return TDExecutor.get_surrounding(td_account, position, strike_count+10, above_below=above_below)
else:
return [chain.loc[symbols[special_index]] for special_index in range(index-above_below, index+(above_below+1))]
return TDExecutor.get_surrounding(td_account, position, strike_count+10, above_below=above_below)
@staticmethod
def order_proper(position, td_account, dollar_cost):
available_cash = td_account.get_account_cash()
ticker = position.ticker
position, option, spread = TDExecutor.choose_best(td_account, position)
print(available_cash)
if available_cash > dollar_cost:
quote = td_account.get_quotes(option)
mark = quote['mark']*100
ask = quote['askPrice']*100
cost_per_option = int((mark+ask)/2)
print(cost_per_option, mark, ask, spread)
option_amt = int(dollar_cost/cost_per_option)
if option_amt >= 1:
if spread < 0.15:
try:
response = td_account.place_order_limit(option, option_amt, (cost_per_option/100), 'BUY')
return (position, option, option_amt, ticker, dollar_cost, " Sucess, Option ordered", response)
except Exception as e:
print('Unexpected ordering error', e)
return(position, option, option_amt, ticker, 0, ' TotalFailure INVALID ORDER ' + str(e), None)
else:
return(position, option, option_amt, ticker, 0, ' TotalFailure Spread too wide', None)
else:
return (position, option, 0, ticker, 0, " TotalFailure Option too expensive", None)
else:
return (position, option, 0, ticker, 0, " TotalFailure NOT ENOUGH CASH", None)
@staticmethod
def close_position(position, td_account):
print(position)
symbol = position.symbol
print(symbol)
positions = td_account.get_positions()
print(positions.keys())
if symbol in positions.keys():
print(symbol, positions[symbol])
return (200, 'Success closed position', str(symbol), td_account.sell_to_close(symbol, positions[symbol]))
else:
return (404, 'Unable to find position in holdings', None)
@staticmethod
def log_transaction(message):
with open('logs.txt', 'a+') as fw:
fw.write(str(message) + '\n')
@staticmethod
def execute(account_name, directory, opening):
response, account = Account.load_account(directory, account_name)
if response == 200:
for pos in account.get_positions():
print(pos)
if not pos.get_executed():
if not pos.short_trade:
pos.executed = True
rest_account = Rest_Account('keys.json')
response = None
if opening:
response = TDExecutor.order_proper(pos, rest_account, 1000)
#response = 'Turned off for elections'
pos.symbol = response[1]
else:
response = TDExecutor.close_position(pos, rest_account)
#response = 'Turned off for elections'
print(response)
TDExecutor.log_transaction(response)
account.save_self('accounts')
#td_account = Rest_Account('keys.json')
#print(td_account.get_positions())
# result, money_man = Account.load_account('accounts', 'MoneyMan')
# for pos in money_man.get_positions():
# strike = pos.strike
# position = pos
# print(position)
# surroundings = TDExecutor.get_surrounding(td_account, position, above_below=10)
#print(find_min_spread(td_account, surroundings))
#print(TDExecutor.get_surrounding(td_account, position))