예제 #1
0
 def test_crypto_quote(self):
     crypto = r.get_crypto_quote(self.bitcoin, info=None)
     assert ('ask_price' in crypto)
     assert ('bid_price' in crypto)
     assert ('mark_price' in crypto)
     assert ('high_price' in crypto)
     assert ('low_price' in crypto)
     assert ('open_price' in crypto)
     assert ('symbol' in crypto)
     assert ('id' in crypto)
     assert ('volume' in crypto)
     crypto = r.get_crypto_quote(self.stock, info=None)
     assert (crypto == None)
     crypto = r.get_crypto_quote(self.fake, info=None)
     assert (crypto == None)
예제 #2
0
def driver_main(m_high_price, m_open_price, m_low_price, m_mark_price):
    rs.login("*****@*****.**", "13428Carnegie!")
    DOGE = rs.get_crypto_quote("DOGE")
    #DOGE = sim_getquote(m_high_price, m_open_price, m_low_price, m_mark_price)
    m_high_price = float(DOGE['high_price'])
    m_low_price = float(DOGE['low_price'])
    m_mark_price = float(DOGE['mark_price'])
    m_open_price = float(DOGE['open_price'])

    print("Starting DOGE monitor")
    notify_open_price(m_high_price, m_open_price, m_low_price, m_mark_price)
    num_pings = 1
    while True:
        if (1 == (num_pings % 5)):
            print("number of pings:" + str(num_pings))
        num_pings += 1

        time.sleep(getWaitTime())
        DOGE = rs.get_crypto_quote("DOGE")  # Uses API
        time.sleep(10)
        #DOGE = sim_getquote(m_high_price, m_low_price, m_mark_price, m_open_price)

        if (abs(m_open_price - float(DOGE['open_price'])) > .01):
            print("new open price")
            m_high_price = float(DOGE['high_price'])
            m_low_price = float(DOGE['low_price'])
            m_mark_price = float(DOGE['mark_price'])
            m_open_price = float(DOGE['open_price'])
            notify_open_price(m_high_price, m_open_price, m_low_price,
                              m_mark_price)

        # Checks for market spike
        m_mark_price = float(DOGE['mark_price'])
        if ((m_mark_price / m_high_price) >= 1.1):
            print("new spike " + DOGE['mark_price'])
            notify_price_spike(m_high_price, m_open_price, m_low_price,
                               m_mark_price)
            m_high_price = m_mark_price

        # Checks dip
        if ((m_mark_price / m_low_price) <= 0.9):
            print("new dip " + DOGE['mark_price'])
            notify_price_dip(m_high_price, m_open_price, m_low_price,
                             m_mark_price)
            m_low_price = float(DOGE['low_price'])
예제 #3
0
def get_next_price():
    t0 = t = t1 = datetime.now()
    v = v1 = float(r.get_crypto_quote("BTC")["mark_price"])
    val = 0
    for i in range(1, 12):
        sleep(5)
        quote = r.get_crypto_quote("BTC")
        t1, t = t, datetime.now()
        v1, v = v, float(quote["mark_price"])
        val += (v + v1) * (t - t1).total_seconds() / 2
    val = val / (t - t0).total_seconds()
    return {
        "time": t,
        "mark": val,
        "ask": float(quote["ask_price"]),
        "bid": float(quote["bid_price"]),
        "vol": float(quote["volume"]),
    }
예제 #4
0
def get_next_price():
    quote = r.get_crypto_quote("BTC")
    return {
        "time": datetime.now(),
        "mark": float(quote["mark_price"]),
        "ask": float(quote["ask_price"]),
        "bid": float(quote["bid_price"]),
        "vol": float(quote["volume"]),
    }
예제 #5
0
파일: STONKS.py 프로젝트: fjggmg/STONKS
def buy():
    f = open("files.txt", "r")
    cash = f.readline()
    f.close()
    qty = float(cash) / float(r.get_crypto_quote('BTC', info='bid_price'))
    f = open("files.txt", "w")
    f.write("0")
    f.write("\n" + str(qty))
    print("trade made " + str(qty) + " btc bought")
    f.close()
예제 #6
0
파일: STONKS.py 프로젝트: fjggmg/STONKS
def sell():
    f = open("files.txt", "r")
    f.readline()
    qty = f.readline()
    f.close()
    f = open("files.txt", "w")
    qty = qty.replace("\n", "")
    cash = float(qty) * float(r.get_crypto_quote('BTC', info='bid_price'))
    f.write(str(cash))
    f.write("\n0" )
    print("trade made " + str(cash) + " btc sold")

    f.close()
예제 #7
0
def get_next_price():
    # sleep(5)
    while True:
        try:
            quote = r.get_crypto_quote("BTC")
            break
        except:
            pass
    return {
        "time": datetime.now(),
        "mark": float(quote["mark_price"]),
        "ask": float(quote["ask_price"]),
        "bid": float(quote["bid_price"]),
        "vol": float(quote["volume"]),
    }
예제 #8
0
    def buy( self, ticker ):
        if self.available_cash == 0 or self.available_cash < config[ 'assets' ][ 'buy_amount_per_trade' ][ 'min' ]:
            return False
        
        # Retrieve the actual ask price from Robinhood
        if not config[ 'bot' ][ 'simulate_api_calls' ]:
            try:
                quote = rh.get_crypto_quote( ticker )
                price = float( quote[ 'ask_price' ] )
                self.api_error_counter = 0
            except:
                print( 'Could not retrieve ask price from Robinhood. Using most recent value.' )
                self.api_error_counter = self.api_error_counter + 1
                price = self.data.iloc[ -1 ][ ticker ]
        else:
            price = self.data.iloc[ -1 ][ ticker ]

        # Values need to be specified to no more precision than listed in min_price_increments.
        # Truncate to 7 decimal places to avoid floating point problems way out at the precision limit
        price_precision = round( floor( price / self.min_price_increments[ ticker ] ) * self.min_price_increments[ ticker ], 7 )
        
        # How much to buy depends on the configuration
        quantity = ( self.available_cash if ( config[ 'assets' ][ 'buy_amount_per_trade' ][ 'max' ] == 0 ) else min( self.available_cash, config[ 'assets' ][ 'buy_amount_per_trade' ][ 'max' ] ) ) / price_precision
        quantity = round( floor( quantity / self.min_share_increments[ ticker ] ) * self.min_share_increments[ ticker ], 7 )

        if config[ 'bot' ][ 'trades_enabled' ] and not config[ 'bot' ][ 'simulate_api_calls' ]:
            try:
                buy_info = rh.order_buy_crypto_limit( str( ticker ), quantity, price_precision )

                # Add this new asset to our orders
                self.orders[ buy_info[ 'id' ] ] = asset( ticker, quantity, price_precision, buy_info[ 'id' ], 'PB' )

                print( '## Submitted order to buy ' +  str( quantity ) + ' ' + str( ticker ) + ' at $' + str( price_precision ) )
                
                if ( price != self.data.iloc[ -1 ][ ticker ] ):
                    print( '## Price Difference: Mark $' + str( self.data.iloc[ -1 ][ ticker ] ) + ', Ask $' + str( price ) )

                self.api_error_counter = 0
            except:
                print( 'An exception occurred while trying to buy.' )
                self.api_error_counter = self.api_error_counter + 1
                return False
        else:
            print( '## Would have bought ' + str( ticker ) + ' ' + str( quantity ) + ' at $' + str( price_precision ) + ', if trades were enabled' )
            return False

        return True
예제 #9
0
파일: STONKS.py 프로젝트: fjggmg/STONKS
def logic(total1):
    x = []
    buywatch = False
    sellwatch = False

    while len(x) < 50:
        #f = open("files.txt", "r")
        #cash = float(f.readline())
        #qty = float(f.readline())
        #f.close()
        cash = str(r.load_phoenix_account(info='account_buying_power'))
        cash = cash.replace("{'currency_code': 'USD', 'currency_id': '1072fc76-1862-41ab-82c2-485837590762', 'amount': '", "")
        cash = cash.replace("'}", "")
        cash = float(cash)

        qty = str(r.get_crypto_positions(info='quantity_available'))
        qty = total.replace("['", "")
        qty = total.replace("']", "")
        qty = float(qty)


        y = float(r.get_crypto_quote('BTC', info='bid_price'))
        print(y)
        x.append(y)

        z = len(x) - 1
        dif = round(((x[0]-x[z])/x[z])*100, 5)
        print(dif)


        if dif > .001 and sellwatch == False and cash != 0:
            buywatch = True


        if buywatch:
            if x[z] > x[z - 1] > x[z - 2]:
                buy1()
                sellwatch = True
                buywatch = False
        if qty != 0:
            if x[z] < x[z - 1] < x[z - 2]:
                sell1()
                sellwatch = False
        time.sleep(1)
예제 #10
0
    def sell( self, asset ):
        # Retrieve the actual bid price from Robinhood
        if not config[ 'bot' ][ 'simulate_api_calls' ]:
            try:
                quote = rh.get_crypto_quote( asset.ticker )
                price = float( quote[ 'bid_price' ] )
                self.api_error_counter = 0
            except:
                print( 'Could not retrieve bid price from Robinhood. Using most recent value.' )
                self.api_error_counter = self.api_error_counter + 1
                price = self.data.iloc[ -1 ][ asset.ticker ]
        else:
            price = self.data.iloc[ -1 ][ asset.ticker ]

        # Values needs to be specified to no more precision than listed in min_price_increments. 
        # Truncate to 7 decimal places to avoid floating point problems way out at the precision limit
        price_precision = round( floor( price / self.min_price_increments[ asset.ticker ] ) * self.min_price_increments[ asset.ticker ], 7 )
        profit = round( ( asset.quantity * price_precision ) - ( asset.quantity * asset.price ), 3 )

        if config[ 'bot' ][ 'trades_enabled' ] and not config[ 'bot' ][ 'simulate_api_calls' ]:
            try:
                sell_info = rh.order_sell_crypto_limit( str( asset.ticker ), asset.quantity, price_precision )

                # Mark this asset as pending sold
                self.orders[ asset.order_id ].status = 'PS'
                self.orders[ asset.order_id ].profit = profit

                print( '## Submitted order to sell ' + str( asset.quantity ) + ' ' + str( asset.ticker ) + ' at $' + str( price_precision ) + ' (estimated profit: $' + str( profit ) + ')' )
            
                if ( price != self.data.iloc[ -1 ][ asset.ticker ] ):
                    print( '## Price Difference: Mark $' + str( self.data.iloc[ -1 ][ asset.ticker ] ) + ', Bid $' + str( price ) )
            
                self.api_error_counter = 0
            except:
                print( 'An exception occurred while trying to sell.' )
                self.api_error_counter = self.api_error_counter + 1
                return False
        else:
            print( '## Would have sold ' + str( asset.ticker ) + ' ' + str( asset.quantity ) + ' at $' + str( price_precision ) + ', if trades were enabled' )
            return False

        return True
예제 #11
0
    def getPrices(self):
        #try:
        #  print(x)
        #except:
        #  print("An exception occurred")

        prices = {}
        emptyDict = {}

        for c in self.coinList:
            try:
                result = r.get_crypto_quote(c)
                price = result['mark_price']
            except:
                print("An exception occurred retrieving prices.")
                return emptyDict

            prices.update({c: float(price)})

        return prices
예제 #12
0
def main():

    login()

    if os.path.exists("state.pkl"):
        with open("state.pkl", "rb") as statefile:
            last_price, last_action = pickle.load(statefile)
    else:
        last_price = last_action = None

    p0 = pp1 = pp2 = pp3 = None

    while True:

        p1 = p0
        while True:
            try:
                p0 = get_next_price()
                break
            except:
                pass

        if p1 is None:
            logger.info(
                "    current price=%s, trend=%s/min, last price=%s, last_action=%s",
                locale.currency(p0["mark"], grouping=True),
                locale.currency(
                    60 * p0["mark"] * pp1 if pp1 is not None else 0,
                    grouping=True,
                ),
                locale.currency(last_price or 0, grouping=True),
                last_action or "None",
            )
            continue

        dt = (p0["time"] - p1["time"]).total_seconds()

        pp1old = pp1
        pp1 = (p0["mark"] - p1["mark"]) / (p0["mark"] * dt)

        logger.info(
            "    current price=%s, trend=%s/min, last price=%s, last_action=%s",
            locale.currency(p0["mark"], grouping=True),
            locale.currency(60 * p0["mark"] * pp1 if pp1 is not None else 0,
                            grouping=True),
            locale.currency(last_price or 0, grouping=True),
            last_action or "None",
        )

        if pp1old is None:
            continue

        pp2old = pp2
        pp2 = (pp1 - pp1old) / dt

        if pp2old is None:
            continue

        pp3 = (pp2 - pp2old) / dt

        action = ["BUY", "HOLD", "SELL"][clf.predict([[pp1, pp2, pp3]])[0]]

        value = get_value()
        holdings = get_holdings()

        if (action == "BUY" and value < 1) or (action == "SELL"
                                               and holdings < 1e-6):
            action = "HOLD"

        quote = r.get_crypto_quote("BTC")

        price = rh.round_price(float(quote["mark_price"]))
        if last_action is not None and last_price is not None:
            if action == "SELL" and (last_action == "BUY"
                                     and price < 1.001 * last_price):
                action = "HOLD"
            if action == "HOLD" and (last_action == "BUY"
                                     and price < 0.97 * last_price):
                action = "SELL"

        logger.info(
            "action=%4s, shares=%.6f, value=%s, total=%s",
            action,
            holdings,
            locale.currency(value, grouping=True),
            locale.currency(holdings * float(quote["mark_price"]) + value,
                            grouping=True),
        )

        if action == "BUY":
            while True:
                order = r.order_buy_crypto_by_price("BTC",
                                                    rh.round_price(value))
                if "non_field_errors" not in order:
                    break
            if "account_id" not in order:
                logger.info(str(order))
            else:
                last_price = price
                last_action = "BUY"
        elif action == "SELL":
            order = r.order_sell_crypto_by_quantity("BTC", round(holdings, 6))
            if "account_id" not in order:
                logger.info(str(order))
            else:
                last_price = price
                last_action = "SELL"

        if last_action is not None and last_price is not None:
            with open("state.pkl", "wb") as statefile:
                pickle.dump((last_price, last_action), statefile)
예제 #13
0
def main():

    login()

    with open("mlpclassifier_candle.pkl", "rb") as pklfile:
        clf = pickle.load(pklfile)

    if os.path.exists("state.pkl"):
        with open("state.pkl", "rb") as statefile:
            last_price, last_action, last_time = pickle.load(statefile)
    else:
        last_price = last_action = None
        last_time = datetime.now()

    val = val1 = None
    while True:

        prices = [get_next_price()]
        for i in range(12):
            sleep(5)
            prices.append(get_next_price())

        open_p = prices[0]["mark"]
        close_p = prices[11]["mark"]
        dt = (prices[11]["time"] - prices[0]["time"]).total_seconds()

        logger.info(
            "    close price=%s, trend=%s/sec, last price=%s, last_action=%s",
            locale.currency(close_p, grouping=True),
            locale.currency(
                (close_p - open_p) / dt,
                grouping=True,
            ),
            locale.currency(last_price or 0, grouping=True),
            last_action or "None",
        )

        if val is None:
            val = close_p / open_p
            continue

        val1 = val
        val = close_p / open_p

        since = (datetime.now() - last_time).total_seconds()

        logger.info(
            "    %.6f  %.6f  %d",
            val,
            val1,
            since,
        )

        ["BUY", "HOLD", "SELL"][clf.predict([[val, val1, since_bs]])[0]]

        value = get_value()
        holdings = get_holdings()

        if (action == "BUY" and value < 1) or (action == "SELL"
                                               and holdings < 1e-6):
            action = "HOLD"

        quote = r.get_crypto_quote("BTC")

        price = rh.round_price(float(quote["mark_price"]))
        if last_action is not None and last_price is not None:
            if action == "SELL" and (last_action == "BUY"
                                     and price < 1.001 * last_price):
                action = "HOLD"
            # if action == "HOLD" and (
            #     last_action == "BUY" and price < 0.99 * last_price
            # ):
            #     action = "SELL"

        logger.info(
            "action=%4s, shares=%.6f, value=%s, total=%s",
            action,
            holdings,
            locale.currency(value, grouping=True),
            locale.currency(holdings * float(quote["mark_price"]) + value,
                            grouping=True),
        )

        if action == "BUY":
            while True:
                order = r.order_buy_crypto_by_price(
                    "BTC",
                    rh.round_price(value),
                )
                if "non_field_errors" not in order:
                    break
            if "account_id" not in order:
                logger.info(str(order))
            else:
                last_price = price
                last_action = "BUY"
                last_time = datetime.now()
        elif action == "SELL":
            order = r.order_sell_crypto_by_quantity(
                "BTC",
                round(holdings, 6),
            )
            if "account_id" not in order:
                logger.info(str(order))
            else:
                last_price = price
                last_action = "SELL"
                last_time = datetime.now()

        if last_action is not None and last_price is not None:
            with open("state.pkl", "wb") as statefile:
                pickle.dump((last_price, last_action, last_time), statefile)
from win10toast import ToastNotifier
import robin_stocks.robinhood as r
from PIL import Image

filename = "doge.png"
img = Image.open(filename)
img.save('doge.ico')
file_name = 'doge.ico'

#Login -> SMS Verification
r.login("*******","*****")


#print(r.get_crypto_info("DOGE"))
print(r.get_crypto_quote("DOGE"))
robinDict = (r.get_crypto_quote("DOGE"))

markPrice= robinDict['mark_price']
highPrice = robinDict['high_price']
lowprice = robinDict['low_price']


# create an object to ToastNotifier class
x = ToastNotifier()

x.show_toast("DogeCoin Notification","Dogecoin Current: "+ markPrice + " " +"Dogecoin High Price: " + highPrice + " " +"DogeCoin Low Price: " +lowprice, icon_path=file_name, duration=30, threaded=True)

#Can automate this using task scheduler
예제 #15
0
    def get_new_data( self, now ):
        # If the current dataset has gaps in it, we refresh it from Kraken
        if self.data_has_gaps( now ) and not self.init_data():
            return False

        new_row = { 'timestamp': pd.Timestamp( now ) }

        # Calculate moving averages and RSI values
        for a_kraken_ticker, a_robinhood_ticker in config[ 'ticker_list' ].items():
            if not config[ 'bot' ][ 'simulate_api_calls' ]:
                if config[ 'bot' ][ 'data_source' ] == 'kraken':
                    try:
                        result = get_json( 'https://api.kraken.com/0/public/Ticker?pair=' + str( a_kraken_ticker ) ).json()

                        if len( result[ 'error' ] ) == 0:
                            new_row[ a_robinhood_ticker ] = round( float( result[ 'result' ][ a_kraken_ticker ][ 'a' ][ 0 ] ), 3 )

                        self.api_error_counter = 0
                    except:
                        print( 'An exception occurred retrieving prices from Kraken.' )
                        self.api_error_counter = self.api_error_counter + 1
                        return False
                else:
                    try:
                        result = rh.get_crypto_quote( a_robinhood_ticker )
                        new_row[ a_robinhood_ticker ] = round( float( result[ 'mark_price' ] ), 3 )
                        self.api_error_counter = 0
                    except:
                        print( 'An exception occurred retrieving prices from Robinhood.' )
                        self.api_error_counter = self.api_error_counter + 1
                        return False
            else:
                new_row[ a_robinhood_ticker ] = round( float( randint( 400000, 500000 ) ), 3 )

            # If the new price is more than 30% lower/higher than the previous reading, assume an error somewhere
            percent_diff = ( abs( new_row[ a_robinhood_ticker ] - self.data.iloc[ -1 ][ a_robinhood_ticker ] ) / self.data.iloc[ -1 ][ a_robinhood_ticker ] ) * 100
            if percent_diff > 30:
                print( 'Error: new price ($' + str( new_row[ a_robinhood_ticker ] ) + ') differs ' + str( round( percent_diff, 2 ) ) + '% from previous value, ignoring.' )
                return False

            self.data = self.data.append( new_row, ignore_index = True )

            # If the API is overloaded, it keeps returning the same value
            if ( self.data.tail( 4 )[ a_robinhood_ticker ].to_numpy()[ -1 ] == self.data.tail( 4 )[ a_robinhood_ticker ].to_numpy() ).all():
                print( 'Repeating values detected for ' + str( a_robinhood_ticker ) + '. Ignoring data point.' )
                self.data = self.data[:-1]
                return False

            elif self.data.shape[ 0 ] > 0:
                self.data[ a_robinhood_ticker + '_SMA_F' ] = self.data[ a_robinhood_ticker ].rolling( window = config[ 'ta' ][ 'moving_average_periods' ][ 'sma_fast' ] ).mean()
                self.data[ a_robinhood_ticker + '_SMA_S' ] = self.data[ a_robinhood_ticker ].rolling( window = config[ 'ta' ][ 'moving_average_periods' ][ 'sma_slow' ] ).mean()
                self.data[ a_robinhood_ticker + '_EMA_F' ] = self.data[ a_robinhood_ticker ].ewm( span = config[ 'ta' ][ 'moving_average_periods' ][ 'ema_fast' ], adjust = False, min_periods = config[ 'ta' ][ 'moving_average_periods' ][ 'ema_fast' ]).mean()
                self.data[ a_robinhood_ticker + '_EMA_S' ] = self.data[ a_robinhood_ticker ].ewm( span = config[ 'ta' ][ 'moving_average_periods' ][ 'ema_slow' ], adjust = False, min_periods = config[ 'ta' ][ 'moving_average_periods' ][ 'ema_slow' ]).mean()
                self.data[ a_robinhood_ticker + '_RSI' ] = RSI( self.data[ a_robinhood_ticker ].values, timeperiod = config[ 'ta' ][ 'rsi_period' ] )
                self.data[ a_robinhood_ticker + '_MACD' ], self.data[ a_robinhood_ticker + '_MACD_S' ], macd_hist = MACD( self.data[ a_robinhood_ticker ].values, fastperiod = config[ 'ta' ][ 'moving_average_periods' ][ 'macd_fast' ], slowperiod = config[ 'ta' ][ 'moving_average_periods' ][ 'macd_slow' ], signalperiod = config[ 'ta' ][ 'moving_average_periods' ][ 'macd_signal' ] )

            if config[ 'bot' ][ 'save_charts' ] == True:
                self.save_chart( [ a_robinhood_ticker, str( a_robinhood_ticker ) + '_SMA_F', str( a_robinhood_ticker ) + '_SMA_S' ], str( a_robinhood_ticker ) + '_sma' )
                self.save_chart( [ a_robinhood_ticker, str( a_robinhood_ticker ) + '_EMA_F', str( a_robinhood_ticker ) + '_EMA_S' ], str( a_robinhood_ticker ) + '_ema' )
                self.save_chart_rescale( [ a_robinhood_ticker, str( a_robinhood_ticker ) + '_RSI' ], str( a_robinhood_ticker ) + '_rsi' )
                self.save_chart_rescale( [ a_robinhood_ticker, str( a_robinhood_ticker ) + '_MACD', str( a_robinhood_ticker ) + '_MACD_S' ], str( a_robinhood_ticker ) + '_macd' )

        return True
예제 #16
0
def main():

    login()

    with open("mlpclassifier_5sec.pkl", "rb") as pklfile:
        gsc = pickle.load(pklfile)

    if os.path.exists("state.pkl"):
        with open("state.pkl", "rb") as statefile:
            last_price, last_action = pickle.load(statefile)
    else:
        last_price = last_action = None
    prev_last_price = prev_last_action = None

    p0 = pp1 = pp2 = pp3 = None

    while True:

        p1 = p0
        while True:
            try:
                p0 = get_next_price()
                break
            except:
                pass

        if p1 is None:
            logger.info(
                "    current price=%s, trend=%s/min, last price=%s, last_action=%s",
                cur(p0["mark"]),
                cur(60 * p0["mark"] * pp1 if pp1 is not None else 0),
                cur(last_price or 0),
                last_action or "None",
            )
            continue

        dt = (p0["time"] - p1["time"]).total_seconds()

        pp1old = pp1
        pp1 = (p0["mark"] - p1["mark"]) / (p0["mark"] * dt)

        logger.info(
            "    current price=%s, trend=%s/min, last price=%s, last_action=%s",
            cur(p0["mark"]),
            cur(60 * p0["mark"] * pp1),
            cur(last_price or 0),
            last_action or "None",
        )

        if pp1old is None:
            logger.info("    pp1=%.4g", pp1)
            continue

        pp2old = pp2
        pp2 = (pp1 - pp1old) / dt

        if pp2old is None:
            logger.info("    pp1=%.4g, pp2=%.4g", pp1, pp2)
            continue

        pp3old = pp3
        pp3 = (pp2 - pp2old) / dt

        if pp3old is None:
            logger.info("    pp1=%.4g, pp2=%.4g, pp3=%.4g", pp1, pp2, pp3)
            continue

        pp4 = (pp3 - pp3old) / dt

        logger.info("    pp1=%.4g, pp2=%.4g, pp3=%.4g, pp4=%.4g", pp1, pp2,
                    pp3, pp4)

        action = ["BUY", "HOLD", "SELL"][gsc.predict([[pp1, pp2, pp3,
                                                       pp4]])[0]]

        value = get_value()
        holdings = get_holdings()

        if (action == "BUY" and value < 1) or (action == "SELL"
                                               and holdings < 1e-6):
            action = "HOLD"

        while True:
            try:
                quote = r.get_crypto_quote("BTC")
                break
            except Exception as e:
                print(e)

        price = rh.round_price(float(quote["mark_price"]))
        if last_action is not None and last_price is not None:
            if action == "SELL" and (last_action == "BUY"
                                     and price < 1.001 * last_price):
                action = "HOLD"
            # if action == "HOLD" and (
            #     last_action == "BUY" and price < 0.97 * last_price
            # ):
            #     action = "SELL"

        logger.info(
            "action=%4s, shares=%.6f, value=%s, total=%s",
            action,
            holdings,
            cur(value),
            cur(holdings * float(quote["mark_price"]) + value),
        )

        while True:
            try:
                for order in r.get_all_open_crypto_orders():
                    r.cancel_crypto_order(order["id"])
                    last_price = prev_last_price
                    last_action = prev_last_action
                break
            except Exception as e:
                print(e)

        if action == "BUY":
            while True:
                order = r.order_buy_crypto_limit_by_price(
                    "BTC",
                    rh.round_price(0.99 * value),
                    rh.round_price(float(quote["ask_price"])),
                )
                # order = r.order_buy_crypto_by_price(
                #     "BTC",
                #     rh.round_price(value),
                # )
                if "non_field_errors" not in order:
                    break
            if "account_id" not in order:
                logger.info(str(order))
            else:
                prev_last_price = last_price
                prev_last_action = last_action
                last_price = price
                last_action = "BUY"
        elif action == "SELL":
            order = r.order_sell_crypto_limit(
                "BTC",
                round(holdings, 6),
                rh.round_price(float(quote["bid_price"])),
            )
            # order = r.order_sell_crypto_by_quantity(
            #     "BTC",
            #     round(holdings, 6),
            # )
            if "account_id" not in order:
                logger.info(str(order))
            else:
                prev_last_price = last_price
                prev_last_action = last_action
                last_price = price
                last_action = "SELL"

        if last_action is not None and last_price is not None:
            with open("state.pkl", "wb") as statefile:
                pickle.dump((last_price, last_action), statefile)