Example #1
0
def login2(data,
           payload,
           url,
           pickle_path,
           device_token,
           auth_key,
           store_session=True):
    if data:
        if 'mfa_required' in data:
            mfa_token = auth_key
            payload['mfa_code'] = mfa_token
            res = helper.request_post(url, payload, jsonify_data=False)
            if (res.status_code != 200):
                raise Exception(
                    "That MFA code was not correct. Please type in another MFA code."
                )
            data = res.json()
        elif 'challenge' in data:
            challenge_id = data['challenge']['id']
            sms_code = auth_key
            res = respond_to_challenge(challenge_id, sms_code)
            if 'challenge' in res and res['challenge'][
                    'remaining_attempts'] > 0:
                raise Exception(
                    'That code was not correct. {0} tries remaining. Please type in another code: '
                    .format(res['challenge']['remaining_attempts']))

            helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID',
                                  challenge_id)
            data = helper.request_post(url, payload)
        # Update Session data with authorization or raise exception with the information present in data.
        if 'access_token' in data:
            token = '{0} {1}'.format(data['token_type'], data['access_token'])
            helper.update_session('Authorization', token)
            helper.set_login_state(True)
            data['detail'] = "logged in with brand new authentication code."
            if store_session:
                with open(pickle_path, 'wb') as f:
                    pickle.dump(
                        {
                            'token_type': data['token_type'],
                            'access_token': data['access_token'],
                            'refresh_token': data['refresh_token'],
                            'device_token': device_token
                        }, f)
        else:
            raise Exception(data['detail'])
    else:
        raise Exception(
            'Error: Trouble connecting to robinhood API. Check internet connection.'
        )
    return True, (data)
Example #2
0
def cancel_all_open_option_orders():
    """Cancels all open orders.

    :returns: The list of orders that were cancelled.

    """
    items = get_all_open_option_orders()
    for item in items:
        cancel_url = item.get('cancel_url')
        helper.request_post(cancel_url)

    print('All Orders Cancelled')
    return items
Example #3
0
def login(username, password, expiresIn=86400, scope='internal'):
    """This function will effectivly log the user into robinhood by getting an
    authentication token and saving it to the session header.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :returns:  A dictionary with log in information.

    """
    if not username or not password:
        raise Exception('login must be called with a non-empty username and '
                        'password')

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username
    }
    data = helper.request_post(url, payload)
    token = 'Bearer {}'.format(data['access_token'])
    helper.update_session('Authorization', token)
    helper.set_login_state(True)
    return (data)
Example #4
0
def delete_symbols_from_watchlist(inputSymbols, name="My First List"):
    """Deletes multiple stock tickers from a watchlist.

    :param inputSymbols: May be a single stock ticker or a list of stock tickers.
    :type inputSymbols: str or list
    :param name: The name of the watchlist to delete data from.
    :type name: Optional[str]
    :returns: Returns result of the delete request.

    """
    symbols = helper.inputs_to_set(inputSymbols)
    ids = stocks.get_instruments_by_symbols(symbols, info='id')
    data = []

    #Get id of requested watchlist
    all_watchlists = get_all_watchlists()
    watchlist_id = ''
    for wl in all_watchlists['results']:
        if wl['display_name'] == name:
            watchlist_id = wl['id']

    for id in ids:
        payload = {
            watchlist_id: [{
                "object_type": "instrument",
                "object_id": id,
                "operation": "delete"
            }]
        }
        url = urls.watchlists(name, True)
        data.append(helper.request_post(url, payload, json=True))

    return (data)
Example #5
0
def generate_challenge(user: str, passwd: str, device_token: str, sendviasms: bool) -> str:
    """
    Generate a 2FA challenge ID by logging into Robinhood with the user/pass.
    
    This is a stripped down version of the robin_stocks.authentication module.
    """
    if sendviasms:
        challenge_type = "sms"
    else:
        challenge_type = "email"
    
    url = urls.login_url()
    
    #Client ID appears to be a hardcoded magic number from client requests?
    #Something to watch out for. Could be related to User-Agent & app version.
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': 86400,
        'grant_type': 'password',
        'password': passwd,
        'scope': 'internal',
        'username': user,
        'challenge_type': challenge_type,
        'device_token': device_token
    }
    
    #Initial query to create a challenge request.
    data = helper.request_post(url, payload)
    
    try:
        return data['challenge']['id']
    except KeyError:
        return ""
Example #6
0
def order(symbol, quantity, orderType, trigger, side, limitPrice = None, stopPrice = None, timeInForce = 'gtc', extendedHours = False):
    """A generic order function. All parameters must be supplied.

    :param symbol: The stock ticker of the stock to sell.
    :type symbol: str
    :param quantity: The number of stocks to sell.
    :type quantity: int
    :param orderType: Either 'market' or 'limit'
    :type orderType: str
    :param trigger: Either 'immediate' or 'stop'
    :type trigger: str
    :param side: Either 'buy' or 'sell'
    :type side: str
    :param limitPrice: The price to trigger the market order.
    :type limitPrice: float
    :param stopPrice: The price to trigger the limit or market order.
    :type stopPrice: float
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: str
    :param extendedHours: Premium users only. Allows trading during extended hours. Should be true or false.
    :type extendedHours: Optional[str]
    :returns: Dictionary that contains information regarding the purchase or selling of stocks, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
    except AttributeError as message:
        print(message)
        return None

    if stopPrice:
        stopPrice = helper.round_price(stopPrice)

    if limitPrice:
        limitPrice = helper.round_price(limitPrice)
    else:
        limitPrice = helper.round_price(stocks.get_latest_price(symbol)[0])
    payload = {
    'account': profiles.load_account_profile(info='url'),
    'instrument': stocks.get_instruments_by_symbols(symbol, info='url')[0],
    'symbol': symbol,
    'price': limitPrice,
    'quantity': quantity,
    'ref_id': str(uuid4()),
    'type': orderType,
    'stop_price': stopPrice,
    'time_in_force': timeInForce,
    'trigger': trigger,
    'side': side,
    'extended_hours': extendedHours
    }

    url = urls.orders()
    data = helper.request_post(url, payload)

    return(data)
Example #7
0
def cancel_all_open_orders():
    """Cancels all open orders.

    :returns: The list of orders that were cancelled.

    """
    url = urls.orders()
    items = helper.request_get(url, 'pagination')

    items = [item['id'] for item in items if item['cancel'] is not None]

    for item in items:
        cancel_url = urls.cancel(item)
        helper.request_post(cancel_url)

    print('All Orders Cancelled')
    return (items)
Example #8
0
def order_sell_stop_limit(symbol,
                          quantity,
                          limitPrice,
                          stopPrice,
                          timeInForce='gtc',
                          extendedHours=False):
    """Submits a stop order to be turned into a limit order once a certain stop price is reached.

    :param symbol: The stock ticker of the stock to sell.
    :type symbol: str
    :param quantity: The number of stocks to sell.
    :type quantity: int
    :param limitPrice: The price to trigger the market order.
    :type limitPrice: float
    :param stopPrice: The price to trigger the limit order.
    :type stopPrice: float
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :param extendedHours: Premium users only. Allows trading during extended hours. Should be true or false.
    :type extendedHours: Optional[str]
    :returns: Dictionary that contains information regarding the selling of stocks, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
        latestPrice = helper.round_price(stocks.get_latest_price(symbol)[0])
        stopPrice = helper.round_price(stopPrice)
        limitPrice = helper.round_price(limitPrice)
    except AttributeError as message:
        print(message)
        return None

    if (latestPrice < stopPrice):
        print('Error: stopPrice must be below the current price.')
        return (None)

    payload = {
        'account': profiles.load_account_profile(info='url'),
        'instrument': stocks.get_instruments_by_symbols(symbol, info='url')[0],
        'symbol': symbol,
        'price': limitPrice,
        'quantity': quantity,
        'ref_id': str(uuid4()),
        'type': 'limit',
        'stop_price': stopPrice,
        'time_in_force': timeInForce,
        'trigger': 'stop',
        'side': 'sell',
        'extended_hours': extendedHours
    }

    url = urls.orders()
    data = helper.request_post(url, payload)

    return (data)
Example #9
0
def order_sell_option_limit(positionEffect, price, symbol, quantity, expirationDate, strike, optionType='both', timeInForce='gfd'):
    """Submits a limit order for an option. i.e. place a short call or a short put.

    :param positionEffect: Either 'open' for a sell to open effect or 'close' for a sell to close effect.
    :type positionEffect: str
    :param price: The limit price to trigger a sell of the option.
    :type price: float
    :param symbol: The stock ticker of the stock to trade.
    :type symbol: str
    :param quantity: The number of options to sell.
    :type quantity: int
    :param expirationDate: The expiration date of the option in 'YYYY-MM-DD' format.
    :type expirationDate: str
    :param strike: The strike price of the option.
    :type strike: float
    :param optionType: This should be 'call' or 'put'
    :type optionType: str
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the selling of options, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
    except AttributeError as message:
        print(message)
        return None

    optionID = helper.id_for_option(symbol, expirationDate, strike, optionType)
	
    if (positionEffect == 'close'):
        direction = 'debit'
    else:
        direction = 'credit'

    payload = {
    'account': profiles.load_account_profile(info='url'),
    'direction': direction,
    'time_in_force': timeInForce,
    'legs': [
        {'position_effect': positionEffect, 'side' : 'sell', 'ratio_quantity': 1, 'option': urls.option_instruments(optionID) },
    ],
    'type': 'limit',
    'trigger': 'immediate',
    'price': price,
    'quantity': quantity,
    'override_day_trade_checks': False,
	'override_dtbp_checks': False,
    'ref_id': str(uuid4()),
    }

    url = urls.option_orders()
    data = helper.request_post(url, payload, json=True)

    return(data)
Example #10
0
def order_option_spread(direction, price, symbol, quantity, spread, timeInForce='gfd'):
    """Submits a limit order for an option spread. i.e. place a debit / credit spread

    :param direction: credit or debit spread
    :type direction: str
    :param price: The limit price to trigger a trade of the option.
    :type price: float
    :param symbol: The stock ticker of the stock to trade.
    :type symbol: str
    :param quantity: The number of options to trade.
    :type quantity: int
    :param spread: A dictionary of spread options with the following keys: \n
        - expirationDate: The expiration date of the option in 'YYYY-MM-DD' format.\n
        - strike: The strike price of the option.\n
        - optionType: This should be 'call' or 'put'
    :type spread: dict
    :param timeInForce: Changes how long the order will be in effect for.
     'gtc' = good until cancelled. \
     'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the selling of options, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.
    """
    try:
        symbol = symbol.upper().strip()
    except AttributeError as message:
        print(message)
        return None
    legs = []
    for each in spread:
        optionID = helper.id_for_option(symbol,
                                        each['expirationDate'],
                                        each['strike'],
                                        each['optionType'])
        legs.append({'position_effect': each['effect'],
                     'side' : each['action'],
                     'ratio_quantity': 1,
                     'option': urls.option_instruments(optionID)})

    payload = {
        'account': profiles.load_account_profile(info='url'),
        'direction': direction,
        'time_in_force': timeInForce,
        'legs': legs,
        'type': 'limit',
        'trigger': 'immediate',
        'price': price,
        'quantity': quantity,
        'override_day_trade_checks': False,
        'override_dtbp_checks': False,
        'ref_id': str(uuid4()),
    }

    url = urls.option_orders()
    data = helper.request_post(url,payload, json=True)

    return(data)
Example #11
0
def order_option_by_id(option_id,
                       price,
                       quantity,
                       direction='credit',
                       effect='close',
                       side='sell',
                       time_in_force='gfd'):
    """

    :param option_id:
    :param price:
    :param quantity:
    :param direction:
    :param effect:
    :param side:
    :param time_in_force:
    :return:
    """

    payload = {
        'account':
        profiles.load_account_profile(info='url'),
        'direction':
        direction,
        'time_in_force':
        time_in_force,
        'legs': [
            {
                'position_effect': effect,
                'side': side,
                'ratio_quantity': 1,
                'option': urls.option_instruments(option_id)
            },
        ],
        'type':
        'limit',
        'trigger':
        'immediate',
        'price':
        price,
        'quantity':
        quantity,
        'override_day_trade_checks':
        False,
        'override_dtbp_checks':
        False,
        'ref_id':
        str(uuid4()),
    }

    url = urls.option_orders()
    data = helper.request_post(url, payload, json=True)
    print(data)
    return data
Example #12
0
def order_buy_crypto_by_price(symbol,
                              amountInDollars,
                              priceType='ask_price',
                              timeInForce='gtc'):
    """Submits a market order for a crypto by specifying the amount in dollars that you want to trade.
    Good for share fractions up to 8 decimal places.

    :param symbol: The crypto ticker of the crypto to trade.
    :type symbol: str
    :param amountInDollars: The amount in dollars of the crypto you want to buy.
    :type amountInDollars: float
    :param priceType: The type of price to get. Can be 'ask_price', 'bid_price', or 'mark_price'
    :type priceType: str
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the selling of options, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
    except AttributeError as message:
        print(message)
        return None

    crypto_info = crypto.get_crypto_info(symbol)
    price = round(
        float(
            crypto.get_crypto_quote_from_id(crypto_info['id'],
                                            info=priceType)), 8)
    # turn the money amount into decimal number of shares
    try:
        shares = round(amountInDollars / float(price), 8)
    except:
        shares = 0

    payload = {
        'mimeType': 'application/json',
        'account_id': crypto.load_crypto_profile(info="id"),
        'currency_pair_id': crypto_info['id'],
        'price': price,
        'quantity': shares,
        'ref_id': str(uuid4()),
        'side': 'buy',
        'time_in_force': timeInForce,
        'type': 'market'
    }

    url = urls.order_crypto()
    data = helper.request_post(url, payload, json=True)

    return (data)
Example #13
0
def unlink_bank_account(id):
    """Unlinks a bank account.

    :param id: The bank id.
    :type id: str
    :returns: Information returned from post request.

    """
    url = urls.linked(id, True)
    data = helper.request_post(url)
    return(data)
Example #14
0
def _post_to_login(username, password, challenge_type='sms'):
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': TOKEN_DURATION,
        'grant_type': 'password',
        'password': password,
        'scope': 'internal',
        'username': username,
        'challenge_type': challenge_type,
        'device_token': '3559a85e-0726-eec9-e83b-6c6803d6ddad'
    }  # TODO: make device-specific.
    return helper.request_post(urls.login_url(), payload)
Example #15
0
def cancel_option_order(orderID):
    """Cancels a specific option order.
    :param orderID: The ID associated with the order. Can be found using get_all_orders(info=None) or get_all_orders(info=None).
    :type orderID: str
    :returns: Returns the order information for the order that was cancelled.
    """
    url = urls.option_cancel(orderID)
    data = helper.request_post(url)

    if data:
        print('Order ' + orderID + ' cancelled')
    return (data)
Example #16
0
def refresh_token(refresh_token=None):
    url = "https://api.robinhood.com/oauth2/token/"
    payload = {
        "grant_type": "refresh_token",
        "refresh_token": refresh_token,
        "scope": "internal",
        "client_id": "c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS",
        "expires_in": 734000,
    }
    helper.update_session('Authorization', None)
    data = helper.request_post(url, payload)
    return data
Example #17
0
def respond_to_challenge(challenge_id, sms_code):
    """This functino will post to the challenge url.

    :param challenge_id: The challenge id.
    :type challenge_id: str
    :param sms_code: The sms code.
    :type sms_code: str
    :returns:  The response from requests.

    """
    url = urls.challenge_url(challenge_id)
    payload = {'response': sms_code}
    return (helper.request_post(url, payload))
Example #18
0
def order_buy_stop_limit(symbol,quantity,limitPrice,stopPrice,timeInForce='gtc'):
    """Submits a stop order to be turned into a limit order once a certain stop price is reached.

    :param symbol: The stock ticker of the stock to purchase.
    :type symbol: str
    :param quantity: The number of stocks to buy.
    :type quantity: int
    :param limitPrice: The price to trigger the market order.
    :type limitPrice: float
    :param stopPrice: The price to trigger the limit order.
    :type stopPrice: float
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the purchase of stocks, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
        latestPrice = float(stocks.get_latest_price(symbol)[0])
        stopPrice = float(stopPrice)
        limitPrice = float(limitPrice)
    except AttributeError as message:
        print(message)
        return None

    if (latestPrice > stopPrice):
        print('Error: stopPrice must be above the current price.')
        return(None)

    payload = {
    'account': profiles.load_account_profile(info='url'),
    'instrument': stocks.get_instruments_by_symbols(symbol,info='url')[0],
    'symbol': symbol,
    'price': limitPrice,
    'quantity': quantity,
    'ref_id': helper.get_device_token(),
    'type': 'limit',
    'stop_price': stopPrice,
    'time_in_force': timeInForce,
    'trigger': 'stop',
    'side': 'buy'
    }

    url = urls.orders()
    data = helper.request_post(url,payload)

    return(data)
Example #19
0
def post_symbols_to_watchlist(inputSymbols, name='Default'):
    """Posts multiple stock tickers to a watchlist.

    :param inputSymbols: May be a single stock ticker or a list of stock tickers.
    :type inputSymbols: str or list
    :param name: The name of the watchlist to post data to.
    :type name: Optional[str]
    :returns: Returns result of the post request.

    """
    symbols = helper.inputs_to_set(inputSymbols)
    payload = {'symbols': ','.join(symbols)}
    url = urls.watchlists(name, True)
    data = helper.request_post(url, payload)

    return (data)
Example #20
0
def post_symbols_to_watchlist(*inputSymbols, name='Default'):
    """Posts multiple stock tickers to a watchlist.

    :param inputSymbols: This is a variable length parameter that represents a stock ticker. \
    May be several tickers seperated by commas or a list of tickers.
    :type inputSymbols: str or list
    :param name: The name of the watchlist to post data to.
    :type name: Optional[str]
    :returns: Returns result of the post request.

    """
    symbols = helper.inputs_to_set(inputSymbols)
    payload = {'symbols': ','.join(symbols)}
    url = urls.watchlists(name, True)
    data = helper.request_post(url, payload)

    return (data)
Example #21
0
def order_sell_crypto_limit(symbol, quantity, price, timeInForce='gtc'):
    """Submits a limit order for a crypto by specifying the decimal amount of shares to sell.
    Good for share fractions up to 8 decimal places.

    :param symbol: The crypto ticker of the crypto to trade.
    :type symbol: str
    :param quantity: The decimal amount of shares to sell.
    :type quantity: float
    :param price: The limit price to set for the crypto.
    :type price: float
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the selling of crypto, \
    such as the order id, the state of order (queued, confired, filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
    except AttributeError as message:
        print(message)
        return None

    crypto_info = crypto.get_crypto_info(symbol)

    if crypto_info['display_only']:
        print(
            "WARNING: The dictionary returned by crypto.get_crypto_info() for this crypto has key 'display_only' set to True. May not be able to trade this crypto."
        )

    payload = {
        'account_id': crypto.load_crypto_profile(info="id"),
        'currency_pair_id': crypto_info['id'],
        'price': price,
        'quantity': quantity,
        'ref_id': str(uuid4()),
        'side': 'sell',
        'time_in_force': timeInForce,
        'type': 'limit'
    }

    url = urls.order_crypto()
    data = helper.request_post(url, payload, json=True)

    return (data)
Example #22
0
def order_buy_market(symbol,
                     quantity,
                     timeInForce='gtc',
                     extendedHours='false'):
    """Submits a market order to be executed immediately.

    :param symbol: The stock ticker of the stock to purchase.
    :type symbol: str
    :param quantity: The number of stocks to buy.
    :type quantity: int
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :param extendedHours: Premium users only. Allows trading during extended hours. Should be true or false.
    :type extendedHours: str
    :returns: Dictionary that contains information regarding the purchase of stocks, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
    except AttributeError as message:
        print(message)
        return None

    payload = {
        'account': profiles.load_account_profile(info='url'),
        'instrument': stocks.get_instruments_by_symbols(symbol, info='url')[0],
        'symbol': symbol,
        'price': float(stocks.get_latest_price(symbol)[0]),
        'quantity': quantity,
        'ref_id': str(uuid4()),
        'type': 'market',
        'stop_price': None,
        'time_in_force': timeInForce,
        'trigger': 'immediate',
        'side': 'buy',
        "extended_hours": extendedHours
    }

    url = urls.orders()
    data = helper.request_post(url, payload)

    return (data)
Example #23
0
def order_sell_crypto_by_quantity(symbol,
                                  quantity,
                                  priceType='ask_price',
                                  timeInForce='gtc'):
    """Submits a market order for a crypto by specifying the decimal amount of shares to buy.
    Good for share fractions up to 8 decimal places.

    :param symbol: The crypto ticker of the crypto to trade.
    :type symbol: str
    :param quantity: The decimal amount of shares to sell.
    :type quantity: float
    :param priceType: The type of price to get. Can be 'ask_price', 'bid_price', or 'mark_price'
    :type priceType: str
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the selling of options, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """

    crypto_info = crypto.get_crypto_info(symbol)
    price = round(
        float(
            crypto.get_crypto_quote_from_id(crypto_info['id'],
                                            info=priceType)), 8)
    print("pice is ", price)

    payload = {
        'account_id': crypto.load_crypto_profile(info="id"),
        'currency_pair_id': crypto_info['id'],
        'price': price,
        'quantity': quantity,
        'ref_id': str(uuid4()),
        'side': 'sell',
        'time_in_force': timeInForce,
        'type': 'market'
    }

    url = urls.order_crypto()
    data = helper.request_post(url, payload, json=True)

    return (data)
Example #24
0
def order_sell_limit(symbol,quantity,limitPrice,timeInForce='gtc'):
    """Submits a limit order to be executed once a certain price is reached.

    :param symbol: The stock ticker of the stock to sell.
    :type symbol: str
    :param quantity: The number of stocks to sell.
    :type quantity: int
    :param limitPrice: The price to trigger the sell order.
    :type limitPrice: float
    :param timeInForce: Changes how long the order will be in effect for. 'gtc' = good until cancelled. \
    'gfd' = good for the day. 'ioc' = immediate or cancel. 'opg' execute at opening.
    :type timeInForce: Optional[str]
    :returns: Dictionary that contains information regarding the selling of stocks, \
    such as the order id, the state of order (queued,confired,filled, failed, canceled, etc.), \
    the price, and the quantity.

    """
    try:
        symbol = symbol.upper().strip()
        limitPrice = helper.round_price(limitPrice)
    except AttributeError as message:
        print(message)
        return None

    payload = {
    'account': profiles.load_account_profile(info='url'),
    'instrument': stocks.get_instruments_by_symbols(symbol,info='url')[0],
    'symbol': symbol,
    'price': limitPrice,
    'quantity': quantity,
    'ref_id': str(uuid4()),
    'type': 'limit',
    'stop_price': None,
    'time_in_force': timeInForce,
    'trigger': 'immediate',
    'side': 'sell'
    }

    url = urls.orders()
    data = helper.request_post(url,payload)

    return(data)
Example #25
0
def base_login(username,password,device_token,expiresIn=86400,scope='internal',by_sms=True):
    """This function will try to log the user in and will return the response data.
    It may contain a challenge (sms) or the access token.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param device_token: The device_token you should re-use (can be saved with "robin_stocks.get_new_device_token()").
    :type device_token: str
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :param by_sms: Specifies whether to send an email(False) or an sms(True)
    :type by_sms: Optional[boolean]
    :returns:  A dictionary with response information.


    """
    if not username or not password:
        raise Exception('login must be called with a non-empty username and '
            'password')

    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
    'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
    'expires_in': expiresIn,
    'grant_type': 'password',
    'password': password,
    'scope': scope,
    'username': username,
    'challenge_type': challenge_type,
    'device_token': device_token
    }
    data = helper.request_post(url,payload)
    return(data)
Example #26
0
def process_login(username=None, password=None, device_token=None, mfa_code=None, challenge_id=None):
    expires_in = 86400
    scope = 'internal'
    challenge_type = 'sms'

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expires_in,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }
    if challenge_id != None:
        helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
    data = helper.request_post(url, payload)
    return data
Example #27
0
def deposit_funds_to_robinhood_account(ach_relationship, amount, info=None):
    """Submits a post request to deposit a certain amount of money from a bank account to Robinhood.

    :param ach_relationship: The url of the bank account you want to deposit the money from.
    :type ach_relationship: str
    :param amount: The amount of money you wish to deposit.
    :type amount: float
    :param info: Will filter the results to get a specific value.
    :type info: Optional[str]
    :returns: Returns a list of dictionaries of key/value pairs for the transaction.

    """
    url = urls.banktransfers()
    payload = {
        "amount": amount,
        "direction": "deposit",
        "ach_relationship": ach_relationship,
        "ref_id": str(uuid4())
    }
    data = helper.request_post(url, payload)
    return (helper.filter_data(data, info))
def login(username, password, expiresIn=86400, scope='internal', by_sms=True):
    device_token = authentication.generate_device_token()

    # Challenge type is used if not logging in with two-factor authentication.
    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }
    data = helper.request_post(url, payload)
    payload['login_response'] = data
    return payload
Example #29
0
def login(username, password, expiresIn=86400, scope='internal'):
    """This function will effectivly log the user into robinhood by getting an
    authentication token and saving it to the session header.

    :param name: The username.
    :type name: str
    :param password: The password.
    :type state: str
    :returns:  A dictionary with log in information.

    """
    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username
    }
    data = helper.request_post(url, payload)
    token = 'Bearer {}'.format(data['access_token'])
    helper.update_session('Authorization', token)
    return (data)
Example #30
0
def login(username,
          password,
          expiresIn=86400,
          scope='internal',
          by_sms=True,
          store_session=True):
    """This function will effectivly log the user into robinhood by getting an
    authentication token and saving it to the session header. By default, it will store the authentication
    token in a pickle file and load that value on subsequent logins.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :param by_sms: Specifies whether to send an email(False) or an sms(True)
    :type by_sms: Optional[boolean]
    :param store_session: Specifies whether to save the log in authorization for future log ins.
    :type store_session: Optional[boolean]
    :returns:  A dictionary with log in information. The 'access_token' keyword contains the access token, and the 'detail' keyword \
    contains information on whether the access token was generated or loaded from pickle file.

    """
    device_token = generate_device_token()
    dir_path = os.path.dirname(os.path.realpath(__file__))
    pickle_path = os.path.join(dir_path, "data.pickle")
    # Challenge type is used if not logging in with two-factor authentication.
    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }
    # If authentication has been stored in pickle file then load it. Stops login server from being pinged so much.
    if os.path.isfile(pickle_path):
        # If store_session has been set to false then delete the pickle file, otherwise try to load it.
        # Loading pickle file will fail if the acess_token has expired.
        if store_session:
            try:
                with open(pickle_path, 'rb') as f:
                    pickle_data = pickle.load(f)
                    access_token = pickle_data['access_token']
                    token_type = pickle_data['token_type']
                    refresh_token = pickle_data['refresh_token']
                    # Set device_token to be the original device token when first logged in.
                    pickle_device_token = pickle_data['device_token']
                    payload['device_token'] = pickle_device_token
                    # Set login status to True in order to try and get account info.
                    helper.set_login_state(True)
                    helper.update_session(
                        'Authorization',
                        '{0} {1}'.format(token_type, access_token))
                    # Try to load account profile to check that authorization token is still valid.
                    res = helper.request_get(urls.portfolio_profile(),
                                             'regular',
                                             payload,
                                             jsonify_data=False)
                    # Raises exception is response code is not 200.
                    res.raise_for_status()
                    return ({
                        'access_token': access_token,
                        'token_type': token_type,
                        'expires_in': expiresIn,
                        'scope': scope,
                        'detail':
                        'logged in using authentication in data.pickle',
                        'backup_code': None,
                        'refresh_token': refresh_token
                    })
            except:
                print(
                    "ERROR: There was an issue loading pickle file. Authentication may be expired - logging in normally."
                )
                helper.set_login_state(False)
                helper.update_session('Authorization', None)
        else:
            os.remove(pickle_path)
    # Try to log in normally.
    data = helper.request_post(url, payload)
    # Handle case where mfa or challenge is required.
    if 'mfa_required' in data:
        mfa_token = input("Please type in the MFA code: ")
        payload['mfa_code'] = mfa_token
        res = helper.request_post(url, payload, jsonify_data=False)
        while (res.status_code != 200):
            mfa_token = input(
                "That MFA code was not correct. Please type in another MFA code: "
            )
            payload['mfa_code'] = mfa_token
            res = helper.request_post(url, payload, jsonify_data=False)
        data = res.json()
    elif 'challenge' in data:
        challenge_id = data['challenge']['id']
        sms_code = input('Enter Robinhood code for validation: ')
        res = respond_to_challenge(challenge_id, sms_code)
        while 'challenge' in res and res['challenge']['remaining_attempts'] > 0:
            sms_code = input(
                'That code was not correct. {0} tries remaining. Please type in another code: '
                .format(res['challenge']['remaining_attempts']))
            res = respond_to_challenge(challenge_id, sms_code)
        helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID',
                              challenge_id)
        data = helper.request_post(url, payload)
    # Update Session data with authorization or raise exception with the information present in data.
    if 'access_token' in data:
        token = '{0} {1}'.format(data['token_type'], data['access_token'])
        helper.update_session('Authorization', token)
        helper.set_login_state(True)
        data['detail'] = "logged in with brand new authentication code."
        if store_session:
            with open(pickle_path, 'wb') as f:
                pickle.dump(
                    {
                        'token_type': data['token_type'],
                        'access_token': data['access_token'],
                        'refresh_token': data['refresh_token'],
                        'device_token': device_token
                    }, f)
    else:
        raise Exception(data['detail'])
    return (data)