From c3d908341a56f97fa9be99b3162563319a964919 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Thu, 10 Nov 2022 07:20:20 +0000 Subject: [PATCH] Refactor and ignore n/a exchange callbacks --- core/lib/market.py | 38 +++++++++++++++++++++++-------------- core/lib/schemas/oanda_s.py | 9 +++++++++ core/models.py | 5 +---- core/templatetags/pretty.py | 6 ++---- core/views/accounts.py | 2 +- core/views/hooks.py | 3 +++ 6 files changed, 40 insertions(+), 23 deletions(-) diff --git a/core/lib/market.py b/core/lib/market.py index eae789c..64a6a43 100644 --- a/core/lib/market.py +++ b/core/lib/market.py @@ -1,20 +1,20 @@ from decimal import Decimal as D -from alpaca.common.exceptions import APIError - +from core.exchanges import GenericAPIError from core.models import Strategy, Trade from core.util import logs -from core.exchanges import GenericAPIError log = logs.get_logger(__name__) -def to_usd(account, amount, from_currency): - if account.exchange == "alpaca": - separator = "/" - elif account.exchange == "oanda": - separator = "_" - symbol = f"{from_currency.upper()}{separator}{to_currency.upper()}" - prices = account.client.get_currencies([symbol]) + +# def to_usd(account, amount, from_currency): +# if account.exchange == "alpaca": +# separator = "/" +# elif account.exchange == "oanda": +# separator = "_" +# symbol = f"{from_currency.upper()}{separator}{to_currency.upper()}" +# prices = account.client.get_currencies([symbol]) + def to_currency(direction, account, amount, from_currency, to_currency): if account.exchange == "alpaca": @@ -41,6 +41,7 @@ def to_currency(direction, account, amount, from_currency, to_currency): return converted + def get_pair(account, base, quote): if account.exchange == "alpaca": separator = "/" @@ -52,17 +53,23 @@ def get_pair(account, base, quote): return False return symbol -def get_trade_size_in_base(direction, account, strategy, cash_balance, price, base, precision): + +def get_trade_size_in_base( + direction, account, strategy, cash_balance, price, base, precision +): trade_size_as_ratio = D(strategy.trade_size_percent) / D(100) log.debug(f"Trade size as ratio: {trade_size_as_ratio}") amount_fiat = D(trade_size_as_ratio) * D(cash_balance) log.debug(f"Trade size: {amount_fiat}") # We can do this because the quote IS in $ or equivalent # trade_size_in_base = D(amount_fiat) / D(price) - trade_size_in_base = to_currency(direction, account, amount_fiat, account.currency, base) + trade_size_in_base = to_currency( + direction, account, amount_fiat, account.currency, base + ) log.debug(f"Trade size in base: {trade_size_in_base}") return trade_size_in_base + def get_tp_sl(direction, strategy, price): stop_loss_as_ratio = D(strategy.stop_loss_percent) / D(100) take_profit_as_ratio = D(strategy.take_profit_percent) / D(100) @@ -85,6 +92,7 @@ def get_tp_sl(direction, strategy, price): log.debug(f"Take profit: {take_profit}") return (stop_loss, take_profit) + def get_price_bound(direction, strategy, price): price_slippage_as_ratio = D(strategy.price_slippage_percent) / D(100) log.debug(f"Price slippage as ratio: {price_slippage_as_ratio}") @@ -145,8 +153,10 @@ def execute_strategy(callback, strategy): # type = "limit" type = "market" - - trade_size_in_base = get_trade_size_in_base(direction, account, strategy, cash_balance, price, base, display_precision) + + trade_size_in_base = get_trade_size_in_base( + direction, account, strategy, cash_balance, price, base, display_precision + ) stop_loss, take_profit = get_tp_sl(direction, strategy, price) price_bound = round(get_price_bound(direction, strategy, price), display_precision) diff --git a/core/lib/schemas/oanda_s.py b/core/lib/schemas/oanda_s.py index e703ac5..3b6137b 100644 --- a/core/lib/schemas/oanda_s.py +++ b/core/lib/schemas/oanda_s.py @@ -353,6 +353,7 @@ AccountInstrumentsSchema = { ) } + class OrderTransaction(BaseModel): id: str accountID: str @@ -367,9 +368,11 @@ class OrderTransaction(BaseModel): positionFill: str reason: str + class OrderCreate(BaseModel): orderCreateTransaction: OrderTransaction + OrderCreateSchema = { "id": "orderCreateTransaction.id", "accountID": "orderCreateTransaction.accountID", @@ -385,18 +388,22 @@ OrderCreateSchema = { "reason": "orderCreateTransaction.reason", } + class PriceBid(BaseModel): price: str liquidity: int + class PriceAsk(BaseModel): price: str liquidity: int + class PriceQuoteHomeConversionFactors(BaseModel): positiveUnits: str negativeUnits: str + class Price(BaseModel): type: str time: str @@ -409,10 +416,12 @@ class Price(BaseModel): quoteHomeConversionFactors: PriceQuoteHomeConversionFactors instrument: str + class PricingInfo(BaseModel): time: str prices: list[Price] + PricingInfoSchema = { "time": "time", "prices": ( diff --git a/core/models.py b/core/models.py index ce79cf5..e399321 100644 --- a/core/models.py +++ b/core/models.py @@ -69,10 +69,7 @@ class User(AbstractUser): class Account(models.Model): - EXCHANGE_CHOICES = ( - ("alpaca", "Alpaca"), - ("oanda", "OANDA") - ) + EXCHANGE_CHOICES = (("alpaca", "Alpaca"), ("oanda", "OANDA")) user = models.ForeignKey(User, on_delete=models.CASCADE) name = models.CharField(max_length=255) exchange = models.CharField(choices=EXCHANGE_CHOICES, max_length=255) diff --git a/core/templatetags/pretty.py b/core/templatetags/pretty.py index 0020438..74e7401 100644 --- a/core/templatetags/pretty.py +++ b/core/templatetags/pretty.py @@ -1,11 +1,9 @@ -from django import template import orjson +from django import template register = template.Library() @register.filter def pretty(data): - return orjson.dumps( - data, option=orjson.OPT_INDENT_2 - ).decode("utf-8") + return orjson.dumps(data, option=orjson.OPT_INDENT_2).decode("utf-8") diff --git a/core/views/accounts.py b/core/views/accounts.py index 79cc289..2afc901 100644 --- a/core/views/accounts.py +++ b/core/views/accounts.py @@ -1,5 +1,5 @@ import uuid -import orjson + from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponseBadRequest from django.shortcuts import render diff --git a/core/views/hooks.py b/core/views/hooks.py index 441c84b..c710131 100644 --- a/core/views/hooks.py +++ b/core/views/hooks.py @@ -78,6 +78,9 @@ class HookAPI(APIView): except Hook.DoesNotExist: return HttpResponseBadRequest("Hook does not exist.") + if data["exchange"].lower() == "n/a": + log.debug("HookAPI callback: exchange is N/A, skipping") + return HttpResponse("OK") # Create the callback object callback = Callback.objects.create(hook=hook, **data) callback.save()