| Overall Statistics |
|
Total Trades 2 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $56000000.00 Lowest Capacity Asset NVDA RHM8UTD8DT2D Portfolio Turnover 3.45% |
#region imports
from AlgorithmImports import *
#endregion
import statistics
from sklearn.linear_model import LinearRegression
from scipy.stats import norm
import time as tm
import statsmodels.formula.api as smf
import talib
import pandas
import scipy.stats as stats
def green_red_prop_any(self, opened, high, low, close):
green = 0
red = 0
#doji
if opened == close:
return .50
#green
elif close > opened:
green += abs(close - opened)
#wicks
green += abs(high - close) + abs(low - opened)
red += abs(high - close) + abs(low - opened)
#red
elif close < opened:
red += abs(close - opened)
#wicks
green += abs(high - opened) + abs(low - close)
red += abs(high - opened) + abs(low - close)
if (green + red) != 0:
return green / (green + red)
else:
return "NA"
#is there any EMA that has never decreased and is >= midpnt
#long
#see physical notes from 7/3/21
def find_EMA_neverDecr_gteMid(self, s, closes, midpnt):
l, r = 0, 2048
while l <= r:
mid = (l + r) // 2
currEMA, EMAs = calcEMA(self, s, closes, mid)
#ever decreased?
#i.e., is sorted? could maybe be faster
currEMAeverDecr = 0
for i in range(1, len(EMAs)):
if EMAs[i] < EMAs[i-1]:
currEMAeverDecr = 1
break
#gte midpnt
currEMA_gte_midpnt = 0
if currEMA >= midpnt:
currEMA_gte_midpnt = 1
#make longer
if currEMAeverDecr and currEMA_gte_midpnt:
l = mid + 1
elif currEMAeverDecr and (not currEMA_gte_midpnt):
return -1
#return left
elif (not currEMAeverDecr) and currEMA_gte_midpnt:
return mid
#make shorter
elif (not currEMAeverDecr) and (not currEMA_gte_midpnt):
r = mid - 1
return -1
def find_EMA_lte_midpnt(self, s, closes, midpnt, l):
r = 2048
while l <= r:
mid = (l + r) // 2
currEMA, EMAs = calcEMA(self, s, closes, mid)
if currEMA <= midpnt:
return mid
else:
r = mid - 1
return -1
#get the EMA that:
# always increasing
# it will ever decrease if price < EMA
# start of EMA is of shortest time backward
# of longest period
# closest to midpnt of ignition move
def EMA_midpoint(self, s, closes, midpnt, which = "NA"):
#if which == "CD":
# self.Debug("which CD")
low = closes[0]
low_start = 0
high_start = len(closes) - 1
mid_start = 0
closes_start = []
start_final = "NA"
best_period_final = "NA"
while low_start <= high_start:
mid_start = (high_start + low_start) // 2
closes_start = closes[mid_start:]
closes_start[0] = low
#if midpoint EMA exist on this iteration of while loop
found_EMA = 0
#does midpoint EMA exist at
left = find_EMA_neverDecr_gteMid(self, s, closes_start, midpnt)
if left != -1:
right = find_EMA_lte_midpnt(self, s, closes_start, midpnt, left)
if right != -1:
best_period = "NA"
closest_dist = 2000000000
count = 0
all_bounds = set()
#binary search within these bounds
while left <= right:
#the current middle is the period
middle = (left + right) // 2
currEMA, EMAs = calcEMA(self, s, closes_start, middle)
#closer to midpoint, and hasn't decreased yet
if abs(currEMA - midpnt) < closest_dist:
best_period = middle
closest_dist = abs(currEMA - midpnt)
#make longer
if currEMA > midpnt:
left = middle + 1
#make shorter
elif currEMA < midpnt:
right = middle + 1
#return current start from closes, and the best EMA period
else:
found_EMA = 1
start_final = mid_start
best_period_final = best_period
break
###repeat bounds
#not sure why this makes infinite loop otherwise (at least for TWTR 9:33 6/24 RH, start should be 0 period 6)
curr_bounds = str(middle) + " " + str(left) + " " + str(right)
if curr_bounds in all_bounds:
for val in middle, left, right:
currEMA, EMAs = calcEMA(self, s, closes, val)
#closer to midpoint, and hasn't decreased yet
if abs(currEMA - midpnt) <= closest_dist:
best_period = val
closest_dist = abs(currEMA - midpnt)
found_EMA = 1
start_final = mid_start
best_period_final = best_period
break
###
all_bounds.add(curr_bounds)
#if which == "CD":
# self.Debug("binary search iter, mid start and whether found EMA or not")
# self.Debug(mid_start)
# self.Debug(found_EMA)
found_EMA = 1
start_final = mid_start
best_period_final = best_period
#break
###go to next iter
#found, so try longer
if found_EMA == 1:
high_start = mid_start - 1
#not found, try shorter
else:
low_start = mid_start + 1
#did log2(len(closes)) tries, return
return start_final, best_period_final
#is there any EMA that has never increasined and is <= midpnt
def find_EMA_neverIncr_lteMid(self, s, closes, midpnt):
l, r = 0, 2048
while l <= r:
mid = (l + r) // 2
currEMA, EMAs = calcEMA(self, s, closes, mid)
#ever decreased?
#i.e., is sorted? could maybe be faster
currEMAeverIncr = 0
for i in range(1, len(EMAs)):
if EMAs[i] > EMAs[i-1]:
currEMAeverIncr = 1
break
#gte midpnt
currEMA_lte_midpnt = 0
if currEMA <= midpnt:
currEMA_lte_midpnt = 1
#make longer
if currEMAeverIncr and currEMA_lte_midpnt:
l = mid + 1
elif currEMAeverIncr and (not currEMA_lte_midpnt):
return -1
#return left
elif (not currEMAeverIncr) and currEMA_lte_midpnt:
return mid
#make shorter
elif (not currEMAeverIncr) and (not currEMA_lte_midpnt):
r = mid - 1
return -1
def find_EMA_gte_midpnt(self, s, closes, midpnt, l):
r = 2048
while l <= r:
mid = (l + r) // 2
currEMA, EMAs = calcEMA(self, s, closes, mid)
if currEMA >= midpnt:
return mid
else:
r = mid - 1
return -1
def EMA_midpoint_short(self, s, closes, midpnt):
low = closes[0]
for start in range(0, len(closes)):
left = find_EMA_neverIncr_lteMid(self, s, closes, midpnt)
if left != -1:
right = find_EMA_gte_midpnt(self, s, closes, midpnt, left)
if right != -1:
best_period = "NA"
closest_dist = 2000000000
count = 0
all_bounds = set()
#binary search within these bounds
while left <= right:
#the current middle is the period
middle = (left + right) // 2
currEMA, EMAs = calcEMA(self, s, closes, middle)
#closer to midpoint, and hasn't decreased yet
if abs(currEMA - midpnt) < closest_dist:
best_period = middle
closest_dist = abs(currEMA - midpnt)
#make longer
if currEMA < midpnt:
left = middle + 1
#make shorter
elif currEMA > midpnt:
right = middle + 1
#return current start from closes, and the best EMA period
else:
return start, best_period
###repeat bounds
#not sure why this makes infinite loop otherwise (at least for TWTR 9:33 6/24 RH, start should be 0 period 6)
curr_bounds = str(middle) + " " + str(left) + " " + str(right)
if curr_bounds in all_bounds:
for val in middle, left, right:
currEMA, EMAs = calcEMA(self, s, closes, val)
#closer to midpoint, and hasn't decreased yet
if abs(currEMA - midpnt) <= closest_dist:
best_period = val
closest_dist = abs(currEMA - midpnt)
return start, best_period
###
all_bounds.add(curr_bounds)
return start, best_period
#couldn't find EMA -- go to next iter
closes.pop(0)
try:
closes[0] = low
except:
self.EMAerrorShort.add(s)
break
return "NA", "NA"
def grp_second(self, s, df):
#volume sums
tot_vol = 0
green_vol_day = 0
red_vol_day = 0
green_vol_tot = 0
red_vol_tot = 0
green_vol_ignition = 0
red_vol_ignition = 0
green_vol_consol = 0
red_vol_consol = 0
green_vol_cd = 0
red_vol_cd = 0
green_vol_second_cd = 0
red_vol_second_cd = 0
w_green_vol_cd = 0
w_red_vol_cd = 0
w_green_vol_second_cd = 0
w_red_vol_second_cd = 0
w_green_vol_consol = 0
w_red_vol_consol = 0
#start of move, start of consol, start of cd leg
move_start = 0
consol_count = 0
cd_count = 0
second_half_consol_count = 0
#second half of consol
n_rows = df.shape[0]
consol_length = "NA"
#n_consol = 0
row_count = 0
start_of_second_consol = "NA"
#GRP weights
#weight = 1.0 - (proportion_remaining / 2.0)
#proportion_remaining = amount_left / total
#proportion_remaining = n_rows - / (n_rows - start)
#=1 so they don't error out with divide by 0
CD_length = 1
second_half_consol_length = 1
consol_length = 1
for row in df.itertuples():
row_count += 1
low = float(row.low)
high = float(row.high)
curr_range = high - low
#did moves/legs start
if low <= self.moves[s]["RH"]["low"]:
move_start = 1
if move_start == 1 and high >= self.moves[s]["RH"]["high"] and consol_count == 0:
consol_count = 1
consol_length = n_rows - row_count
start_second_CD = row_count + ((n_rows - row_count)/2) #halfway through consolidation
if move_start == 1 and consol_count >= 1 and low <= self.moves[s]["RH"]["consol_low"]:
cd_count = 1
CD_length = n_rows - row_count
if cd_count >= 1 and row_count >= start_second_CD and second_half_consol_count == 0:
second_half_consol_count = 1
second_half_consol_length = n_rows - row_count
#can remove this, just to print
start_of_second_consol = row
###assign grp
opened = float(row.open)
close = float(row.close)
volume = float(row.volume)
low = float(row.low)
green = .50
red = .50
#green proportion
if high - low != 0:
###green proportion as total distance moved
body = abs(opened - close)
wick1 = 0
wick2 = 0
if close > opened: #green
wick1 = high - close
wick2 = opened - low
else: #red
wick1 = high - opened
wick2 = close - low
distance = body + (wick1*2.0) + (wick2*2.0)
try:
if close > opened:
green = (body + wick1 + wick2) / distance
red = 1.0 - green
else:
red = (body + wick1 + wick2) / distance
green = 1.0 - red
except:
error=1
#assign volume
green_vol_day += green * volume
red_vol_day += red * volume
tot_vol += volume
if move_start == 1: #all of move
green_vol_tot += green * volume
red_vol_tot += red * volume
if consol_count == 0: #ignition only
green_vol_ignition += green * volume
red_vol_ignition += red*volume
if consol_count > 0: #all of consolidation
green_vol_consol += green * volume
red_vol_consol += red * volume
consol_count += 1
#add weighted
if consol_length == 0: #error for some reason very infrequently
consol_length = 1
weight = (1.0 - ((consol_length - consol_count) / consol_length) / 2.0)
w_green_vol_consol += green * volume * weight
w_red_vol_consol += red * volume * weight
if cd_count > 0:
green_vol_cd += green * volume
red_vol_cd += red * volume
cd_count += 1
#add weighted
try:
weight = 1.0 - (((CD_length - cd_count) / CD_length) / 2.0)
except:
self.CDerror.add(s.Value)
weight = 0
w_green_vol_cd += green * volume * weight
w_red_vol_cd += red * volume * weight
if second_half_consol_count > 0:
green_vol_second_cd += green * volume
red_vol_second_cd += red * volume
second_half_consol_count += 1
#add weighted
weight = 1.0 - (((second_half_consol_length - second_half_consol_count) / second_half_consol_length) / 2.0)
w_green_vol_second_cd += green * volume * weight
w_red_vol_second_cd += red * volume * weight
return green_vol_day, red_vol_day, green_vol_tot, red_vol_tot, green_vol_consol, red_vol_consol, green_vol_cd, red_vol_cd, green_vol_second_cd, red_vol_second_cd, green_vol_ignition, red_vol_ignition, start_of_second_consol, w_green_vol_cd, w_red_vol_cd, w_green_vol_second_cd, w_red_vol_second_cd, w_green_vol_consol, w_red_vol_consol
#this is mostly from chatGPT
def getDailyVolumes2(self, s):
df = self.History(self.Symbol(s), timedelta(365), Resolution.Minute)
df = df.drop(columns=['askclose', 'askhigh', 'asklow', 'askopen', 'asksize', 'bidclose', 'bidhigh', 'bidlow', 'bidopen', 'bidsize'])
dates = pd.Series(df.index.get_level_values(1)).dt.date.astype(str).to_numpy()
minutes = (df.index.get_level_values(1).hour * 60 + df.index.get_level_values(1).minute).to_numpy()
pm = minutes <= 570 #all the time before 9:30, not including 9:30 i.e., including the 9:30:00 is before premarket
rh = ~pm & (minutes <= 574)
volumes = df['volume'].to_numpy()
rh_vol = np.zeros_like(volumes)
pm_vol = np.zeros_like(volumes)
rh_vol[rh] = volumes[rh]
pm_vol[pm] = volumes[pm]
daily_volumes = pd.DataFrame({'Date': dates, 'RH_vol': rh_vol, 'PM_vol': pm_vol})
daily_volumes = daily_volumes.groupby('Date').agg({'RH_vol': 'sum', 'PM_vol': 'sum'}).reset_index()
last_row = daily_volumes.iloc[-1] # Store the last row before removing it
daily_volumes = daily_volumes.tail(-1) #remove today's row
return daily_volumes, last_row
#predict EOD range based on current volume, range, distance
#split by PM and regular hours
def getDailyVolumes(self, s):
df = self.History(self.Symbol(s), timedelta(365), Resolution.Minute)
df = df.drop(columns=['askclose', 'askhigh', 'asklow', 'askopen', 'asksize', 'bidclose','bidhigh', 'bidlow', 'bidopen', 'bidsize'])
#self.Debug(s)
#self.Debug(df.shape)
#self.Debug(self.Time)
vals = {}
for row in df.itertuples():
date, clock = str(row.Index[1]).split()
date = str(date)
vol = row.volume
if date not in vals:
vals[date] = {}
vals[date]["PM_vol"] = 0
vals[date]["RH_vol"] = 0
pm = self.isPM(clock)
#update premarket values
if pm == 1:
vals[date]["PM_vol"] += vol
#update regular hours
elif pm == 0 and self.timeInMin(clock) <= 573 : #change to first 3 minutes
vals[date]["RH_vol"] += vol
#print out to file
rows_list = []
for date in sorted(vals): #these are correctly sorted by date
row = {}
row["Date"] = date
row["RH_vol"] = vals[date]["RH_vol"]
row["PM_vol"] = vals[date]["PM_vol"]
rows_list.append(row)
tmp = pd.DataFrame(rows_list)
return tmp
#get mean volumes
def getMeanVols(self, vols):
past20 = vols.tail(21)
past20 = past20[:-1]
past20['Sum_vol'] = past20['RH_vol'] + past20['PM_vol']
mean_RH = past20["RH_vol"].mean()
mean_PM = past20["PM_vol"].mean()
mean_sum = past20["Sum_vol"].mean()
return [float(mean_RH), float(mean_PM), float(mean_sum)]
#predict interval for today's EOD range based on past 14 days, using daily candles from past year
def modelEODRange(self, s):
start = tm.time()
df = self.History(self.Symbol(s), timedelta(365), Resolution.Daily)
#self.Debug(df.tail())
#put OHLC, range, ATR, in lists
vals = {}
vals["date"] = []
vals["open"] = []
vals["high"] = []
vals["low"] = []
vals["close"] = []
vals["range"] = []
vals["atr"] = []
count = 0
date_minus1 = ""
for row in df.itertuples():
if count > 0:
opened = row.open
high = row.high
low = row.low
close = row.close
vals["date"].append(date_minus1)
vals["open"].append(opened)
vals["high"].append(high)
vals["low"].append(low)
vals["close"].append(close)
vals["range"].append(high - low)
if count >1:
vals["atr"].append(max(high-low, abs(high - vals["close"][-2]), abs(low - vals["close"][-2])))
else:
vals["atr"].append("NA")
date_minus1 = str(row.Index[1])
count += 1
#less than 50 daily days
if len(vals["date"]) < 50:
return "NA", "NA", "NA"
#minutely prediction
vols, today_vols = getDailyVolumes2(self, s)
#self.Debug(vols)
#return "NA", "NA", "NA"
mean_vols = getMeanVols(self, vols)
#calc avgs for output to df:
#abs_gap, sqrt(abs_gap)
#atr, average range, inside day
#yday, 2 day, 5 day, 14 day
rows_list = []
today_row = {}
for i in range(16, len(vals["open"])):
row = {}
date = vals["date"][i].replace(" 00:00:00", "")
row["Date"] = date
row["Range_today"] = vals["range"][i]
#gap
row["abs_Gap"] = abs(vals["open"][i] - vals["close"][i-1])
row["sqrt_abs_Gap"] = math.sqrt(abs(vals["open"][i] - vals["close"][i-1]))
#average range and ATR
for j in [1, 2, 5, 14]:
row["range_" + str(j) + "day"] = statistics.mean(vals["range"][i-j:i])
row["atr_" + str(j) + "day"] = statistics.mean(vals["atr"][i-j:i])
#inside or not
if vals["open"][i] > vals["high"][i-1] or vals["open"][i] < vals["low"][i-1]:
row["outside_open"] = 1
else:
row["outside_open"] = 0
if vals["high"][i] > vals["high"][i-1] or vals["low"][i] < vals["low"][i-1]:
row["outside_ever"] = 1
else:
row["outside_ever"] = 0
#if yesterday closed outside of 2 days ago range
if vals["close"][i-1] > vals["high"][i-2] or vals["close"][i-1] < vals["low"][i-2]:
row["outside_close"] = 1
else:
row["outside_close"] = 0
###today's values that can be filled in before open
for j in [1, 2, 5, 14]:
today_row["range_" + str(j) + "day"] = statistics.mean(vals["range"][-j:])
today_row["atr_" + str(j) + "day"] = statistics.mean(vals["atr"][-j:])
if vals["close"][-1] > vals["high"][-2] or vals["close"][-1] < vals["low"][-2]:
today_row["outside_close"] = 1
else:
today_row["outside_close"] = 0
#df conversion
rows_list.append(row)
tmp = pd.DataFrame(rows_list)
merged = pd.merge(vols, tmp, on="Date")
'''
#output lbl
# Print the headers
self.Debug('\t'.join(merged.columns))
# Iterate through each row
count = 0
total_rows = merged.shape[0] # Get total number of rows
for index, row in merged.iterrows():
count += 1
if count <= 20 or count == total_rows: # Print the first 19 rows and the final row
self.Debug('\t'.join(row.astype(str)))
'''
X = merged[["abs_Gap", "sqrt_abs_Gap", "range_1day", "range_2day", "range_5day", "range_14day", "outside_open", "outside_ever", "RH_vol", "PM_vol"]]
y = merged["Range_today"]
#currPred = runModel.iloc[-1:].drop(columns=["Range_today"])
#rmLast = runModel.iloc[:-1]
#quantile regression
#models = []
#for tau in taus:
# model = smf.quantreg('Range_today ~ abs_Gap + sqrt_abs_Gap + range_1day + range_2day + range_5day + range_14day + outside_open + outside_ever + RH_vol + PM_vol', merged).fit(q=tau)
# models.append(model)
#linear regression
model = LinearRegression().fit(X, y)
predictions = model.predict(X)
#all you need is SD of residuals
sd = get_residuals_sd(self, y, predictions)
#model rsq
#self.Debug(model.summary())
rsq = model.score(X, y)
#self.Debug(rsq)
#self.Debug(sd)
today_row["RH_vol"] = today_vols['RH_vol']
today_row["PM_vol"] = today_vols['PM_vol']
#return the model, its rsq, todays vals that dont depend on today, and ydays ohlc
return model, today_row, [opened, high, low, close], sd, mean_vols, rsq
def predictEODRange(self, model, tr, today_ohl, yday_ohlc):
#tr stands for today_row
yday_o, yday_h, yday_l, yday_c = yday_ohlc
today_o, today_h, today_l = today_ohl
###make the rest of features that need values from today
#gap
tr["abs_Gap"] = abs(today_o - yday_c)
tr["sqrt_abs_Gap"] = math.sqrt(abs(today_o - yday_c))
#inside or not
if today_o > yday_h or today_o < yday_l:
tr["outside_open"] = 1
else:
tr["outside_open"] = 0
if today_h > yday_h or today_l < yday_l:
tr["outside_ever"] = 1
else:
tr["outside_ever"] = 0
#volume
#tr["RH_vol"] = threeMinVol
#tr["PM_vol"] = pmvol
#self.Debug(threeMinVol)
#self.Debug(pmvol)
###get prediction
#topred_vals = [[tr["abs_Gap"], tr["sqrt_abs_Gap"], tr["range_1day"], tr["atr_1day"], tr["range_2day"], tr["atr_2day"], tr["range_5day"], tr["atr_5day"], tr["range_14day"], tr["atr_14day"], tr["outside_open"], tr["outside_ever"], tr["outside_close"]]]
#topred = pd.DataFrame(topred_vals, columns = ["abs_Gap", "sqrt_abs_Gap", "range_1day", "atr_1day", "range_2day", "atr_2day", "range_5day", "atr_5day", "range_14day", "atr_14day", "outside_open", "outside_ever", "outside_close"])
topred_vals = [[tr["abs_Gap"], tr["sqrt_abs_Gap"], tr["range_1day"], tr["range_2day"], tr["range_5day"], tr["range_14day"], tr["outside_open"], tr["outside_ever"], tr["RH_vol"], tr["PM_vol"]]]
topred = pd.DataFrame(topred_vals, columns = ["abs_Gap", "sqrt_abs_Gap", "range_1day", "range_2day", "range_5day", "range_14day", "outside_open", "outside_ever", "RH_vol", "PM_vol"])
pred = model.predict(topred)
return pred
def get_residuals_sd(self, y_test, test_predictions):
#get standard deviation of y_test
sum_errs = np.sum((y_test - test_predictions)**2)
stdev = np.sqrt(1 / (len(y_test) - 2) * sum_errs)
return stdev
#https://towardsdatascience.com/prediction-intervals-in-linear-regression-2ea14d419981
def get_prediction_interval(self, prediction, stdev, bound):
#get interval from standard deviation
ppf_lookup = 1 - bound
z_score = norm.ppf(ppf_lookup)
interval = z_score * stdev
#generate prediction interval lower bound
return (prediction-interval)
#https://towardsdatascience.com/prediction-intervals-in-linear-regression-2ea14d419981
def get_prediction_interval(self, prediction, y_test, test_predictions, bound):
#get standard deviation of y_test
sum_errs = np.sum((y_test - test_predictions)**2)
stdev = np.sqrt(1 / (len(y_test) - 2) * sum_errs)
#get interval from standard deviation
ppf_lookup = 1 - bound
z_score = norm.ppf(ppf_lookup)
interval = z_score * stdev
#generate prediction interval lower bound
return (prediction-interval)
#EMA for consolidation, midpoint of high to low
def calc_shortEMA(self, s, all_prices):
#consolidation prices
consol_short_prices = []
hit_high = 0
for p in all_prices:
if p == self.moves[s]["RH"]["high"]:
hit_high = 1
if hit_high == 1:
consol_short_prices.append(p)
#midpoint, and low of consolidation to high of consolidation post-low
midpoint_short = ((max(consol_short_prices) + min(consol_short_prices)) / 2.0)
consol_hl = []
for p in consol_short_prices:
consol_hl.append(p)
if p == min(consol_short_prices):
break
#only need to recalc if it's going to be different
start_short = "N"
EMA_period_short = "A"
if s not in self.prevStartPeriod["bc"] or consol_hl != self.prevPrices["bc"][s]:
start_short, EMA_period_short = EMA_midpoint_short(self, s, consol_hl, midpoint_short)
self.prevStartPeriod["bc"][s] = str(start_short) + " " + str(EMA_period_short)
self.prevPrices["bc"][s] = consol_hl
else:
start_short, EMA_period_short = self.prevStartPeriod["bc"][s].split()
if not (start_short != "N" and EMA_period_short != "A"):
self.Debug("is NA")
return
adj_closes_EMA_short = consol_short_prices[int(start_short):]
adj_closes_EMA_short[0] = consol_short_prices[0]
currEMA, EMAs = calcEMA(self, s, adj_closes_EMA_short, EMA_period_short)
return currEMA, EMAs, start_short
def calc_ignEMA(self, s):
midpoint = ((self.moves[s]["RH"]["high"] + self.moves[s]["RH"]["low"]) / 2.0)
#ign_time = self.moves[s]["ign_time"]*2
#ign is low to high
ign_prices = []
for p in self.moves[s]["RH"]["candle_halves"]["Price"]:
ign_prices.append(p)
if p == self.moves[s]["RH"]["high"]:
break
#only need to recalc if it's going to be different
start = "NA"
EMA_period = "NA"
if s not in self.prevStartPeriod["ab"] or (s in self.prevPrices["ab"] and ign_prices != self.prevPrices["ab"][s]):
start, EMA_period = EMA_midpoint(self, s, ign_prices, midpoint)
self.prevStartPeriod["ab"][s] = str(start) + " " + str(EMA_period)
self.prevPrices["ab"][s] = ign_prices
else:
start, EMA_period = self.prevStartPeriod["ab"][s].split(" ")
start = int(start)
EMA_period = int(EMA_period)
if not (start != "NA" and EMA_period != "NA"): return 0, [], "NA"
adj_closes_EMA = self.moves[s]["RH"]["candle_halves"]["Price"][int(start):]
adj_closes_EMA[0] = self.moves[s]["RH"]["candle_halves"]["Price"][0]
currEMA, EMAs = calcEMA(self, s, adj_closes_EMA, int(EMA_period))
return currEMA, EMAs, start
def calc_ignEMAwPM(self, s):
midpoint_wPM = ((self.moves[s]["PM"]["high"] + self.moves[s]["PM"]["low"]) / 2.0)
#ign is low to high
ign_prices_wPM = []
for p in self.moves[s]["PM"]["candle_halves"]["Price"]:
ign_prices_wPM.append(p)
if p == self.moves[s]["PM"]["high"]:
break
#only need to recalc if it's going to be different
start_wPM = "NA"
EMA_period_wPM = "NA"
if s not in self.prevStartPeriod["ab_wPM"] or ign_prices_wPM != self.prevPrices["ab_wPM"][s]:
start_wPM, EMA_period_wPM = EMA_midpoint(self, s, ign_prices_wPM, midpoint_wPM)
self.prevStartPeriod["ab_wPM"][s] = str(start_wPM) + " " + str(EMA_period_wPM)
self.prevPrices["ab_wPM"][s] = ign_prices_wPM
else:
start_wPM, EMA_period_wPM = self.prevStartPeriod["ab_wPM"][s].split()
adj_closes_EMA_wPM = []
try:
adj_closes_EMA_wPM = self.moves[s]["PM"]["candle_halves"]["Price"][int(start_wPM):]
except:
self.EMAwPMerror.add(s)
return 0, [], "NA"
adj_closes_EMA_wPM[0] = self.moves[s]["PM"]["candle_halves"]["Price"][0]
currEMA_wPM, EMAs_wPM = calcEMA(self, s, adj_closes_EMA_wPM, int(EMA_period_wPM))
return currEMA_wPM, EMAs_wPM, start_wPM
def calc_CD_EMA_postBreak(self, s, all_prices, ema_price):
#midpoint = ((self.movesCurr[s]["high"] + self.movesCurr[s]["low"]) / 2.0)
#start of move to postbreak
prices = []
for p in all_prices:
prices.append(p)
if p > self.movesCurr[s]["high"]:
break
prices.append(self.movesCurr[s]["high"])
#only need to recalc if it's going to be different
start = "NA"
EMA_period = "NA"
if s not in self.prevStartPeriod["postbreak"] or prices != self.prevPrices["postbreak"][s]:
start, EMA_period = EMA_midpoint(self, s, prices, ema_price, "CD")
self.prevStartPeriod["postbreak"][s] = str(start) + " " + str(EMA_period)
self.prevPrices["postbreak"][s] = prices
else:
start, EMA_period = self.prevStartPeriod["postbreak"][s].split()
start = int(start)
EMA_period = int(EMA_period)
adj_closes_EMA = []
try:
adj_closes_EMA = all_prices[start:]
except:
#self.EMAwPMerror.add(s)
return 0, [], "NA"
adj_closes_EMA[0] = all_prices[0]
currEMA, EMAs = calcEMA(self, s, adj_closes_EMA, EMA_period)
return currEMA, EMAs, start
#calculate VWMA
def VWMA(self, s, volumes, prices, fromwhen, vp = 0, v = 0):
vwmas = []
#vp = 0
#v = 0
if fromwhen == "beginning":
for i in range(0, len(volumes)):
vp += volumes[i]*prices[i]
v += volumes[i]
if v != 0:
vwmas.append(vp/v)
else:
vwmas.append(prices[i])
self.VWMA_vol_zero.add(s.Value)
elif fromwhen == "consol":
high_ind = ign_high_ind(self, prices, self.moves[s]["RH"]["high"])
try:
for i in range(high_ind, len(volumes)): #archived: high_ind+1 to start after the high
vp += volumes[i]*prices[i]
v += volumes[i]
if v != 0:
vwmas.append(vp/v)
#divide by 0 error, else just make it price. should be super infrequent
else:
vwmas.append(prices[i])
self.VWMA_vol_zero.add(s.Value)
except:
self.Debug("ign_high_ind error")
self.Debug(s)
self.Debug(self.Time)
self.Debug(self.moves[s]["RH"]["high"])
elif fromwhen == "cd":
consol_low_ind = get_consol_low_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"], self.moves[s]["RH"]["consol_low"])
for i in range(consol_low_ind, len(self.moves[s]["RH"]["candle_halves"]["Volume"])): #archived: high_ind+1 to start after the high
vp += self.moves[s]["RH"]["candle_halves"]["Volume"][i]*self.moves[s]["RH"]["candle_halves"]["Price"][i]
v += self.moves[s]["RH"]["candle_halves"]["Volume"][i]
if v != 0:
vwmas.append(vp/v)
#divide by 0 error, else just make it price. should be super infrequent
else:
vwmas.append(self.moves[s]["RH"]["candle_halves"]["Price"][i])
self.VWMA_vol_zero.add(s.Value)
elif fromwhen == "bc_curr" or fromwhen == "cd_curr":
ind = "NA"
if fromwhen == "bc_curr":
ind = ign_high_ind(self, self.movesCurr[s]["candle_halves"]["Price"], self.movesCurr[s]["high"])
elif fromwhen == "cd_curr":
ind = get_consol_low_ind(self, self.movesCurr[s]["candle_halves"]["Price"], self.movesCurr[s]["high"], self.movesCurr[s]["consol_low"])
for i in range(ind, len(self.movesCurr[s]["candle_halves"]["Volume"])):
vp += self.movesCurr[s]["candle_halves"]["Volume"][i]*self.movesCurr[s]["candle_halves"]["Price"][i]
v += self.movesCurr[s]["candle_halves"]["Volume"][i]
if v != 0:
vwmas.append(vp/v)
#divide by 0 error, else just make it price. should be super infrequent
else:
vwmas.append(self.moves[s]["RH"]["candle_halves"]["Price"][i])
self.VWMA_vol_zero.add(s.Value)
elif fromwhen == "single_minute":
for i in range(0, len(volumes)):
vp += volumes[i]*prices[i]
v += volumes[i]
if v != 0:
vwmas.append(vp/v)
#divide by 0 error, else just make it price. should be super infrequent
else:
vwmas.append(prices[i])
self.VWMA_vol_zero.add(s.Value)
#self.Debug(vwmas)
elif fromwhen == "breakout":
ind = "NA"
for i in range(len(prices)-1, -1, -1):
if prices[i] < self.movesCurr[s]["high"]:
ind = i
break
for i in range(ind, len(volumes)):
vp += volumes[i]*prices[i]
v += volumes[i]
if v != 0:
vwmas.append(vp/v)
#divide by 0 error, else just make it price. should be super infrequent
else:
vwmas.append(prices[i])
self.VWMA_vol_zero.add(s.Value)
elif fromwhen == "d":
ind = "NA"
for i in range(len(prices)-1, -1, -1):
if prices[i] == max(prices):
ind = i
break
for i in range(ind, len(volumes)):
vp += volumes[i]*prices[i]
v += volumes[i]
if v != 0:
vwmas.append(vp/v)
#divide by 0 error, else just make it price. should be super infrequent
else:
vwmas.append(prices[i])
self.VWMA_vol_zero.add(s.Value)
else:
self.Debug("ERROR: input from when for VWMA()")
return vwmas, vp, v
def get_currhigh_ind(s, prices):
ind = "NA"
max_price = max(prices)
for i in range(0, len(prices)):
if prices[i] < max_price:
return i
def get_breakout_ind(self, s, prices):
ind = "NA"
for i in range(len(prices)-1, -1, -1):
if prices[i] < self.movesCurr[s]["high"]:
return i
def calc_z(self, x, mu, sd):
return (x - mu) / sd
def x_from_z(self, z, mu, sd):
return (z*sd) + mu
#index of consol low
def get_consol_low_ind(self, prices, high, curr_consol_low):
consol_low_ind = "NA"
hit_high = 0
for i in range(0, len(prices)):
if prices[i] == high:
hit_high = 1
if hit_high == 1 and prices[i] == curr_consol_low:
consol_low_ind = i
break
return consol_low_ind
#index of ignition high
def ign_high_ind(self, prices, high):
for i in range(0, len(prices)):
if prices[i] == high:
return i
#calculate long EMA
#start to generalize this
def calc_longEMA_general(self, s, EMAtype, all_prices):
point = "NA"
prices = []
#(bigger) AB leg, when in position, so check every second
if EMAtype == "ab_inpos":
#midpoint of low and current high
point = ((self.movesCurr[s]["new_high"] + self.movesCurr[s]["low"]) / 2.0)
#ign is low to high
for p in all_prices:
prices.append(p)
if p == self.movesCurr[s]["new_high"]:
break
#only need to recalc if it's going to be different
start = "NA"
EMA_period = "NA"
if s not in self.prevStartPeriod[EMAtype] or (s in self.prevPrices[EMAtype] and prices != self.prevPrices[EMAtype][s]):
start, EMA_period = EMA_midpoint(self, s, prices, point)
if start != "NA" and EMA_period != "NA":
self.prevStartPeriod[EMAtype][s] = str(start) + " " + str(EMA_period)
self.prevPrices[EMAtype][s] = prices
else:
start, EMA_period = self.prevStartPeriod[EMAtype][s].split(" ")
start = int(start)
EMA_period = int(EMA_period)
if not (start != "NA" and EMA_period != "NA"): return 0, [], "NA"
adj_closes_EMA = all_prices[int(start):]
adj_closes_EMA[0] = all_prices[0]
currEMA, EMAs = calcEMA(self, s, adj_closes_EMA, int(EMA_period))
return currEMA, EMAs, start
#Returns a value rounded down to a specific number of decimal places.
#https://kodify.net/python/math/round-decimals/
def round_decimals_down(self, number:float, decimals:int=2):
if not isinstance(decimals, int):
raise TypeError("decimal places must be an integer")
elif decimals < 0:
raise ValueError("decimal places has to be 0 or more")
elif decimals == 0:
return math.floor(number)
factor = 10 ** decimals
return math.floor(number * factor) / factor
#volume profile from start of day
#so basically, VWAP instead of VWMA
#market open is fine
def volumeProfile_general(self, s, stop, highs, lows, vols):
#high and low of every candle
hls_set = set()
for i in range(0, len(highs)):
hls_set.add(highs[i])
hls_set.add(lows[i])
#sorted list
hls = sorted(hls_set)
#for each stretch between highs and lows of any candle
#get all candles that overlap
#add the candle's volume / proportion of the candle in the current range
vp = {}
for i in range(0, len(hls) - 1):
range_low = hls[i]
range_high = hls[i+1]
for j in range(0, len(highs)):
#if ranges overlap, add to vp
overlap = 0
if lows[j] >= range_low and lows[j] < range_high and highs[j] >= range_high:
overlap = range_high - lows[j]
elif highs[j] >= range_low and highs[j] < range_high and lows[j] < range_low:
overlap = highs[j] - range_low
elif lows[j] >= range_low and highs[j] < range_high:
overlap = highs[j] - lows[j]
elif lows[j] < range_low and highs[j] >= range_high:
overlap = range_high - range_low
#if any overlap, set to proportion
if highs[j] - lows[j] != 0:
overlap = overlap / (highs[j] - lows[j])
else: #this should never happen because set()
overlap = 0
#formatting
lowprint = str(range_low)
if len(lowprint.split(".")[1]) == 1:
lowprint = lowprint + "0"
if lowprint not in vp:
vp[lowprint] = overlap*vols[j]
else:
vp[lowprint] += overlap*vols[j]
#print and return
#self.Debug("Range_inclusive_to_next_LessThan\tVolume")
#totVol = 0
#for lowprint in sorted(vp):
# self.Debug(lowprint + "\t" + str(vp[lowprint]))
# totVol += vp[lowprint]
#self.Debug(str(self.moves[s]["high"]) + "\t0")
#these should be equal
#self.Debug("candlesum: " + str(sum(self.moves[s]["candles"]["v"])))
#self.Debug("vp sum: " + str(totVol))
quantile = vpQuantile(self, s, vp, stop)
return quantile
#running simple moving average
def calcSMA(self, vals):
count = 1.0
running_sum = 0.0
SMAs = []
for v in vals:
running_sum += v
SMAs.append(running_sum / count)
count += 1
return SMAs
def vpQuantile(self, s, vp, quantile):
#wantedVol = sum(self.moves[s]["candles"]["v"]) * quantile
wantedVol = sum(self.pastNoPre[s]["v"]) * quantile
#find the prices with volume that wantedVol is between
prevVol = 0
currVol = 0
currPrice = "NA"
nextPrice = "NA"
broken = 0
excessVol = 0
for price in sorted(vp):
if broken == 0:
prevVol = currVol
currPrice = price
currVol += vp[price]
if currVol >= wantedVol:
excessVol = currVol - wantedVol
broken = 1
else:
nextPrice = price
break
return float(currPrice) + ( (excessVol / (currVol - prevVol)) * (float(nextPrice) - float(currPrice)) )
#thinkorswim also includes 4 lookback periods
#the earliest are first
def calcEMA(self, s, closes, period):
currEMA = 0
EMAs = []
alpha = 2.0 / (float(period)+1.0)
for i in range(0, len(closes)):
if i == 0:
currEMA = closes[0]
else:
try:
currEMA = alpha*closes[i] + (1.0-alpha)*currEMA
except:
self.calc_EMA_error.add(s)
return 0, 0
EMAs.append(currEMA)
return currEMA, EMAs
#calc daily EMAs for intervals in wanted_EMAs
def calcDailyEMAs(self, s, close, wanted_EMAs):
df = self.History(self.Symbol(s), timedelta(365), Resolution.Daily)
closes = []
for row in df.itertuples():
closes.append(row.close)
closes.append(close) #current price
all_EMAs = []
for i in wanted_EMAs:
ema, EMAs = calcEMA(self, s, closes[-i:], i)
all_EMAs.append(EMAs)
return all_EMAs
#not sure this is right but perhaps it is off by a constant (related to the 2.0 scaling)
def weighted_RVOL(self, s, vols, avg_day_vol):
tot_rvol = 0
tot_weight = 0
len_vols = len(vols)
try:
for i in range(0, len_vols):
weight = (1.0 - ((len_vols - i) / len_vols) / 2.0)
tot_rvol += (vols[i] / avg_day_vol) * weight
tot_weight += weight
except:
return "weighted RVOL error"
#add current minute
if self.Time.second != 1:
tot_rvol += self.pastSixty[s]["v"] / (avg_day_vol * (self.Time.second /60.0))
tot_weight += self.Time.second / 60.0
w_avg_CD_vol = tot_rvol / tot_weight
return w_avg_CD_vol
def dailyMA_percAwayChange(self, close, EMAs):
#price % away from EMA
dist = close - EMAs[-1]
perc_away = (dist/close)*100.0
#total change
perc_change = ((EMAs[-1] - EMAs[0]) / EMAs[0])*100.0
return perc_away, perc_change
#sharpe
def calcSharpe(self, rr, pr): #pr is prob_success
E = rr*pr + -1*(1-pr) #expectation
sd = math.sqrt( pr*((rr-E)**2) + (1-pr)*((-1-E)**2)) #standard devation
sharpe = 0
if pr == 1:
sharpe = 999
elif pr == 0:
sharpe = -999
if sd != 0:
sharpe = E/sd
return E, sharpe
#gt_yday_high and lt_yday_low
def gt_lt_yday(self, yday, close):
gt_yday_high = "NA"
lt_yday_low = "NA"
if close > yday.high:
gt_yday_high = 1
else:
gt_yday_high = 0
if close < yday.low:
lt_yday_low = 1
else:
lt_yday_low = 0
return gt_yday_high, lt_yday_low
def anchoredVWAP(self, s, period, close, volume): #today's volume
df = self.History(self.Symbol(s), timedelta(365), Resolution.Daily)
closes = []
volumes = []
for row in df.itertuples():
closes.append(row.close)
volumes.append(row.volume)
#today's price and volume
closes.append(close)
volumes.append(volume)
#self.Debug(closes[-3])
#self.Debug(closes[-2])
#self.Debug(closes[-1])
#self.Debug(volumes[-3])
#self.Debug(volumes[-2])
#self.Debug(volumes[-1])
#you want this to be a little different than VWMA actually
#it shouldn't have any values for the first N periods b/c it needs them all to have the first one
#like, else the first VWMA value is always just the price of the first entry
vwmas = []
for i in range(len(volumes) - period, len(volumes)): #only get those in period
vp = 0
v = 0
for j in range(i - period+1, i+1):
try:
vp += volumes[j]*closes[j]
v += volumes[j]
except:
self.AVWAPerror.add(s)
if v != 0:
vwmas.append(vp/v)
else:
try:
vwmas.append(closes[i])
except:
self.AVWAPerror.add(s)
return vwmas
#this is grp_second copy+pasted but changed for pastNoPre
def grp_minute(self, s, df):
#volume sums
tot_vol = 0
green_vol_day = 0
red_vol_day = 0
green_vol_tot = 0
red_vol_tot = 0
green_vol_ignition = 0
red_vol_ignition = 0
green_vol_consol = 0
red_vol_consol = 0
green_vol_cd = 0
red_vol_cd = 0
green_vol_second_cd = 0
red_vol_second_cd = 0
w_green_vol_cd = 0
w_red_vol_cd = 0
w_green_vol_second_cd = 0
w_red_vol_second_cd = 0
w_green_vol_consol = 0
w_red_vol_consol = 0
#start of move, start of consol, start of cd leg
move_start = 0
consol_count = 0
cd_count = 0
second_half_consol_count = 0
#second half of consol
n_rows = len(df["v"]) #different from second
consol_length = "NA"
#n_consol = 0
row_count = 0
start_of_second_consol = "NA"
#GRP weights
#weight = 1.0 - (proportion_remaining / 2.0)
#proportion_remaining = amount_left / total
#proportion_remaining = n_rows - / (n_rows - start)
#=1 so they don't error out with divide by 0
CD_length = 1
second_half_consol_length = 1
consol_length = 1
for minute_num in range(0, len(df["v"])):
row_count += 1
low = float(df["l"][minute_num])
high = float(df["h"][minute_num])
curr_range = high - low
#did moves/legs start
if low <= self.moves[s]["RH"]["low"]:
move_start = 1
if move_start == 1 and high >= self.moves[s]["RH"]["high"] and consol_count == 0:
consol_count = 1
consol_length = n_rows - row_count
start_second_CD = row_count + ((n_rows - row_count)/2) #halfway through consolidation
if move_start == 1 and consol_count >= 1 and low <= self.moves[s]["RH"]["consol_low"]:
cd_count = 1
CD_length = n_rows - row_count
if cd_count >= 1 and row_count >= start_second_CD and second_half_consol_count == 0:
second_half_consol_count = 1
second_half_consol_length = n_rows - row_count
###assign grp
opened = float(df["o"][minute_num])
close = float(df["c"][minute_num])
volume = float(df["v"][minute_num])
low = float(df["l"][minute_num])
green = .50
red = .50
#green proportion
if high - low != 0:
###green proportion as total distance moved
body = abs(opened - close)
wick1 = 0
wick2 = 0
if close > opened: #green
wick1 = high - close
wick2 = opened - low
else: #red
wick1 = high - opened
wick2 = close - low
distance = body + (wick1*2.0) + (wick2*2.0)
try:
if close > opened:
green = (body + wick1 + wick2) / distance
red = 1.0 - green
else:
red = (body + wick1 + wick2) / distance
green = 1.0 - red
except:
error=1
#assign volume
green_vol_day += green * volume
red_vol_day += red * volume
tot_vol += volume
if move_start == 1: #all of move
green_vol_tot += green * volume
red_vol_tot += red * volume
if consol_count == 0: #ignition only
green_vol_ignition += green * volume
red_vol_ignition += red*volume
if consol_count > 0: #all of consolidation
green_vol_consol += green * volume
red_vol_consol += red * volume
consol_count += 1
#add weighted
if consol_length == 0: #error for some reason very infrequently
consol_length = 1
weight = (1.0 - ((consol_length - consol_count) / consol_length) / 2.0)
w_green_vol_consol += green * volume * weight
w_red_vol_consol += red * volume * weight
if cd_count > 0:
green_vol_cd += green * volume
red_vol_cd += red * volume
cd_count += 1
#add weighted
try:
weight = 1.0 - (((CD_length - cd_count) / CD_length) / 2.0)
except:
self.CDerror.add(s.Value)
weight = 0
w_green_vol_cd += green * volume * weight
w_red_vol_cd += red * volume * weight
if second_half_consol_count > 0:
green_vol_second_cd += green * volume
red_vol_second_cd += red * volume
second_half_consol_count += 1
#add weighted
weight = 0
try:
weight = 1.0 - (((second_half_consol_length - second_half_consol_count) / second_half_consol_length) / 2.0)
except:
tmp=1
w_green_vol_second_cd += green * volume * weight
w_red_vol_second_cd += red * volume * weight
return green_vol_day, red_vol_day, green_vol_tot, red_vol_tot, green_vol_consol, red_vol_consol, green_vol_cd, red_vol_cd, green_vol_second_cd, red_vol_second_cd, green_vol_ignition, red_vol_ignition, start_of_second_consol, w_green_vol_cd, w_red_vol_cd, w_green_vol_second_cd, w_red_vol_second_cd, w_green_vol_consol, w_red_vol_consol
#is level between current price and target/stop
def priceIsBetween(self, level, target_stop, close, sr):
if sr == "r":
if close < level <= target_stop:
return 1
else:
return 0
elif sr == "s":
if target_stop < level <= close:
return 1
else:
return 0
return "NA"
def solve_percentile(x, mean, sd):
percentile = stats.norm.cdf(x, loc=mean, scale=sd)
return percentile#region imports
from AlgorithmImports import *
import calculate
import statistics
import copy
import time as tm
import print_or_update
#endregion
def enter(self, s, close): #have enter() return the step that it go to if self.enter_time is not ""
vwap = self.sd[s].vwap.Current.Value
########
# misc #
########
if not self.Portfolio[s].Invested and s in self.positions:
del self.positions[s]
if not (not self.Portfolio[s].Invested): return #doesn't already have position
if s in self.positions: return "already_in_position"
##############
# above VWAP #
##############
if not (close > vwap): return "below_VWAP" #above VWAP
#doesn't need to break VWAP, just be above it
######################
# resolution too low #
######################
if not (len(self.moves[s]["RH"]["candles"]["o"]) >= 4): return "lt_4_minutes"
#################
# enough volume #
#################
if not (statistics.median(self.moves[s]["RH"]["candles"]["v"]) > 9000): return "lt_9000_volume"
#####################
# above/broke level #
#####################
df = self.History(self.Symbol(s), timedelta(7), Resolution.Daily)
self.ydays[s] = df.iloc[-1] #save this for later
if s not in self.pmHigh:
self.notInPMHigh.add(s)
return "no_PM_high_for_some_reason"
levels = set()
try:
levels.add(self.pmHigh[s])
#only add levels if they haven't been broken by something more recent
if df.iloc[-1].high > self.pmHigh[s]: #yday high will always be >= yday close
levels.add(df.iloc[-1].high)
except:
self.Debug("single positional indexer is out-of-bounds")
self.Debug(s)
self.Debug(self.Time)
return "PM_high_failed"
#hod level has broken other level
toremove = set()
if s in self.hod_level:
for l in levels:
if self.hod_level[s] > l:
toremove.add(l)
for l in toremove:
levels.remove(l)
levels.add(self.hod_level[s])
#get level that was max broken
max_broken = 0
for level in levels:
if self.moves[s]["RH"]["high"] > level and level > max_broken:
max_broken = level
#if not (close >= max_broken and max_broken != 0): return "not_above_max_broken_level" #must be above broken level
if not (close >= max_broken or max_broken == 0): return "not_above_max_broken_level, levels: " + str(levels)
###############
# >daily EMAs #
###############
wanted_EMAs = [2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 50, 200]
all_EMAs = calculate.calcDailyEMAs(self, s, close, wanted_EMAs)
EMAs = {}
EMAs["2"] = all_EMAs[0]
EMAs["3"] = all_EMAs[1]
EMAs["4"] = all_EMAs[2]
EMAs["5"] = all_EMAs[3]
EMAs["6"] = all_EMAs[4]
EMAs["7"] = all_EMAs[5]
EMAs["8"] = all_EMAs[6]
EMAs["9"] = all_EMAs[7]
EMAs["10"] = all_EMAs[8]
EMAs["20"] = all_EMAs[9]
EMAs["50"] = all_EMAs[10]
EMAs["200"] = all_EMAs[11]
#########################
# get EOD prediction #
#########################
if (tm.time() - self.startTime) > 500: #lt 500 seconds
return "time out EOD pred"
#sd = 0
#mean_vols = [-1, -1, -1]
#rsq = 0
if s not in self.EODmodels and s.Value not in self.EODmodelErr:
try:
model, today_row, yday_ohlc, sd, mean_vols, rsq = calculate.modelEODRange(self, s)
self.EODmodels[s] = [model, today_row, yday_ohlc, sd, mean_vols, rsq]
except:
self.EODmodelErr.add(s.Value)
return "EOD_model_error_1 " + str(s)
if not (s.Value not in self.EODmodelErr and self.EODmodels[s][0] != "NA"): return "EOD_model_error_2" #check for self.EODmodelErr also
pred_series = calculate.predictEODRange(self, self.EODmodels[s][0], self.EODmodels[s][1], [self.open[s], self.hod[s], self.lod[s]], self.EODmodels[s][2]) #self.pmVol[s], self.rhvol3[s]
pred = pred_series[0]
#only enter if today range < predicted EOD range
if not ( (self.hod[s] - self.lod[s]) < pred ): return "gt_daily_prediction"
#add to <1 minute staging
self.staging[s] = {}
self.staging[s]["max_broken"] = max_broken
self.staging[s]["pred"] = pred
self.staging[s]["levels"] = levels
self.staging[s]["EMAs"] = EMAs
self.staging[s]["sd"] = self.EODmodels[s][3]
self.staging[s]["mean_vols"] = self.EODmodels[s][4]
self.staging[s]["rsq"] = self.EODmodels[s][5]
enter_staged(self, s, close, max_broken, pred, levels, EMAs, self.EODmodels[s][4], self.EODmodels[s][3], self.EODmodels[s][5])
return "enter_staged"
def enter_staged(self, s, close, max_broken, pred, levels, dailyEMAs, mean_vols, sd, rsq):
vwap = self.sd[s].vwap.Current.Value
#VWAP increasing
if not (vwap >= self.prevVWAP[s]): return "VWAP_not_increasing"
#######################
# enough volume in CD #
#######################
#average volume CD or total volume CD wrt to ignition
#the "candle_halves" are thirds (20 seconds) not halves (30 seconds) btw
cd_ind = calculate.get_consol_low_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"], self.moves[s]["RH"]["consol_low"])
ign_ind = calculate.ign_high_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"])
#max length of CD is half of consolidation
cd_length = 0
consol_length = 0
cd_half = 0
if cd_ind != "NA" and ign_ind != "NA":
cd_length = len(self.moves[s]["RH"]["candle_halves"]["Price"]) - int(cd_ind)
consol_length = len(self.moves[s]["RH"]["candle_halves"]["Price"]) - int(ign_ind)
if cd_length > ((consol_length)/2.0):
cd_ind = int(ign_ind + (consol_length/2))
cd_half = 1
cd_length = len(self.moves[s]["RH"]["candle_halves"]["Price"]) - int(cd_ind)
#AB volume
ign_vol = 0
for i in range(0, len(self.moves[s]["RH"]["candle_halves"]["Price"])):
ign_vol += self.moves[s]["RH"]["candle_halves"]["Volume"][i]
if i == ign_ind:
break
#BC volume
bc_vol = 0
for i in range(ign_ind, len(self.moves[s]["RH"]["candle_halves"]["Price"])):
bc_vol += self.moves[s]["RH"]["candle_halves"]["Volume"][i]
if i == cd_ind:
break
#CD volume
cd_vol = 0
try:
for i in range(cd_ind, len(self.moves[s]["RH"]["candle_halves"]["Price"])):
cd_vol += self.moves[s]["RH"]["candle_halves"]["Volume"][i]
except:
return "CD_volume_error, cd_ind: " + str(cd_ind) + " len candle_halves[]: " + str(len(self.moves[s]["RH"]["candle_halves"]["Price"]))
if self.Time.second != 1:
cd_vol += self.pastSixty[s]["v"]
if not (cd_vol > (ign_vol * .35)): return ("CD_volume_lt_1/3_ign_volume") #ENPH 8/2/22 9:40
if not (cd_vol > (bc_vol * .35)): return ("CD_volume_lt_1/3_BC_volume, cd_vol:" + str(cd_vol) + " bc_vol: " + str(bc_vol))
################################
# Holding above level and VWAP #
################################
cd_closes = self.moves[s]["RH"]["candle_halves"]["Price"][int(cd_ind):]
bd_closes = self.moves[s]["RH"]["candle_halves"]["Price"][int(ign_ind):]
if self.Time.second != 1:
cd_closes.append(close)
bd_closes.append(close)
cd_SMAs = calculate.calcSMA(self, cd_closes)
bd_SMAs = calculate.calcSMA(self, bd_closes)
#if not ((cd_SMAs[-1] > vwap and cd_SMAs[-1] > max_broken) or (bc_SMAs[-1] > vwap and bc_SMAs[-1] > max_broken)): return
if not (bd_SMAs[-1] > vwap and cd_SMAs[-1] > vwap): return "not holding above level and VWAP"
###########################
# EMA midpnt of ignition #
###########################
currEMA, EMAs, start = calculate.calc_ignEMA(self, s)
#staging
if self.Time.second != 1:
start, EMA_period = self.prevStartPeriod["ab"][s].split(" ")
start = int(start)
EMA_period = int(EMA_period)
if not (start != "NA" and EMA_period != "NA"): return 0, [], "NA"
adj_closes_EMA = self.moves[s]["RH"]["candle_halves"]["Price"][int(start):]
adj_closes_EMA[0] = self.moves[s]["RH"]["candle_halves"]["Price"][0]
currEMA, EMAs = calculate.calcEMA(self, s, adj_closes_EMA, int(EMA_period))
#TypeError : 'NoneType' object is not iterable
if currEMA == 0 or EMAs == [] or start == "NA": return "failed to calc EMA for some reason"
#start = int(start) - 1
#bollinger
diffs = []
closes = self.moves[s]["RH"]["candle_halves"]["Price"][int(start):]
for i in range(0, len(EMAs)):
diffs.append(abs(closes[i] - EMAs[i]))
avgdevs = calculate.calcSMA(self, diffs)
#if avg devs ever < 60% of avgdevs[ign_ind] and avg devs ever < 25% of move
ign_dev = 0
try:
ign_dev = avgdevs[ign_ind]
except:
return "avgdevs[ign_ind] error"
lt_perc_avgdev = 0
lt_perc_move = 0
try:
for i in range(ign_ind + 1, len(avgdevs)):
if avgdevs[i] < .60*ign_dev: #ENPH 8/2/22 ~9:40 from .60
lt_perc_avgdev = 1
if avgdevs[i] < .25*(self.moves[s]["RH"]["high"] - self.moves[s]["RH"]["low"]):
lt_perc_move = 1
if lt_perc_move == 1 and lt_perc_avgdev == 1:
break
except:
#self.Debug("ign ind + 1 oob len(avgdevs)")
self.avgdevoob.add(s)
if not (lt_perc_move == 1 and lt_perc_avgdev == 1): return "has not consolidated enough from bollinger, lt_perc_move: " + str(lt_perc_move) + " lt_perc_avgdev: " + str(lt_perc_avgdev)
##################################################
# EMA midpoint of low of entire day including PM #
##################################################
currEMA_wPM, EMAs_wPM, start_wPM = calculate.calc_ignEMAwPM(self, s)
#TypeError : 'NoneType' object is not iterable
if currEMA_wPM == 0 or EMAs_wPM == [] or start_wPM == "NA": return "failed to calc EMA for some reason"
#EMA within 25% of total move of current close
if not( abs(close - currEMA_wPM) < .25*(self.moves[s]["PM"]["high"] - self.moves[s]["PM"]["low"])): return "EMA not close enough to close"
###########
# high RR #
###########
#use minutes including PM
mins = self.timeInMin(str(self.Time).split()[1])
df_mins = self.History(self.Symbol(s), timedelta(1), Resolution.Minute)
df_mins = df_mins.tail(mins - 240)
highs = []
lows = []
vols = []
closes = []
#use minutes for premarket
for row in df_mins.itertuples():
highs.append(float(row.high))
lows.append(float(row.low))
vols.append(float(row.volume))
closes.append(float(row.close))
# stop calculation, use volume profile
#vp_stop = calculate.volumeProfile_general(self, s, .30, highs, lows, vols)
#stop = vp_stop
currRange = self.moves[s]["RH"]["high"] - self.lod[s]
#bd_range = max(bd_closes) - min(bd_closes)
half = int(len(self.moves[s]["RH"]["candle_halves"]["Price"])/2)
second_half = self.moves[s]["RH"]["candle_halves"]["Price"][half:]
second_half_range = max(second_half) - min(second_half)
#avg_range = (currRange + bd_range) / 2.0
avg_range = (currRange + second_half_range) / 2.0
consol_stop = min(cd_closes) - (avg_range*.15)
vwap_stop = vwap - (avg_range*.15)
stop = min(consol_stop, vwap_stop)
#if stop > consol_low:
# stop = (stop + consol_low) / 2.0
#stop must be at least 5 cents, else resolution is too low
if (abs(stop - close) < .04): return "stop resolution too low"
#stop = close - .05
#only enter if R:R is big e.g., >2.5:1
#self.Debug(preds)
rr = ((pred + self.lod[s]) - close) / (close - stop)
#rr_25 = ((preds[1] + self.lod[s]) - close) / (close - stop)
if not (rr > 1.0 and rr < 10.0): return "RR not high enough or too high, rr: " + str(rr) #change this from .10 pred and 1.0
VWMAs, vp, v = calculate.VWMA(self, s, vols, closes, "beginning")
##############
# GRP volume #
##############
grp_timeframe = "minute"
df = self.pastNoPre[s]
if grp_timeframe == "second":
df = self.History(self.Symbol(s), timedelta(1), Resolution.Second)
df = df.tail((mins - 570)*60 + 1 + int(self.Time.second))
#green_vol_day, red_vol_day, green_vol_tot, red_vol_tot, green_vol_consol, red_vol_consol, green_vol_cd, red_vol_cd, green_vol_half_cd, red_vol_half_cd, green_vol_ign, red_vol_ign, half_cd_start, w_green_vol_cd, w_red_vol_cd, w_green_vol_second_cd, w_red_vol_second_cd, w_green_vol_consol, w_red_vol_consol = calculate.grp_second(self, s, df)
green_vol_day, red_vol_day, green_vol_tot, red_vol_tot, green_vol_consol, red_vol_consol, green_vol_cd, red_vol_cd, green_vol_half_cd, red_vol_half_cd, green_vol_ign, red_vol_ign, half_cd_start, w_green_vol_cd, w_red_vol_cd, w_green_vol_second_cd, w_red_vol_second_cd, w_green_vol_consol, w_red_vol_consol = calculate.grp_minute(self, s, df)
#error out
if red_vol_tot + green_vol_tot == 0: return "error in init red and green volume tot"
if red_vol_consol + green_vol_consol == 0: return "error in init red and green volume consol"
if red_vol_cd + green_vol_cd == 0: return "error in init red and green volume cd"
if red_vol_ign + green_vol_ign == 0: return "error in init red and green volume ign"
#calc grp
grp_day = green_vol_day / (green_vol_day + red_vol_day)
grp_tot = green_vol_tot / (red_vol_tot + green_vol_tot)
grp_consol = (green_vol_consol / (red_vol_consol + green_vol_consol))
grp_cd = green_vol_cd / (red_vol_cd + green_vol_cd)
grp_ign = green_vol_ign / (red_vol_ign + green_vol_ign)
w_grp_cd = 0
try:
w_grp_cd = w_green_vol_cd / (w_red_vol_cd + w_green_vol_cd)
except:
tmp=1
if cd_half == 1:
try:
grp_cd = green_vol_half_cd / (red_vol_half_cd + green_vol_half_cd)
w_grp_cd = w_green_vol_second_cd / (w_red_vol_second_cd + w_green_vol_second_cd)
except:
grp_cd = 0
w_grp_cd = 0
#to print out later
move_day_vol = (green_vol_tot + red_vol_tot) / (red_vol_day + green_vol_day)
#filter
#if self.printout == 0:
if not (grp_day > .25): return "green vol day: " + str(grp_day) #EQT 5/3/22
if not (grp_tot > .25): return "green vol tot: " + str(grp_tot) #EQT 5/3/22
if not (grp_consol > .25): return "grp consol: " + str(grp_consol)
if not (grp_cd > .40): return "green vol cd: " + str(grp_cd) #HAL 8/16/22 10:07
###################################
# relative volume CD and ignition #
###################################
#number of 30 second periods
len_CD = len(self.moves[s]["RH"]["candle_halves"]["Price"]) - cd_ind
len_ign = ign_ind
len_day = (len(self.pastNoPre[s]["o"]))*3.0 + (self.Time.second/20.0)
len_tot = len(self.moves[s]["RH"]["candle_halves"]["Volume"]) #this is in thirds (20 seconds), not halves (30 seconds)
#CD volume
ign_vols = self.moves[s]["RH"]["candle_halves"]["Volume"][:ign_ind]
cd_vols = self.moves[s]["RH"]["candle_halves"]["Volume"][cd_ind:]
#avg_ign_vol = statistics.mean(ign_vols)
#avg_cd_vol = statistics.mean(cd_vols)
#avg_day_vol = ((red_vol_day + green_vol_day) / len_day) #per 30 seconds, so compare directly to values in "candle_halves"
#w_avg_move_move = calculate.weighted_RVOL(self, s, self.moves[s]["RH"]["candle_halves"]["Volume"], statistics.mean(self.moves[s]["RH"]["candle_halves"]["Volume"]))
#w_avg_move_day = calculate.weighted_RVOL(self, s, self.moves[s]["RH"]["candle_halves"]["Volume"], avg_day_vol)
#average vol, per minute
avg_CD_vol = ((cd_vol) / len_CD)*2.0
avg_ign_vol = ((ign_vol) / len_ign)*2.0
avg_day_vol = ((red_vol_day + green_vol_day) / len_day)*2.0
###calculate RVOLs
#weighted
w_avg_CD_ign = calculate.weighted_RVOL(self, s, cd_vols, avg_ign_vol)
w_avg_CD_day = calculate.weighted_RVOL(self, s, cd_vols, avg_day_vol)
w_avg_ign_day = calculate.weighted_RVOL(self, s, ign_vols, avg_day_vol)
#unweighted
avg_CD_ign = avg_CD_vol / avg_ign_vol
avg_CD_day = avg_CD_vol / avg_day_vol
avg_ign_day = avg_ign_vol / avg_day_vol
#proportion of ign and CD of total move
prop_len_CD = len_CD / len_tot
prop_len_ign = float(len_ign) / float(len_tot)
#prop total move vs day
prop_len_move_day = len_tot / len_day
#rvol_ign_x_cd = w_avg_ign_day * w_avg_CD_day
#rvol_ign_plus_cd = w_avg_ign_day + w_avg_CD_day
#if self.printout == 0:
if not (w_avg_CD_day > .40): return "avg CD vol < 40 perc of avg day volume: " + str(w_avg_CD_day) #0.48739860564657245 HAL 10:07 8/16/22, you need to save OXY 5/3/22 though
# if not (w_avg_CD_ign > .40): return "avg CD vol < 40 perc of avg ign vol: " + str(w_avg_CD_ign) #EQT 5/3/22
#############
# EMA calcs #
#############
perc_aways = []
perc_changes = []
for i in ["2", "3", "4", "5", "6", "7", "8", "9", "10", "20", "50", "200"]:
perc_away, perc_change = calculate.dailyMA_percAwayChange(self, close, dailyEMAs[i])
perc_aways.append(perc_away)
perc_changes.append(perc_change)
#######################
# Anchored VWAP calcs #
#######################
avwaps = {}
avwaps["avwap2"] = calculate.anchoredVWAP(self, s, 2, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap3"] = calculate.anchoredVWAP(self, s, 3, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap4"] = calculate.anchoredVWAP(self, s, 4, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap5"] = calculate.anchoredVWAP(self, s, 5, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap6"] = calculate.anchoredVWAP(self, s, 6, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap7"] = calculate.anchoredVWAP(self, s, 7, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap8"] = calculate.anchoredVWAP(self, s, 8, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap9"] = calculate.anchoredVWAP(self, s, 9, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap10"] = calculate.anchoredVWAP(self, s, 10, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap20"] = calculate.anchoredVWAP(self, s, 20, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap50"] = calculate.anchoredVWAP(self, s, 50, close, sum(self.pastNoPre[s]["v"]))
avwaps["avwap200"] = calculate.anchoredVWAP(self, s, 200, close, sum(self.pastNoPre[s]["v"]))
perc_aways_avwap = {}
perc_changes_avwap = {}
for avwap in avwaps:
perc_away, perc_change = calculate.dailyMA_percAwayChange(self, close, avwaps[avwap])
perc_aways_avwap[avwap] = perc_away
perc_changes_avwap[avwap] = perc_change
##########################
# levels, today and yday #
##########################
#PM high, today open, yday close, yday low, yday high, PM low
pm_high = self.pmHigh[s]
pm_low = self.pmLow[s]
yday_high = self.ydays[s].high
yday_low = self.ydays[s].low
yday_close = self.ydays[s].close
today_open = self.open[s]
#stop and tp
target = self.lod[s] + pred
#stop = stop
#close = close
resistance = {}
support = {}
resistance["pm_high"] = calculate.priceIsBetween(self, pm_high, target, close, "r")
resistance["pm_low"] = calculate.priceIsBetween(self, pm_low, target, close, "r")
resistance["yday_high"] = calculate.priceIsBetween(self, yday_high, target, close, "r")
resistance["yday_low"] = calculate.priceIsBetween(self, yday_low, target, close, "r")
resistance["yday_close"] = calculate.priceIsBetween(self, yday_close, target, close, "r")
resistance["today_open"] = calculate.priceIsBetween(self, today_open, target, close, "r")
resistance["hod"] = 0
if self.hod[s] > self.moves[s]["RH"]["high"]:
resistance["hod"] = 1
support["pm_high"] = calculate.priceIsBetween(self, pm_high, stop, close, "s")
support["pm_low"] = calculate.priceIsBetween(self, pm_low, stop, close, "s")
support["yday_high"] = calculate.priceIsBetween(self, yday_high, stop, close, "s")
support["yday_low"] = calculate.priceIsBetween(self, yday_low, stop, close, "s")
support["yday_close"] = calculate.priceIsBetween(self, yday_close, stop, close, "s")
support["today_open"] = calculate.priceIsBetween(self, today_open, stop, close, "s")
support["lod"] = calculate.priceIsBetween(self, self.lod[s], stop, close, "s")
#######################
# prob success vs R:R #
#######################
#calc spread
spread = ((self.Securities[s].AskPrice - self.Securities[s].BidPrice) + self.pastNoPre[s]["mean_spread"][-1] + self.pastNoPre[s]["mean_spread"][-2] + self.pastNoPre[s]["mean_spread"][-3]) / 4.0 #avg of past 3 minutes
shares, shares_no_spread = self.positionSize(stop, close, self.dollar_risk, spread)
ratio = shares / shares_no_spread
#make GRP variables
GRP_CD_scaled = (grp_cd - .50) * prop_len_CD * prop_len_move_day
wRVOL_CD_scaled = w_avg_CD_day * prop_len_CD * prop_len_move_day
CD_RVOL_x_GRP = GRP_CD_scaled * wRVOL_CD_scaled
df = pd.DataFrame()
#today's range percentile of EOD pred
percentile = calculate.solve_percentile((self.hod[s] - self.lod[s]), pred, self.EODmodels[s][3])
#X = df[["RR.10", "GRP_day", "GRP_CD_scaled", "CD_wRVOL_x_GRP", "wRVOL_CD_scaled", "PercChangeEMA3", "PercChangeEMA5", "PercChangeEMA8", "PercFromPriceEMA3", "PercFromPriceEMA5", "PercFromPriceEMA8", "PercChangeAVWAP3", "PercChangeAVWAP5", "PercChangeAVWAP8", "PercFromPriceAVWAP3", "PercFromPriceAVWAP5", "PercFromPriceAVWAP8", "pm_high_bw_target", "yday_high_bw_target", "yday_close_bw_target", "today_open_bw_target", "yday_low_bw_target", "pm_low_bw_target", "pm_high_bw_stop", "yday_high_bw_stop", "yday_close_bw_stop", "today_open_bw_stop"]]
#y = df[["ReachedTarget"]]
df['RR'] = [float(rr)]
df['GRP_day'] = [float(grp_day)]
df['GRP_CD_scaled'] = [float(GRP_CD_scaled)]
df['CD_wRVOL_x_GRP'] = [float(CD_RVOL_x_GRP)]
df['wRVOL_CD_scaled'] = [float(wRVOL_CD_scaled)]
df['PercChangeEMA3'] = [float(perc_changes[1])]
df['PercChangeEMA5'] = [float(perc_changes[3])]
df['PercChangeEMA8'] = [float(perc_changes[6])]
df['PercFromPriceEMA3'] = [float(perc_aways[1])]
df['PercFromPriceEMA5'] = [float(perc_aways[3])]
df['PercFromPriceEMA8'] = [float(perc_aways[6])]
df['PercChangeAVWAP3'] = [float(perc_changes_avwap["avwap3"])]
df['PercChangeAVWAP5'] = [float(perc_changes_avwap["avwap5"])]
df['PercChangeAVWAP8'] = [float(perc_changes_avwap["avwap8"])]
df['PercFromPriceAVWAP3'] = [float(perc_aways_avwap["avwap3"])]
df['PercFromPriceAVWAP5'] = [float(perc_aways_avwap["avwap5"])]
df['PercFromPriceAVWAP8'] = [float(perc_aways_avwap["avwap8"])]
df['pm_high_bw_target'] = resistance["pm_high"]
df['yday_high_bw_target'] = resistance["yday_high"]
df['yday_close_bw_target'] = resistance["yday_close"]
df['today_open_bw_target'] = resistance["today_open"]
df['yday_low_bw_target'] = resistance["yday_low"]
df['pm_low_bw_target'] = resistance["pm_low"]
df['pm_high_bw_stop'] = support["pm_high"]
df['yday_high_bw_stop'] = support["yday_high"]
df['yday_close_bw_stop'] = support["yday_close"]
df['today_open_bw_stop'] = support["today_open"]
'''
calibrated_proba = self.calibrated.predict_proba(df)
E, sharpe = calculate.calcSharpe(self, rr_10*ratio, calibrated_proba[:,1])
if self.sharpeThresh != "":
if not (sharpe > float(self.sharpeThresh)): return "Sharpe not high enough"
'''
#########
# enter #
#########
#order
shares_close = -1.0*shares
#put in orders
self.MarketOrder(s, shares) #enter
self.StopMarketOrder(s, shares_close, stop) #stop
#self.StopMarketOrder(s, shares_close, float(self.lod[s] + preds[0])) #take profit
#logs
self.Debug("LONG: " + s.Value + " " + str(self.Time))
if self.printMinimal != 1:
self.Debug("Stop: " + str(stop))
self.Debug("Current price: " + str(close))
self.Debug("Take profit: " + str(self.lod[s] + pred))
#self.Debug("VWAP: " + str(vwap))
#self.Debug("VWMA, market open: " + str(VWMAs[-1]))
#self.Debug("Levels: ")
#self.Debug(levels)
#self.Debug("max broken: " + str(max_broken))
#self.Debug("green:red : " + str(self.green_red_prop_ignition(s)))
#self.Debug("pred 25% EOD range: " + str(pred))
self.Debug("RR pred: " + str(rr*ratio))
#self.Debug("RR 25%: " + str(rr_25*ratio))
#self.Debug("Prob success: " + str(prob))
#self.Debug("Prob success needed: " + str(prob_needed))
#self.Debug("Prob/prob needed: " + str(prob/prob_needed))
#self.Debug("median 1-min volume:" + str(statistics.median(self.moves[s]["RH"]["candles"]["v"])))
#self.Debug("CD length: " + str(cd_length))
#self.Debug("Consol length: " + str(consol_length))
#self.Debug("grp_day: " + str(grp_day))
#self.Debug("grp_tot: " + str(grp_tot))
#self.Debug("grp_ign: " + str(grp_ign))
#self.Debug("grp_consol: " + str(grp_consol))
#self.Debug("grp_cd: " + str(grp_cd))
#self.Debug("w_grp_cd: " + str(w_grp_cd))
#self.Debug("ign_vol: " + str(ign_vol))
#self.Debug("bc_vol: " + str(bc_vol))
#self.Debug("cd_vol: " + str(cd_vol))
#self.Debug("weighted avg CD/ign: " + str(w_avg_CD_ign))
#self.Debug("weighted avg CD/day: " + str(w_avg_CD_day))
#self.Debug("weighted avg ign/day: " + str(w_avg_ign_day))
self.Debug("prob success:" + str(calibrated_proba[:,1]))
self.Debug("expectation: " + str(E))
self.Debug("sharpe: " + str(sharpe))
#self.Debug("weighted avg move/move: " + str(w_avg_move_move))
#self.Debug("weighted avg move/day: " + str(w_avg_move_day))
#self.Debug("rvol_ign_x_cd: " + str(rvol_ign_x_cd))
#self.Debug("rvol_ign_plus_cd: " + str(rvol_ign_plus_cd))
#self.Debug("avg CD vol ratio: " + str(avg_CD_vol / avg_day_vol))
#self.Debug("avg CD vol: " + str(avg_CD_vol))
#self.Debug("avg ign vol: " + str(avg_ign_vol))
#self.Debug("avg day vol: " + str(avg_day_vol))
#end print
#move for current position
self.movesCurr[s] = copy.deepcopy(self.moves[s]["RH"]) #don't use dict() or .copy() https://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy
self.movesCurr[s]["ign_ind"] = calculate.ign_high_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"])
self.movesCurr[s]["consol_low_ind"] = calculate.get_consol_low_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"], self.moves[s]["RH"]["consol_low"])
self.movesCurr[s]["broke_high"] = 0
#self.movesCurr[s]["green_vol"] = green_vol_tot
#self.movesCurr[s]["red_vol"] = red_vol_tot
self.movesCurr[s]["new_high"] = self.movesCurr[s]["high"]
#add to current positions
self.positions[s] = {}
self.positions[s]["time"] = self.Time
self.positions[s]["pred"] = pred
self.positions[s]["stop"] = stop
self.positions[s]["shares_close"] = shares_close
self.positions[s]["take_profit"] = float(self.lod[s] + pred)
if s not in self.everPosition:
self.everPosition[s] = {}
self.everPosition[s]["N_entries"] = 1
else:
self.everPosition[s]["N_entries"] += 1
self.everPosition[s]["time"] = self.Time
self.everPosition[s]["pmvol"] = self.PM_atSelection[s]["pmvol"]
self.everPosition[s]["pm_nonzero"] = self.PM_atSelection[s]["pm_nonzero"]
self.everPosition[s]["R:R"] = rr
self.everPosition[s]["prop_of_pred_range"] = (self.hod[s] - self.lod[s]) / pred
#if in position, not stagin anymore
del self.staging[s]
#above/below yday
gt_yday_high, lt_yday_low = calculate.gt_lt_yday(self, self.ydays[s], close)
#what percentage of expected move is today's range
percentage_EOD_pred = (self.hod[s] - self.lod[s]) / pred
#to print out later
self.movesCurr[s]["time"] = self.Time
self.movesCurr[s]["high_move"] = close #Max_moved_before_stop
self.movesCurr[s]["curr_range"] = self.hod[s] - self.lod[s]
self.movesCurr[s]["entry"] = close
self.movesCurr[s]["stopLOD"] = self.lod[s] - .01
self.movesCurr[s]["stopCustom"] = stop
self.movesCurr[s]["entryLOD"] = self.lod[s]
self.movesCurr[s]["entryHOD"] = self.hod[s]
self.movesCurr[s]["pred"] = pred
self.movesCurr[s]["stoppedCustom"] = 0
self.movesCurr[s]["stoppedLOD"] = 0
self.movesCurr[s]["stoppedCustomTime"] = "NA"
self.movesCurr[s]["stoppedLODTime"] = "NA"
self.movesCurr[s]["stopCustom_highMove"] = "NA"
self.movesCurr[s]["stopLOD_highMove"] = "NA"
self.movesCurr[s]["grp_day"] = grp_day
self.movesCurr[s]["grp_tot"] = grp_tot
self.movesCurr[s]["grp_ign"] = grp_ign
self.movesCurr[s]["grp_consol"] = grp_consol
self.movesCurr[s]["grp_cd"] = grp_cd
self.movesCurr[s]["w_grp_cd"] = w_grp_cd
self.movesCurr[s]["rvol_CD_day"] = avg_CD_day
self.movesCurr[s]["rvol_CD_ign"] = avg_CD_ign
self.movesCurr[s]["rvol_ign_day"] = avg_ign_day
self.movesCurr[s]["w_rvol_CD_day"] = w_avg_CD_day
self.movesCurr[s]["w_rvol_CD_ign"] = w_avg_CD_ign
self.movesCurr[s]["w_rvol_ign_day"] = w_avg_ign_day
self.movesCurr[s]["prop_len_CD"] = prop_len_CD
self.movesCurr[s]["prop_len_ign"] = prop_len_ign
self.movesCurr[s]["perc_aways"] = perc_aways
self.movesCurr[s]["perc_changes"] = perc_changes
self.movesCurr[s]["time_in_sec"] = self.timeInSec(str(self.Time).split()[1])
self.movesCurr[s]["move_day_vol"] = move_day_vol
self.movesCurr[s]["prop_len_move_day"] = prop_len_move_day
self.movesCurr[s]["gt_yday_high"] = gt_yday_high
self.movesCurr[s]["lt_yday_low"] = lt_yday_low
self.movesCurr[s]["EMAs"] = dailyEMAs
self.movesCurr[s]["perc_aways_avwap"] = perc_aways_avwap
self.movesCurr[s]["perc_changes_avwap"] = perc_changes_avwap
self.movesCurr[s]["resistance"] = resistance
self.movesCurr[s]["support"] = support
self.movesCurr[s]["spread"] = spread
self.movesCurr[s]["mean_vols"] = mean_vols
self.movesCurr[s]["percentage_EOD_pred"] = percentage_EOD_pred
self.movesCurr[s]["pm_high"] = pm_high
self.movesCurr[s]["pm_low"] = pm_low
self.movesCurr[s]["yday_high"] = yday_high
self.movesCurr[s]["yday_low"] = yday_low
self.movesCurr[s]["yday_close"] = yday_close
self.movesCurr[s]["today_open"] = today_open
self.movesCurr[s]["bd_SMA"] = bd_SMAs[-1]
self.movesCurr[s]["cd_SMA"] = cd_SMAs[-1]
self.movesCurr[s]["residual_sd"] = sd
self.movesCurr[s]["model_rsq"] = rsq
self.movesCurr[s]["percentile"] = percentile
#move_len = len(self.moves[s]["RH"]["candle_halves"]["Price"])
#self.movesCurr[s]["ign_prop"] = float(ign_ind) / float(move_len)
#self.movesCurr[s]["cd_prop"] = float(cd_length) / float(move_len)
return "entered"
def updateMovesPrint(self, s, close, low, high):
if high > self.movesCurr[s]["high_move"]:
self.movesCurr[s]["high_move"] = high
if self.movesCurr[s]["stoppedCustom"] == 0 and close <= self.movesCurr[s]["stopCustom"]: #did it stop out, and when
self.movesCurr[s]["stoppedCustom"] = 1
self.movesCurr[s]["stoppedCustomTime"] = self.Time
self.movesCurr[s]["stopCustom_highMove"] = self.movesCurr[s]["high_move"]
if self.movesCurr[s]["stoppedLOD"] == 0 and close <= self.movesCurr[s]["stopLOD"]: #did it stop out, and when
self.movesCurr[s]["stoppedLOD"] = 1
self.movesCurr[s]["stoppedLODTime"] = self.Time
self.movesCurr[s]["stopLOD_highMove"] = self.movesCurr[s]["high_move"]
if self.Time.hour == 3 and self.Time.minute == 55 and self.Time.second == 1:
self.movesCurr[s]["EOD_price"] = close
return
#get high and low of move, volume
#and convert candles to 1/4 candles
#the price and volume candles of movesCurr should be the same as moves
#movesCurr is mostly just for the consol_low, so it doesn't get recent on new high
def updateMovesCurr(self, s, opened, high, low, close, vol):
mins = self.timeInMin(str(self.Time).split()[1])
#new low of consolidation
if self.movesCurr[s]["consol_low"] == "NA" or low < self.movesCurr[s]["consol_low"]:
self.movesCurr[s]["consol_low"] = low
self.movesCurr[s]["consol_low_ind"] = calculate.get_consol_low_ind(self, self.movesCurr[s]["candle_halves"]["Price"], self.movesCurr[s]["high"], self.movesCurr[s]["consol_low"])
#updateCandleHalves
self.movesCurr[s]["candle_halves"]["Price"][-1] = (self.movesCurr[s]["candle_halves"]["Price"][-1] + opened) / 2.0
self.movesCurr[s]["candle_halves"]["Volume"][-1] += vol / 6.0
if close >= opened:
self.movesCurr[s]["candle_halves"]["Price"].append(low)
self.movesCurr[s]["candle_halves"]["Price"].append(high)
elif close < opened:
self.movesCurr[s]["candle_halves"]["Price"].append(high)
self.movesCurr[s]["candle_halves"]["Price"].append(low)
self.movesCurr[s]["candle_halves"]["Price"].append(close)
self.movesCurr[s]["candle_halves"]["Volume"].append(vol / 3.0)
self.movesCurr[s]["candle_halves"]["Volume"].append(vol / 3.0)
self.movesCurr[s]["candle_halves"]["Volume"].append(vol / 6.0)
self.movesCurr[s]["candle_halves"]["Time"].append(mins - .667)
self.movesCurr[s]["candle_halves"]["Time"].append(mins - .333)
self.movesCurr[s]["candle_halves"]["Time"].append(mins)
self.movesCurr[s]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
self.movesCurr[s]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
self.movesCurr[s]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
return 0
#get high and low of move, volume
#and convert candles to 1/4 candles
def updateMoves(self, s, opened, high, low, close, vol, t):
#t = time = "PM" or "RH"
#init for either t
if s not in self.moves:
self.moves[s] = {}
##############################
# ignition and consolidation #
##############################
mins = self.timeInMin(str(self.Time).split()[1])
#new low, reset everything
if (t in self.moves[s] and low < self.moves[s][t]["low"]) or t not in self.moves[s]:
#update HOD
#it's the highest high of all previous moves
##so, can only exist if there is a new move
if t == "RH" and t in self.moves[s]:
#if s not in self.hod_level or self.moves[s]["RH"]["high"] > self.hod_level[s]:
if s not in self.hod_level or self.hod[s] > self.hod_level[s]:
#self.Debug("update hod_level")
#self.Debug(self.Time)
#self.Debug(self.moves[s]["RH"]["high"])
#self.hod_level[s] = self.moves[s]["RH"]["high"]
self.hod_level[s] = self.hod[s]
#update move
self.moves[s][t] = {}
self.moves[s][t]["low"] = low
self.moves[s][t]["consol_low"] = "NA"
#self.moves[s][t]["ign_vol"] = vol
#self.moves[s][t]["consol_vol"] = 0
self.moves[s][t]["candles"] = {} #reset all candles
#half candles
print_or_update.updateCandleHalves(self, s, mins, opened, high, low, close, vol, "low", t)
#if first candle is red and is max of move, then high won't exist in candle_halves
self.moves[s][t]["high"] = max(self.moves[s][t]["candle_halves"]["Price"])
#if self.Time.hour == 9 and 30 < self.Time.minute < 40:
# self.Debug(self.Time)
# self.Debug(self.moves[s][t]["candle_halves"]["Price"])
#new high (post new low)
elif high > self.moves[s][t]["high"]:
self.moves[s][t]["high"] = high
#self.moves[s][t]["ign_vol"] += vol + self.moves[s][t]["consol_vol"]
self.moves[s][t]["consol_low"] = "NA"
#self.moves[s][t]["consol_vol"] = 0
#half candles
print_or_update.updateCandleHalves(self, s, mins, opened, high, low, close, vol, "high", t)
#consolidation (no new high)
else:
#self.moves[s][t]["consol_vol"] += vol
#new low of consolidation
if self.moves[s][t]["consol_low"] == "NA" or low < self.moves[s][t]["consol_low"]:
self.moves[s][t]["consol_low"] = low
print_or_update.updateCandleHalves(self, s, mins, opened, high, low, close, vol, "consol_low", t)
#always add new candle
if "o" not in self.moves[s][t]["candles"]:
self.moves[s][t]["candles"]["o"] = []
self.moves[s][t]["candles"]["h"] = []
self.moves[s][t]["candles"]["l"] = []
self.moves[s][t]["candles"]["c"] = []
self.moves[s][t]["candles"]["v"] = []
self.moves[s][t]["candles"]["o"].append(opened)
self.moves[s][t]["candles"]["h"].append(high)
self.moves[s][t]["candles"]["l"].append(low)
self.moves[s][t]["candles"]["c"].append(close)
self.moves[s][t]["candles"]["v"].append(vol)
return 0
#region imports
from AlgorithmImports import *
#endregion
from clr import AddReference
AddReference("QuantConnect.Research")
#clr.AddReference('QuantConnect.Research')
from QuantConnect.Research import QuantBook
import statistics
import pandas as pd
import numpy as np
import math
import time as tm
from sklearn.linear_model import LinearRegression
import statsmodels.formula.api as smf
import statsmodels.api as sm
import copy
from scipy.stats import zscore
import pickle
import base64
import sklearn
from sklearn.calibration import CalibratedClassifierCV
import io
from datetime import datetime
import calendar
import calculate
import print_or_update
import longs
class TachyonMultidimensionalChamber(QCAlgorithm):
def Initialize(self):
'''
#run for two weeks
wk = 2
month = 6
yr = 2022
if wk == 1:
#run for first two weeks
start_date = datetime(yr, month, 1)
end_date = datetime(yr, month, 15)
self.SetStartDate(start_date.year, start_date.month, start_date.day)
self.SetEndDate(end_date.year, end_date.month, end_date.day)
elif wk == 2:
#run for rest of month
start_date = datetime(yr, month, 16)
_, last_day_of_month = calendar.monthrange(start_date.year, start_date.month)
self.SetStartDate(start_date.year, start_date.month, start_date.day)
self.SetEndDate(start_date.year, start_date.month, last_day_of_month)
'''
#run for a month
#start_date = datetime(2020, 4, 1)
#_, last_day_of_month = calendar.monthrange(start_date.year, start_date.month)
#self.SetStartDate(start_date.year, start_date.month, start_date.day)
#self.SetEndDate(start_date.year, start_date.month, last_day_of_month)
#run for a day
self.SetStartDate(2021, 6, 11) # Set Start Date
self.SetEndDate(2021, 6, 11) # Set End Date
#set these values
self.symbol = "NVDA" #set this to "" and it will do all, set to "<symbol>" for that symbol
self.got_to_time = "" #"" if don't report, else do format colon-delimited time e.g., "9:35"
self.EODmodels_in_PM = 0 #1 if calc models in PM (e.g., running live), 0 if not (e.g., backtesting)
self.sharpeThresh = "" #"" if no threshold
self.printMinimal = 1 #1 for just entries and exits, 0 for more stuff
#do other stuff
self.SetCash(400000) # Set Strategy Cash
self.AddUniverse(self.CoarseSelectionFunction)
self.SetSecurityInitializer(self.SecurityInitializer)
self.UniverseSettings.ExtendedMarketHours = True
#self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.SetBrokerageModel(BrokerageName.TradierBrokerage, AccountType.Margin)
self.UniverseSettings.Leverage = 4
self.UniverseSettings.Resolution = Resolution.Second #can comment/change this out
#self.SetWarmUp(5)
for s in self.Securities:
self.Debug(self.Securities[s])
###variables to keep track of
self.sd = {} #all symbol data
self.initVars()
def initVars(self):
self.pastNoPre = {}
self.movesCurr = {} #moves for those currently in position
self.moves = {} #this has both PM and RH moves
self.ranges = {}
self.curr_ranges = {} #ranges for a certain day, for updateRangesMultiple()
self.pastSixty = {}
self.models = {}
self.dollar_risk = 50.0 #size of positins in dollars
#pm
self.pmHigh = {}
self.pmLow = {}
self.pmVol = {}
self.pmNonZeroVol = {}
self.everBelowOneDollar = set()
self.PM_atSelection = {}
#
self.prevVWMA = {}
self.prevVWAP = {}
self.end = tm.time()
self.start = tm.time()
self.dailyLevels = {}
self.hod = {}
self.lod = {}
self.hod_level = {} #hod and lod for calculating daily range
self.lod_level = {}
self.open = {}
self.positions = {} #symbol, and misc things about position entry/exit
self.minutes = {} #n minutes of data for each symbol
self.everPosition = {}
self.buffer = 0 #only enter trades after N buffer after open (prob just 1 minute)
#error reports
self.max_arg_empty_seq = set()
self.calc_EMA_error = set()
self.VWMA_vol_zero = set()
self.EMAwPMerror = set()
self.EMAerrorLong = set()
self.EMAerrorShort = set()
self.EODmodelErr = set()
self.avgdevoob = set()
self.notInPMHigh = set()
self.CDerror = set()
self.AVWAPerror = set()
self.noVolVWAP = set()
#prevPrices, for EMA calc -- if they are the same, don't need to recalculate EMA
self.prevPrices = {}
self.prevPrices["ab"] = {}
self.prevPrices["ab_wPM"] = {}
self.prevPrices["bc"] = {}
self.prevPrices["cd"] = {}
self.prevPrices["postbreak"] = {}
self.prevPrices["ab_inpos"] = {}
self.prevStartPeriod = {}
self.prevStartPeriod["ab"] = {}
self.prevStartPeriod["ab_wPM"] = {}
self.prevStartPeriod["bc"] = {}
self.prevStartPeriod["cd"] = {}
self.prevStartPeriod["postbreak"] = {}
self.prevStartPeriod["ab_inpos"] = {}
#EOD model things
self.EODmodels = {}
self.EODmodels_entry = {}
#times (just regular hours), debugging
self.times = {}
self.s_times = {}
#end day early
self.stop_trading = 0
self.stop_entering = 0
#check these on shorter tf than 1 minute
self.staging = {}
#model coefficients for
self.content = {}
#has printed out yet
self.EOD = 0
#model
self.calibrated = ""
#yday ohlc
self.ydays = {}
#4 min volume
self.rhvol3 = {}
def SecurityInitializer(self, security):
security.SetLeverage(4)
def CoarseSelectionFunction(self, universe):
selected = []
blacklist = set()
for coarse in universe:
#if coarse.Volume > 5000000 and coarse.Value > 1 and coarse.HasFundamentalData:
if self.symbol != "":
if coarse.Symbol.Value == self.symbol:
symbol = coarse.Symbol
selected.append(symbol)
elif coarse.Volume > 2000000 and coarse.Value > .90 and coarse.HasFundamentalData:
if coarse.Symbol.Value not in blacklist:
symbol = coarse.Symbol
selected.append(symbol)
return selected #list of objects of type Symbol
def OnSecuritiesChanged(self, changed):
for security in changed.AddedSecurities:
symbol = security.Symbol
if symbol not in self.sd:
self.sd[symbol] = SymbolData(self, symbol)
security.SetFeeModel(ConstantFeeModel(0))
for security in changed.RemovedSecurities:
symbol = security.Symbol
self.sd.pop(symbol, None)
###reset these every day
def OnEndOfDay(self):
for s in self.sd:
self.sd[s].vwap.Reset()
self.initVars()
################
# functions #
################
###sizing
def positionSize(self, stop, currPrice, dollars_risk, spread):
#nShares_spread = math.floor(dollars_risk / (abs(stop - currPrice) + 2.0*spread + .001) ) #these are with .1 cents per share fee
#nShares = math.floor(dollars_risk / abs(stop - currPrice) + .001)
nShares_spread = math.floor(dollars_risk / (abs(stop - currPrice) + 2.0*spread) ) #2.0 spread for entry and exit
nShares = math.floor(dollars_risk / abs(stop - currPrice))
return nShares_spread, nShares
#do this every second if in position
def take_profit(self, s, close, high, low):
if close >= self.positions[s]["take_profit"] and self.Portfolio[s].Invested:
self.Debug("Exited: " + s.Value + " " + str(self.Time))
self.Liquidate(s)
self.Transactions.CancelOpenOrders(s)
if s in self.positions:
del self.positions[s]
return
#time is premarket or not
#time of format: HH:MM:SS
def isPM(self, clock):
hour, minute, sec = clock.split(":")
if int(hour) < 9:
return 1
if int(hour) == 9 and int(minute) < 31:
return 1
return 0
def isAH(self, clock):
hour, minute, sec = clock.split(":")
if int(hour) > 16 or (int(hour) == 16 and int(minute) > 0):
return 1
return 0
#time in minutes (since midnight)
def timeInMin(self, clock):
hour, minute, sec = clock.split(":")
return (int(hour)*60) + int(minute)
def timeInSec(self, clock):
hour, minute, sec = clock.split(":")
return (int(hour)*3600) + (int(minute)*60) + int(sec)
#read in and calibrate xgboost model
def read_and_calibrate_model(self):
base64_model = self.Download("https://www.dropbox.com/s/38ro5pddzmsucge/xgboost_withAVWAP_Jan21-Dec22_minusJanJuneJuneDec_uncalibrated.base64?dl=1")
base64_bytes_model = base64_model.encode('ascii')
decoded_model = base64.b64decode(base64_bytes_model)
clf = pickle.loads(decoded_model)
data = self.Download("https://www.dropbox.com/s/b4qbls7qgcodaov/Jan21-Dec22_minusJanJunJunDec_test.txt?dl=1")
df = pd.read_csv(io.StringIO(data), sep="\t")
X = df[["RR.10", "GRP_day", "GRP_CD_scaled", "CD_wRVOL_x_GRP", "wRVOL_CD_scaled", "PercChangeEMA3", "PercChangeEMA5", "PercChangeEMA8", "PercFromPriceEMA3", "PercFromPriceEMA5", "PercFromPriceEMA8", "PercChangeAVWAP3", "PercChangeAVWAP5", "PercChangeAVWAP8", "PercFromPriceAVWAP3", "PercFromPriceAVWAP5", "PercFromPriceAVWAP8", "pm_high_bw_target", "yday_high_bw_target", "yday_close_bw_target", "today_open_bw_target", "yday_low_bw_target", "pm_low_bw_target", "pm_high_bw_stop", "yday_high_bw_stop", "yday_close_bw_stop", "today_open_bw_stop"]]
y = df[["ReachedTarget"]]
'''
#calibrated = CalibratedClassifierCV(clf, method='isotonic', cv=2) #if you don't use cv='prefit' it times out
try:
calibrated = CalibratedClassifierCV(clf, method='isotonic', cv='prefit') #isotonic or sigmoid
calibrated.fit(X, y.values.ravel())
except:
calibrated = clf
'''
calibrated = CalibratedClassifierCV(clf, method='isotonic', cv='prefit') #isotonic or sigmoid
calibrated.fit(X, y.values.ravel())
return calibrated
###########
# on data #
###########
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
#close all at 3:55 if any positions are open
#should change this to time to market close
#use BeforeMarketClose()
if self.Time.hour >= 15 and self.Time.minute >= 55 and self.EOD == 0:
self.Debug("Stopping -- EOD 3:55")
print_or_update.printMoves(self)
self.EOD = 1
self.positions = {}
self.Liquidate()
return
#stop trading at whatever time e.g., noon
#then, after market closes, allow trading again for the next day
if self.stop_trading != 0:
return
if self.Time.hour == 4 and self.Time.minute == 1 and self.Time.second == 1:
self.initVars()
self.Debug("Number of symbols: ")
self.Debug(len(self.sd))
#every minute:
#check if below 1, if so remove
#only do this in PM (?)
#reset list of symbols to stage
if self.Time.second == 1:
for s in self.everBelowOneDollar:
if s in self.ranges:
del self.ranges[s]
if s in self.sd:
del self.sd[s]
self.staging = {}
#remove stocks without premarket action
if self.Time.hour == 9 and self.Time.minute == 0 and self.Time.second == 1:
self.Debug("stocks before pm removal: " + str(len(self.sd)))
toremove = set()
for s in self.sd:
if s in self.pmVol and s in self.pmNonZeroVol:
if self.pmVol[s] < 5000 or self.pmNonZeroVol[s] < 20:
toremove.add(s)
elif s not in self.pmHigh:
toremove.add(s)
for s in toremove:
if s in self.sd:
del self.sd[s]
self.Debug("stocks after pm removal: " + str(len(self.sd)))
for s in self.sd:
if s in self.pmVol and s in self.pmNonZeroVol:
#self.Debug(str(s))
#self.Debug(self.pmVol[s])
#self.Debug(self.pmNonZeroVol[s])
self.PM_atSelection[s] = {}
self.PM_atSelection[s]["pmvol"] = self.pmVol[s]
self.PM_atSelection[s]["pm_nonzero"] = self.pmNonZeroVol[s]
#get model
#self.calibrated = self.read_and_calibrate_model()
#calculate EOD models
if self.EODmodels_in_PM == 1:
for s in self.sd:
if self.Time.hour == 9 and self.Time.minute < 29:
try:
model, today_row, yday_ohlc, sd, mean_vols = calculate.modelEODRange(self, s)
self.EODmodels[s] = [model, today_row, yday_ohlc]
except Exception as e:
self.Debug(e)
self.EODmodelErr.add(s.Value)
return "EOD_model_error_1"
self.startTime = tm.time()
for s in self.sd:
start = tm.time()
if data.ContainsKey(s) and data.Bars.ContainsKey(s) and self.Time.hour < 16:
second = data.Bars
if s not in self.pastSixty:
self.pastSixty[s] = {}
self.pastSixty[s]["o"] = second[s].Open
self.pastSixty[s]["h"] = second[s].High
self.pastSixty[s]["l"] = second[s].Low
self.pastSixty[s]["v"] = second[s].Volume
self.pastSixty[s]["spread"] = [self.Securities[s].AskPrice - self.Securities[s].BidPrice]
else:
#update
if second[s].Open > self.pastSixty[s]["h"]:
self.pastSixty[s]["h"] = second[s].High
if second[s].Low < self.pastSixty[s]["l"]:
self.pastSixty[s]["l"] = second[s].Low
self.pastSixty[s]["v"] += second[s].Volume
self.pastSixty[s]["spread"].append(self.Securities[s].AskPrice - self.Securities[s].BidPrice)
if self.Time.second == 1: #should this be 1 actually?
#add 1 more minute for this symbol
if s not in self.minutes:
self.minutes[s] = 1
else:
self.minutes[s] += 1
#if s in self.positions:
# self.Debug(self.Portfolio[s].Quantity)
############################################################
# store ohlcv for past self.keepPast candles, with premarket #
############################################################
###consolidate past 60 seconds
opened = self.pastSixty[s]["o"]
high = self.pastSixty[s]["h"]
low = self.pastSixty[s]["l"]
close = second[s].Close
vol = self.pastSixty[s]["v"]
currRange = self.pastSixty[s]["h"] - self.pastSixty[s]["l"]
#re-init with current second
self.pastSixty[s]["o"] = second[s].Open
self.pastSixty[s]["h"] = second[s].High
self.pastSixty[s]["l"] = second[s].Low
self.pastSixty[s]["v"] = second[s].Volume
self.pastSixty[s]["spread"] = [self.Securities[s].AskPrice - self.Securities[s].BidPrice]
#save pmHigh
#put this in a function
if not self.IsMarketOpen(s):
if s not in self.pmHigh:
self.pmHigh[s] = high
self.pmLow[s] = low
self.pmVol[s] = vol
self.pmNonZeroVol[s] = 0
if high > self.pmHigh[s]:
self.pmHigh[s] = high
if low < self.pmLow[s]:
self.pmLow[s] = low
self.pmVol[s] += vol
if vol != 0:
self.pmNonZeroVol[s] += 1
if low < 1:
self.everBelowOneDollar.add(s)
longs.updateMoves(self, s, opened, high, low, close, vol, "PM")
#update moves
if s in self.positions:
longs.updateMovesCurr(self, s, opened, high, low, close, vol)
#these are to print out the positions, for modeling
if s in self.everPosition:
longs.updateMovesPrint(self, s, close, low, high)
#market is open now
if self.IsMarketOpen(s) and self.buffer == 1 and self.stop_entering == 0:
if s not in self.open:
self.open[s] = opened
##########################################################
# save ohlcv of intraday candles (save range separately) #
##########################################################
if s not in self.pastNoPre:
self.pastNoPre[s] = {}
self.pastNoPre[s]["o"] = []
self.pastNoPre[s]["h"] = []
self.pastNoPre[s]["l"] = []
self.pastNoPre[s]["c"] = []
self.pastNoPre[s]["v"] = []
self.pastNoPre[s]["VWAP"] = []
self.pastNoPre[s]["mean_spread"] = []
self.pastNoPre[s]["o"].append(opened)
self.pastNoPre[s]["h"].append(high)
self.pastNoPre[s]["l"].append(low)
self.pastNoPre[s]["c"].append(close)
self.pastNoPre[s]["v"].append(vol)
self.pastNoPre[s]["VWAP"].append(self.sd[s].vwap.Current.Value)
self.pastNoPre[s]["mean_spread"].append(statistics.mean(self.pastSixty[s]["spread"]))
#volume in first 4 minutes
if s not in self.rhvol3 and len(self.pastNoPre[s]["v"]) >= 4:
self.rhvol3[s] = self.pastNoPre[s]["v"][0] + self.pastNoPre[s]["v"][1] + self.pastNoPre[s]["v"][2] + self.pastNoPre[s]["v"][3]
################################
# price, volume, time of moves #
# ranges #
# levels #
# EMAs (trend) #
################################
longs.updateMoves(self, s, opened, high, low, close, vol, "RH")
#######################
# HOD and LOD, for range calc #
# these are not necessarily levels
# literally just HOD and LOD
#######################
if s not in self.hod or (s in self.hod and high > self.hod[s]):
self.hod[s] = high
if s not in self.lod or (s in self.lod and low < self.lod[s]):
self.lod[s] = low
#########
# entry #
#########
if s not in self.positions: #perhaps comment this out when actually running, instead of just printing out
if s not in self.everPosition: #one entry per day
got_to = longs.enter(self, s, close)
print_or_update.print_got_to(self, got_to, self.got_to_time)
###################
# previous values #
###################
self.prevVWAP[s] = self.sd[s].vwap.Current.Value
##############################
# debugging #
# print things out for test #
##############################
#if self.Time.hour == 9 and self.Time.minute == 45:
# print_or_update.printCandleHalves(self, s)
#if self.Time.hour == 11 and self.Time.minute == 19:
# if s in self.positions:
# self.printCandleHalvesCurr(s)
#########################################
# adjustments and exit #
# check/update every second, not minute #
#########################################
#every second see if hit T/P
if s in self.positions:
self.take_profit(s, second[s].Close, second[s].High, second[s].Low)
#get price 10 seconds after entry (for modeling time delay)
if self.timeInSec(str(self.Time).split()[1]) == self.movesCurr[s]["time_in_sec"] + 10:
self.movesCurr[s]["10_seconds_after"] = second[s].Close
#############################################################
# if not in position, close all open orders for that symbol #
#############################################################
if not self.Portfolio[s].Invested:
self.Transactions.CancelOpenOrders(s)
#openOrders = self.Transactions.GetOpenOrders()
#if len(openOrders)> 0:
# self.Debug(self.Time)
# for x in openOrders:
# self.Debug(x)
# self.Transactions.CancelOrder(x.Id)
#self.Transactions.CancelOpenOrders(s)
################################################
# if symbol is in staging from previous minute #
################################################
if s in self.staging:
#if self.Time.second == 1:
#self.staging[s]["volumes_thisMin"] = []
#self.staging[s]["closes_thisMin"] = []
#self.staging[s]["volumes_thisMin"].append(second[s].Volume)
#self.staging[s]["closes_thisMin"].append(second[s].Close)
if self.Time.second % 10 == 1:
got_to = longs.enter_staged(self, s, second[s].Close, self.staging[s]["max_broken"], self.staging[s]["pred"], self.staging[s]["levels"], self.staging[s]["EMAs"], self.staging[s]["mean_vols"], self.staging[s]["sd"], self.staging[s]["rsq"])
print_or_update.print_got_to(self, got_to, self.got_to_time)
#count total time for each ticker
end = tm.time()
if s not in self.times:
self.times[s] = 0
self.times[s] += (end - start)
#no new entries after 11
if self.Time.hour >= 11 and self.stop_entering == 0:
self.Debug("11:00 -- no new positions")
self.stop_entering = 1
self.Debug("max() arg is empty sequence:")
for s in self.max_arg_empty_seq:
self.Debug(s.Value)
self.Debug("Volume in VWMA is 0: " + str(self.VWMA_vol_zero))
#for s in self.VWMA_vol_zero:
# self.Debug(s.Value)
self.Debug("calc EMA error:")
for s in self.calc_EMA_error:
self.Debug(s.Value)
self.Debug("calc EMA wPM error:")
for s in self.EMAwPMerror:
self.Debug(s.Value)
self.Debug("calc EMA long error:")
for s in self.EMAerrorLong:
self.Debug(s.Value)
#self.Debug("ever positions:")
#for s in self.everPosition:
# self.Debug(str(s.Value) + "\t" + str(self.everPosition[s]["time"]))
self.Debug("EOD model error: " + str(self.EODmodelErr))
#self.Debug(EODmodelErr)
#for s in self.EODmodelErr:
# self.Debug(str(s.Value))
self.Debug("no volume for VWAP: " + str(self.noVolVWAP))
self.Debug("avg dev ign ind oob: ")
for s in self.avgdevoob:
self.Debug(str(s.Value))
self.Debug("CD GRP error: " + str(self.CDerror))
#for s in self.CDerror:
# self.Debug(s.Value)
self.Debug("AVWAP error: ")
for s in self.AVWAPerror:
self.Debug(s.Value)
profit_s = {}
for s in self.everPosition:
profit_s[self.Portfolio[s].NetProfit] = s
#for profit in sorted(profit_s):
# self.Debug("net profit " + profit_s[profit].Value + ": " + str(profit))
#print_or_update.printEverPosition(self)
#print_or_update.printMoves(self)
#if no current positions
if not self.positions:
self.stop_trading = 1
#first minute has passed
if self.sd and self.IsMarketOpen(s) and self.buffer == 0 and self.Time.minute == 31:
#if self.Time.hour == 9 and self.Time.minute == 31 and self.buffer == 0:
self.buffer = 1
def exit(self, s):
self.Debug("Exited")
self.Debug(s.Value)
self.Debug(self.Time)
self.Liquidate(s)
self.Transactions.CancelOpenOrders(s)
if s in self.positions:
del self.positions[s]
return
class SymbolData:
def __init__(self, algorithm, symbol):
self.vwap = algorithm.VWAP(symbol, 2000, Resolution.Minute) #60*24 = 1440 minutes in a day
prehist = algorithm.History(symbol, 10, Resolution.Minute)
if not prehist.empty:
hist = prehist.loc[symbol]
if 'volume' not in prehist.columns:
#algorithm.Log(f"No volume: {symbol}") #don't need to output b/c makes log longer
#self.noVolVWAP.add(str(symbol)) #Runtime Error: 'SymbolData' object has no attribute 'noVolVWAP'
return
for idx, bar in hist.iterrows():
tradeBar = TradeBar(idx, symbol, bar.open, bar.high, bar.low, bar.close, bar.volume, timedelta(minutes=1))
self.vwap.Update(tradeBar)
#region imports
from AlgorithmImports import *
#endregion
#print out table of ranges
def print_ranges(self, s):
#self.Debug("Period\tOpen\tHigh\tLow\tClose\tVolume\tDistance\tMins_since_first\tTime\tDate")
self.Debug("Period\tClose_minus_open\tVolume\tMins_since_first") #start is Mins_since_first - Period
for date in self.ranges[s]:
for period in sorted(self.ranges[s][date]):
for i in range (0, len(self.ranges[s][date][period]["high"])):
#self.Debug(str(period) + "\t" + str(round(self.ranges[s][period]["open"][i], 2)) + "\t" + str(round(self.ranges[s][period]["high"][i], 2)) + "\t" + str(round(self.ranges[s][period]["low"][i], 2)) + "\t" + str(round(self.ranges[s][period]["close"][i], 2)) + "\t" + str(self.ranges[s][period]["vol"][i]) + "\t" + str(self.ranges[s][period]["time"][i]))
#self.Debug(str(period) + "\t" + str(round(self.ranges[s][date][period]["open"][i], 2)) + "\t" + str(round(self.ranges[s][date][period]["high"][i], 2)) + "\t" + str(round(self.ranges[s][date][period]["low"][i], 2)) + "\t" + str(round(self.ranges[s][date][period]["close"][i], 2)) + "\t" + str(self.ranges[s][date][period]["vol"][i]) + "\t" + str(round(self.ranges[s][date][period]["dist"][i], 2)) + "\t" + str(self.ranges[s][date][period]["minute"][i]) + "\t" + str(self.ranges[s][date][period]["time"][i]) + "\t" + date)
self.Debug(str(period) + "\t" + str(round(self.ranges[s][date][period]["close"][i] - self.ranges[s][date][period]["open"][i], 2)) + "\t" + str(self.ranges[s][date][period]["vol"][i]) + "\t" + str(self.ranges[s][date][period]["minute"][i]))
'''
#low of consolidation to current price, postbreak
#so, use movesCurr
def calc_consolEMA(self, s):
#get consol low to current price
consol_low_prices = []
hit_high = 0
consol_low = 2000000000
for p in self.moves[s]["RH"]["candle_halves"]["Price"]:
#consolidating, get the low
if hit_high == 1:
#if new low, reset
if p < consol_low:
consol_low = p
consol_low_prices = []
consol_low_prices.append(p)
#past high of the move
elif p == self.moves[s]["RH"]["high"]:
hit_high = 1
if len(consol_low_prices) == 0:
return "NAN"
#midpoint, and low of consolidation to high of consolidation post-low
midpoint_consol = ((max(consol_low_prices) + min(consol_low_prices)) / 2.0)
consol_low_to_high = []
for p in consol_low_prices:
consol_low_to_high.append(p)
if p == max(consol_low_prices):
break
#only need to recalc if it's going to be different
start_consol = "N"
EMA_period_consol = "A"
if s not in self.prevStartPeriod["cd"] or consol_low_to_high != self.prevPrices["cd"][s]:
start_consol, EMA_period_consol = EMA_midpoint(self, s, consol_low_to_high, midpoint_consol)
self.prevStartPeriod["cd"][s] = str(start_consol) + " " + str(EMA_period_consol)
self.prevPrices["cd"][s] = consol_low_to_high
else:
start_consol, EMA_period_consol = self.prevStartPeriod["cd"][s].split()
if not (start_consol != "N" and EMA_period_consol != "A"):
self.Debug("is NA")
return
adj_closes_EMA_consol = []
try:
adj_closes_EMA_consol = consol_low_prices[int(start_consol):]
except:
self.Debug("adj_closes_EMA_consol error")
self.Debug(s)
adj_closes_EMA_consol[0] = consol_low_prices[0]
currEMA_consol, EMAs_consol = self.calcEMA(s, adj_closes_EMA_consol, EMA_period_consol)
return currEMA_consol, EMAs_consol, start_consol
'''#region imports
from AlgorithmImports import *
#endregion
import calculate
def printMoves(self):
################
# print header #
################
#Ticker Date Time Entry Stop_custom Stop_LOD Time_stopped_custom Time_stopped_LOD Max_moved_before_stop_custom Max_moved_before_stop_LOD EOD_price Percentage_moves_[.10..95]
header = "Ticker\tDate\tEntry_time\tMinutes_since_opened\tEntry_price\tEntry_price_10s\tSpread\t"
header += "Current_range\tLOD_entry\tHOD_entry\t"
header += "Stop\tStop_LOD\tStop_risk\tStop_LOD_risk\tRR\t"
header += "GRP_day\tGRP_total_move\tGRP_ignition\tGRP_consol\tGRP_CD\tw_GRP_CD\t"
header += "RVOL_CD_day\tw_RVOL_CD_day\tRVOL_ignition_day\t"
header += "Prop_len_ign_move\tProp_len_CD_move\tProp_len_move_day\tProp_vol_move_day\t"
header += "RVOL20_RH\tRVOL20_PM\tRVOL20_sum\t"
header += "BD_SMA\tCD_SMA\t"
header += "PercFromPriceEMA2\tPercFromPriceEMA3\tPercFromPriceEMA4\tPercFromPriceEMA5\t"
header += "PercFromPriceEMA6\tPercFromPriceEMA7\tPercFromPriceEMA8\tPercFromPriceEMA9\t"
header += "PercFromPriceEMA10\tPercFromPriceEMA20\tPercFromPriceEMA50\tPercFromPriceEMA200\t"
header += "PercChangeEMA2\tPercChangeEMA3\tPercChangeEMA4\tPercChangeEMA5\t"
header += "PercChangeEMA6\tPercChangeEMA7\tPercChangeEMA8\tPercChangeEMA9\t"
header += "PercChangeEMA10\tPercChangeEMA20\tPercChangeEMA50\tPercChangeEMA200\t"
header += "PercFromPriceAVWAP2\tPercFromPriceAVWAP3\tPercFromPriceAVWAP4\tPercFromPriceAVWAP5\t"
header += "PercFromPriceAVWAP6\tPercFromPriceAVWAP7\tPercFromPriceAVWAP8\tPercFromPriceAVWAP9\t"
header += "PercFromPriceAVWAP10\tPercFromPriceAVWAP20\tPercFromPriceAVWAP50\tPercFromPriceAVWAP200\t"
header += "PercChangeAVWAP2\tPercChangeAVWAP3\tPercChangeAVWAP4\tPercChangeAVWAP5\t"
header += "PercChangeAVWAP6\tPercChangeAVWAP7\tPercChangeAVWAP8\tPercChangeAVWAP9\t"
header += "PercChangeAVWAP10\tPercChangeAVWAP20\tPercChangeAVWAP50\tPercChangeAVWAP200\t"
header += "gt_yday_high\tlt_yday_low\t"
header += "pm_high\tpm_low\tyday_high\tyday_low\tyday_close\ttoday_open\t"
header += "pm_high_bw_target\tpm_low_bw_target\tyday_high_bw_target\tyday_low_bw_target\tyday_close_bw_target\ttoday_open_bw_target\t"
header += "pm_high_bw_stop\tpm_low_bw_stop\tyday_high_bw_stop\tyday_low_bw_stop\tyday_close_bw_stop\ttoday_open_bw_stop\t"
header += "LOD_support\tHOD_resistance\t"
header += "Time_stopped\tTime_stopped_LOD\t"
header += "High_post_entry\tHigh_before_stop\tHigh_before_stop_LOD\tEOD_price\tpred_EOD_range\tresid_SD\tresid_SD_div_pred_EOD_range\tmodel_R2\tPercentileEOD\tPropEOD"
self.Debug(header)
################
# print values #
################
for s in self.everPosition:
###values
close = self.pastSixty[s]["o"]
stop_custom_risk = str(self.movesCurr[s]["entry"] - self.movesCurr[s]["stopCustom"])
stop_LOD_risk = str(self.movesCurr[s]["entry"] - self.movesCurr[s]["stopLOD"])
#min since open
min_since_open = (self.movesCurr[s]["time_in_sec"] - 34201 ) / 60.0
###print
toprint = s.Value + "\t" + str(self.Time).split()[0] + "\t" + str(self.movesCurr[s]["time"]).split()[1] + "\t" + str(min_since_open) + "\t" + str(self.movesCurr[s]["entry"]) + "\t" + str(self.movesCurr[s]["10_seconds_after"]) + "\t" + str(self.movesCurr[s]["spread"]) + "\t"
toprint += str(self.movesCurr[s]["curr_range"]) + "\t" + str(self.movesCurr[s]["entryLOD"]) + "\t" + str(self.movesCurr[s]["entryHOD"]) + "\t"
toprint += str(self.movesCurr[s]["stopCustom"]) + "\t" + str(self.movesCurr[s]["stopLOD"]) + "\t" + stop_custom_risk + "\t" + stop_LOD_risk + "\t" + str(self.everPosition[s]["R:R"]) + "\t"
toprint += str(self.movesCurr[s]["grp_day"]) + "\t" + str(self.movesCurr[s]["grp_tot"]) + "\t" + str(self.movesCurr[s]["grp_ign"]) + "\t" + str(self.movesCurr[s]["grp_consol"]) + "\t" + str(self.movesCurr[s]["grp_cd"]) + "\t" + str(self.movesCurr[s]["w_grp_cd"]) + "\t"
toprint += str(self.movesCurr[s]["rvol_CD_day"]) + "\t" + str(self.movesCurr[s]["w_rvol_CD_day"]) + "\t" + str(self.movesCurr[s]["rvol_ign_day"]) + "\t"
toprint += str(self.movesCurr[s]["prop_len_ign"]) + "\t" + str(self.movesCurr[s]["prop_len_CD"]) + "\t" + str(self.movesCurr[s]["prop_len_move_day"]) + "\t" + str(self.movesCurr[s]["move_day_vol"]) + "\t"
toprint += str(float(self.rhvol3[s])/self.movesCurr[s]["mean_vols"][0]) + "\t" + str(self.pmVol[s]/self.movesCurr[s]["mean_vols"][1]) + "\t" + str((float(self.rhvol3[s]) + self.pmVol[s])/self.movesCurr[s]["mean_vols"][2]) + "\t"
toprint += str(self.movesCurr[s]["bd_SMA"]) + "\t" + str(self.movesCurr[s]["cd_SMA"]) + "\t"
for i in range(0, len(self.movesCurr[s]["perc_aways"])):
toprint += str(self.movesCurr[s]["perc_aways"][i]) + "\t"
for i in range(0, len(self.movesCurr[s]["perc_changes"])):
toprint += str(self.movesCurr[s]["perc_changes"][i]) + "\t"
for avwap in self.movesCurr[s]["perc_aways_avwap"]:
toprint += str(self.movesCurr[s]["perc_aways_avwap"][avwap]) + "\t"
for avwap in self.movesCurr[s]["perc_changes_avwap"]:
toprint += str(self.movesCurr[s]["perc_aways_avwap"][avwap]) + "\t"
toprint += str(self.movesCurr[s]["gt_yday_high"]) + "\t" + str(self.movesCurr[s]["lt_yday_low"]) +"\t"
toprint += str(self.movesCurr[s]["pm_high"]) + "\t" + str(self.movesCurr[s]["pm_low"]) + "\t" + str(self.movesCurr[s]["yday_high"]) + "\t" + str(self.movesCurr[s]["yday_low"]) + "\t" + str(self.movesCurr[s]["yday_close"]) + "\t" + str(self.movesCurr[s]["today_open"]) + "\t"
#levels
for levels in ["resistance", "support"]:
toprint += str(self.movesCurr[s][levels]["pm_high"]) + "\t" + str(self.movesCurr[s][levels]["pm_low"]) + "\t" + str(self.movesCurr[s][levels]["yday_high"]) + "\t" + str(self.movesCurr[s][levels]["yday_low"]) + "\t" + str(self.movesCurr[s][levels]["yday_close"]) + "\t" + str(self.movesCurr[s][levels]["today_open"]) + "\t"
toprint += str(self.movesCurr[s]["support"]["lod"]) + "\t" + str(self.movesCurr[s]["resistance"]["hod"]) + "\t"
toprint += str(self.movesCurr[s]["stoppedCustomTime"]) + "\t" + str(self.movesCurr[s]["stoppedLODTime"]) + "\t"
if str(self.movesCurr[s]["stopCustom_highMove"]) == "NA":
self.movesCurr[s]["stopCustom_highMove"] = self.movesCurr[s]["high_move"]
if str(self.movesCurr[s]["stopLOD_highMove"]) == "NA":
self.movesCurr[s]["stopLOD_highMove"] = self.movesCurr[s]["high_move"]
toprint += str(self.movesCurr[s]["high_move"]) + "\t" + str(self.movesCurr[s]["stopCustom_highMove"]) + "\t" + str(self.movesCurr[s]["stopLOD_highMove"]) + "\t" + str(close) + "\t"
toprint += str(self.movesCurr[s]["pred"]) + "\t" + str(self.movesCurr[s]["residual_sd"]) + "\t" + str(self.movesCurr[s]["residual_sd"] / self.movesCurr[s]["pred"]) + "\t" + str(self.movesCurr[s]["model_rsq"]) + "\t"
toprint += str(self.movesCurr[s]["percentile"]) + "\t" + str(self.movesCurr[s]["curr_range"] / self.movesCurr[s]["pred"])
#round to 4 decimal places, to make log smaller
toprint_list = toprint.split("\t")
toprint_rounded = ""
for l in toprint_list:
try:
val = round(float(l), 4)
toprint_rounded += str(val) + "\t"
except:
toprint_rounded += l + "\t"
self.Debug(toprint_rounded.rstrip())
return
#add to "candle_halves", every half of candle
def updateCandleHalves(self, s, mins, opened, high, low, close, vol, typeOfCandle, t):
#new point every 20 seconds
#change between earlier high or low depending on if candle red or green
#init new move
if "candle_halves" not in self.moves[s][t] or typeOfCandle == "low":
self.moves[s][t]["candle_halves"] = {}
self.moves[s][t]["candle_halves"]["Price"] = []
self.moves[s][t]["candle_halves"]["Volume"] = []
self.moves[s][t]["candle_halves"]["Time"] = []
self.moves[s][t]["candle_halves"]["Time_wall"] = []
#add first candle, start at low
self.moves[s][t]["candle_halves"]["Price"].append(low)
self.moves[s][t]["candle_halves"]["Volume"].append(vol / 3.0)
self.moves[s][t]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
if close >= opened:
self.moves[s][t]["candle_halves"]["Price"].append(high)
self.moves[s][t]["candle_halves"]["Volume"].append(vol / 3.0)
self.moves[s][t]["candle_halves"]["Time"].append(mins - .667)
self.moves[s][t]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
self.moves[s][t]["candle_halves"]["Time"].append(mins - .333)
#always add the close
self.moves[s][t]["candle_halves"]["Price"].append(close)
self.moves[s][t]["candle_halves"]["Volume"].append(vol / 6.0)
self.moves[s][t]["candle_halves"]["Time"].append(mins)
self.moves[s][t]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
else:
#add new 1/3 candles
#add 1/6 of volume to old close
#1/3 to low, 1/3 to high
#1/6 to current close
self.moves[s][t]["candle_halves"]["Price"][-1] = (self.moves[s][t]["candle_halves"]["Price"][-1] + opened) / 2.0
self.moves[s][t]["candle_halves"]["Volume"][-1] += vol / 6.0
if close >= opened:
self.moves[s][t]["candle_halves"]["Price"].append(low)
self.moves[s][t]["candle_halves"]["Price"].append(high)
elif close < opened:
self.moves[s][t]["candle_halves"]["Price"].append(high)
self.moves[s][t]["candle_halves"]["Price"].append(low)
#these are always the same
self.moves[s][t]["candle_halves"]["Price"].append(close)
self.moves[s][t]["candle_halves"]["Volume"].append(vol / 3.0)
self.moves[s][t]["candle_halves"]["Volume"].append(vol / 3.0)
self.moves[s][t]["candle_halves"]["Volume"].append(vol / 6.0)
self.moves[s][t]["candle_halves"]["Time"].append(mins - .667)
self.moves[s][t]["candle_halves"]["Time"].append(mins - .333)
self.moves[s][t]["candle_halves"]["Time"].append(mins)
self.moves[s][t]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
self.moves[s][t]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
self.moves[s][t]["candle_halves"]["Time_wall"].append(str(self.Time).split()[1])
return 0
#print out half candles, so can plot in R
def printCandleHalves(self, s):
#VWMAs
vwmas, vp, v = calculate.VWMA(self, s, self.moves[s]["RH"]["candle_halves"]["Volume"], self.moves[s]["RH"]["candle_halves"]["Price"], "beginning")
vwma_consol, vp_consol, v_consol = calculate.VWMA(self, s, self.moves[s]["RH"]["candle_halves"]["Volume"], self.moves[s]["RH"]["candle_halves"]["Price"], "consol")
#vwma_cd = self.VWMA(s, self.moves[s]["RH"]["candle_halves"]["Volume"], self.moves[s]["RH"]["candle_halves"]["Price"], "cd")
#EMAs
currEMA, EMAs, start = calculate.calc_ignEMA(self, s)
#currEMA_consol, EMAs_consol, start_consol = calculate.calc_consolEMA(self, s)
currEMA_short, EMAs_short, start_short = calculate.calc_shortEMA(self, s, self.moves[s]["RH"]["candle_halves"]["Price"])
#ign time and time to consol low
ign_time = calculate.ign_high_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"])
consol_low_ind = calculate.get_consol_low_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"], self.moves[s]["RH"]["consol_low"])
######print
self.Debug("Time_wall\tTime\tValue\tCategory")
#including PM
#for i in range(0, len(self.moves_wPM[s]["candle_halves"]["Time"])):
# for cat in sorted(self.moves_wPM[s]["candle_halves"]):
# if cat != "Time" and cat != "Time_wall" and cat != "Price_noconsollow" and cat != "Price_nohighs":
# self.Debug(str(self.moves_wPM[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves_wPM[s]["candle_halves"]["Time"][i]) + "\t" + str(self.moves_wPM[s]["candle_halves"][cat][i]) + "\t" + cat + "_wPM")
#
# if i >= start_wPM:
# self.Debug(str(self.moves_wPM[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves_wPM[s]["candle_halves"]["Time"][i]) + "\t" + str(EMAs_wPM[i - start_wPM]) + "\tEMA_wPM")
#market hours
short_ind = 0
consol_ind = 0
for i in range(0, len(self.moves[s]["RH"]["candle_halves"]["Time"])):
for cat in sorted(self.moves[s]["RH"]["candle_halves"]):
if cat != "Time" and cat != "Time_wall" and cat != "Price_noconsollow" and cat != "Price_nohighs":
self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"][cat][i]) + "\t" + cat)
self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(vwmas[i]) + "\tVWMA")
if i >= start:
self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(EMAs[i - start]) + "\tEMA")
if i >= ign_time:
self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(vwma_consol[i-ign_time]) + "\tVWMA_consol")
#if i >= consol_low_ind:
# self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(vwma_cd[i-consol_low_ind]) + "\tVWMA_cd")
#short EMA
if i >= (len(self.moves[s]["RH"]["candle_halves"]["Time"]) - len(EMAs_short)):
self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(EMAs_short[short_ind]) + "\tEMA_short")
short_ind += 1
#consol low to consol high EMA
#if i >= (len(self.moves[s]["RH"]["candle_halves"]["Time"]) - len(EMAs_consol)):
# self.Debug(str(self.moves[s]["RH"]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.moves[s]["RH"]["candle_halves"]["Time"][i]) + "\t" + str(EMAs_consol[consol_ind]) + "\tEMA_consol")
# consol_ind += 1
#print out half candles, so can plot in R
def printCandleHalvesCurr(self, s):
#VWMAs
vwma_cd, vp_cd, v_cd = calculate.VWMA(self, s, self.movesCurr[s]["candle_halves"]["Volume"], self.movesCurr[s]["candle_halves"]["Price"], "cd_curr")
#VWMAs bc
#vwma_bc, vp_bc, v_bc = calculate.VWMA(self, s, self.movesCurr[s]["candle_halves"]["Volume"], self.movesCurr[s]["candle_halves"]["Price"], "bc_curr")
#VWMAs bc not Curr
vwma_bc_nc, vp_bc_nc, v_bc_nc = calculate.VWMA(self, s, self.moves[s]["RH"]["candle_halves"]["Volume"], self.moves[s]["RH"]["candle_halves"]["Price"], "consol")
#VWMAs breakout
vwmas_break, vp_break, v_break = calculate.VWMA(self, s, self.movesCurr[s]["candle_halves"]["Volume"], self.movesCurr[s]["candle_halves"]["Price"], "breakout")
#VWMAs d
#vwmas_d, vp_d, v_d = calculate.VWMA(self, s, self.movesCurr[s]["candle_halves"]["Volume"], self.movesCurr[s]["candle_halves"]["Price"], "d")
#EMAs
currEMA, EMAs, start = calculate.calc_CD_EMA_postBreak(self, s, self.movesCurr[s]["candle_halves"]["Price"], self.movesCurr[s]["moving_stop"])
#ign time and time to consol low
#ign_ind = calculate.ign_high_ind(self, self.movesCurr[s]["candle_halves"]["Price"], self.movesCurr[s]["high"])
consol_low_ind = calculate.get_consol_low_ind(self, self.movesCurr[s]["candle_halves"]["Price"], self.movesCurr[s]["high"], self.movesCurr[s]["consol_low"])
ign_ind_ncurr = calculate.ign_high_ind(self, self.moves[s]["RH"]["candle_halves"]["Price"], self.moves[s]["RH"]["high"])
breakout_ind = calculate.get_breakout_ind(self, s, self.movesCurr[s]["candle_halves"]["Price"])
#curr_high_ind = calculate.get_currhigh_ind(self, self.movesCurr[s]["candle_halves"]["Price"])
#self.Debug(ign_ind_ncurr)
#AB EMA, in position
currEMA_inpos, EMAs_inpos, start_inpos = calculate.calc_longEMA_general(self, s, "ab_inpos", self.movesCurr[s]["candle_halves"]["Price"])
######print
self.Debug("Time_wall\tTime\tValue\tCategory")
#market hours
short_ind = 0
consol_ind = 0
for i in range(0, len(self.movesCurr[s]["candle_halves"]["Time"])):
for cat in sorted(self.movesCurr[s]["candle_halves"]):
if cat != "Time" and cat != "Time_wall" and cat != "Price_noconsollow" and cat != "Price_nohighs":
self.Debug(str(self.movesCurr[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.movesCurr[s]["candle_halves"]["Time"][i]) + "\t" + str(self.movesCurr[s]["candle_halves"][cat][i]) + "\t" + cat)
if i >= start:
self.Debug(str(self.movesCurr[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.movesCurr[s]["candle_halves"]["Time"][i]) + "\t" + str(EMAs[i - start]) + "\tEMA_movingStop")
if i >= consol_low_ind:
self.Debug(str(self.movesCurr[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.movesCurr[s]["candle_halves"]["Time"][i]) + "\t" + str(vwma_cd[i-consol_low_ind]) + "\tVWMA_cd")
if i >= start_inpos:
self.Debug(str(self.movesCurr[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.movesCurr[s]["candle_halves"]["Time"][i]) + "\t" + str(EMAs_inpos[i-start_inpos]) + "\tEMA_AB")
if i >= breakout_ind:
self.Debug(str(self.movesCurr[s]["candle_halves"]["Time_wall"][i]) + "\t" + str(self.movesCurr[s]["candle_halves"]["Time"][i]) + "\t" + str(vwmas_break[i-breakout_ind]) + "\tVWMA_breakout")
return
def printEverPosition(self):
#Symbol [each category] Return
self.Debug("Symbol\tReturn\tEntry_time_in_min\tPM_vol\tPM_nonZeroOneMinCandles\tR:R_lastTrade\tProp_of_pred_range_last_trade\tN_entries")
for s in self.everPosition:
time = self.timeInMin(str(self.everPosition[s]["time"]).split()[1])
#self.Debug(s.Value + "\t" + str(self.Portfolio[s].NetProfit) + "\t" + str(time) + "\t" + str(self.everPosition[s]["pmvol"]) + "\t" + str(self.everPosition[s]["pm_nonzero"]) + "\t" + str(self.everPosition[s]["R:R"]) + "\t" + str(self.everPosition[s]["prop_of_pred_range"]) + "\t" + str(self.everPosition[s]["N_entries"]))
self.Debug(s.Value + "\t" + str(self.Portfolio[s].LastTradeProfit) + "\t" + str(time) + "\t" + str(self.everPosition[s]["pmvol"]) + "\t" + str(self.everPosition[s]["pm_nonzero"]) + "\t" + str(self.everPosition[s]["R:R"]) + "\t" + str(self.everPosition[s]["prop_of_pred_range"]) + "\t" + str(self.everPosition[s]["N_entries"]))
return
def print_got_to(self, got_to, got_to_time):
if got_to_time != "":
hour, minute = self.got_to_time.split(":")
if minute == "00":
minute = "0"
#self.Debug(str(self.Time.minute))
#minute = minute.lstrip("0")
if hour == str(self.Time.hour) and minute == str(self.Time.minute):
self.Debug(self.Time)
self.Debug(got_to)
return