예제 #1
0
 def handle_api_error(self, err: str, reason: str) -> pd.DataFrame:
     if self.debug:
         if self.die_on_api_error:
             raise SystemExit(err)
         else:
             Logger.debug(err)
             return pd.DataFrame()
     else:
         if self.die_on_api_error:
             raise SystemExit(f"{reason}: {self._api_url}")
         else:
             Logger.info(f"{reason}: {self._api_url}")
             return pd.DataFrame()
예제 #2
0
    def getTakerFee(self, market: str = '') -> float:
        if len(market) != None:
            fees = self.getFees(market)
        else:
            fees = self.getFees()

        if len(fees) == 0 or 'taker_fee_rate' not in fees:
            Logger.error(
                f"error: 'taker_fee_rate' not in fees (using {DEFAULT_TAKER_FEE_RATE} as a fallback)"
            )
            return DEFAULT_TAKER_FEE_RATE

        return float(fees['taker_fee_rate'].to_string(index=False).strip())
예제 #3
0
    def getTime(self) -> datetime:
        """Retrieves the exchange time"""

        try:
            resp = self.authAPI("GET", "time")
            if "epoch" in resp:
                epoch = int(resp["epoch"])
                return datetime.fromtimestamp(epoch)
            else:
                Logger.error(resp)
                return None
        except Exception as e:
            Logger.error(f"Error: {e}")
            return None
 def _write_data(self, name: str = "") -> bool:
     file = self.filename if name == "" else name
     try:
         with open(
                 os.path.join(self.app.telegramdatafolder, "telegram_data",
                              file),
                 "w",
                 encoding="utf8",
         ) as outfile:
             json.dump(self.data, outfile, indent=4)
         return True
     except JSONDecodeError as err:
         Logger.critical(str(err))
         return False
예제 #5
0
    def getMakerFee(self, market: str='') -> float:
        if market == '':
            fees = self.getFees()
        else:
            fees = self.getFees(market)
        
        if len(fees) == 0 or 'maker_fee_rate' not in fees:
            Logger.error(f"error: 'maker_fee_rate' not in fees (using {DEFAULT_MAKER_FEE_RATE} as a fallback)")
            return DEFAULT_MAKER_FEE_RATE

        if market == '':
            return fees
        else:
            return float(fees['maker_fee_rate'].to_string(index=False).strip())
예제 #6
0
    def minimumOrderBase(self, base, balancechk: bool = False):
        self.app.insufficientfunds = False
        if self.app.getExchange() == Exchange.BINANCE:
            df = self.api.getMarketInfoFilters(self.app.getMarket())
            if len(df) > 0:
                base_min = float(df[df["filterType"] == "LOT_SIZE"][[
                    "minQty"
                ]].values[0][0])
                base = float(base)

        elif self.app.getExchange() == Exchange.COINBASEPRO:
            product = self.api.authAPI("GET",
                                       f"products/{self.app.getMarket()}")
            if len(product) == 0:
                sys.tracebacklimit = 0
                raise Exception(f"Market not found! ({self.app.getMarket()})")

            base = float(base)
            base_min = float(product["base_min_size"])

        elif self.app.getExchange() == Exchange.KUCOIN:
            resp = self.api.authAPI("GET", "api/v1/symbols")
            product = resp[resp["symbol"] == self.app.getMarket()]
            if len(product) == 0:
                sys.tracebacklimit = 0
                raise Exception(f"Market not found! ({self.app.getMarket()})")

            base = float(base)
            base_min = float(product["baseMinSize"])

        # additional check for last order type
        if balancechk:
            if base > base_min:
                return True
            else:
                return
        elif base < base_min:
            if self.app.enableinsufficientfundslogging:
                self.app.insufficientfunds = True
                Logger.warning(
                    f"Insufficient Base Funds! (Actual: {base}, Minimum: {base_min})"
                )
                return

            sys.tracebacklimit = 0
            raise Exception(
                f"Insufficient Base Funds! (Actual: {base}, Minimum: {base_min})"
            )
        else:
            return
예제 #7
0
 def __init__(self) -> None:
     for i in range(10):
         try:
             self.client = Client()
             break
         except Exception as e:
             if i == 9:
                 raise SystemExit(
                     "Can not create instance of AuthAPI client.")
             Logger.error('Exception: ' + str(e))
             Logger.error(
                 'Error on creating instance of AuthAPI Client. Trying again... Attempt: '
                 + str(i))
             sleep(0.1)
예제 #8
0
    def saveCSV(self, filename: str='tradingdata.csv') -> None:
        """Saves the DataFrame to an uncompressed CSV."""

        p = compile(r"^[\w\-. ]+$")
        if not p.match(filename):
            raise TypeError('Filename required.')

        if not isinstance(self.df, DataFrame):
            raise TypeError('Pandas DataFrame required.')

        try:
            self.df.to_csv(filename)
        except OSError:
            Logger.critical(f'Unable to save: {filename}')
예제 #9
0
    def getTicker(self, market: str = DEFAULT_MARKET) -> tuple:
        """Retrieves the market ticker"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise TypeError("Kucoin market required.")

        resp = {}
        trycnt, maxretry = (0, 5)
        while trycnt <= maxretry:

            resp = self.authAPI(
                "GET", f"api/v1/market/orderbook/level1?symbol={market}")

            if "data" not in resp:  # if not proper response, retry
                Logger.warning(
                    f"Kucoin API Error for getTicket - 'data' not in response - retrying - attempt {trycnt}"
                )
                time.sleep(15)
                trycnt += 1
            elif "time" in resp["data"]:  # if time returned, check format
                resptime = ""
                respprice = ""
                try:
                    resptime = datetime.strptime(
                        str(
                            datetime.fromtimestamp(
                                int(resp["data"]["time"]) / 1000)),
                        "%Y-%m-%d %H:%M:%S.%f",
                    )
                    respprice = float(resp["data"]["price"])
                    if resptime != "" and respprice != "":  # if format is correct, return
                        return (
                            datetime.strptime(
                                str(
                                    datetime.fromtimestamp(
                                        int(resp["data"]["time"]) / 1000)),
                                "%Y-%m-%d %H:%M:%S.%f",
                            ).strftime("%Y-%m-%d %H:%M:%S"),
                            respprice,
                        )
                except:
                    Logger.warning(
                        f"Kucoin API Error for Get Ticker - retrying - attempt {trycnt}"
                    )
                    time.sleep(15)
                    trycnt += 1
            else:  # time wasn't in response
                now = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
                return (now, 0.0)
예제 #10
0
    def marketSell(self,
                   market: str = "",
                   base_quantity: float = 0,
                   test: bool = False) -> list:
        """Executes a market sell providing a crypto amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError("Binance market is invalid.")

        if not isinstance(base_quantity, int) and not isinstance(
                base_quantity, float):
            raise TypeError("The crypto amount is not numeric.")

        try:
            df_filters = self.getMarketInfoFilters(market)
            step_size = float(df_filters.loc[df_filters["filterType"] ==
                                             "LOT_SIZE"]["stepSize"])
            precision = int(round(-math.log(step_size, 10), 0))

            # remove fees
            base_quantity = base_quantity - (base_quantity *
                                             self.getTradeFee(market))

            # execute market sell
            stepper = 10.0**precision
            truncated = math.trunc(stepper * base_quantity) / stepper

            order = {
                "symbol": market,
                "side": "SELL",
                "type": "MARKET",
                "quantity": truncated,
                "recvWindow": self.recv_window,
            }

            Logger.debug(order)

            # POST /api/v3/order/test
            if test is True:
                resp = self.authAPI("POST", "/api/v3/order/test", order)
            else:
                resp = self.authAPI("POST", "/api/v3/order", order)

            return resp
        except Exception as err:
            ts = datetime.now().strftime("%d-%m-%Y %H:%M:%S")
            Logger.error(f"{ts} Binance  marketSell {str(err)}")
            return []
예제 #11
0
파일: api.py 프로젝트: marioK93/pycryptobot
    def getMakerFee(self, market: str = "") -> float:
        """Retrieves the maker fee"""

        if len(market):
            fees = self.getFees(market)
        else:
            fees = self.getFees()

        if len(fees) == 0 or "maker_fee_rate" not in fees:
            Logger.error(
                f"error: 'maker_fee_rate' not in fees (using {DEFAULT_MAKER_FEE_RATE} as a fallback)"
            )
            return DEFAULT_MAKER_FEE_RATE

        return float(fees["maker_fee_rate"].to_string(index=False).strip())
예제 #12
0
파일: api.py 프로젝트: marioK93/pycryptobot
    def handle_api_error(self, err: str, reason: str) -> dict:
        """Handler for API errors"""

        if self.debug:
            if self.die_on_api_error:
                raise SystemExit(err)
            else:
                Logger.error(err)
                return {}
        else:
            if self.die_on_api_error:
                raise SystemExit(f"{reason}: {self._api_url}")
            else:
                Logger.info(f"{reason}: {self._api_url}")
                return {}
예제 #13
0
    def getTicker(self, market: str = DEFAULT_MARKET) -> tuple:
        """Retrieves the market ticker"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise TypeError("Kucoin market required.")

        resp = self.authAPI("GET",
                            f"api/v1/market/orderbook/level1?symbol={market}")

        if "time" in resp["data"] and "price" in resp["data"]:
            # make sure the time format is correct, if not, pause and submit request again
            trycnt, maxretry = (1, 3)
            while trycnt <= maxretry:
                resptime = ""
                try:
                    resptime = datetime.strptime(
                        str(
                            datetime.fromtimestamp(
                                int(resp["data"]["time"]) / 1000)),
                        "%Y-%m-%d %H:%M:%S.%f",
                    )
                except:
                    Logger.warning(
                        f"Kucoin API Error for Get Ticker: time format not correct - retrying - attempt {trycnt}"
                    )
                    time.sleep(15)
                    resp = self.authAPI(
                        "GET",
                        f"api/v1/market/orderbook/level1?symbol={market}")
                    trycnt += 1
                if resptime != "":
                    break

            return (
                datetime.strptime(
                    str(
                        datetime.fromtimestamp(
                            int(resp["data"]["time"]) / 1000)),
                    "%Y-%m-%d %H:%M:%S.%f",
                ).strftime("%Y-%m-%d %H:%M:%S"),
                float(resp["data"]["price"]),
            )
        else:
            now = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
            return (now, 0.0)
예제 #14
0
    def authAPI(self, method: str, uri: str, payload: str = "") -> dict:
        """Initiates a REST API call"""

        if not isinstance(method, str):
            raise TypeError("Method is not a string.")

        if not method in ["GET", "POST"]:
            raise TypeError("Method not GET or POST.")

        if not isinstance(uri, str):
            raise TypeError("URI is not a string.")

        try:
            if method == "GET":
                resp = requests.get(self._api_url + uri)
            elif method == "POST":
                resp = requests.post(self._api_url + uri, json=payload)

            if resp.status_code != 200:
                resp_message = resp.json()["message"]
                message = f"{method} ({resp.status_code}) {self._api_url}{uri} - {resp_message}"
                if self.die_on_api_error:
                    raise Exception(message)
                else:
                    Logger.error(f"Error: {message}")
                    return {}

            resp.raise_for_status()
            return resp.json()

        except requests.ConnectionError as err:
            Logger.error("requests.ConnectionError")  # remove this later
            return self.handle_api_error(err, "ConnectionError")

        except requests.exceptions.HTTPError as err:
            Logger.error("requests.exceptions.HTTPError")  # remove this later
            return self.handle_api_error(err, "HTTPError")

        except requests.Timeout as err:
            Logger.error("requests.Timeout")  # remove this later
            return self.handle_api_error(err, "Timeout")

        except json.decoder.JSONDecodeError as err:
            Logger.error("json.decoder.JSONDecodeError")  # remove this later
            return self.handle_api_error(err, "JSONDecodeError")
예제 #15
0
 def _read_data(self, name: str = "") -> None:
     file = self.filename if name == "" else name
     try:
         with open(
                 os.path.join(self.app.telegramdatafolder, "telegram_data",
                              file),
                 "r",
                 encoding="utf8",
         ) as json_file:
             self.data = json.load(json_file)
     except (JSONDecodeError, Exception) as err:
         Logger.critical(str(err))
         with open(
                 os.path.join(self.app.telegramdatafolder, "telegram_data",
                              file),
                 "r",
                 encoding="utf8",
         ) as json_file:
             self.data = json.load(json_file)
예제 #16
0
    def marketBuy(self,
                  market: str = "",
                  quote_quantity: float = 0) -> pd.DataFrame:
        """Executes a market buy providing a funding amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError("Coinbase Pro market is invalid.")

        # validates quote_quantity is either an integer or float
        if not isinstance(quote_quantity, int) and not isinstance(
                quote_quantity, float):
            Logger.critical("Please report this to Michael Whittle: " +
                            str(quote_quantity) + " " +
                            str(type(quote_quantity)))
            raise TypeError("The funding amount is not numeric.")

        # funding amount needs to be greater than 10
        if quote_quantity < MINIMUM_TRADE_AMOUNT:
            raise ValueError(
                f"Trade amount is too small (>= {MINIMUM_TRADE_AMOUNT}).")

        try:
            order = {
                "product_id": market,
                "type": "market",
                "side": "buy",
                "funds": self.marketQuoteIncrement(market, quote_quantity),
            }

            Logger.debug(order)

            # connect to authenticated coinbase pro api
            model = AuthAPI(self._api_key, self._api_secret,
                            self._api_passphrase, self._api_url)

            # place order and return result
            return model.authAPI("POST", "orders", order)

        except:
            return pd.DataFrame()
예제 #17
0
    def marketBuy(self,
                  market: str = "",
                  quote_quantity: float = 0) -> pd.DataFrame:
        """Executes a market buy providing a funding amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError("Kucoin market is invalid.")

        # validates quote_quantity is either an integer or float
        if not isinstance(quote_quantity, int) and not isinstance(
                quote_quantity, float):
            Logger.critical("Please report this to Michael Whittle: " +
                            str(quote_quantity) + " " +
                            str(type(quote_quantity)))
            raise TypeError("The funding amount is not numeric.")

        # funding amount needs to be greater than 10
        if quote_quantity < MINIMUM_TRADE_AMOUNT:
            raise ValueError(
                f"Trade amount is too small (>= {MINIMUM_TRADE_AMOUNT}).")

        dt_obj = datetime.strptime(str(datetime.now()), "%Y-%m-%d %H:%M:%S.%f")
        millisec = dt_obj.timestamp() * 1000

        order = {
            "clientOid": str(millisec),
            "symbol": market,
            "type": "market",
            "side": "buy",
            "funds": self.marketQuoteIncrement(market, quote_quantity),
        }

        # Logger.debug(order)

        # connect to authenticated Kucoin api
        model = AuthAPI(self._api_key, self._api_secret, self._api_passphrase,
                        self._api_url)

        # place order and return result
        return model.authAPI("POST", "api/v1/orders", order)
예제 #18
0
    def isSellSignal(self) -> bool:
        # required technical indicators or candle sticks for buy signal strategy
        required_indicators = ["ema12ltema26co", "macdltsignal"]

        for indicator in required_indicators:
            if indicator not in self._df_last:
                raise AttributeError(f"'{indicator}' not in Pandas dataframe")

        # criteria for a sell signal 1
        if (
            bool(self._df_last["ema12ltema26co"].values[0]) is True
            and (
                bool(self._df_last["macdltsignal"].values[0]) is True
                or self.app.disableBuyMACD()
            )
            and self.state.last_action not in ["", "SELL"]
        ):

            Logger.debug("*** Sell Signal ***")
            for indicator in required_indicators:
                Logger.debug(f"{indicator}: {self._df_last[indicator].values[0]}")
            Logger.debug(f"last_action: {self.state.last_action}")

            return True

        return False
예제 #19
0
    def isSellSignal(self) -> bool:
        # required technical indicators or candle sticks for buy signal strategy
        required_indicators = [
            'ema12ltema26co', 'macdltsignal', 'macdltsignalco'
        ]

        for indicator in required_indicators:
            if indicator not in self._df_last:
                raise AttributeError(f"'{indicator}' not in Pandas dataframe")

        # if EMA, MACD are disabled, do not sell
        if self.app.disableBuyEMA() and self.app.disableBuyMACD():
            return False

        # criteria for a sell signal 1
        if (bool(self._df_last['ema12ltema26co'].values[0]) is True or self.app.disableBuyEMA()) \
            and (bool(self._df_last['macdltsignalco'].values[0]) is True or self.app.disableBuyMACD()) \
            and self.state.last_action not in ['', 'SELL']:

            Logger.debug('*** Sell Signal ***')
            for indicator in required_indicators:
                Logger.debug(
                    f'{indicator}: {self._df_last[indicator].values[0]}')
            Logger.debug(f'last_action: {self.state.last_action}')

            return True

        return False
예제 #20
0
    def checkTrailingBuy(self, app, state, price: float = 0.0):
        # If buy signal, save the price and check if it decreases before buying.
        trailing_buy_logtext = ""
        waitpcnttext = ""
        immediate_action = False
        if state.trailing_buy == 0:
            state.waiting_buy_price = price
            pricechange = 0
        elif state.trailing_buy == 1 and state.waiting_buy_price > 0:
            pricechange = ((price - state.waiting_buy_price) /
                           state.waiting_buy_price * 100)
            if price < state.waiting_buy_price:
                state.waiting_buy_price = price
                waitpcnttext += f"Price decreased - resetting wait price. "

        waitpcnttext += f"** {app.getMarket()} - "
        if pricechange < app.getTrailingBuyPcnt(
        ):  # get pcnt from config, if not, use 0%
            state.action = "WAIT"
            state.trailing_buy = 1
            if app.getTrailingBuyPcnt() > 0:
                trailing_buy_logtext = f" - Wait Chg: {_truncate(pricechange,2)}%/{app.getTrailingBuyPcnt()}%"
                waitpcnttext += f"Waiting to buy until {state.waiting_buy_price} increases {app.getTrailingBuyPcnt()}% - change {_truncate(pricechange,2)}%"
            else:
                trailing_buy_logtext = f" - Wait Chg: {_truncate(pricechange,2)}%"
                waitpcnttext += f"Waiting to buy until {state.waiting_buy_price} stops decreasing - change {_truncate(pricechange,2)}%"
        else:
            state.action = "BUY"
            state.trailing_buy = 1
            if app.trailingImmediateBuy():
                immediate_action = True
            trailing_buy_logtext = f" - Ready Chg: {_truncate(pricechange,2)}%/{app.getTrailingBuyPcnt()}%"
            waitpcnttext += f"Ready to buy. {state.waiting_buy_price} change of {_truncate(pricechange,2)}% is above setting of {app.getTrailingBuyPcnt()}%"

        if app.isVerbose() and (not app.isSimulation() or
                                (app.isSimulation()
                                 and not app.simResultOnly())):
            Logger.info(waitpcnttext)

        return state.action, state.trailing_buy, trailing_buy_logtext, immediate_action
    def _read_data(self, name: str = "") -> bool:
        file = self.filename if name == "" else name

        read_ok, try_count = False, 0
        while not read_ok and try_count <= 5:
            try_count += 1
            try:
                with open(
                        os.path.join(self.app.telegramdatafolder,
                                     "telegram_data", file),
                        "r",
                        encoding="utf8",
                ) as json_file:
                    self.data = json.load(json_file)
                read_ok = True
            except FileNotFoundError:
                Logger.warning("File Not Found:  Recreating File..")
                self.create_bot_data()
            except JSONDecodeError:
                if len(self.data) > 0:
                    Logger.warning("JSON Decode Error: Recreating File..")
                    if name == "":
                        self._write_data()
                else:
                    Logger.warning("JSON Decode Error: Removing File..")
                    if name == "":
                        self.removeactivebot()
        return read_ok
예제 #22
0
    def is6hEMA1226Bull(self, iso8601end: str = ''):
        try:
            if self.isSimulation() and isinstance(self.ema1226_6h_cache,
                                                  pd.DataFrame):
                df_data = self.ema1226_6h_cache[(self.ema1226_6h_cache['date']
                                                 <= iso8601end)]
            elif self.exchange == 'coinbasepro':
                api = CBPublicAPI()
                df_data = api.getHistoricalData(self.market, 21600)
                self.ema1226_6h_cache = df_data
            elif self.exchange == 'binance':
                api = BPublicAPI()
                df_data = api.getHistoricalData(self.market, '6h')
                self.ema1226_6h_cache = df_data
            else:
                return False

            ta = TechnicalAnalysis(df_data)

            if 'ema12' not in df_data:
                ta.addEMA(12)

            if 'ema26' not in df_data:
                ta.addEMA(26)

            df_last = ta.getDataFrame().copy().iloc[-1, :]
            df_last['bull'] = df_last['ema12'] > df_last['ema26']

            Logger.debug("---- EMA1226 6H Check----")
            if self.isSimulation():
                Logger.debug("simdate: " + str(df_last['date']))
                Logger.debug("ema12 6h: " + str(df_last['ema12']))
                Logger.debug("ema26 6h: " + str(df_last['ema26']))

            Logger.debug("bull 6h: " +
                         str(df_last['ema12'] > df_last['ema26']))

            return bool(df_last['bull'])
        except Exception:
            return False
예제 #23
0
    def is1hSMA50200Bull(self, iso8601end: str = ''):
        try:
            if self.isSimulation() and isinstance(self.sma50200_1h_cache,
                                                  pd.DataFrame):
                df_data = self.sma50200_1h_cache[(
                    self.sma50200_1h_cache['date'] <= iso8601end)]
            elif self.exchange == 'coinbasepro':
                api = CBPublicAPI()
                df_data = api.getHistoricalData(self.market, 3600)
                self.sma50200_1h_cache = df_data
            elif self.exchange == 'binance':
                api = BPublicAPI()
                df_data = api.getHistoricalData(self.market, '1h')
                self.sma50200_1h_cache = df_data
            else:
                return False

            ta = TechnicalAnalysis(df_data)

            if 'sma50' not in df_data:
                ta.addSMA(50)

            if 'sma200' not in df_data:
                ta.addSMA(200)

            df_last = ta.getDataFrame().copy().iloc[-1, :]

            Logger.debug("---- SMA50200 1H Check----")
            if self.isSimulation():
                Logger.debug("simdate: " + str(df_last['date']))
                Logger.debug("sma50 1h: " + str(df_last['sma50']))
                Logger.debug("sma200 1h: " + str(df_last['sma200']))

            Logger.debug("bull 1h: " +
                         str(df_last['sma50'] > df_last['sma200']))

            df_last['bull'] = df_last['sma50'] > df_last['sma200']
            return bool(df_last['bull'])
        except Exception:
            return False
예제 #24
0
    def marketSell(self,
                   market: str = '',
                   base_quantity: float = 0) -> pd.DataFrame:
        if not self._isMarketValid(market):
            raise ValueError('Coinbase Pro market is invalid.')

        if not isinstance(base_quantity, int) and not isinstance(
                base_quantity, float):
            raise TypeError('The crypto amount is not numeric.')

        order = {
            'product_id': market,
            'type': 'market',
            'side': 'sell',
            'size': self.marketBaseIncrement(market, base_quantity)
        }

        Logger.debug(order)

        model = AuthAPI(self._api_key, self._api_secret, self._api_passphrase,
                        self._api_url)
        return model.authAPI('POST', 'orders', order)
예제 #25
0
    def authAPI(self, method: str, uri: str, payload: str = '') -> dict:
        if not isinstance(method, str):
            raise TypeError('Method is not a string.')

        if not method in ['GET', 'POST']:
            raise TypeError('Method not GET or POST.')

        if not isinstance(uri, str):
            raise TypeError('URI is not a string.')

        try:
            if method == 'GET':
                resp = requests.get(self._api_url + uri)
            elif method == 'POST':
                resp = requests.post(self._api_url + uri, json=payload)

            if resp.status_code != 200:
                resp_message = resp.json()['message']
                message = f"{method} ({resp.status_code}) {self._api_url}{uri} - {resp_message}"
                if self.die_on_api_error:
                    raise Exception(message)
                else:
                    Logger.error(f"Error: {message}")
                    return {}

            resp.raise_for_status()
            return resp.json()

        except requests.ConnectionError as err:
            return self.handle_api_error(err, "ConnectionError")

        except requests.exceptions.HTTPError as err:
            return self.handle_api_error(err, "HTTPError")

        except requests.Timeout as err:
            return self.handle_api_error(err, "Timeout")

        except json.decoder.JSONDecodeError as err:
            return self.handle_api_error(err, "JSONDecodeError")
예제 #26
0
    def marketBuy(self, market: str = '', quote_quantity: float = 0) -> list:
        """Executes a market buy providing a funding amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError('Binance market is invalid.')

        # validates quote_quantity is either an integer or float
        if not isinstance(quote_quantity, int) and not isinstance(
                quote_quantity, float):
            raise TypeError('The funding amount is not numeric.')

        try:
            current_price = self.getTicker(market)[1]

            base_quantity = np.divide(quote_quantity, current_price)

            df_filters = self.getMarketInfoFilters(market)
            step_size = float(df_filters.loc[df_filters['filterType'] ==
                                             'LOT_SIZE']['stepSize'])
            precision = int(round(-math.log(step_size, 10), 0))

            # remove fees
            base_quantity = base_quantity - (base_quantity *
                                             self.getTradeFee(market))

            # execute market buy
            stepper = 10.0**precision
            truncated = math.trunc(stepper * base_quantity) / stepper
            Logger.info('Order quantity after rounding and fees: ' +
                        str(truncated))

            return self.client.order_market_buy(symbol=market,
                                                quantity=truncated)
        except Exception as err:
            ts = datetime.now().strftime("%d-%m-%Y %H:%M:%S")
            Logger.error(ts + ' Binance ' + ' marketBuy ' + str(err))
            return []
예제 #27
0
    def marketBuy(self,
                  market: str = '',
                  quote_quantity: float = 0) -> pd.DataFrame:
        """Executes a market buy providing a funding amount"""

        # validates the market is syntactically correct
        if not self._isMarketValid(market):
            raise ValueError('Coinbase Pro market is invalid.')

        # validates quote_quantity is either an integer or float
        if not isinstance(quote_quantity, int) and not isinstance(
                quote_quantity, float):
            Logger.critical('Please report this to Michael Whittle: ' +
                            str(quote_quantity) + ' ' +
                            str(type(quote_quantity)))
            raise TypeError('The funding amount is not numeric.')

        # funding amount needs to be greater than 10
        if quote_quantity < MINIMUM_TRADE_AMOUNT:
            raise ValueError(
                f"Trade amount is too small (>= {MINIMUM_TRADE_AMOUNT}).")

        order = {
            'product_id': market,
            'type': 'market',
            'side': 'buy',
            'funds': self.marketQuoteIncrement(market, quote_quantity)
        }

        Logger.debug(order)

        # connect to authenticated coinbase pro api
        model = AuthAPI(self._api_key, self._api_secret, self._api_passphrase,
                        self._api_url)

        # place order and return result
        return model.authAPI('POST', 'orders', order)
예제 #28
0
    def printSupportResistanceLevel(self, price: float=0) -> None:
        if isinstance(price, int) or isinstance(price, float):
            df = self.getSupportResistanceLevels()

            if len(df) > 0:
                df_last = df.tail(1)
                if float(df_last[0]) < price:
                    Logger.info(' Support level of ' + str(df_last[0]) + ' formed at ' + str(df_last.index[0]))
                elif float(df_last[0]) > price:
                    Logger.info(' Resistance level of ' + str(df_last[0]) + ' formed at ' + str(df_last.index[0]))
                else:
                    Logger.info(' Support/Resistance level of ' + str(df_last[0]) + ' formed at ' + str(df_last.index[0]))
예제 #29
0
    def getTime(self) -> datetime:
        """Retrieves the exchange time"""

        try:
            resp = self.authAPI("GET", "time")
            if "epoch" in resp:
                epoch = int(resp["epoch"])
                return datetime.fromtimestamp(epoch)
            else:
                Logger.error(
                    "resp does not contain the epoch key for some reason!"
                )  # remove this later
                Logger.error(resp)
                return None
        except Exception as e:
            Logger.error(f"Error: {e}")
            return None
예제 #30
0
    def isBuySignal(self,
                    now: datetime = datetime.today().strftime(
                        '%Y-%m-%d %H:%M:%S'),
                    price: float = 0.0) -> bool:
        # required technical indicators or candle sticks for buy signal strategy
        required_indicators = [
            'ema12gtema26co', 'macdgtsignal', 'goldencross', 'obv_pc',
            'eri_buy'
        ]

        for indicator in required_indicators:
            if indicator not in self._df_last:
                raise AttributeError(f"'{indicator}' not in Pandas dataframe")

        # buy signal exclusion (if disabled, do not buy within 3% of the dataframe close high)
        if self.state.last_action == 'SELL' and self.app.disableBuyNearHigh(
        ) is True and (price > (self._df['close'].max() * 0.97)):
            log_text = str(now) + ' | ' + self.app.getMarket(
            ) + ' | ' + self.app.printGranularity(
            ) + ' | Ignoring Buy Signal (price ' + str(
                price) + ' within 3% of high ' + str(
                    self._df['close'].max()) + ')'
            Logger.warning(log_text)

            return False

        # criteria for a buy signal 1
        if bool(self._df_last['ema12gtema26co'].values[0]) is True \
                and (bool(self._df_last['macdgtsignal'].values[0]) is True or self.app.disableBuyMACD()) \
                and (bool(self._df_last['goldencross'].values[0]) is True or self.app.disableBullOnly()) \
                and (float(self._df_last['obv_pc'].values[0]) > -5 or self.app.disableBuyOBV()) \
                and (bool(self._df_last['eri_buy'].values[0]) is True or self.app.disableBuyElderRay()) \
                and self.state.last_action != 'BUY': # required for all strategies

            Logger.debug('*** Buy Signal ***')
            for indicator in required_indicators:
                Logger.debug(
                    f'{indicator}: {self._df_last[indicator].values[0]}')
            Logger.debug(f'last_action: {self.state.last_action}')

            return True

        # criteria for buy signal 2 (optionally add additional buy singals)
        elif bool(self._df_last['ema12gtema26co'].values[0]) is True \
                and bool(self._df_last['macdgtsignalco'].values[0]) is True \
                and (bool(self._df_last['goldencross'].values[0]) is True or self.app.disableBullOnly()) \
                and (float(self._df_last['obv_pc'].values[0]) > -5 or self.app.disableBuyOBV()) \
                and (bool(self._df_last['eri_buy'].values[0]) is True or self.app.disableBuyElderRay()) \
                and self.state.last_action != 'BUY': # required for all strategies

            Logger.debug('*** Buy Signal ***')
            for indicator in required_indicators:
                Logger.debug(
                    f'{indicator}: {self._df_last[indicator].values[0]}')
            Logger.debug(f'last_action: {self.state.last_action}')

            return True

        return False