Last active
December 24, 2020 22:59
-
-
Save rm-rf-etc/68d1e14a710d024c656ee3ec9b2fe8ba to your computer and use it in GitHub Desktop.
Intrinsic Time implementation, from the Alpha Engine whitepaper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class IntrinsicTimeBar: | |
reversal: False | |
direction: None | |
time: None | |
high: None | |
low: None | |
def __init__(self, time, high: float, low: float, direction=None, reversal=False): | |
self.reversal = reversal | |
self.direction = direction | |
self.time = time | |
self.high = high | |
self.low = low | |
class IntrinsicTimeChart: | |
maxPercentageChange = None | |
maxLength = None | |
lengthNow = 0 | |
OnStep = None | |
upper = None | |
lower = None | |
def __init__(self, max_percentage_change: float, length=2): | |
self.maxPercentageChange = max_percentage_change | |
self.maxLength = max(2, length) | |
self.window = [] | |
self.upper = None | |
self.lower = None | |
def __AddBar(self, new_bar: IntrinsicTimeBar): | |
self.window.insert(0, new_bar) | |
length = len(self.window) | |
if length > self.maxLength: | |
self.window.pop(self.maxLength) | |
else: | |
self.lengthNow = length | |
if self.OnStep: | |
self.OnStep(new_bar) | |
def NewCandlestick(self, time, bar): | |
if self.upper == None: | |
self.upper = bar['Open'] | |
self.lower = bar['Open'] | |
self.NewPrice(time, bar['Open']) | |
# process high and low in order of proximity from the open | |
ohlc_order = [ | |
('High', abs(bar['Open'] - bar['High'])), | |
('Low', abs(bar['Open'] - bar['Low'])) | |
] | |
for prop in [t[0] for t in sorted(ohlc_order, key=lambda t: t[1])]: | |
self.NewPrice(time, bar[prop]) | |
self.NewPrice(time, bar['Close']) | |
def NewPrice(self, time, price: float): | |
self.upper = price if self.upper == None else max(price, self.upper) | |
self.lower = price if self.lower == None else min(price, self.lower) | |
allowable_change = (self.upper + self.lower) * \ | |
0.5 * self.maxPercentageChange | |
possible_high = self.lower + allowable_change | |
possible_low = self.upper - allowable_change | |
surpassed_high = price > possible_high | |
surpassed_low = price < possible_low | |
if not surpassed_high and not surpassed_low: | |
return | |
new_level = None | |
new_bar = IntrinsicTimeBar( | |
time, self.upper, self.lower, direction=None, reversal=False) | |
if surpassed_high: | |
new_bar.high = new_level = possible_high | |
new_bar.direction = 'u' | |
if self.lengthNow > 0 and self.window[0].direction == 'd': | |
new_bar.reversal = True | |
elif surpassed_low: | |
new_bar.low = new_level = possible_low | |
new_bar.direction = 'd' | |
if self.lengthNow > 0 and self.window[0].direction == 'u': | |
new_bar.reversal = True | |
else: | |
raise Exception('the impossible has happened') | |
self.upper = new_level | |
self.lower = new_level | |
self.__AddBar(new_bar) | |
self.NewPrice(time, price) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
qb = QuantBook() | |
spy = qb.AddEquity("SPY") | |
days = 360 | |
history = qb.History(qb.Securities.Keys, days, Resolution.Daily) | |
#--------------------------------------------------------------# | |
import pandas as pd | |
import mplfinance as mpf | |
from IntrinsicTime import IntrinsicTimeChart | |
itc = IntrinsicTimeChart(0.03, days + 1) | |
# write every new bar into table struct at IT run-time | |
new_table = {"Date":[], "Open": [], "High": [], "Low": [], "Close": []} | |
def writeBarToTable(bar): | |
new_table["Date"].append(bar.time) | |
new_table["Low"].append(bar.low) | |
new_table["High"].append(bar.high) | |
if bar.direction == 'u': | |
new_table["Open"].append(bar.low) | |
new_table["Close"].append(bar.high) | |
else: | |
new_table["Open"].append(bar.high) | |
new_table["Close"].append(bar.low) | |
itc.OnStep = writeBarToTable | |
# push price history into IT | |
for index, row in history.iterrows(): | |
bar = TradeBar() | |
bar.Open = row['open'] | |
bar.High = row['high'] | |
bar.Low = row['low'] | |
bar.Close = row['close'] | |
itc.NewCandlestick(index[1], bar) | |
# plot intrinsic time series | |
df = pd.DataFrame(new_table) | |
df.index = df["Date"] | |
mpf.plot(df, type='candlestick') | |
# plot historical price as OHLC | |
spy = history.loc['SPY'] | |
spy.rename(columns={"open": "Open", "high": "High", "low": "Low", "close": "Close", "volume": "Volume"}, inplace=True) | |
mpf.plot(spy) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment