diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..db0e730a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +node_modules/ +dist/ +.env +__pycache__/ +*.pyc +.pytest_cache/ +.coverage +coverage/ +*.log \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..deb12fca --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +# Gekko API Client + +A Python client for interacting with the Gekko trading platform API. + +## Features + +- Retrieve exchange information +- List available trading strategies +- Start and stop trading sessions +- Flexible configuration options + +## Installation + +1. Clone the repository +2. Install dependencies: + ``` + pip install -r requirements.txt + ``` + +## Usage + +```python +from src.gekko_api import GekkoAPI + +# Initialize the API client +api = GekkoAPI(base_url='http://localhost:3000') + +# Get exchange information +exchange_info = api.get_exchange_info('Binance') + +# List available strategies +strategies = api.list_strategies() + +# Start a trading session +config = { + 'exchange': 'Binance', + 'pair': 'BTC/USDT', + 'strategy': 'MACD' +} +session_id = api.start_trading(config) + +# Stop trading +api.stop_trading(session_id) +``` + +## Running Tests + +``` +pytest tests/ +``` + +## Contributing + +Please read the guidelines for contributing to this project. + +## License + +[Insert License Information] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..d9ce6ee8 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +requests==2.26.0 +pytest==7.3.1 +flask==2.1.0 +python-dotenv==0.20.0 \ No newline at end of file diff --git a/src/gekko_api.py b/src/gekko_api.py new file mode 100644 index 00000000..7099eb1f --- /dev/null +++ b/src/gekko_api.py @@ -0,0 +1,78 @@ +import requests +from typing import Dict, Any, Optional + +class GekkoAPI: + """ + A client for interacting with the Gekko trading platform API. + + Supports basic operations like fetching exchange info, trading strategies, + and managing trade configurations. + """ + + def __init__(self, base_url: str = 'http://localhost:3000'): + """ + Initialize the Gekko API client. + + :param base_url: Base URL of the Gekko API server + """ + self.base_url = base_url + + def get_exchange_info(self, exchange: str) -> Dict[str, Any]: + """ + Retrieve information about a specific exchange. + + :param exchange: Name of the exchange + :return: Exchange information dictionary + :raises ValueError: If exchange is not found or API request fails + """ + try: + response = requests.get(f'{self.base_url}/exchanges/{exchange}') + response.raise_for_status() + return response.json() + except requests.RequestException as e: + raise ValueError(f"Failed to retrieve exchange info: {str(e)}") + + def list_strategies(self) -> Dict[str, Any]: + """ + List available trading strategies. + + :return: Dictionary of available trading strategies + :raises RuntimeError: If API request fails + """ + try: + response = requests.get(f'{self.base_url}/strategies') + response.raise_for_status() + return response.json() + except requests.RequestException as e: + raise RuntimeError(f"Failed to list strategies: {str(e)}") + + def start_trading(self, config: Dict[str, Any]) -> Optional[str]: + """ + Start a trading session with the given configuration. + + :param config: Trading configuration dictionary + :return: Trading session ID or None + :raises ValueError: If configuration is invalid or start fails + """ + if not config: + raise ValueError("Trading configuration cannot be empty") + + try: + response = requests.post(f'{self.base_url}/trade', json=config) + response.raise_for_status() + return response.json().get('sessionId') + except requests.RequestException as e: + raise ValueError(f"Failed to start trading: {str(e)}") + + def stop_trading(self, session_id: str) -> bool: + """ + Stop a specific trading session. + + :param session_id: ID of the trading session to stop + :return: True if trading stopped successfully, False otherwise + """ + try: + response = requests.delete(f'{self.base_url}/trade/{session_id}') + return response.status_code == 200 + except requests.RequestException: + return False \ No newline at end of file diff --git a/tests/test_gekko_api.py b/tests/test_gekko_api.py new file mode 100644 index 00000000..d0075fef --- /dev/null +++ b/tests/test_gekko_api.py @@ -0,0 +1,82 @@ +import pytest +import requests +from unittest.mock import patch +from src.gekko_api import GekkoAPI + +class MockResponse: + def __init__(self, json_data, status_code=200): + self.json_data = json_data + self.status_code = status_code + + def json(self): + return self.json_data + + def raise_for_status(self): + if self.status_code >= 400: + raise requests.HTTPError(f"HTTP Error: {self.status_code}") + +def test_gekko_api_initialization(): + api = GekkoAPI() + assert api.base_url == 'http://localhost:3000' + +@patch('requests.get') +def test_get_exchange_info(mock_get): + mock_get.return_value = MockResponse({ + 'name': 'Binance', + 'supported': True, + 'requiredPermissions': ['trade', 'balance'] + }) + + api = GekkoAPI() + exchange_info = api.get_exchange_info('Binance') + + assert exchange_info['name'] == 'Binance' + mock_get.assert_called_once_with('http://localhost:3000/exchanges/Binance') + +@patch('requests.get') +def test_list_strategies(mock_get): + mock_get.return_value = MockResponse({ + 'strategies': ['MACD', 'RSI', 'EMA'] + }) + + api = GekkoAPI() + strategies = api.list_strategies() + + assert 'strategies' in strategies + assert len(strategies['strategies']) == 3 + mock_get.assert_called_once_with('http://localhost:3000/strategies') + +@patch('requests.post') +def test_start_trading(mock_post): + mock_post.return_value = MockResponse({ + 'sessionId': 'trade_123456' + }) + + api = GekkoAPI() + config = { + 'exchange': 'Binance', + 'pair': 'BTC/USDT', + 'strategy': 'MACD' + } + + session_id = api.start_trading(config) + + assert session_id == 'trade_123456' + mock_post.assert_called_once_with('http://localhost:3000/trade', json=config) + +@patch('requests.post') +def test_start_trading_empty_config(mock_post): + api = GekkoAPI() + + with pytest.raises(ValueError, match="Trading configuration cannot be empty"): + api.start_trading({}) + +@patch('requests.delete') +def test_stop_trading(mock_delete): + mock_delete.return_value = MockResponse({}, status_code=200) + + api = GekkoAPI() + result = api.stop_trading('trade_123456') + + assert result is True + mock_delete.assert_called_once_with('http://localhost:3000/trade/trade_123456') \ No newline at end of file