-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from rhettre/feature/alphasquared
Implemented AlphaSquared Trading
- Loading branch information
Showing
6 changed files
with
201 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from .enhanced_rest_client import EnhancedRESTClient | ||
from .alphasquared_trader import AlphaSquaredTrader | ||
|
||
__all__ = ['EnhancedRESTClient', 'AlphaSquaredTrader'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from decimal import Decimal, ROUND_DOWN | ||
import logging | ||
from .enhanced_rest_client import EnhancedRESTClient | ||
from alphasquared import AlphaSquared | ||
from coinbase_advanced_trader.models import Order | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
class AlphaSquaredTrader: | ||
def __init__(self, coinbase_client: EnhancedRESTClient, alphasquared_client: AlphaSquared): | ||
self.coinbase_client = coinbase_client | ||
self.alphasquared_client = alphasquared_client | ||
|
||
def execute_strategy(self, product_id: str, strategy_name: str): | ||
try: | ||
asset, base_currency = product_id.split('-') | ||
|
||
current_risk = self.alphasquared_client.get_current_risk(asset) | ||
logger.info(f"Current {asset} Risk: {current_risk}") | ||
|
||
action, value = self.alphasquared_client.get_strategy_value_for_risk(strategy_name, current_risk) | ||
logger.info(f"Strategy suggests: Action = {action.upper()}, Value = {value}") | ||
|
||
if value <= 0: | ||
logger.info("No action taken based on current risk and strategy.") | ||
return | ||
|
||
if action.lower() == 'buy': | ||
self._execute_buy(product_id, value) | ||
elif action.lower() == 'sell': | ||
self._execute_sell(product_id, asset, base_currency, value) | ||
else: | ||
logger.info(f"Unknown action: {action}. No trade executed.") | ||
|
||
except Exception as e: | ||
logger.error(f"Error in execute_strategy: {str(e)}") | ||
logger.exception("Full traceback:") | ||
|
||
def _execute_buy(self, product_id: str, value: float): | ||
try: | ||
order = self.coinbase_client.fiat_limit_buy(product_id, str(value), price_multiplier="0.995") | ||
if isinstance(order, Order): | ||
logger.info(f"Buy limit order placed: ID={order.id}, Size={order.size}, Price={order.price}") | ||
else: | ||
logger.warning(f"Unexpected order response type: {type(order)}") | ||
except Exception as e: | ||
logger.error(f"Error placing buy order: {str(e)}") | ||
logger.exception("Full traceback:") | ||
|
||
def _execute_sell(self, product_id, asset, base_currency, value): | ||
balance = Decimal(self.coinbase_client.get_crypto_balance(asset)) | ||
logger.info(f"Current {asset} balance: {balance}") | ||
|
||
product_details = self.coinbase_client.get_product(product_id) | ||
base_increment = Decimal(product_details['base_increment']) | ||
quote_increment = Decimal(product_details['quote_increment']) | ||
current_price = Decimal(product_details['price']) | ||
logger.info(f"Current {asset} price: {current_price} {base_currency}") | ||
|
||
sell_amount = (balance * Decimal(value) / Decimal('100')).quantize(base_increment, rounding=ROUND_DOWN) | ||
logger.info(f"Sell amount: {sell_amount} {asset}") | ||
|
||
if sell_amount > base_increment: | ||
limit_price = (current_price * Decimal('1.005')).quantize(quote_increment, rounding=ROUND_DOWN) | ||
|
||
order = self.coinbase_client.limit_order_gtc_sell( | ||
client_order_id=self.coinbase_client._order_service._generate_client_order_id(), | ||
product_id=product_id, | ||
base_size=str(sell_amount), | ||
limit_price=str(limit_price) | ||
) | ||
|
||
logger.info(f"Sell limit order placed for {sell_amount} {asset} at {limit_price} {base_currency}: {order}") | ||
else: | ||
logger.info(f"Sell amount {sell_amount} {asset} is too small. Minimum allowed is {base_increment}. No order placed.") |
63 changes: 63 additions & 0 deletions
63
coinbase_advanced_trader/tests/test_alphasquared_trader.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import unittest | ||
from unittest.mock import Mock, patch | ||
from decimal import Decimal | ||
from coinbase_advanced_trader.alphasquared_trader import AlphaSquaredTrader | ||
from coinbase_advanced_trader.models import Order, OrderSide, OrderType | ||
|
||
class TestAlphaSquaredTrader(unittest.TestCase): | ||
def setUp(self): | ||
self.mock_coinbase_client = Mock() | ||
self.mock_alphasquared_client = Mock() | ||
self.trader = AlphaSquaredTrader(self.mock_coinbase_client, self.mock_alphasquared_client) | ||
|
||
def test_execute_strategy_buy(self): | ||
self.mock_alphasquared_client.get_current_risk.return_value = 30 | ||
self.mock_alphasquared_client.get_strategy_value_for_risk.return_value = ('buy', 100) | ||
|
||
mock_order = Order( | ||
id='123', | ||
product_id='BTC-USDC', | ||
side=OrderSide.BUY, | ||
type=OrderType.LIMIT, | ||
size=Decimal('0.001'), | ||
price=Decimal('50000') | ||
) | ||
self.mock_coinbase_client.fiat_limit_buy.return_value = mock_order | ||
|
||
self.trader.execute_strategy('BTC-USDC', 'TestStrategy') | ||
|
||
self.mock_alphasquared_client.get_current_risk.assert_called_once_with('BTC') | ||
self.mock_alphasquared_client.get_strategy_value_for_risk.assert_called_once_with('TestStrategy', 30) | ||
self.mock_coinbase_client.fiat_limit_buy.assert_called_once_with('BTC-USDC', '100', price_multiplier='0.995') | ||
|
||
def test_execute_strategy_sell(self): | ||
self.mock_alphasquared_client.get_current_risk.return_value = 70 | ||
self.mock_alphasquared_client.get_strategy_value_for_risk.return_value = ('sell', 50) | ||
|
||
self.mock_coinbase_client.get_crypto_balance.return_value = '1.0' | ||
self.mock_coinbase_client.get_product.return_value = { | ||
'base_increment': '0.00000001', | ||
'quote_increment': '0.01', | ||
'price': '50000' | ||
} | ||
|
||
mock_order = Order( | ||
id='456', | ||
product_id='BTC-USDC', | ||
side=OrderSide.SELL, | ||
type=OrderType.LIMIT, | ||
size=Decimal('0.5'), | ||
price=Decimal('50250') | ||
) | ||
self.mock_coinbase_client.limit_order_gtc_sell.return_value = mock_order | ||
|
||
self.trader.execute_strategy('BTC-USDC', 'TestStrategy') | ||
|
||
self.mock_alphasquared_client.get_current_risk.assert_called_once_with('BTC') | ||
self.mock_alphasquared_client.get_strategy_value_for_risk.assert_called_once_with('TestStrategy', 70) | ||
self.mock_coinbase_client.get_crypto_balance.assert_called_once_with('BTC') | ||
self.mock_coinbase_client.get_product.assert_called_once_with('BTC-USDC') | ||
self.mock_coinbase_client.limit_order_gtc_sell.assert_called_once() | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ requests >=2.31.0 | |
urllib3 >= 2.2.2 | ||
PyYAML >= 6.0.1 | ||
cryptography>=42.0.4 | ||
cffi | ||
cffi | ||
alphasquared-py>=0.3.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters