diff --git a/core/exchanges/oanda.py b/core/exchanges/oanda.py index 75a5383..bcfb1de 100644 --- a/core/exchanges/oanda.py +++ b/core/exchanges/oanda.py @@ -61,8 +61,13 @@ class OANDAExchange(BaseExchange): "positionFill": "DEFAULT", } } - if trade.type == "limit": - data["order"]["price"] = str(trade.price) + if trade.price is not None: + print("PRICE IS NOT NONE") + if trade.type == "limit": + data["order"]["price"] = str(trade.price) + elif trade.type == "market": + print("IS MARKET") + data["order"]["priceBound"] = str(trade.price) r = orders.OrderCreate(self.account_id, data=data) response = self.call(r) trade.response = response diff --git a/core/lib/market.py b/core/lib/market.py index b2acad4..eae789c 100644 --- a/core/lib/market.py +++ b/core/lib/market.py @@ -41,6 +41,67 @@ def to_currency(direction, account, amount, from_currency, to_currency): return converted +def get_pair(account, base, quote): + if account.exchange == "alpaca": + separator = "/" + elif account.exchange == "oanda": + separator = "_" + + symbol = f"{base.upper()}{separator}{quote.upper()}" + if symbol not in account.supported_symbols: + return False + return symbol + +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) + 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) + log.debug(f"Stop loss as ratio: {stop_loss_as_ratio}") + log.debug(f"Take profit as ratio: {take_profit_as_ratio}") + + stop_loss_var = D(price) * D(stop_loss_as_ratio) + take_profit_var = D(price) * D(take_profit_as_ratio) + log.debug(f"Stop loss var: {stop_loss_var}") + log.debug(f"Take profit var: {take_profit_var}") + + if direction == "buy": + stop_loss = D(price) - D(stop_loss_var) + take_profit = D(price) + D(take_profit_var) + elif direction == "sell": + stop_loss = D(price) + D(stop_loss_var) + take_profit = D(price) - D(take_profit_var) + + log.debug(f"Stop loss: {stop_loss}") + 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}") + price_slippage = D(price) * D(price_slippage_as_ratio) + + log.debug(f"Price slippage: {price_slippage}") + + if direction == "buy": + price_bound = D(price) - D(price_slippage) + elif direction == "sell": + price_bound = D(price) + D(price_slippage) + price_bound = D(price) + D(price_slippage) + + log.debug(f"Price bound: {price_bound}") + return price_bound + + def execute_strategy(callback, strategy): cash_balance = strategy.account.client.get_balance() instruments = strategy.account.instruments @@ -55,18 +116,10 @@ def execute_strategy(callback, strategy): if callback.exchange != account.exchange: log.error("Market exchange differs from account exchange.") return - if account.exchange == "alpaca": - separator = "/" - elif account.exchange == "oanda": - separator = "_" - if account.exchange == "alpaca": - if quote not in ["usd", "usdt", "usdc", "busd"]: - log.error(f"Quote not compatible with Dollar: {quote}") - return False - quote = "usd" # TODO: MASSIVE HACK - symbol = f"{base.upper()}{separator}{quote.upper()}" - if symbol not in account.supported_symbols: + symbol = get_pair(account, base, quote) + + if not symbol: log.error(f"Symbol not supported by account: {symbol}") return False @@ -80,6 +133,10 @@ def execute_strategy(callback, strategy): except KeyError: log.error(f"Precision not found for {symbol}") return False + + price = round(D(callback.price), display_precision) + log.debug(f"Extracted price of quote: {price}") + # market_from_alpaca = get_market_value(account, symbol) # change_percent = abs(((float(market_from_alpaca)-price)/price)*100) # if change_percent > strategy.price_slippage_percent: @@ -88,36 +145,11 @@ def execute_strategy(callback, strategy): # type = "limit" type = "market" - 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}") - price = round(D(callback.price), display_precision) - if not price: - return - log.debug(f"Extracted price of quote: {price}") + + 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) - # 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) - log.debug(f"Trade size in base: {trade_size_in_base}") - - # calculate sl/tp - stop_loss_as_ratio = D(strategy.stop_loss_percent) / D(100) - take_profit_as_ratio = D(strategy.take_profit_percent) / D(100) - log.debug(f"Stop loss as ratio: {stop_loss_as_ratio}") - log.debug(f"Take profit as ratio: {take_profit_as_ratio}") - - stop_loss_subtract = D(price) * D(stop_loss_as_ratio) - take_profit_add = D(price) * D(take_profit_as_ratio) - log.debug(f"Stop loss subtract: {stop_loss_subtract}") - log.debug(f"Take profit add: {take_profit_add}") - - stop_loss = D(price) - D(stop_loss_subtract) - take_profit = D(price) + D(take_profit_add) - - log.debug(f"Stop loss: {stop_loss}") - log.debug(f"Take profit: {take_profit}") + price_bound = round(get_price_bound(direction, strategy, price), display_precision) new_trade = Trade.objects.create( user=user, @@ -127,7 +159,7 @@ def execute_strategy(callback, strategy): type=type, # amount_fiat=amount_fiat, amount=float(round(trade_size_in_base, trade_precision)), - # price=price, + price=price_bound, stop_loss=float(round(stop_loss, display_precision)), take_profit=float(round(take_profit, display_precision)), direction=direction, diff --git a/core/models.py b/core/models.py index e399321..ce79cf5 100644 --- a/core/models.py +++ b/core/models.py @@ -69,7 +69,10 @@ 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)