Skip to content

Instantly share code, notes, and snippets.

@kpmiller
Created January 16, 2017 05:02
Show Gist options
  • Save kpmiller/605486ea8e461732056e1ece044e8cbe to your computer and use it in GitHub Desktop.
Save kpmiller/605486ea8e461732056e1ece044e8cbe to your computer and use it in GitHub Desktop.
scanner for iron condor trades matching some criteria
#!/usr/bin/python
import sys,os,glob,re,csv
try:
loglevel = int(os.environ["LOGLEVEL"])
except:
loglevel = 1
def Log(s, level=1):
if level <= loglevel:
print s
def QuotesFromFile(filename = None):
##########################
# Parse the file
##########################
try:
loglevel = int(os.environ["LOGLEVEL"])
except:
loglevel = 1
optcsv = []
f = open(filename, "r")
for line in csv.reader(f):
optcsv.append(line)
f.close()
optd = { "underlying" : "", "symbol" : "", "series" : {} }
key = ""
opts = {}
state=0
for line in optcsv:
#state 0: looking for first line for quote symbol
#state 10: looking for "UNDERLYING"
#state 11: eating underlying header
#state 12: taking underlying
#state 1: looking for a date line with a () days to exp
#state 2: store header, go to 3
#state 3: eating lines until empty line, then end date, reset key and opts
Log(str(line), 900)
Log(str(state), 900)
if state == 0:
m = re.match(".*option quote for (\w*) on ", line[0])
if m != None:
optd["symbol"] = m.group(1)
Log("\nScanning " + optd["symbol"], level = 1)
state = 10
elif state == 10:
if len(line)>0 and line[0].rfind ("UNDERLYING") != -1:
state = 11
elif state == 11:
state = 12
elif state == 12:
optd["underlying"] = float(line[0])
Log("Quote " + str(optd["underlying"]), level = 1)
state = 1
elif state == 1:
if len(line)>0:
m = re.match("\d* ... .. \((\d*)\)", line[0])
if m != None:
key = line[0]
state = 2
elif state == 2:
if len(line) == 0:
state = 1
else:
header = line
state = 3
elif state == 3:
if len(line) == 0:
optd["series"][key] = opts
opts = {}
key = ""
state = 1
else:
if line[2] != "<empty>":
puts = {}
calls = {}
strike = ""
exp = ""
isCalls = True
#Make a dictionary based on the header instead of
#by csv position. Things that come before "Strike" will get
#added to calls, after get added to puts
for i in range(0,len(header)):
if len(header[i]) == 0:
pass
elif header[i] == "Exp":
exp = line[i]
elif header[i] == "Strike":
strike = line[i]
isCalls = False
elif isCalls:
calls[header[i]] = line[i]
else:
puts[header[i]] = line[i]
# ['', '', 'Mark', 'Volume', 'Open.Int', 'Prob.ITM', 'Delta', 'BID', 'BX', 'ASK', 'AX', 'Exp', 'Strike', 'BID', 'BX', 'ASK', 'AX', 'Mark', 'Volume', 'Open.Int', 'Prob.ITM', 'Delta', '', '']
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
try:
st = float(strike)
cm = float(calls["Mark"])
cp = float(calls["Prob.ITM"].replace("%",""))
co = int(calls["Open.Int"].replace(",", ""))
cv = int(calls["Volume"].replace(",", ""))
if (co > cv): cv = co
pm = float(puts["Mark"])
pp = float(puts["Prob.ITM"].replace("%",""))
po = int(puts["Open.Int"].replace(",", ""))
pv = int(puts["Volume"].replace(",", ""))
if (po > pv): pv = po
opts[st] = {
"strike" : st,
"callmark" : cm,
"probcall" : cp,
"callvol" : cv,
"putmark" : pm,
"probput" : pp,
"putvol" : pv
}
except:
Log("conversion error",1)
return optd
def BestMatchBuyPut(series, putstrike, width):
bestmatch = 1000000000.0
beststrike = None
for key in series.keys():
if key < putstrike:
if abs(putstrike - (key + width)) < bestmatch:
bestmatch = abs(putstrike - (key + width))
if bestmatch == 0.0:
return key
beststrike = key
if bestmatch < width * 2.0:
return beststrike
else:
return None
def BestMatchBuyCall(series, callstrike, width):
bestmatch = 1000000000.0
beststrike = None
for key in series.keys():
if key > callstrike:
if abs(callstrike - (key - width)) < bestmatch:
bestmatch = abs(callstrike - (key - width))
if bestmatch == 0.0:
return key
beststrike = key
if bestmatch < width * 2.0:
return beststrike
else:
return None
def BestCallMatch(series, putprob, totalprob=30.0):
strikes = series.keys()
strikes.sort()
bestdiff = 1000000.0
callmatch = None
for key in strikes:
diff = totalprob - (putprob + series[key]["probcall"])
if abs(diff) < bestdiff:
bestdiff = abs(diff)
callmatch = key
return callmatch
def LogMatch(putb=0.0, puts=0.0, callb=0.0, calls=0.0, width=0.0, prob=0.0, credit=0.0, pricediff=0.0, underlying=None, header=False):
if header:
print "%8s %8s %8s %8s %8s %8s %8s %8s %-11s" % ("+put", "-put", "-call", "+call", "$wide", "ITM prob", "credit", "$-risk", "tilt")
else:
ulstr = ""
if underlying != None:
diff = int(9.0 * (calls - underlying) / (calls - puts))
ulstr = "[" + "-"*(8-diff) + "|" + "-"*diff + "]"
print "%8.2f %8.2f %8.2f %8.2f %8.2f %7.2f%% %8.2f %8.2f %11s" % (putb, puts, calls, callb, width, prob, credit, pricediff, ulstr)
def FindIronCondors(series, underlying=None):
strikes = series.keys()
strikes.sort()
widths = [1.0, 2.0, 3.0, 4.0]
LogMatch(header=True)
for width in widths:
for key in strikes:
putprob = series[key]['probput']
if putprob > 5.0 and putprob < 30:
try:
putsell = series[key]
callsell = series[BestCallMatch(series, putprob)]
putbuy = series[BestMatchBuyPut(series, key, width)]
callbuy = series[BestMatchBuyCall(series, callsell["strike"], width)]
totalprob = putprob + callsell["probcall"]
credit = putsell["putmark"] + callsell["callmark"] - (putbuy["putmark"] + callbuy["callmark"])
riskdiff = credit - ((totalprob/100.0) * width)
LogMatch(putbuy["strike"], putsell["strike"], callbuy["strike"], callsell["strike"], width, totalprob, credit, riskdiff, underlying)
except:
pass
def ProcessData(info):
for key in info["series"].keys():
m = re.match("\d* ... .. \((\d*)\)", key)
if int(m.group(1)) > 75: #skip far away options
continue
print "Processing %s (%.02f) - %s" % (info["symbol"], info["underlying"], key)
FindIronCondors(info["series"][key],info["underlying"])
print
if len(sys.argv) == 1:
scandir = os.path.abspath(os.path.expanduser("~/Desktop"))
else:
scandir = os.path.abspath(os.path.expanduser(sys.argv[1]))
files = glob.glob(scandir+"/*.csv")
for f in files:
info = QuotesFromFile(f)
ProcessData(info)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment