Skip to content

Commit

Permalink
Merge pull request #111 from rhettre/feature/python3.12
Browse files Browse the repository at this point in the history
Feature/python3.12
  • Loading branch information
rhettre authored Dec 29, 2024
2 parents dbb3a83 + 05079de commit a7733e7
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 77 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/publish-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
python-version: '3.12'

- name: Install dependencies
run: |
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
python-version: '3.12'

- name: Install dependencies
run: |
Expand All @@ -71,15 +71,15 @@ jobs:
mkdir python
PACKAGE_VERSION=$(python setup.py --version)
echo "coinbase-advancedtrade-python==$PACKAGE_VERSION" > requirements.txt
docker run --rm -v "$PWD":/var/task public.ecr.aws/sam/build-python3.9:latest /bin/sh -c "pip install --platform manylinux2014_x86_64 --implementation cp --python 3.9 --only-binary=:all: --upgrade -r requirements.txt -t python"
zip -r layer-python3.9-x86_64.zip python
docker run --rm -v "$PWD":/var/task public.ecr.aws/sam/build-python3.12:latest /bin/sh -c "pip install --platform manylinux2014_x86_64 --implementation cp --python 3.12 --only-binary=:all: --upgrade -r requirements.txt -t python"
zip -r layer-python3.12-x86_64.zip python
- name: Upload Layer to Release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./layer-python3.9-x86_64.zip
asset_name: layer-python3.9-x86_64.zip
asset_path: ./layer-python3.12-x86_64.zip
asset_name: layer-python3.12-x86_64.zip
asset_content_type: application/zip
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,27 +99,39 @@ Note: Both methods use a caching mechanism to reduce API calls. The account data

### Usage of Fear and Greed Index

The client uses the [fear-and-greed-crypto](https://github.com/rhettre/fear-and-greed-crypto) package to fetch the current Fear and Greed Index value. This index helps determine market sentiment and automate trading decisions.

```python
# Trade based on Fear and Greed Index
client.trade_based_on_fgi("BTC-USDC", "10")
```

You can also update and retrieve the Fear and Greed Index schedule:
You can customize the trading behavior by updating the Fear and Greed Index schedule:

```python
# Get current FGI schedule
current_schedule = client.get_fgi_schedule()

# Update FGI schedule
# Update FGI schedule with custom thresholds and actions
new_schedule = [
{'threshold': 15, 'factor': 1.2, 'action': 'buy'},
{'threshold': 37, 'factor': 1.0, 'action': 'buy'},
{'threshold': 35, 'factor': 0.8, 'action': 'sell'},
{'threshold': 45, 'factor': 0.6, 'action': 'sell'}
{'threshold': 15, 'factor': 1.2, 'action': 'buy'}, # Buy more in extreme fear
{'threshold': 37, 'factor': 1.0, 'action': 'buy'}, # Buy normal amount in fear
{'threshold': 35, 'factor': 0.8, 'action': 'sell'}, # Sell some in greed
{'threshold': 45, 'factor': 0.6, 'action': 'sell'} # Sell more in extreme greed
]
client.update_fgi_schedule(new_schedule)
```

The schedule determines:
- When to buy or sell based on the Fear and Greed Index value
- How much to adjust the trade amount (using the factor)
- What action to take at each threshold

For example, with the above schedule:
- If FGI is 10 (Extreme Fear), it will buy with 1.2x the specified amount
- If FGI is 50 (Neutral), no trade will be executed
- If FGI is 80 (Extreme Greed), it will sell with 0.6x the specified amount

## AlphaSquared Integration

This client now includes integration with AlphaSquared, allowing you to execute trading strategies based on AlphaSquared's risk analysis.
Expand Down Expand Up @@ -176,6 +188,15 @@ This will:

You can create custom strategies by modifying the `execute_strategy` method in the `AlphaSquaredTrader` class. This allows you to define specific trading logic based on the risk levels provided by AlphaSquared.

## AWS Lambda Compatibility

When using this package in AWS Lambda, ensure your Lambda function is configured to use Python 3.12. The cryptography binaries in the Lambda layer are compiled for Python 3.12, and using a different Python runtime version will result in compatibility issues.

To configure your Lambda function:
1. Set the runtime to Python 3.12
2. Use the provided Lambda layer from the latest release
3. If building custom layers, ensure they are built using the same Python version as the Lambda runtime.

## Legacy Support

The legacy authentication method is still supported but moved to a separate module. It will not receive the latest updates from the Coinbase SDK. To use the legacy method:
Expand Down
4 changes: 1 addition & 3 deletions coinbase_advanced_trader/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
DEFAULT_CONFIG = {
'BUY_PRICE_MULTIPLIER': 0.9995,
'SELL_PRICE_MULTIPLIER': 1.005,
'FEAR_AND_GREED_API_URL': 'https://api.alternative.me/fng/?limit=1',
'LOG_FILE_PATH': 'coinbase_advanced_trader.log',
'LOG_LEVEL': 'DEBUG',
'FGI_CACHE_DURATION': 3600
'LOG_LEVEL': 'DEBUG'
}
26 changes: 5 additions & 21 deletions coinbase_advanced_trader/services/fear_and_greed_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from decimal import Decimal
from typing import Optional, Tuple

import requests
from fear_and_greed import FearAndGreedIndex

from coinbase_advanced_trader.config import config_manager
from coinbase_advanced_trader.logger import logger
from coinbase_advanced_trader.models import Order
from coinbase_advanced_trader.trading_config import FEAR_AND_GREED_API_URL
from .trading_strategy_service import BaseTradingStrategy


Expand All @@ -26,8 +25,7 @@ def __init__(self, order_service, price_service, config):
"""
super().__init__(order_service, price_service)
self.config = config
self._last_fgi_fetch_time: float = 0
self._fgi_cache: Optional[Tuple[int, str]] = None
self._fgi_client = FearAndGreedIndex()

def execute_trade(self, product_id: str, fiat_amount: str) -> Optional[Order]:
"""
Expand All @@ -37,7 +35,9 @@ def execute_trade(self, product_id: str, fiat_amount: str) -> Optional[Order]:
:param fiat_amount: The amount of fiat currency to trade.
:return: An Order object if a trade is executed, None otherwise.
"""
fgi, fgi_classification = self.get_fear_and_greed_index()
fgi = self._fgi_client.get_current_value()
fgi_classification = self._fgi_client.get_current_classification()

logger.info(f"FGI retrieved: {fgi} ({fgi_classification}) "
f"for trading {product_id}")

Expand All @@ -57,22 +57,6 @@ def execute_trade(self, product_id: str, fiat_amount: str) -> Optional[Order]:
logger.warning(f"No trading condition met for FGI: {fgi}")
return None

def get_fear_and_greed_index(self) -> Tuple[int, str]:
"""
Retrieve the Fear and Greed Index (FGI) from the API or cache.
:return: A tuple containing the FGI value and classification.
"""
current_time = time.time()
cache_duration = config_manager.get('FGI_CACHE_DURATION')
if (not self._fgi_cache or
(current_time - self._last_fgi_fetch_time > cache_duration)):
response = requests.get(FEAR_AND_GREED_API_URL)
data = response.json()['data'][0]
self._fgi_cache = (int(data['value']), data['value_classification'])
self._last_fgi_fetch_time = current_time
return self._fgi_cache

def _execute_trade(self, product_id: str, fiat_amount: str,
action: str) -> Optional[Order]:
"""
Expand Down
2 changes: 0 additions & 2 deletions coinbase_advanced_trader/tests/test_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ def test_load_config_with_existing_file(self, mock_yaml_load, mock_file,
self.assertEqual(test_config['BUY_PRICE_MULTIPLIER'], 0.9990)
self.assertEqual(test_config['SELL_PRICE_MULTIPLIER'], 1.010)
self.assertEqual(test_config['LOG_LEVEL'], 'INFO')
self.assertEqual(test_config['FEAR_AND_GREED_API_URL'],
self.default_config['FEAR_AND_GREED_API_URL'])

@patch('coinbase_advanced_trader.config.Path.exists')
def test_load_config_without_existing_file(self, mock_exists):
Expand Down
44 changes: 11 additions & 33 deletions coinbase_advanced_trader/tests/test_fear_and_greed_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ def setUp(self):
self.price_service_mock,
self.config_mock
)
# Mock the FearAndGreedIndex client
self.strategy._fgi_client = Mock()

@patch('coinbase_advanced_trader.services.fear_and_greed_strategy.requests.get')
def test_execute_trade_buy(self, mock_get):
def test_execute_trade_buy(self):
"""Test execute_trade method for a buy scenario."""
mock_get.return_value.json.return_value = {
'data': [{'value': '25', 'value_classification': 'Extreme Fear'}]
}
self.strategy._fgi_client.get_current_value.return_value = 25
self.strategy._fgi_client.get_current_classification.return_value = "Extreme Fear"

self.config_mock.get_fgi_schedule.return_value = [
{'threshold': 30, 'factor': 1.2, 'action': 'buy'},
Expand All @@ -52,15 +52,11 @@ def test_execute_trade_buy(self, mock_get):
call_args = self.order_service_mock.fiat_limit_buy.call_args
self.assertEqual(call_args[0][0], 'BTC-USDC')
self.assertAlmostEqual(Decimal(call_args[0][1]), Decimal('12.00'), places=8)
mock_get.assert_called_once()
self.config_mock.get_fgi_schedule.assert_called_once()

@patch('coinbase_advanced_trader.services.fear_and_greed_strategy.requests.get')
def test_execute_trade_sell(self, mock_get):
def test_execute_trade_sell(self):
"""Test execute_trade method for a sell scenario."""
mock_get.return_value.json.return_value = {
'data': [{'value': '75', 'value_classification': 'Extreme Greed'}]
}
self.strategy._fgi_client.get_current_value.return_value = 75
self.strategy._fgi_client.get_current_classification.return_value = "Extreme Greed"

self.config_mock.get_fgi_schedule.return_value = [
{'threshold': 30, 'factor': 1.2, 'action': 'buy'},
Expand All @@ -84,15 +80,11 @@ def test_execute_trade_sell(self, mock_get):
call_args = self.order_service_mock.fiat_limit_sell.call_args
self.assertEqual(call_args[0][0], 'BTC-USDC')
self.assertAlmostEqual(Decimal(call_args[0][1]), Decimal('8.00'), places=8)
mock_get.assert_called_once()
self.config_mock.get_fgi_schedule.assert_called_once()

@patch('coinbase_advanced_trader.services.fear_and_greed_strategy.requests.get')
def test_execute_trade_no_condition_met(self, mock_get):
def test_execute_trade_no_condition_met(self):
"""Test execute_trade method when no condition is met."""
mock_get.return_value.json.return_value = {
'data': [{'value': '50', 'value_classification': 'Neutral'}]
}
self.strategy._fgi_client.get_current_value.return_value = 50
self.strategy._fgi_client.get_current_classification.return_value = "Neutral"

self.config_mock.get_fgi_schedule.return_value = [
{'threshold': 30, 'factor': 1.2, 'action': 'buy'},
Expand All @@ -104,20 +96,6 @@ def test_execute_trade_no_condition_met(self, mock_get):
self.assertIsNone(result)
self.order_service_mock.fiat_limit_buy.assert_not_called()
self.order_service_mock.fiat_limit_sell.assert_not_called()
mock_get.assert_called_once()
self.config_mock.get_fgi_schedule.assert_called_once()

@patch('coinbase_advanced_trader.services.fear_and_greed_strategy.requests.get')
def test_get_fear_and_greed_index(self, mock_get):
"""Test get_fear_and_greed_index method."""
mock_get.return_value.json.return_value = {
'data': [{'value': '47', 'value_classification': 'Neutral'}]
}

result = self.strategy.get_fear_and_greed_index()

self.assertEqual(result, (47, 'Neutral'))
mock_get.assert_called_once()


if __name__ == '__main__':
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ urllib3 >= 2.2.2
PyYAML >= 6.0.1
cryptography >= 42.0.4
cffi
alphasquared-py >= 0.3.0
alphasquared-py >= 0.3.0
fear-and-greed-crypto >= 0.1.0
7 changes: 2 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

setup(
name='coinbase-advancedtrade-python',
version='0.3.3',
version='0.4.0',
description='The unofficial Python client for the Coinbase Advanced Trade API',
long_description=long_description,
long_description_content_type="text/markdown",
Expand All @@ -30,10 +30,7 @@
'Topic :: Software Development :: Libraries :: Python Modules',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
],
python_requires='>=3.9',
python_requires='>=3.12',
)

0 comments on commit a7733e7

Please sign in to comment.