Created
November 14, 2016 22:34
-
-
Save RayLty/d828ebe4391e564c3319680dbd0dc300 to your computer and use it in GitHub Desktop.
Foreign Exchange Algorithmic Trading method
This file contains hidden or 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
| from base import * | |
| import datetime | |
| class FXBot(BaseBot): | |
| def __init__(self): | |
| super(FXBot, self).__init__() | |
| self.contracts = [0]*8 # EURJPY/CHFJPY/USDCHF/EURCHF/EURCAD/EURUSD/USDJPY/USDCAD | |
| self.cash = [0]*8 # JPY/EUR/USD/CHF/CAD | |
| self.limits = [102000000, 900000, 1000000, 980000, 1300000] # JPY/EUR/USD/CHF/CAD | |
| self.bids = [] # EURJPY/CHFJPY/USDCHF/EURCHF/EURCAD/EURUSD/USDJPY/USDCAD | |
| self.asks = [] # EURJPY/CHFJPY/USDCHF/EURCHF/EURCAD/EURUSD/USDJPY/USDCAD | |
| self.tickers = ['EURJPY', 'CHFJPY', 'USDCHF', 'EURCHF', 'EURCAD', 'EURUSD', 'USDJPY', 'USDCAD'] | |
| self.bestBids = [0]*8 | |
| self.bestAsks = [0]*8 | |
| self.bestQuantityBids = [0]*8 | |
| self.bestQuantityAsks = [0]*8 | |
| self.lastActionTime = 0 | |
| self.currentTime = 0 | |
| def update_state(self, msg): | |
| if self.options.get('verbose'): | |
| print(msg) | |
| message_type = msg.get('message_type') | |
| orders = [] | |
| if msg.get('elapsed_time'): | |
| self.currentTime = msg.get('elapsed_time') | |
| if len(self.bids) > 0 and self.currentTime - self.lastActionTime >= 1: | |
| self.lastActionTime = self.currentTime | |
| self.bestBidAndQuantity() | |
| self.bestAskAndQuantity() | |
| orders.extend(self.arbitrage()) | |
| if len(orders) > 0: | |
| action = { | |
| 'message_type': 'MODIFY ORDERS', | |
| 'orders': orders, | |
| } | |
| orders = dumps(action) | |
| if message_type == 'ACK REGISTER': | |
| market_states = msg['market_states'] | |
| self.bids = [market_states['EURJPY']['bids'], market_states['CHFJPY']['bids'], \ | |
| market_states['USDCHF']['bids'], market_states['EURCHF']['bids'],\ | |
| market_states['EURCAD']['bids'], market_states['EURUSD']['bids'],\ | |
| market_states['USDJPY']['bids'], market_states['USDCAD']['bids']] | |
| self.asks = [market_states['EURJPY']['asks'], market_states['CHFJPY']['asks'], \ | |
| market_states['USDCHF']['asks'], market_states['EURCHF']['asks'],\ | |
| market_states['EURCAD']['asks'], market_states['EURUSD']['asks'],\ | |
| market_states['USDJPY']['asks'], market_states['USDCAD']['asks']] | |
| positions = msg['trader_state']['positions'] | |
| self.contracts = [positions['EURJPY'], positions['CHFJPY'], \ | |
| positions['USDCHF'], positions['EURCHF'], positions['EURCAD'], \ | |
| positions['EURUSD'], positions['USDJPY'], positions['USDCAD']] | |
| cash = msg['trader_state']['cash'] | |
| self.cash = [cash['JPY'], cash['EUR'], cash['USD'], cash['CHF'], cash['CAD']] | |
| print('register') | |
| elif message_type == 'TRADER UPDATE': | |
| positions = msg['trader_state']['positions'] | |
| cash = msg['trader_state']['cash'] | |
| self.contracts = [positions['EURJPY'], positions['CHFJPY'], \ | |
| positions['USDCHF'], positions['EURCHF'], positions['EURCAD'], \ | |
| positions['EURUSD'], positions['USDJPY'], positions['USDCAD']] | |
| self.cash = [cash['JPY'], cash['EUR'], cash['USD'], cash['CHF'], cash['CAD']] | |
| elif message_type == 'MARKET UPDATE': | |
| #print('market update') | |
| ticker = msg['market_state']['ticker'] | |
| bids = msg['market_state']['bids'] | |
| asks = msg['market_state']['asks'] | |
| if ticker == 'EURJPY': | |
| self.bids[0] = bids | |
| self.asks[0] = asks | |
| if ticker == 'CHFJPY': | |
| self.bids[1] = bids | |
| self.asks[1] = asks | |
| if ticker == 'USDCHF': | |
| self.bids[2] = bids | |
| self.asks[2] = asks | |
| if ticker == 'EURCHF': | |
| self.bids[3] = bids | |
| self.asks[3] = asks | |
| if ticker == 'EURCAD': | |
| self.bids[4] = bids | |
| self.asks[4] = asks | |
| if ticker == 'EURUSD': | |
| self.bids[5] = bids | |
| self.asks[5] = asks | |
| if ticker == 'USDJPY': | |
| self.bids[6] = bids | |
| self.asks[6] = asks | |
| if ticker == 'USDCAD': | |
| self.bids[7] = bids | |
| self.asks[7] = asks | |
| elif message_type == 'NEWS': | |
| print('News update:') | |
| print(msg) | |
| elif message_type == 'TRADE': | |
| pass | |
| elif message_type == 'PING': | |
| pass | |
| elif message_type == 'ACK MODIFY ORDERS': | |
| #print(msg) | |
| pass | |
| else: | |
| print('Other message:') | |
| print(msg) | |
| if len(orders) > 0: | |
| return orders | |
| else: | |
| return None | |
| def bestBidAndQuantity(self): | |
| for index in range(8): | |
| bids = self.bids[index] | |
| highestBid = 0.0 | |
| quantity = 0 | |
| for bid in bids: | |
| if float(bid) > highestBid: | |
| highestBid = float(bid) | |
| quantity = bids[bid] | |
| self.bestBids[index] = highestBid | |
| self.bestQuantityBids[index] = quantity | |
| def bestAskAndQuantity(self): | |
| for index in range(8): | |
| asks = self.asks[index] | |
| lowestAsk = 1000.0 | |
| quantity = 0 | |
| for ask in asks: | |
| if float(ask) < lowestAsk: | |
| lowestAsk = float(ask) | |
| quantity = asks[ask] | |
| self.bestAsks[index] = lowestAsk | |
| self.bestQuantityAsks[index] = quantity | |
| def idxBaseQuote(self, index): | |
| if index == 0: | |
| return (1, 0) | |
| if index == 1: | |
| return (3, 0) | |
| if index == 2: | |
| return (2, 3) | |
| if index == 3: | |
| return (1, 3) | |
| if index == 4: | |
| return (1, 4) | |
| if index == 5: | |
| return (1, 2) | |
| if index == 6: | |
| return (2, 0) | |
| if index == 7: | |
| return (2, 4) | |
| def calculateQuantities(self, idx1, idx2, idx3, direction): | |
| multiplier = -1.05 | |
| (base1, quote1) = self.idxBaseQuote(idx1) | |
| (base2, quote2) = self.idxBaseQuote(idx2) | |
| (base3, quote3) = self.idxBaseQuote(idx3) | |
| quantity1 = 0 | |
| quantity2 = 0 | |
| quantity3 = 0 | |
| if direction == -1: # short | |
| quantity1 = self.bestQuantityBids[idx1] | |
| quantity2 = self.bestQuantityBids[idx2] | |
| quantity3 = self.bestQuantityAsks[idx3] | |
| exchange1 = self.bestBids[idx1] | |
| exchange2 = self.bestBids[idx2] | |
| exchange3 = self.bestAsks[idx3] | |
| quantity1_limit = min(self.limits[base1]+self.cash[base1], 1.0/exchange1*(self.limits[quote1]-self.cash[quote1]), 1000) | |
| quantity2_limit = min(self.limits[base2]+self.cash[base2], 1.0/exchange2*(self.limits[quote2]-self.cash[quote2]), 1000) | |
| quantity3_limit = min(self.limits[base3]-self.cash[base3], 1.0/exchange3*(self.limits[quote3]+self.cash[quote3]), 1000) | |
| quantity1 = min(quantity1, quantity1_limit) | |
| quantity2 = min(quantity2, quantity2_limit) | |
| quantity3 = min(quantity3, quantity3_limit) | |
| quantity1 = min(quantity1, quantity3, quantity2/exchange1) | |
| quantity1 = int(quantity1*multiplier) | |
| quantity2 = int(quantity1*exchange1) | |
| quantity3 = int(-quantity1) | |
| else: | |
| quantity1 = self.bestQuantityAsks[5] | |
| quantity2 = self.bestQuantityAsks[7] | |
| quantity3 = self.bestQuantityBids[4] | |
| exchange1 = self.bestAsks[idx1] | |
| exchange2 = self.bestAsks[idx2] | |
| exchange3 = self.bestBids[idx3] | |
| quantity1_limit = min(self.limits[base1]-self.cash[base1], 1.0/exchange1*(self.limits[quote1]+self.cash[quote1]), 1000) | |
| quantity2_limit = min(self.limits[base2]-self.cash[base2], 1.0/exchange2*(self.limits[quote2]+self.cash[quote2]), 1000) | |
| quantity3_limit = min(self.limits[base3]+self.cash[base3], 1.0/exchange3*(self.limits[quote3]-self.cash[quote3]), 1000) | |
| quantity1 = min(quantity1, quantity1_limit) | |
| quantity2 = min(quantity2, quantity2_limit) | |
| quantity3 = min(quantity3, quantity3_limit) | |
| quantity1 = min(quantity1, quantity3, quantity2/exchange1) | |
| quantity1 = int(-quantity1*multiplier) | |
| quantity2 = int(quantity1*exchange1) | |
| quantity3 = int(-quantity1) | |
| if abs(quantity1) < 10 or abs(quantity2) < 10 or abs(quantity3) < 10: | |
| quantity1 = 0 | |
| quantity2 = 0 | |
| quantity3 = 0 | |
| return (quantity1, quantity2, quantity3) | |
| def arbitrage(self): | |
| orders = [] | |
| order_quantities = [0]*8 # EURJPY/CHFJPY/USDCHF/EURCHF/EURCAD/EURUSD/USDJPY | |
| # EURUSD*USDCAD=EURCAD | |
| if self.bestBids[5]*self.bestBids[7] > self.bestAsks[4]: | |
| #print('arb1') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(5, 7, 4, -1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[5]*self.bestBids[7]-self.bestAsks[4])/self.bestBids[7]: | |
| if abs(order_quantities[5] + quantity1) >= 10 and abs(order_quantities[7] + quantity2) >= 10 and abs(order_quantities[4] + quantity3) >= 10: | |
| order_quantities[5] += quantity1 | |
| order_quantities[7] += quantity2 | |
| order_quantities[4] += quantity3 | |
| if self.bestAsks[5]*self.bestAsks[7] < self.bestBids[4]: | |
| #print('arb2') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(5, 7, 4, 1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[4]-self.bestAsks[5]*self.bestAsks[7])/self.bestAsks[7]: | |
| if abs(order_quantities[5] + quantity1) >= 10 and abs(order_quantities[7] + quantity2) >= 10 and abs(order_quantities[4] + quantity3) >= 10: | |
| order_quantities[5] += quantity1 | |
| order_quantities[7] += quantity2 | |
| order_quantities[4] += quantity3 | |
| # EURUSD*USDCHF = EURCHF | |
| if self.bestBids[5]*self.bestBids[2] > self.bestAsks[3]: | |
| #print('arb3') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(5, 2, 3, -1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[5]*self.bestBids[2]-self.bestAsks[3])/self.bestBids[2]: | |
| if abs(order_quantities[5] + quantity1) >= 10 and abs(order_quantities[2] + quantity2) >= 10 and abs(order_quantities[3] + quantity3) >= 10: | |
| order_quantities[5] += quantity1 | |
| order_quantities[2] += quantity2 | |
| order_quantities[3] += quantity3 | |
| if self.bestAsks[5]*self.bestAsks[2] < self.bestBids[3]: | |
| #print('arb4') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(5, 2, 3, 1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[3]-self.bestAsks[5]*self.bestAsks[2])/self.bestAsks[2]: | |
| if abs(order_quantities[5] + quantity1) >= 10 and abs(order_quantities[2] + quantity2) >= 10 and abs(order_quantities[3] + quantity3) >= 10: | |
| order_quantities[5] += quantity1 | |
| order_quantities[2] += quantity2 | |
| order_quantities[3] += quantity3 | |
| # EURUSD*USDJPY=EURJPY | |
| if self.bestBids[5]*self.bestBids[6] > self.bestAsks[0]: | |
| #print('arb5') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(5, 6, 0, -1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[5]*self.bestBids[6]-self.bestAsks[0])/self.bestBids[6]: | |
| if abs(order_quantities[5] + quantity1) >= 10 and abs(order_quantities[6] + quantity2) >= 10 and abs(order_quantities[0] + quantity3) >= 10: | |
| order_quantities[5] += quantity1 | |
| order_quantities[6] += quantity2 | |
| order_quantities[0] += quantity3 | |
| if self.bestAsks[5]*self.bestAsks[6] < self.bestBids[0]: | |
| #print('arb6') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(5, 6, 0, 1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[0]-self.bestAsks[5]*self.bestAsks[6])/self.bestAsks[6]: | |
| if abs(order_quantities[5] + quantity1) >= 10 and abs(order_quantities[6] + quantity2) >= 10 and abs(order_quantities[0] + quantity3) >= 10: | |
| order_quantities[5] += quantity1 | |
| order_quantities[6] += quantity2 | |
| order_quantities[0] += quantity3 | |
| # USDCHF*CHFJPY=USDJPY | |
| if self.bestBids[2]*self.bestBids[1] > self.bestAsks[6]: | |
| #print('arb7') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(2, 1, 6, -1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[2]*self.bestBids[1]-self.bestAsks[6])/self.bestBids[6]: | |
| if abs(order_quantities[2] + quantity1) >= 10 and abs(order_quantities[1] + quantity2) >= 10 and abs(order_quantities[6] + quantity3) >= 10: | |
| order_quantities[2] += quantity1 | |
| order_quantities[1] += quantity2 | |
| order_quantities[6] += quantity3 | |
| if self.bestAsks[2]*self.bestAsks[1] < self.bestBids[6]: | |
| #print('arb8') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(2, 1, 6, 1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[6]-self.bestAsks[2]*self.bestAsks[1])/self.bestAsks[6]: | |
| if abs(order_quantities[2] + quantity1) >= 10 and abs(order_quantities[1] + quantity2) >= 10 and abs(order_quantities[6] + quantity3) >= 10: | |
| order_quantities[2] += quantity1 | |
| order_quantities[1] += quantity2 | |
| order_quantities[6] += quantity3 | |
| # EURCHF*CHFJPY=EURJPY | |
| if self.bestBids[3]*self.bestBids[1] > self.bestAsks[0]: | |
| #print('arb9') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(3, 1, 0, -1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[3]*self.bestBids[1]-self.bestAsks[0])/self.bestBids[6]: | |
| if abs(order_quantities[3] + quantity1) >= 10 and abs(order_quantities[1] + quantity2) >= 10 and abs(order_quantities[0] + quantity3) >= 10: | |
| order_quantities[3] += quantity1 | |
| order_quantities[1] += quantity2 | |
| order_quantities[0] += quantity3 | |
| if self.bestAsks[3]*self.bestAsks[1] < self.bestBids[0]: | |
| #print('arb10') | |
| (quantity1, quantity2, quantity3) = self.calculateQuantities(3, 1, 0, 1) | |
| #if 0.005*(abs(quantity1)+abs(quantity2)+abs(quantity3)) < 1.0*abs(quantity1)*(self.bestBids[0]-self.bestAsks[3]*self.bestAsks[1])/self.bestAsks[6]: | |
| if abs(order_quantities[3] + quantity1) >= 10 and abs(order_quantities[1] + quantity2) >= 10 and abs(order_quantities[0] + quantity3) >= 10: | |
| order_quantities[3] += quantity1 | |
| order_quantities[1] += quantity2 | |
| order_quantities[0] += quantity3 | |
| for i in range(len(order_quantities)): | |
| if order_quantities[i]: | |
| ticker = self.tickers[i] | |
| buy = order_quantities[i] > 0 | |
| quantity = abs(order_quantities[i]) | |
| orders.append({ | |
| 'ticker': ticker, | |
| 'buy': buy, | |
| 'quantity': quantity, | |
| }) | |
| # print('trade: ', order_quantities) | |
| # print('bids: ', self.bestBids) | |
| # print('asks: ', self.bestAsks) | |
| # print('quantitybid: ', self.bestQuantityBids) | |
| # print('quantityask: ', self.bestQuantityAsks) | |
| # if (datetime.datetime.now() - self.last_time) > datetime.timedelta(microseconds = 30) | |
| # self.last_time = datetime.datetime.now() | |
| return orders | |
| def process(self, msg): | |
| orders = super(FXBot, self).process(msg) | |
| #print(orders) | |
| return orders | |
| if __name__ == '__main__': | |
| bot = FXBot() | |
| print "options are", bot.options.data | |
| for t in bot.makeThreads(): | |
| t.daemon = True | |
| t.start() | |
| while not bot.done: | |
| sleep(0.03) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment