Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ dmypy.json

# JetBrains stuff
.idea
.DS_Store

# Data stuff
**.csv
21 changes: 13 additions & 8 deletions backend/auth0login/forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from django import forms
from django.core.exceptions import ValidationError

from backend.tradingbot.apimanagers import AlpacaManager
from backend.tradingbot.apiutility import place_general_order


class CredentialForm(forms.Form):
"""credential for user"""
Expand Down Expand Up @@ -43,14 +46,16 @@ def place_order(self, user, user_details):
transaction_type = self.cleaned_data['transaction_type']
quantity = self.cleaned_data['quantity']
time_in_force = self.cleaned_data['time_in_force']
from backend.tradingbot.apiutility import place_general_order
try:
place_general_order(user=user, user_details=user_details, ticker=ticker, quantity=quantity,
order_type=order_type,
transaction_type=transaction_type, time_in_force=time_in_force)
return "Order placed successfully"
except Exception as e:
return str(e)
place_general_order(
user=user,
user_details=user_details,
ticker=ticker,
quantity=quantity,
order_type=order_type,
transaction_type=transaction_type,
time_in_force=time_in_force,
alpaca_manager=AlpacaManager(user.credential.alpaca_id, user.credential.alpaca_key)
)


class StrategyForm(forms.Form):
Expand Down
2 changes: 1 addition & 1 deletion backend/tradingbot/apimanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import alpaca_trade_api.common


class AlpacaManager(): # API manager for Alpaca
class AlpacaManager: # API manager for Alpaca
def __init__(self, API_KEY, SECRET_KEY):
self.BASE_URL = alpaca_trade_api.common.URL("https://paper-api.alpaca.markets")
self.ACCOUNT_URL = "{}/v2/account".format(self.BASE_URL)
Expand Down
61 changes: 33 additions & 28 deletions backend/tradingbot/apiutility.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def create_local_order(user, ticker, quantity, order_type, transaction_type, sta
order.save()


def place_general_order(user, user_details, ticker, quantity, transaction_type, order_type, time_in_force):
def place_general_order(user, user_details, ticker, quantity, transaction_type, order_type, time_in_force,
alpaca_manager: AlpacaManager) -> bool:
"""
General place order function that takes account of database, margin, and alpaca synchronization.
supports market buy/sell
Expand All @@ -42,52 +43,56 @@ def place_general_order(user, user_details, ticker, quantity, transaction_type,
('S', 'Sell'),
]
"""
if not alpaca_manager.validate_api():
return False
backend_api = validate_backend()
user_api = AlpacaManager(user.credential.alpaca_id, user.credential.alpaca_key)

# 1. check if ticker exists and check buy / sell availability and errors
check, price = backend_api.get_price(ticker)
if not check:
raise ValidationError(f'Failed to get price for {ticker}, are you sure that the ticker name is correct?')
if transaction_type == 'B':
a_transaction_type = 'buy'
a_order_type = buy_order_check(order_type=order_type, price=price, quantity=quantity,
usable_cash=user_details['usable_cash'])
a_order_type = buy_order_check(
order_type=order_type,
price=price,
quantity=quantity,
usable_cash=user_details['usable_cash']
)
elif transaction_type == 'S':
a_transaction_type = 'sell'
a_order_type = sell_order_check(order_type=order_type, price=price, quantity=quantity,
usable_cash=user_details['usable_cash'])
a_order_type = sell_order_check(
order_type=order_type,
price=price,
quantity=quantity,
usable_cash=user_details['usable_cash']
)
else:
raise ValidationError("invalid transaction type")

# 2. store order to database
# 2.1 check if stock and company exists
stock, _ = sync_database_company_stock(ticker)
from backend.tradingbot.models import Order
order = Order(user=user, stock=stock, order_type=order_type,
quantity=quantity, transaction_type=transaction_type,
status='A')
order = Order(
user=user,
stock=stock,
order_type=order_type,
quantity=quantity,
transaction_type=transaction_type,
status='A'
)
order.save()
client_order_id = order.order_number
# 3. place order to Alpaca
try:
user_api.api.submit_order(
symbol=ticker,
qty=float(quantity),
side=a_transaction_type,
type=a_order_type,
time_in_force=time_in_force,
client_order_id=str(client_order_id)
)
except Exception as e:
# 4. delete order if not valid.
order.delete()
raise ValidationError(e)

alpaca_manager.api.submit_order(
symbol=ticker,
qty=float(quantity),
side=a_transaction_type,
type=a_order_type,
time_in_force=time_in_force,
client_order_id=str(client_order_id)
)
return True


def buy_order_check(order_type, price, quantity, usable_cash):
def buy_order_check(order_type, price, quantity, usable_cash) -> str:
a_order_type = ''
if order_type == 'M':
a_order_type = 'market'
Expand Down
1 change: 1 addition & 0 deletions backend/tradingbot/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
class TradingbotConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'backend.tradingbot'
verbose_name = 'Trading Bot'
2 changes: 2 additions & 0 deletions backend/tradingbot/synchronization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from alpaca_trade_api.rest import APIError

from backend.tradingbot.models import StockInstance, Stock, Company


def validate_backend():
from backend.settings import BACKEND_ALPACA_ID, BACKEND_ALPACA_KEY
Expand Down
35 changes: 35 additions & 0 deletions backend/tradingbot/test_apiutility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.test import TestCase
from unittest.mock import MagicMock
from .apimanagers import AlpacaManager
from django.contrib.auth.models import User
from .models import Order

from .apiutility import place_general_order


class TestApiUtilities(TestCase):
def setUp(self):
self.api_manager = AlpacaManager("test", "test")

def test_place_general_order(self):
self.api_manager.validate_api = MagicMock(return_value=True)
self.api_manager.get_price = MagicMock(return_value=(True, 100))
self.api_manager.api.submit_order = MagicMock(return_value=True)

user = User.objects.create_user(username="test", password="test")
user_details = {
"usable_cash": 100,
}
# result = place_general_order(
# user,
# user_details,
# "test_ticker",
# 1,
# "B",
# "M",
# "time_in_force",
# self.api_manager
# )
# self.assertTrue(result)
# num_orders = Order.objects.filter(user=user).count()
# self.assertTrue(num_orders == 1)