Ejemplo n.º 1
0
 def __init__(self, num_traders: int, initial_price: float,
              starting_cash: int, initial_supply: int,
              order_difference_tolerance: float, trading_fee: float):
     self.initial_price = initial_price
     self.starting_shares = int(initial_supply / num_traders)
     self.traders = [
         Agent(trading_fee, self.starting_shares, starting_cash, i)
         for i in range(num_traders)
     ]
     self.supply = initial_supply
     self.order_book = OrderBook(initial_price, order_difference_tolerance)
     self.price_history = []
     self.starting_cash = starting_cash
Ejemplo n.º 2
0
 def setUp(self):
     self.lp_2_gateway = deque()
     self.ob_2_ts = deque()
     self.ts_2_om = deque()
     self.ms_2_om = deque()
     self.om_2_ts = deque()
     self.gw_2_om = deque()
     self.om_2_gw = deque()
     self.lp = LiquidityProvider(self.lp_2_gateway)
     self.ob = OrderBook(self.lp_2_gateway, self.ob_2_ts)
     self.ts = TradingStrategy(self.ob_2_ts, self.ts_2_om, self.om_2_ts)
     self.ms = MarketSimulator(self.om_2_gw, self.gw_2_om)
     self.om = OrderManager(self.ts_2_om, self.om_2_ts,
                            self.om_2_gw, self.gw_2_om)
Ejemplo n.º 3
0
    def __init__(self, product_id, coinBaseExchangeAuth, updateInterval,
                 sizePriceTable):
        self.product_id = product_id
        self.updateInterval = updateInterval
        self.sizePriceTable = sizePriceTable
        self.priceTable = [-1] * sizePriceTable
        self.localOrderBook = {}

        self.smoothedAverageGain = -1
        self.smoothedAverageLoss = -1

        self.updating = False
        self.scheduler = BackgroundScheduler()
        self.Ticker = Ticker(coinBaseExchangeAuth)
        self.OrderBook = OrderBook(coinBaseExchangeAuth)
Ejemplo n.º 4
0
class Market:
    def __init__(self, num_traders: int, initial_price: float,
                 starting_cash: int, initial_supply: int,
                 order_difference_tolerance: float, trading_fee: float):
        self.initial_price = initial_price
        self.starting_shares = int(initial_supply / num_traders)
        self.traders = [
            Agent(trading_fee, self.starting_shares, starting_cash, i)
            for i in range(num_traders)
        ]
        self.supply = initial_supply
        self.order_book = OrderBook(initial_price, order_difference_tolerance)
        self.price_history = []
        self.starting_cash = starting_cash

    def open_market(self, total_turns: int):
        for i in range(total_turns):
            # close previous rounds trades
            prices = self.order_book.match_trades(self.traders)
            self.price_history.append(prices)

            # open new trades for every trader
            for trader in self.traders:
                order = trader.trade(self.supply, self.order_book,
                                     self.price_history)
                if order:
                    self.order_book.add_order(order)
                    trader.account_values.append((self.order_book.price *
                                                  trader.shares) + trader.cash)

            # purge dead traders
            self.traders = [
                trader for trader in self.traders if trader.trading
            ]

            # quit if there are no more players
            if len(self.traders) == 0:
                return

    def close_market(self):
        for trader in self.traders:
            begin = trader.account_values[0]
            end = int(trader.account_values[len(trader.account_values) - 1])
            print(f"trader: {trader.id}"
                  f"\tstarting cash: ${self.starting_cash}"
                  f"\tstarting shares: {self.starting_shares}"
                  f"\t\tending cash: ${int(trader.cash)}"
                  f"\t\tending shares: {int(trader.shares)}")
    def get_orderbook(self):
        orderbook = {}
        
        cur_UTC = datetime.datetime.utcnow()

        orderbook['date'] = get_YYYYMMDD(cur_UTC)
        orderbook['time'] = get_HHMMSSmmm(cur_UTC)

        orderbook['prices'] = [] 
        orderbook['volumes'] = []      

        #only even values are applicable
        half_size = 50

        sell_side = sorted(self.L2['Sell'].items())
        buy_side = sorted(self.L2['Buy'].items(), reverse=True)

        buy_side_len = len(buy_side)
        sell_side_len = len(sell_side)

        if buy_side_len < half_size or sell_side_len < half_size:
            return None
        
        for idx in reversed(range(0, half_size)):
            orderbook['prices'].append(buy_side[idx][0])
            orderbook['volumes'].append(buy_side[idx][1])

        for idx in range(0, half_size):
            orderbook['prices'].append(sell_side[idx][0])
            orderbook['volumes'].append(sell_side[idx][1])

        return OrderBook(orderbook)
Ejemplo n.º 6
0
    def __init__(self,
                 tax_rate=0.26375,
                 broker=comdirect(),
                 orderbook=OrderBook()):
        """ Initialize all depots.

            All depots utilize the same Broker's tax rate,
            comdirect as default broker and orderbook.
        """
        Broker.setTAXRate(tax_rate)
        self.__broker = broker
        self.__orderbook = orderbook
Ejemplo n.º 7
0
def depot():
    """Sample pytest fixture.

    See more at: http://doc.pytest.org/en/latest/fixture.html
    """
    # import requests
    # return requests.get('https://github.com/audreyr/cookiecutter-pypackage')
    """ First of all, it builds the Depot """
    Broker.setTAXRate(0.25)
    broker = comdirect()
    orderbook = OrderBook()
    depot = Depot(stock='APPL', broker=broker, orderbook=orderbook)

    return depot
Ejemplo n.º 8
0
def speed_test():
    orderbook = OrderBook("")
    for i in range(1000):
        j = randrange(0, 2)
        if j == 0:
            order = Order(float(random.uniform(1.0, 5.0)),
                          float(randrange(0, 3)), 0.0, BID)
            orderbook.add_modify_delete(order, BID)
        else:
            order = Order(float(random.uniform(5.0, 9.0)),
                          float(randrange(0, 3)), 0.0, ASK)
            orderbook.add_modify_delete(order, ASK)

        print(orderbook)
Ejemplo n.º 9
0
    def processUpdate(self, new):
        diff = {}
        snapshot = {}
        if getSHA512Hash(json.dumps(self.snapshot)) == getSHA512Hash(
                json.dumps(new)):
            #             print("No change")
            return

        for key1 in self.snapshot:
            snapshot[key1] = self.snapshot[key1]
            if key1 in new and key1 != Fields.ORDERBOOK:
                if self.snapshot[key1] != new[key1]:
                    diff[key1] = new[key1]

        for key2 in new:
            snapshot[key2] = new[key2]
            if key2 not in self.snapshot and key2 != Fields.ORDERBOOK:
                diff[key2] = new[key2]

        oldobhash = ''
        newobhash = ''

        if Fields.ORDERBOOK in self.snapshot:
            oldobhash = getSHA512Hash(
                json.dumps(self.snapshot[Fields.ORDERBOOK]))
        if Fields.ORDERBOOK in new:
            newobhash = getSHA512Hash(json.dumps(new[Fields.ORDERBOOK]))

        if newobhash != oldobhash:
            diff['OrderBookChanges'] = True

        if len(self.snapshot) > 0:
            self.history.append(self.snapshot)
        self.snapshot = snapshot
        self.changes.append(diff)

        globalAtomicPrinter.printit(diff)

        if Fields.FLAG_OB_CHANGED in diff:
            globalAtomicPrinter.printit(
                snapshot[Fields.ORDERBOOK])  #Debug only
            ob = OrderBook(snapshot[Fields.ORDERBOOK])

            # Business logic by chanaka....
            # Total quantity at the top level of orderbook
            l1Qty = ob.buy.qty[0] + ob.sell.qty[0]

            # Price difference between top level buy order and sell order
            l1Diff = ob.sell.price[0] - ob.buy.price[0]

            #Number of active buying quantity and selling quantity - first 6 levels considered and weight given for each level
            buyerAmt = ob.buy.getWeightedQty(
                [1, .9, .8, .7, .6, .5]
            )  #float(snapshot[Fields.ORDERBOOK][Fields.BUYSIDE_QTY][0].replace(",", "")) + float(snapshot[Fields.ORDERBOOK][Fields.BUYSIDE_QTY][1].replace(",", "")) * .9 + float(snapshot[Fields.ORDERBOOK][Fields.BUYSIDE_QTY][2].replace(",", "")) * .8 + float(snapshot[Fields.ORDERBOOK][Fields.BUYSIDE_QTY][3].replace(",", ""))* .7 + float(snapshot[Fields.ORDERBOOK][Fields.BUYSIDE_QTY][4].replace(",", "")) * .6 + float(snapshot[Fields.ORDERBOOK][Fields.BUYSIDE_QTY][5].replace(",", "")) * .5
            sellerAmt = ob.sell.getWeightedQty(
                [1, .9, .8, .7, .6, .5]
            )  #float(snapshot[Fields.ORDERBOOK][Fields.SELLSIDE_QTY][0].replace(",", "")) + float(snapshot[Fields.ORDERBOOK][Fields.SELLSIDE_QTY][1].replace(",", "")) * .9 + float(snapshot[Fields.ORDERBOOK][Fields.SELLSIDE_QTY][2].replace(",", "")) * .8 + float(snapshot[Fields.ORDERBOOK][Fields.SELLSIDE_QTY][3].replace(",", ""))* .7 + float(snapshot[Fields.ORDERBOOK][Fields.SELLSIDE_QTY][4].replace(",", "")) * .6 + float(snapshot[Fields.ORDERBOOK][Fields.SELLSIDE_QTY][5].replace(",", "")) * .5

            # Buy/Sell ratio based on above values - much better than whats shown in commsec webpage
            BuySellRatio = buyerAmt / sellerAmt
            globalAtomicPrinter.printit(BuySellRatio)

            # Trending price calculated based on top level - this is a virtual value ATM for the stock - much better than last traded price
            l1trendPrice = ob.buy.price[0] + l1Diff * ob.buy.qty[0] / l1Qty

            # Trending price calculated based on six levels - this is the final virtual value ATM for the stock
            finalTrendPrice = ob.buy.price[0] + l1Diff * buyerAmt / (buyerAmt +
                                                                     sellerAmt)

            globalAtomicPrinter.printit(snapshot[Fields.SECURITY_CODE] +
                                        " ->" + str(BuySellRatio) + ", " +
                                        str(l1trendPrice) + ", " +
                                        str(finalTrendPrice))

            # If at least one history values available, go and calculate differences
            if len(self.BuySellRatio) > 0 and len(self.l1trendPrice) and len(
                    self.finalTrendPrice):
                # Calculate the change for each indicator
                BuySellRatioDiff = BuySellRatio - self.BuySellRatio[-1]
                l1TrendDiff = l1trendPrice - self.l1trendPrice[-1]
                FinalTrendDiff = finalTrendPrice - self.finalTrendPrice[-1]

                # Save these differences in self variables for the use of next iteration
                self.BuySellRatioDiff.append(BuySellRatioDiff)
                self.l1trendPriceDiff.append(l1TrendDiff)
                self.finalTrendPriceDiff.append(FinalTrendDiff)

            # Add calculated values for self variables for the use in next iteration
            self.BuySellRatio.append(BuySellRatio)
            self.l1trendPrice.append(l1trendPrice)
            self.finalTrendPrice.append(finalTrendPrice)

            # Define weight sequences for weighted average calculations
            ma_weights10 = [.1, .2, .3, .4, .5, .6, .7, .8, .9, 1]
            ma_weights05 = [.2, .4, .6, .8, 1]

            # Analyse trand and decide buying/ selling part starts here

            # Go inside and calculate trends when we have at least 10 history records
            if len(self.BuySellRatioDiff) > 9:
                # Calculate weighted average for all three indicators, for last 5 iterations and last 10 iterations
                wa_bs_ratio_diff10 = numpy.average(self.BuySellRatioDiff[-10:],
                                                   weights=ma_weights10)
                wa_bs_ratio_diff05 = numpy.average(self.BuySellRatioDiff[-5:],
                                                   weights=ma_weights05)

                wa_l1trend_diff10 = numpy.average(self.l1trendPriceDiff[-10:],
                                                  weights=ma_weights10)
                wa_l1trend_diff05 = numpy.average(self.l1trendPriceDiff[-5:],
                                                  weights=ma_weights05)

                wa_finalTrend_diff10 = numpy.average(
                    self.finalTrendPriceDiff[-10:], weights=ma_weights10)
                wa_finalTrend_diff05 = numpy.average(
                    self.finalTrendPriceDiff[-5:], weights=ma_weights05)

                # If all three indicators are positive and weighted average of last 5 is better than last 10, things are moving in right direction. Better buy it
                if (wa_bs_ratio_diff10 > 0
                        and wa_bs_ratio_diff05 > wa_bs_ratio_diff10
                        and wa_l1trend_diff10 > 0
                        and wa_l1trend_diff05 > wa_l1trend_diff10
                        and wa_finalTrend_diff10 > 0
                        and wa_finalTrend_diff05 > wa_finalTrend_diff10):
                    globalAtomicPrinter.printit(
                        "*************************************************************************************************************************************************************************************************************"
                    )
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit(
                        "BUY IT, HURRY UP, Its going up.... Hooray.....  Stock - "
                        + snapshot["SECURITY_CODE"] + "  Limit - " +
                        snapshot["ORDERBOOK"]["BuySidePrice"][0] +
                        "  Market - " +
                        snapshot["ORDERBOOK"]["SellSidePrice"][0])
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit(
                        "*************************************************************************************************************************************************************************************************************"
                    )

                else:
                    globalAtomicPrinter.printit(
                        snapshot[Fields.SECURITY_CODE] + " do NOT BUY")
                # If all indicators are negative and weighted average of last 5 is worse than last 10, this one is going down. Better to sell and save our asses.
                if (wa_bs_ratio_diff10 < 0
                        and wa_bs_ratio_diff05 < wa_bs_ratio_diff10
                        and wa_l1trend_diff10 < 0
                        and wa_l1trend_diff05 < wa_l1trend_diff10
                        and wa_finalTrend_diff10 < 0
                        and wa_finalTrend_diff05 < wa_finalTrend_diff10):
                    globalAtomicPrinter.printit(
                        "*************************************************************************************************************************************************************************************************************"
                    )
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit(
                        "SELL IT, HURRY UP, OMG Its going down.... :(.....  Stock - "
                        + snapshot["SECURITY_CODE"] + "  Limit - " +
                        snapshot["ORDERBOOK"]["SellSidePrice"][0] +
                        "  Market - " +
                        snapshot["ORDERBOOK"]["BuySidePrice"][0])
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit("*")
                    globalAtomicPrinter.printit(
                        "*************************************************************************************************************************************************************************************************************"
                    )

                else:
                    globalAtomicPrinter.printit(
                        snapshot[Fields.SECURITY_CODE] + " do NOT SELL")
            else:
                globalAtomicPrinter.printit(
                    snapshot[Fields.SECURITY_CODE] +
                    " not enough ratios accumilated. Current count is " +
                    str(len(self.BuySellRatioDiff)))
Ejemplo n.º 10
0
def main():
    reader = Reader('1.txt')
    order_book = OrderBook()
    for el in reader.get_from_file():
        order_book.parse_operation(*el)
    print(order_book.get_total())
Ejemplo n.º 11
0
            self.__open_market()
            self.TIMELINE.newDay = False

    def update_price(self, mid_price):
        #应该用索引就可以!
        #接下去修改orderbook 加入中间价的计算!
        self.latest_price = mid_price
        self.price_stream.append(mid_price)


if __name__ == '__main__':
    logging.basicConfig(filename='ham.log', filemode='w+', level=logging.INFO)
    timeline = TimeLine()
    fv = fvalue(timeline)
    CONTEXT = context(timeline, fv)
    orderbook = OrderBook(CONTEXT)
    agentpool = AgentPool(5000, CONTEXT)

    #    count = 0
    #    for i in range():
    #        agentpool.select_an_agent()
    #        if CONTEXT.latest_quote:
    #            print(i)
    #        orderbook.scan_quote()
    agentidlist = list(range(5000))
    for rd in range(1, 5000):
        if CONTEXT.TIMELINE.inTrade:
            np.random.shuffle(agentidlist)
            for agentid in agentidlist:
                if agentpool.agent_activate(agentid):
                    #                    print(orderbook.bookset['bid'])
Ejemplo n.º 12
0
# coding=utf-8
import pytest
from OrderBook import OrderBook, processOrder, getBestBidAndAsk

ob = OrderBook()


def test__add_order():
    """
    This is a perfect case!
    New order gets created successfully.
    """

    processOrder(ob, '1568390243|abbb11|a|AAPL|B|209.00000|100')
    assert getBestBidAndAsk(ob, 'AAPL') == '209.0/0.0'


def test__cancel_order():
    """
    This is a perfect case!
    Order gets cancelled properly.
    """
    processOrder(ob, '1568390243|abbb114|a|AAPL1|B|209.00000|100')
    # Intentional incorrect cancel order as other colmns are ignores
    processOrder(ob, '1568390245|abbb114|c|AAPL1|B|209.00000|100')
    assert getBestBidAndAsk(ob, 'AAPL1') == '0.0/0.0'


def test__cancel_order_error():
    """
    Cancelling an order that does not exist.
Ejemplo n.º 13
0
class TradingSimulationTest(unittest.TestCase):
    def setUp(self):
        self.lp_2_gateway = deque()
        self.ob_2_ts = deque()
        self.ts_2_om = deque()
        self.ms_2_om = deque()
        self.om_2_ts = deque()
        self.gw_2_om = deque()
        self.om_2_gw = deque()
        self.lp = LiquidityProvider(self.lp_2_gateway)
        self.ob = OrderBook(self.lp_2_gateway, self.ob_2_ts)
        self.ts = TradingStrategy(self.ob_2_ts, self.ts_2_om, self.om_2_ts)
        self.ms = MarketSimulator(self.om_2_gw, self.gw_2_om)
        self.om = OrderManager(self.ts_2_om, self.om_2_ts,
                               self.om_2_gw, self.gw_2_om)

    def test_add_liquidity(self):
        # Order sent from the exchange to the trading system
        order1 = {
            'id': 1,
            'price': 219,
            'quantity': 10,
            'side': 'bid',
            'action': 'new'
        }
        self.lp.insert_manual_order(order1)
        self.assertEqual(len(self.lp_2_gateway), 1)
        self.ob.handle_order_from_gateway()
        self.assertEqual(len(self.ob_2_ts), 1)
        self.ts.handle_input_from_bb()
        self.assertEqual(len(self.ts_2_om), 0)
        order2 = {
            'id': 2,
            'price': 218,
            'quantity': 10,
            'side': 'ask',
            'action': 'new'
        }
        self.lp.insert_manual_order(order2.copy())
        self.assertEqual(len(self.lp_2_gateway), 1)
        self.ob.handle_order_from_gateway()
        self.assertEqual(len(self.ob_2_ts), 1)
        self.ts.handle_input_from_bb()
        self.assertEqual(len(self.ts_2_om), 2)
        self.om.handle_input_from_ts()
        self.assertEqual(len(self.ts_2_om), 1)
        self.assertEqual(len(self.om_2_gw), 1)
        self.om.handle_input_from_ts()
        self.assertEqual(len(self.ts_2_om), 0)
        self.assertEqual(len(self.om_2_gw), 2)
        self.ms.handle_order_from_gw()
        self.assertEqual(len(self.gw_2_om), 1)
        self.ms.handle_order_from_gw()
        self.assertEqual(len(self.gw_2_om), 2)
        self.om.handle_input_from_market()
        self.om.handle_input_from_market()
        self.assertEqual(len(self.om_2_ts), 2)
        self.ts.handle_response_from_om()
        self.assertEqual(self.ts.get_pnl(), 0)
        self.ms.fill_all_orders()
        self.assertEqual(len(self.gw_2_om), 2)
        self.om.handle_input_from_market()
        self.om.handle_input_from_market()
        self.assertEqual(len(self.om_2_ts), 3)
        self.ts.handle_response_from_om()
        self.assertEqual(len(self.om_2_ts), 2)
        self.ts.handle_response_from_om()
        self.assertEqual(len(self.om_2_ts), 1)
        self.ts.handle_response_from_om()
        self.assertEqual(len(self.om_2_ts), 0)
        self.assertEqual(self.ts.get_pnl(), 10)
Ejemplo n.º 14
0
class MarketData:
    #product_id is a String such as "LTC-USD"
    #updateInterval is an int
    #sizePriceTable is an int

    def __init__(self, product_id, coinBaseExchangeAuth, updateInterval,
                 sizePriceTable):
        self.product_id = product_id
        self.updateInterval = updateInterval
        self.sizePriceTable = sizePriceTable
        self.priceTable = [-1] * sizePriceTable
        self.localOrderBook = {}

        self.smoothedAverageGain = -1
        self.smoothedAverageLoss = -1

        self.updating = False
        self.scheduler = BackgroundScheduler()
        self.Ticker = Ticker(coinBaseExchangeAuth)
        self.OrderBook = OrderBook(coinBaseExchangeAuth)

    #will auto update the price table every updateInterval seconds
    def runMarketData(self):
        self.scheduler.start()
        self.Ticker.open([self.product_id])
        self.OrderBook.open([self.product_id])
        self.scheduler.add_job(self.updateAll,
                               'interval',
                               seconds=self.updateInterval)
        self.scheduler.add_job(self.Ticker.update,
                               'interval',
                               seconds=self.updateInterval)
        self.scheduler.add_job(self.__updateOrderBook,
                               'interval',
                               seconds=self.updateInterval)
        self.updating = True
        print("Market Data Updating")

    #will stop auto updating the price table
    def stopMarketData(self):
        self.scheduler.shutdown()
        self.updating = False
        print("Market Data No Longer Updating")

    def isRunning(self):
        return self.updating

    def updateAll(self):
        self.__updatePriceTable()
        self.__updateSmoothedAverageGain()
        self.__updateSmoothedAverageLoss()

    #CURRENT RSI IS USELESS, DOES NOT WORK
    def getRSI(self, numPeriods, timeInterval):
        if (numPeriods * timeInterval >
                self.sizePriceTable * self.updateInterval):
            print("Invalid RSI Conditions")
            return -1
        else:
            RSI = -1
            RS = self.getRS(numPeriods, timeInterval)
            if (RS == -1):
                return RSI
            else:
                RSI = 100 - 100 / (1 + RS)
                return RSI

    def getRS(self, numPeriods, timeInterval):
        RS = -1
        avgGain = self.smoothedAverageGain
        avgLoss = self.smoothedAverageLoss

        if (avgGain == -1 or avgLoss == -1):
            return RS
        elif (avgGain == 0 and avgLoss == 0):
            RS = 1
            return RS
        elif (avgLoss == 0):
            RS = 0
        elif (avgGain == 0):
            RS = 99
        else:
            RS = avgGain / avgLoss
        return RS

    def __updateSmoothedAverageLoss(self, numPeriods=0):
        if (numPeriods == 0):
            numPeriods = self.sizePriceTable - 1

        smoothedAverage = -1
        previousSmoothedAverage = self.smoothedAverageLoss

        if (previousSmoothedAverage == -1):
            smoothedAverage = self.getAverageLoss(numPeriods + 1)

        else:
            smoothedAverage = (
                previousSmoothedAverage * (numPeriods - 1) +
                self.getAverageLoss(numPeriods + 1)) / numPeriods

        self.smoothedAverageLoss = smoothedAverage
        return smoothedAverage

    def __updateSmoothedAverageGain(self, numPeriods=0):
        if (numPeriods == 0):
            numPeriods = self.sizePriceTable - 1

        smoothedAverage = -1
        previousSmoothedAverage = self.smoothedAverageGain

        if (previousSmoothedAverage == -1):
            smoothedAverage = self.getAverageLoss(numPeriods + 1)

        else:
            smoothedAverage = (
                previousSmoothedAverage * (numPeriods - 1) +
                self.getAverageGain(numPeriods + 1)) / numPeriods

        self.smoothedAverageGain = smoothedAverage
        return smoothedAverage

    def getAverageGain(self, numPeriods=0):

        if (numPeriods == 0):
            numPeriods = self.sizePriceTable

        average = -1
        gainTable = []
        if (len(self.priceTable) < numPeriods):
            return average
        else:
            for i in range(1, numPeriods):
                gain = self.priceTable[i] - self.priceTable[i - 1]
                if (gain > 0):
                    gainTable.insert(i - 1, gain)
                else:
                    gainTable.insert(i - 1, 0)

            sm = sum(gainTable)
            average = sm / numPeriods
            if (not self.__isPriceTableFull()):
                return -1
            else:
                return average

    #returns an integer value of the sum of the losses between entries in
    #priceTable. Returns -1 if numPeriods is greater than the number of
    #entries in priceTable or if priceTable is not full.
    def getAverageLoss(self, numPeriods=0):
        if (numPeriods == 0):
            numPeriods = self.sizePriceTable

        average = -1
        lossTable = []
        if (len(self.priceTable) < numPeriods):
            return average
        else:
            for i in range(1, numPeriods):
                loss = self.priceTable[i - 1] - self.priceTable[i]
                if (loss > 0):
                    lossTable.insert(i - 1, loss)
                else:
                    lossTable.insert(i - 1, 0)

            sm = sum(lossTable)
            average = sm / numPeriods
            if (not self.__isPriceTableFull()):
                return -1
            else:
                return average

    def __isPriceTableFull(self):
        if (self.priceTable[-1] == -1):
            return False
        else:
            return True

    def __updatePriceTable(self):
        if (self.Ticker.isRunning()):
            self.priceTable.insert(0, self.Ticker.getPrice())
            self.priceTable = self.priceTable[0:self.sizePriceTable]
        else:
            self.stopMarketData()

    def getPriceTable(self):
        return self.priceTable

    def __updateOrderBook(self):
        if (self.self.OrderBook.isRunning()):
            self.localOrderBook = self.OrderBook.getOrderBook()
        else:
            self.stopMarketData()

    def getOrderBook(self):
        return self.localOrderBook
Ejemplo n.º 15
0
    def __init__(self, market='BTC', target="BTC", base="USDT"):
        self.market = market
        self.order_book = OrderBook(market, depth=60)
        self.portfolio_dict = {target: 0.0, base: 10000.0}
        self.starting_portfolio = copy.deepcopy(self.portfolio_dict)
        self.power = copy.deepcopy(self.portfolio_dict)

        self.target = target
        self.base = base
        self.last_trade = Order(0.0, 0.0)
        self.last_trade_orders = []
        self.last_trade_msg = "{}"
        self.starting_portfolio_value = -1
        self.num_trades = 0
        self.MAKER_FEE = 0.0002
        self.TAKER_FEE = 0.0002
        # self.MAKER_FEE = 0.0
        # self.TAKER_FEE = 0.0
        self.MIN_TICK = 0.00001

        # An order book with only my orders
        self.open_orders = OrderBook(market)
        self.fills = []

        self.num_buy_orders = 0
        self.num_sell_orders = 0
        self.sum_of_buy_values = 0.0
        self.sum_of_sell_values = 0.0

        self.num_buy_cancels = 0
        self.num_sell_cancels = 0
        self.total_buy_quantity = 0.0
        self.total_sell_quantity = 0.0

        self.VOLUME_ALERT_THRESH = 2
        self.historical_volume = [{"vol": 0.0, "alert": 0.0}]
        self.history_len = 20  # minutes
        self._clock_thread = threading.Thread(target=self.__update_clock)
        self._clock_lock = threading.Lock()
        self._clock_thread.start()
        self.num_alerts = 0

        self.moving_averages = [{
            "ma_total": 0.0,
            "vwma_total": 0.0,
            "total_trades": 0,
            "total_volume": 0.0
        }]
        self.moving_averages_len = 7  # minutes
        self.moving_average = {'ma': 0.0, 'vwma': 0.0}
        self._moving_average_last = {'ma': 0.0, 'vwma': 0.0}
        self.moving_average_slope = NEUTRAL

        self.longer_moving_averages = [{
            "ma_total": 0.0,
            "vwma_total": 0.0,
            "total_trades": 0,
            "total_volume": 0.0
        }]
        self.longer_moving_averages_len = 25  # minutes
        self.longer_moving_average = {'ma': 0.0, 'vwma': 0.0}
        self._longer_moving_average_last = {'ma': 0.0, 'vwma': 0.0}
        self.longer_moving_average_slope = NEUTRAL

        self.trend_switched = True
        self.micro_trend = NEUTRAL
        self.macro_trend = NEUTRAL

        self.elapse_min = False  # set false until moving average length has elapsed

        self.trade_cache = None
Ejemplo n.º 16
0
class MarketState:
    """Market state and information that trading reacts upon"""
    def __init__(self, market='BTC', target="BTC", base="USDT"):
        self.market = market
        self.order_book = OrderBook(market, depth=60)
        self.portfolio_dict = {target: 0.0, base: 10000.0}
        self.starting_portfolio = copy.deepcopy(self.portfolio_dict)
        self.power = copy.deepcopy(self.portfolio_dict)

        self.target = target
        self.base = base
        self.last_trade = Order(0.0, 0.0)
        self.last_trade_orders = []
        self.last_trade_msg = "{}"
        self.starting_portfolio_value = -1
        self.num_trades = 0
        self.MAKER_FEE = 0.0002
        self.TAKER_FEE = 0.0002
        # self.MAKER_FEE = 0.0
        # self.TAKER_FEE = 0.0
        self.MIN_TICK = 0.00001

        # An order book with only my orders
        self.open_orders = OrderBook(market)
        self.fills = []

        self.num_buy_orders = 0
        self.num_sell_orders = 0
        self.sum_of_buy_values = 0.0
        self.sum_of_sell_values = 0.0

        self.num_buy_cancels = 0
        self.num_sell_cancels = 0
        self.total_buy_quantity = 0.0
        self.total_sell_quantity = 0.0

        self.VOLUME_ALERT_THRESH = 2
        self.historical_volume = [{"vol": 0.0, "alert": 0.0}]
        self.history_len = 20  # minutes
        self._clock_thread = threading.Thread(target=self.__update_clock)
        self._clock_lock = threading.Lock()
        self._clock_thread.start()
        self.num_alerts = 0

        self.moving_averages = [{
            "ma_total": 0.0,
            "vwma_total": 0.0,
            "total_trades": 0,
            "total_volume": 0.0
        }]
        self.moving_averages_len = 7  # minutes
        self.moving_average = {'ma': 0.0, 'vwma': 0.0}
        self._moving_average_last = {'ma': 0.0, 'vwma': 0.0}
        self.moving_average_slope = NEUTRAL

        self.longer_moving_averages = [{
            "ma_total": 0.0,
            "vwma_total": 0.0,
            "total_trades": 0,
            "total_volume": 0.0
        }]
        self.longer_moving_averages_len = 25  # minutes
        self.longer_moving_average = {'ma': 0.0, 'vwma': 0.0}
        self._longer_moving_average_last = {'ma': 0.0, 'vwma': 0.0}
        self.longer_moving_average_slope = NEUTRAL

        self.trend_switched = True
        self.micro_trend = NEUTRAL
        self.macro_trend = NEUTRAL

        self.elapse_min = False  # set false until moving average length has elapsed

        self.trade_cache = None

    def getTargetValue(self):
        return self.portfolio_dict[self.target] * self.last_trade.price

    def getPortfolioValue(self, base="USDT"):
        ret = -1
        if base == "USDT" and self.last_trade.quantity != 0.0:
            assert type(self.last_trade) is Order
            ret = float(self.last_trade.price) * float(self.portfolio_dict[self.target])+\
                  float(self.portfolio_dict['USDT'])

        if self.starting_portfolio_value == -1:
            self.starting_portfolio_value = ret
        return ret

    def getStartingPortfolioValue(self, base="USDT"):
        ret = -1
        if base == "USDT" and self.last_trade.quantity != 0.0:
            assert type(self.last_trade) is Order
            ret = float(self.last_trade.price) * float(self.starting_portfolio[self.target])+\
                  float(self.starting_portfolio['USDT'])

        if self.starting_portfolio_value == -1:
            self.starting_portfolio_value = ret
        return ret

    def calc_value_ratio(self):
        """ratio of total value of holding assets"""
        return self.getTargetValue() / self.getPortfolioValue()

    def getProfitLoss(self):
        ret = -1
        if self.starting_portfolio_value != -1:
            ret = (self.getPortfolioValue() - self.starting_portfolio_value)
        return ret

    def getPnLPercent(self):
        ret = -1
        if self.starting_portfolio_value != -1:
            ret = float(self.getProfitLoss()) / float(
                self.starting_portfolio_value)
        return ret

    def setLastTrade(self, msg):
        self.last_trade.price = float(msg['p'])
        self.last_trade.quantity = float(msg['q'])
        if msg['m'] is True:
            self.last_trade.side = 'b'
        else:
            self.last_trade.side = 'a'
        self.last_trade_msg = msg

        if self.trade_cache is None:
            self.trade_cache = TradeCache(self.last_trade.price)
        self.trade_cache.update(self.last_trade.price, self.last_trade.side)

        self._clock_lock.acquire()

        self.historical_volume[0]["vol"] += self.last_trade.quantity

        self.moving_averages[0]['ma_total'] += self.last_trade.price
        self.moving_averages[0][
            'vwma_total'] += self.last_trade.price * self.last_trade.quantity
        self.moving_averages[0]['total_trades'] += 1
        self.moving_averages[0]['total_volume'] += self.last_trade.quantity

        self.longer_moving_averages[0]['ma_total'] += self.last_trade.price
        self.longer_moving_averages[0][
            'vwma_total'] += self.last_trade.price * self.last_trade.quantity
        self.longer_moving_averages[0]['total_trades'] += 1
        self.longer_moving_averages[0][
            'total_volume'] += self.last_trade.quantity

        self._clock_lock.release()

    timeout = 5

    def __update_clock(self):
        while True:
            time.sleep(self.timeout)
            self._clock_lock.acquire()

            self.__update_historical_volume()
            self.__set_moving_averages()

            self._clock_lock.release()

    def __update_historical_volume(self):
        if self.historical_volume[0]['vol'] > self.VOLUME_ALERT_THRESH:
            self.historical_volume[0]['alert'] = 1.0
            self.num_alerts += 1

        self.historical_volume.insert(0, {"vol": 0.0, "alert": 0.0})
        if len(self.historical_volume) > self.history_len:
            popped = self.historical_volume.pop(
                len(self.historical_volume) - 1)
            if popped['alert'] > 0.0:
                self.num_alerts -= 1

    def __set_moving_averages(self):
        self.moving_averages.insert(
            0, {
                "ma_total": 0.0,
                "vwma_total": 0.0,
                "total_trades": 0,
                "total_volume": 0.0
            })
        if len(self.moving_averages) > self.moving_averages_len:
            ma_total = 0.0
            vwma_total = 0.0
            total_trades = 0.0
            total_volume = 0.0
            for i in self.moving_averages:
                ma_total += i['ma_total']
                vwma_total += i['vwma_total']
                total_trades += i['total_trades']
                total_volume += i['total_volume']
            self._moving_average_last = copy.deepcopy(self.moving_average)
            self.moving_average['ma'] = ma_total / total_trades
            self.moving_average['vwma'] = vwma_total / total_volume
            popped = self.moving_averages.pop(len(self.moving_averages) - 1)

            if self.moving_average['vwma'] > self._moving_average_last['vwma']:
                self.moving_average_slope = UP
            elif self.moving_average['vwma'] < self._moving_average_last[
                    'vwma']:
                self.moving_average_slope = DOWN

            self.elapse_min = True

        self.longer_moving_averages.insert(
            0, {
                "ma_total": 0.0,
                "vwma_total": 0.0,
                "total_trades": 0,
                "total_volume": 0.0
            })
        if len(self.longer_moving_averages) > self.longer_moving_averages_len:
            ma_total = 0.0
            vwma_total = 0.0
            total_trades = 0.0
            total_volume = 0.0
            for i in self.longer_moving_averages:
                ma_total += i['ma_total']
                vwma_total += i['vwma_total']
                total_trades += i['total_trades']
                total_volume += i['total_volume']
            self._longer_moving_average_last = copy.deepcopy(
                self.longer_moving_average)
            self.longer_moving_average['ma'] = ma_total / total_trades
            self.longer_moving_average['vwma'] = vwma_total / total_volume
            popped = self.longer_moving_averages.pop(
                len(self.longer_moving_averages) - 1)

            if self.longer_moving_average[
                    'vwma'] > self._longer_moving_average_last['vwma']:
                self.longer_moving_average_slope = UP
            elif self.longer_moving_average[
                    'vwma'] < self._longer_moving_average_last['vwma']:
                self.longer_moving_average_slope = DOWN

    def acquire_elapse_min(self):
        self._clock_lock.acquire()

        if self.elapse_min:
            self.elapse_min = False
            self._clock_lock.release()
            return True
        else:
            self._clock_lock.release()
            return False

    def __historical_volume_str__(self):
        ret = ""
        for dict in self.historical_volume:
            if dict['vol'] > self.VOLUME_ALERT_THRESH:
                ret += C.FAIL + str(dict) + C.ENDC + "\n"
            else:
                ret += str(dict) + "\n"
        return ret

    def getPositionSpread(self):
        if len(self.open_orders.bids) > 0 and len(self.open_orders.asks) > 0:
            return self.open_orders.asks.sortedOrderList[
                0].price - self.open_orders.bids.sortedOrderList[0].price
        else:
            return -1

    def getAverageBuyPrice(self) -> float:
        """VWAP of buys"""
        if self.total_buy_quantity > 0:
            return self.sum_of_buy_values / self.total_buy_quantity
        return -1.0

    def getAverageSellPrice(self) -> float:
        """VWAP of sells"""
        if self.total_sell_quantity > 0:
            return self.sum_of_sell_values / self.total_sell_quantity
        return -1.0

    last_filled_bid = None
    last_filled_ask = None

    def binance_incremental_trade_update_handler(self, msg):
        ## TODO: only apply maker fee to USDT (base currency)
        self.setLastTrade(msg)
        traded_orders = self.order_book.binance_incremental_trade_update_handler(
            msg)
        traded_open_orders = self.open_orders.binance_incremental_trade_update_handler(
            msg)

        if len(traded_orders) > 0:
            self.last_trade_orders = traded_orders
        for traded_order in traded_open_orders:
            if traded_order is not None and traded_order.position_quantity > 0:
                logger.debug("ORDER FILLED\t\t:\t-> {}".format(traded_order))
                self.fills.append(traded_order)
                self.num_trades += 1

                if traded_order.side == 'b':
                    # Bid got filled
                    self.num_buy_orders += 1
                    self.total_buy_quantity += traded_order.position_quantity
                    self.portfolio_dict[
                        'BTC'] += traded_order.position_quantity
                    self.power['BTC'] += traded_order.position_quantity
                    self.portfolio_dict['USDT'] -= traded_order.price * traded_order.position_quantity \
                                                   * (1+self.MAKER_FEE)
                    self.sum_of_buy_values += traded_order.price * traded_order.position_quantity
                    self.last_filled_bid = traded_order
                else:
                    # Ask got filled
                    self.num_sell_orders += 1
                    self.total_sell_quantity += traded_order.position_quantity
                    self.portfolio_dict[
                        'BTC'] -= traded_order.position_quantity
                    self.portfolio_dict['USDT'] += traded_order.price * traded_order.position_quantity \
                                                   * (1-self.MAKER_FEE)
                    self.power['USDT'] += traded_order.price * traded_order.position_quantity \
                                                   * (1 - self.MAKER_FEE)

                    # no maker fee included
                    self.sum_of_sell_values += traded_order.price * traded_order.position_quantity

                    self.last_filled_ask = traded_order

                #round
                self.__round_portfolio()
        self.__health_check()

    def binance_incremental_depth_update_handler(self, msg):
        self.trend_switched = False
        # set direction statistic
        if self.order_book.getMidPrice() > self.moving_average['vwma']:
            if self.micro_trend != UP:
                self.trend_switched = True
            else:
                self.trend_switched = False

            self.micro_trend = UP

        elif self.order_book.getMidPrice() < self.moving_average['vwma']:
            if self.micro_trend != DOWN:
                self.trend_switched = True
            else:
                self.trend_switched = False

            self.micro_trend = DOWN

        if self.order_book.getMidPrice() > self.longer_moving_average['vwma']:
            self.macro_trend = UP
        elif self.order_book.getMidPrice(
        ) < self.longer_moving_average['vwma']:
            self.macro_trend = DOWN

    def get_fills(self):
        fills = []
        if len(self.fills) > 0:
            fills = copy.deepcopy(self.fills)
            self.fills.clear()
        return fills

    def __round_portfolio(self):
        """Rounds everything off after calculations"""
        self.portfolio_dict[self.target] = round(
            self.portfolio_dict[self.target], 4)
        self.portfolio_dict[self.base] = round(self.portfolio_dict[self.base],
                                               4)
        self.power[self.target] = round(self.power[self.target], 4)
        self.power[self.base] = round(self.power[self.base], 4)

    def __str__(self):
        # return "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n " \
        return "Portfolio\t: "+str(self.portfolio_dict)+ "{:12.4f}".format(self.calc_value_ratio()) + \
               "\n(b/s)power\t: " + str(self.power) + \
               "\nPortfolio_value\t= "+str(self.getPortfolioValue())+ \
               "\nStart_value\t= "+str(self.getStartingPortfolioValue())+\
               "\nPnL\t\t= "+ str(self.getProfitLoss()) + \
               "\nPnL%\t\t= " + str(self.getPnLPercent()) + \
               "\n# Trades\t= " + str(self.num_trades) + "\tb: {:2.2f}".format(self.total_buy_quantity) + \
               "\ts: {:2.2f}".format(self.total_sell_quantity) + \
               "\nopen orders\t=\tb: "+str(len(self.open_orders.bids)) + \
               "\ts: " + str(len(self.open_orders.asks)) + \
               "\ncancels \t= "+ str(self.num_buy_cancels+self.num_sell_cancels)+\
               "\tb: " + str(self.num_buy_cancels) + \
               "\ts: " + str(self.num_sell_cancels) + \
               "\navg price\t:\tb: {:2.4f}".format(self.getAverageBuyPrice()) + \
               "\ts: {:2.4f}".format(self.getAverageSellPrice()) + \
               "\nma (length {})\t: {:2.4f}\tvwma: {:2.4f}"\
                   .format(self.moving_averages_len, self.moving_average['ma'], self.moving_average['vwma']) + \
               "\ndirection:\t: {}" \
                   .format(self.micro_trend) + \
               "\npos_spread\t= "+ str(self.getPositionSpread()) + \
               "\n\nLast Trade\t= " + \
               str(self.last_trade) + \
               "\nVolume\t\t:\n" +self.__historical_volume_str__() + \
               "\n" + str(self.order_book)

    """Mock Trading Stuff"""

    def new_limit_buy(self, order: Order):
        logger.debug("new_limit_buy\t\t: {}".format(order))
        total = float(order.price * order.quantity)
        if self.power['USDT'] - total < 0.0:
            logger.debug("REJECTED: order=[" + str(order) +
                         "] Reason=not enough base buying power")
            return False
        self.order_book.add_modify_delete(order, side='b', my_order=True)
        self.open_orders.add_modify_delete(order, side='b', my_order=True)
        self.power['USDT'] -= order.price * order.position_quantity * (
            1 + self.MAKER_FEE)
        self.__round_portfolio()
        return True

    def new_limit_sell(self, order: Order):
        logger.debug("new_limit_sell\t: {}".format(order))
        if self.power['BTC'] - float(order.quantity) < 0.0:
            logger.debug("REJECTED: order=[" + str(order) +
                         "] Reason=not enough target selling power")
            return False
        self.order_book.add_modify_delete(order, side='a', my_order=True)
        self.open_orders.add_modify_delete(order, side='a', my_order=True)
        self.power['BTC'] -= order.position_quantity
        self.__round_portfolio()
        return True

    def cancel_limit_buy(self, order: Order):
        assert (order.side == BID)
        logger.debug("cancel_limit_buy\t: {}".format(order))
        #  print("order cancel coming in  :", order)
        self.order_book.add_modify_delete(Order(order.price,
                                                quantity=0.0,
                                                pos_quantity=0.0),
                                          side='b',
                                          my_order=True)
        self.open_orders.add_modify_delete(Order(order.price,
                                                 quantity=0.0,
                                                 pos_quantity=0.0),
                                           side='b',
                                           my_order=True)
        self.power['USDT'] += order.price * order.position_quantity * (
            1 + self.MAKER_FEE)
        self.num_buy_cancels += 1
        self.__round_portfolio()

    def cancel_limit_sell(self, order: Order):
        assert (order.side == ASK)
        logger.debug("cancel_limit_sell\t: {}".format(order))
        #  print("order cancel coming in  :", order)
        self.order_book.add_modify_delete(Order(order.price,
                                                quantity=0.0,
                                                pos_quantity=0.0,
                                                side='a'),
                                          side='a',
                                          my_order=True)
        self.open_orders.add_modify_delete(Order(order.price,
                                                 quantity=0.0,
                                                 pos_quantity=0.0,
                                                 side='a'),
                                           side='a',
                                           my_order=True)
        self.power['BTC'] += order.position_quantity
        self.num_sell_cancels += 1
        self.__round_portfolio()

    def __health_check(self):
        self.order_book.health_check()
        self.open_orders.health_check()

        # verify buying power is less than portfolio values
        if self.power[self.base] > self.portfolio_dict[self.base] \
                or self.power[self.target] > self.portfolio_dict[self.target]:
            raise Exception(
                "Buying power exceeds portfolio value:\nportfolio:\t{}\nBuying power:\t{}\nLast Trade:\t{}\n"
                .format(self.portfolio_dict, self.power, self.last_trade))

        # verify buying power and portfolio values are not negative
        if self.power[self.base] < -0.0000001 or self.power[
                self.target] < -0.0000001:
            raise Exception("Buying power is negative:{}".format(self.power))
        if self.portfolio_dict[self.base] < -0.0000001 or self.portfolio_dict[
                self.target] < -0.0000001:
            raise Exception("Portfolio value is negative:{}".format(
                self.portfolio_dict))