Search code examples
pythonaws-lambdapython-requestsamazon-dynamodb

Stumped on AWS Lambda event and context function error


I've been trying to develop a trading bot using AWS Lambda, DynamoDB, and Eventbridge. However, I've been running into some weird issues with trying to run the test environment in order to ensure that my code works. I'm quite new to the AWS environment and would appreciate any help I can get.

I am trying to let AWS Lambda invoke the lambda_hander by using the default parameters, here is a snippet of my lambda_handler for reference.

import os
import boto3
import ctypes
import logging
from datetime import datetime
from datetime import timedelta
from config import ALPACA_CONFIG

import requests

# Handle fast str -> float conversion
def fast_atof(s) -> float:
    return ctypes.c_float(float(s)).value


def get_current_asset_position(ticker: str) -> dict:
    url = f'https://paper-api.alpaca.markets/v2/positions/{ticker}'
    
    headers = {
        "accept": "application/json",
        "content-type": "application/json",
        "APCA-API-KEY-ID": ALPACA_CONFIG['API_KEY'],
        "APCA-API-SECRET-KEY": ALPACA_CONFIG['API_SECRET']
    }
    
    asset = requests.get(url, headers=headers)
    return asset


def lambda_handler(event, context):
    url = "https://paper-api.alpaca.markets/v2/orders"
    ticker = "AAPL"
    yesterday = str(datetime.today() - timedelta(days = 1))
    
    dynamodb = boto3.resource("dynamodb")
    dynamodb.Table("woolyquant_cria_trading_bot")
    dynamodb_item = dynamodb.get_item(
        Key = {
            'Date': yesterday[0:10],
            'TickerSymbol': ticker,
        }
    )

    alpaca_headers = {
        "accept": "application/json",
        "content-type": "application/json",
        "APCA-API-KEY-ID": ALPACA_CONFIG['API_KEY'],
        "APCA-API-SECRET-KEY": ALPACA_CONFIG['API_SECRET']
    }
    
    alpaca_payload = {
        "symbol": "AAPL",
        "notional": "string",
        "type": "stop",
        "time_in_force": "day",
        "limit_price": "string",
        "stop_price": "string",
        "trail_price": "string",
        "trail_percent": "string",
        "extended_hours": False,
    }
    
    asset = get_current_asset_position(ticker=ticker)
    prev_closing_price = fast_atof(dynamodb_item['ClosingPrice'])
    curr_price = fast_atof(asset['current_price'])
    
    if curr_price > prev_closing_price * 1.05:
        alpaca_payload['qty'] = asset['qty']
        alpaca_payload['side'] = 'sell'
        
        response = requests.post(url, json=alpaca_payload, headers=alpaca_headers)
        return response
    
    if curr_price < prev_closing_price * 0.8995:
        alpaca_payload['qty'] = '10'
        alpaca_payload['side'] = 'buy'
        
        response = requests.post(url, json=alpaca_payload, headers=alpaca_headers)
        return response

However, upon running the code in the test environment, it still invokes this error:

{
  "errorMessage": "lambda_handler() missing 2 required positional arguments: 'event' and 'context'",
  "errorType": "TypeError",
  "requestId": "",
  "stackTrace": [
    "  File \"/var/lang/lib/python3.11/importlib/__init__.py\", line 126, in import_module\n    return _bootstrap._gcd_import(name[level:], package, level)\n",
    "  File \"<frozen importlib._bootstrap>\", line 1204, in _gcd_import\n",
    "  File \"<frozen importlib._bootstrap>\", line 1176, in _find_and_load\n",
    "  File \"<frozen importlib._bootstrap>\", line 1147, in _find_and_load_unlocked\n",
    "  File \"<frozen importlib._bootstrap>\", line 690, in _load_unlocked\n",
    "  File \"<frozen importlib._bootstrap_external>\", line 940, in exec_module\n",
    "  File \"<frozen importlib._bootstrap>\", line 241, in _call_with_frames_removed\n",
    "  File \"/var/task/lambda_function.py\", line 80, in <module>\n    lambda_handler()\n"
  ]
}

[ERROR] TypeError: lambda_handler() missing 2 required positional arguments: 'event' and 'context'
Traceback (most recent call last):
  File "/var/lang/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/var/task/lambda_function.py", line 80, in <module>
    lambda_handler()INIT_REPORT Init Duration: 438.31 ms    Phase: init Status: error   Error Type: Runtime.ExitError
[ERROR] TypeError: lambda_handler() missing 2 required positional arguments: 'event' and 'context'
Traceback (most recent call last):
  File "/var/lang/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/var/task/lambda_function.py", line 80, in <module>
    lambda_handler()INIT_REPORT Init Duration: 5938.52 ms   Phase: invoke   Status: error   Error Type: Runtime.ExitError
START RequestId: cae6b59d-588d-4623-a86c-043ac19e111a Version: $LATEST
Unknown application error occurred
Runtime.Unknown
END RequestId: cae6b59d-588d-4623-a86c-043ac19e111a
REPORT RequestId: cae6b59d-588d-4623-a86c-043ac19e111a  Duration: 6070.53 ms    Billed Duration: 6071 ms    Memory Size: 128 MB Max Memory Used: 27 MB

How I have tried resolving the problem.

  • I have tried increasing the timeout range, thinking that the default 3 seconds was making my code timeout, it did not fix the issue.
  • I've also tried renaming the runtime handler from the default lambda_function.lambda_handler to something else.
  • I've also ensured that all dependencies are properly imported and the correct wheel files have been unpacked into the zipped folder.

However, the issue still persists, and I'm not sure what to do.


Solution

  • There seems to be a lot of issues here, providing your entire Lambda function and not just the handler will help.

    1. It states context and event are missing, even though I see them in your code, which leads me to believe something else is wrong.

    2. You don't show your imports but it's clear you're using the requests library. Lambda doesn't come bundled with that library by default, so you either use a Lambda Layer or import it from botocore: from botocore.vendored import requests

    3. I tested your code, obviously I had some issue with importing config, which is either in your package or Lambda layer. You had more issues with your DynamoDB setup which I fixed. So you just need to be sure you can import your config and things should work:

    import os
    import boto3
    import ctypes
    import logging
    from datetime import datetime
    from datetime import timedelta
    # from config import ALPACA_CONFIG
    
    from botocore.vendored import requests
    
    # Handle fast str -> float conversion
    def fast_atof(s) -> float:
        return ctypes.c_float(float(s)).value
    
    
    def get_current_asset_position(ticker: str) -> dict:
        url = f'https://paper-api.alpaca.markets/v2/positions/{ticker}'
        
        headers = {
            "accept": "application/json",
            "content-type": "application/json",
            "APCA-API-KEY-ID": ALPACA_CONFIG['API_KEY'],
            "APCA-API-SECRET-KEY": ALPACA_CONFIG['API_SECRET']
        }
        
        asset = requests.get(url, headers=headers)
        return asset
    
    
    def lambda_handler(event, context):
        url = "https://paper-api.alpaca.markets/v2/orders"
        ticker = "AAPL"
        yesterday = str(datetime.today() - timedelta(days = 1))
        
        dynamodb = boto3.resource("dynamodb")
        table = dynamodb.Table("woolyquant_cria_trading_bot")
        dynamodb_item = table.get_item(
            Key = {
                'Date': yesterday[0:10],
                'TickerSymbol': ticker,
            }
        )
    
        alpaca_headers = {
            "accept": "application/json",
            "content-type": "application/json",
            "APCA-API-KEY-ID": ALPACA_CONFIG['API_KEY'],
            "APCA-API-SECRET-KEY": ALPACA_CONFIG['API_SECRET']
        }
        
        alpaca_payload = {
            "symbol": "AAPL",
            "notional": "string",
            "type": "stop",
            "time_in_force": "day",
            "limit_price": "string",
            "stop_price": "string",
            "trail_price": "string",
            "trail_percent": "string",
            "extended_hours": False,
        }
        
        asset = get_current_asset_position(ticker=ticker)
        prev_closing_price = fast_atof(dynamodb_item['ClosingPrice'])
        curr_price = fast_atof(asset['current_price'])
        
        if curr_price > prev_closing_price * 1.05:
            alpaca_payload['qty'] = asset['qty']
            alpaca_payload['side'] = 'sell'
            
            response = requests.post(url, json=alpaca_payload, headers=alpaca_headers)
            return response
        
        if curr_price < prev_closing_price * 0.8995:
            alpaca_payload['qty'] = '10'
            alpaca_payload['side'] = 'buy'
            
            response = requests.post(url, json=alpaca_payload, headers=alpaca_headers)
            return response