Skip to content

Commit

Permalink
add req methods
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesmawm committed Jun 18, 2019
1 parent d55d1f4 commit de5125a
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 69 deletions.
68 changes: 38 additions & 30 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,50 @@
from ib_insync import util
from ib_insync import Forex

from models.hft_model import HftModel

if __name__ == '__main__':
model = HftModel(host='localhost',
# port = 4002,
port = 7497,
client_id = 1,
is_use_gateway = False,
evaluation_time_secs = 15,
resample_interval_secs = '30s')
model.run()
model = HftModel(
host='localhost',
# port = 4002,
port=7497,
client_id=1,
is_use_gateway=False,
evaluation_time_secs=15,
resample_interval_secs='30s'
)

# from ib_insync import *
to_trade = [
('EURUSD', Forex('EURUSD')),
('USDJPY', Forex('USDJPY'))
]

# util.startLoop()
model.run(to_trade=to_trade)

# ib = IB()
# ib.connect('127.0.0.1', 7497, clientId=15)
# from ib_insync import *

# util.startLoop()

# ib = IB()
# ib.connect('127.0.0.1', 7497, clientId=15)

# contracts = [Forex(pair) for pair in ('EURUSD', 'USDJPY', 'GBPUSD', 'USDCHF', 'USDCAD', 'AUDUSD')]
# ib.qualifyContracts(*contracts)
#
# eurusd = contracts[0]
#
# for contract in contracts:
# ib.reqMktData(contract, '', False, False)
#
# ticker = ib.ticker(eurusd)
# ib.sleep(2)
# contracts = [Forex(pair) for pair in ('EURUSD', 'USDJPY', 'GBPUSD', 'USDCHF', 'USDCAD', 'AUDUSD')]
# ib.qualifyContracts(*contracts)
#
# eurusd = contracts[0]
#
# for contract in contracts:
# ib.reqMktData(contract, '', False, False)
#
# ticker = ib.ticker(eurusd)
# ib.sleep(2)

# eurusd = Forex('EURUSD')
# ticker = ib.reqTickByTickData(eurusd, 'BidAsk')
# while True:
# print(ticker)
# ib.sleep(2)
#
# print(ticker)
# eurusd = Forex('EURUSD')
# ticker = ib.reqTickByTickData(eurusd, 'BidAsk')
# while True:
# print(ticker)
# ib.sleep(2)
#
# print(ticker)

# from ib_insync import *
#
Expand Down
233 changes: 194 additions & 39 deletions models/hft_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,211 @@

from ib_insync import IB, Stock, Forex
from ib_insync.util import df
from dateutil import tz


class HftModel(object):

def __init__(
self,
host='localhost', port=4001,
client_id=101, is_use_gateway=False, evaluation_time_secs=20,
host='localhost', port=4001, client_id=101,
is_use_gateway=False, evaluation_time_secs=20,
resample_interval_secs='30s',
moving_window_period=dt.timedelta(hours=1)
):
self.host = host
self.port = port
self.client_id = client_id

self.ib = IB()

def run(self):
self.ib.connect('127.0.0.1', 7497, clientId=101)
self.utc_timezone = tz.tzutc()
self.local_timezone = tz.tzlocal()

self.symbol_map = {}
self.historical_data = {} # of mid prices
self.df_hist = None

self.symbols = []

eurusd = Forex('EURUSD')
usdjpy = Forex('USDJPY')
# ticker = ib.reqTickByTickData(eurusd, 'BidAsk')
# ticker = ib.reqTickByTickData(usdjpy, 'BidAsk')
# ib.pendingTickersEvent += self.on_ticker
dt = ''
self.volatility_ratio = 1
self.beta = 0
self.moving_window_period = dt.timedelta(hours=1)
self.is_buy_signal, self.is_sell_signal = False, False

self.hist = self.get_historical_data(eurusd)
# print(hist)
def run(self, to_trade=[]):
self.ib.connect('127.0.0.1', 7497, clientId=101)

self.symbol_map = {str(contract): ident for (ident, contract) in to_trade}
contracts = [contract for (_, contract) in to_trade]
symbols = list(self.symbol_map.values())
self.symbols = symbols

self.request_market_data(eurusd)
self.df_hist = pd.DataFrame(columns=symbols)

# ib.pendingTickersEvent += self.on_ticker
self.request_historical_data(contracts)
self.request_market_data(contracts)

while self.ib.waitOnUpdate():
self.ib.sleep(1)
self.calculate_strategy_params()

def request_market_data(self, contracts):
for contract in contracts:
self.ib.reqMktData(contract)

def request_market_data(self, contract):
self.ib.reqMktData(contract)
self.ib.pendingTickersEvent += self.on_tick

def on_tick(self, tickers):
for ticker in tickers:
print(ticker.time, ticker.close)
dt_obj = pd.to_datetime(ticker.time)
self.hist.loc[dt_obj] = ticker.close
# self.hist.index = self.hist.index.tz_convert('Asia/Singapore')
print(self.hist.tail())
self.get_incoming_tick_data(ticker)

self.perform_trade_logic()

def perform_trade_logic(self):
"""
This part is the 'secret-sauce' where actual trades takes place.
My take is that great experience, good portfolio construction,
and together with robust backtesting will make your strategy viable.
GOOD PORTFOLIO CONTRUCTION CAN SAVE YOU FROM BAD RESEARCH,
BUT BAD PORTFOLIO CONSTRUCTION CANNOT SAVE YOU FROM GREAT RESEARCH
This trade logic uses volatility ratio and beta as our indicators.
- volatility ratio > 1 :: uptrend, volatility ratio < 1 :: downtrend
- beta is calculated as: mean(price A) / mean(price B)
We use the assumption that prive levels will mean-revert.
Expected price A = beta x price B
Consider other methods of identifying our trade logic:
- current trend
- current regime
- detect structural breaks
"""
self.calculate_signals()
self.calculate_positions()
self.check_and_enter_orders()

def check_and_enter_orders(self):
# if is_position_closed and is_sell_signal:
# print
# "=================================="
# print
# "OPEN SHORT POSIITON: SELL A BUY B"
# print
# "=================================="
# self.__place_spread_order(-self.trade_qty)
#
# elif is_position_closed and is_buy_signal:
# print
# "=================================="
# print
# "OPEN LONG POSIITON: BUY A SELL B"
# print
# "=================================="
# self.__place_spread_order(self.trade_qty)
#
# elif is_short and is_buy_signal:
# print
# "=================================="
# print
# "CLOSE SHORT POSITION: BUY A SELL B"
# print
# "=================================="
# self.__place_spread_order(self.trade_qty)
#
# elif is_long and is_sell_signal:
# print
# "=================================="
# print
# "CLOSE LONG POSITION: SELL A BUY B"
# print
# "=================================="
# self.__place_spread_order(-self.trade_qty)
pass

def calculate_positions(self):
# Use account position details
pass

# symbol_a = self.symbols[0]
# position = self.stocks_data[symbol_a].position
# is_position_closed, is_short, is_long = \
# (position == 0), (position < 0), (position > 0)
#
# upnl, rpnl = self.__calculate_pnls()
#
# # Display to terminal dynamically
# signal_text = \
# "BUY" if is_buy_signal else "SELL" if is_sell_signal else "NONE"
# console_output = '\r[%s] signal=%s, position=%s UPnL=%s RPnL=%s\r' % \
# (dt.datetime.now(), signal_text, position, upnl, rpnl)
# sys.stdout.write(console_output)
# sys.stdout.flush()

def calculate_strategy_params(self):
"""
Here, we are calculating beta and volatility ratio
for our signal indicators.
r = self.hist.resample('MS')
print(r)
Consider calculating other statistics here:
- stddevs of errs
- correlations
- co-integration
"""
[symbol_a, symbol_b] = self.symbols

resampled = self.df_hist.resample('30s').ffill().dropna()
mean = resampled.mean()
self.beta = mean[symbol_a] / mean[symbol_b]

stddevs = resampled.pct_change().dropna().std()
self.volatility_ratio = stddevs[symbol_a] / stddevs[symbol_b]

print('beta:', self.beta, 'vr:', self.volatility_ratio)

def calculate_signals(self):
self.trim_historical_data()

is_up_trend, is_down_trend = self.volatility_ratio > 1, self.volatility_ratio < 1
is_overbought, is_oversold = self.is_overbought_or_oversold()

# Our final trade signals
self.is_buy_signal = is_up_trend and is_oversold
self.is_sell_signal = is_down_trend and is_overbought

def trim_historical_data(self):
cutoff_time = dt.datetime.now(tz=self.local_timezone) - self.moving_window_period
self.df_hist = self.df_hist[self.df_hist.index >= cutoff_time]

def is_overbought_or_oversold(self):
[symbol_a, symbol_b] = self.symbols
leg_a_last_price = self.df_hist[symbol_a].dropna().values[-1]
leg_b_last_price = self.df_hist[symbol_b].dropna().values[-1]

expected_leg_a_price = leg_b_last_price * self.beta

is_overbought = leg_a_last_price < expected_leg_a_price # Cheaper than expected
is_oversold = leg_a_last_price > expected_leg_a_price # Higher than expected

return is_overbought, is_oversold

def get_incoming_tick_data(self, ticker):
symbol = self.get_symbol(ticker.contract)

dt_obj = self.convert_utc_datetime(ticker.time)
bid = ticker.bid
ask = ticker.ask
mid = (bid + ask) / 2

self.df_hist.loc[dt_obj, symbol] = mid

def request_historical_data(self, contracts):
for contract in contracts:
self.set_historical_data(contract)

def set_historical_data(self, contract):
symbol = self.get_symbol(contract)

def get_historical_data(self, contract):
bars = self.ib.reqHistoricalData(
contract,
endDateTime=time.strftime('%Y%m%d %H:%M:%S'),
Expand All @@ -63,19 +219,18 @@ def get_historical_data(self, contract):
useRTH=True,
formatDate=1
)
# df_obj = df_close(bars)
df_obj = pd.DataFrame(columns=['datetime', 'last'])
for bar in bars:
# dt_obj = dt.datetime.strptime(str(bar.date), '%Y-%m-%d %H:%M:%S')
dt_obj = pd.to_datetime(bar.date, format='%Y-%m-%d %H:%M:%s')
df_obj['datetime'] = dt_obj
df_obj['last'] = bar.close
#
# df_close = pd.DataFrame(columns=['close'])
# df_close.loc[df.index, 'close'] = df.close
df_obj = df_obj.set_index('datetime')
print(df_obj.head())
return df_obj

def on_ticker(self, ticker):
print('tiker:', ticker)
dt_obj = self.convert_local_datetime(bar.date)
self.df_hist.loc[dt_obj, symbol] = bar.close

def get_symbol(self, contract):
return self.symbol_map.get(str(contract))

def convert_utc_datetime(self, datetime):
utc = datetime.replace(tzinfo=self.utc_timezone)
local_time = utc.astimezone(self.local_timezone)
return pd.to_datetime(local_time)

def convert_local_datetime(self, datetime):
local_time = datetime.replace(tzinfo=self.local_timezone)
return pd.to_datetime(local_time)

0 comments on commit de5125a

Please sign in to comment.