Pull object names from model definitions in CRUD views

This commit is contained in:
Mark Veidemanis 2022-11-04 07:20:42 +00:00
parent 0c52cbd0f8
commit d34ac39d68
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
10 changed files with 29 additions and 159 deletions

View File

@ -24,10 +24,8 @@ def get_market_value(account, symbol):
def execute_strategy(callback, strategy): def execute_strategy(callback, strategy):
success, cash_balance = strategy.account.client.get_balance() cash_balance = strategy.account.client.get_balance()
log.debug(f"Cash balance: {cash_balance}") log.debug(f"Cash balance: {cash_balance}")
if not success:
return None
user = strategy.user user = strategy.user
account = strategy.account account = strategy.account

View File

@ -0,0 +1 @@
from core.lib.schemas import alpaca_s, drakdoo_s, oanda_s # noqa

View File

@ -1,82 +0,0 @@
# Trade handling
from alpaca.common.exceptions import APIError
from alpaca.trading.enums import OrderSide, TimeInForce
from alpaca.trading.requests import LimitOrderRequest, MarketOrderRequest
from core.util import logs
log = logs.get_logger(__name__)
def sync_trades_with_db(user):
pass
def post_trade(trade):
# the trade is not placed yet
trading_client = trade.account.get_client()
if trade.direction == "buy":
direction = OrderSide.BUY
elif trade.direction == "sell":
direction = OrderSide.SELL
else:
raise Exception("Unknown direction")
cast = {"symbol": trade.symbol, "side": direction, "time_in_force": TimeInForce.IOC}
if trade.amount is not None:
cast["qty"] = trade.amount
if trade.amount_usd is not None:
cast["notional"] = trade.amount_usd
if not trade.amount and not trade.amount_usd:
return (False, "No amount specified")
if trade.take_profit:
cast["take_profit"] = {"limit_price": trade.take_profit}
if trade.stop_loss:
stop_limit_price = trade.stop_loss - (trade.stop_loss * 0.005)
cast["stop_loss"] = {
"stop_price": trade.stop_loss,
"limit_price": stop_limit_price,
}
if trade.type == "market":
market_order_data = MarketOrderRequest(**cast)
try:
order = trading_client.submit_order(order_data=market_order_data)
except APIError as e:
log.error(f"Error placing market order: {e}")
return (False, e)
elif trade.type == "limit":
if not trade.price:
return (False, "Limit order with no price")
cast["limit_price"] = trade.price
limit_order_data = LimitOrderRequest(**cast)
try:
order = trading_client.submit_order(order_data=limit_order_data)
except APIError as e:
log.error(f"Error placing limit order: {e}")
return (False, e)
else:
raise Exception("Unknown trade type")
trade.response = order
trade.status = "posted"
trade.order_id = order["id"]
trade.client_order_id = order["client_order_id"]
trade.save()
return (True, order)
def update_trade(self):
pass
def close_trade(trade):
pass
def get_position_info(account, symbol):
trading_client = account.get_client()
try:
position = trading_client.get_open_position(symbol)
except APIError as e:
return (False, e)
return (True, position)

View File

@ -90,8 +90,7 @@ class Account(models.Model):
""" """
client = self.get_client() client = self.get_client()
if client: if client:
success, supported_symbols = client.get_supported_assets() supported_symbols = client.get_supported_assets()
if success:
self.supported_symbols = supported_symbols self.supported_symbols = supported_symbols
super().save(*args, **kwargs) super().save(*args, **kwargs)

View File

@ -12,20 +12,23 @@ from core.util import logs
log = logs.get_logger(__name__) log = logs.get_logger(__name__)
class ObjectList(ListView): class ObjectNameMixin(object):
def __init__(self, *args, **kwargs):
self.title_singular = self.model._meta.verbose_name.title() # Hook
self.context_object_name_singular = self.title_singular.lower() # hook
self.title = self.model._meta.verbose_name_plural.title() # Hooks
self.context_object_name = self.title.lower() # hooks
super().__init__(*args, **kwargs)
class ObjectList(ObjectNameMixin, ListView):
allowed_types = ["modal", "widget", "window", "page"] allowed_types = ["modal", "widget", "window", "page"]
window_content = "window-content/objects.html" window_content = "window-content/objects.html"
list_template = None list_template = None
model = None
context_object_name = "objects"
context_object_name_singular = "object"
page_title = None page_title = None
page_subtitle = None page_subtitle = None
title = "Objects"
title_singular = "Object"
list_url_name = None list_url_name = None
# WARNING: TAKEN FROM locals() # WARNING: TAKEN FROM locals()
list_url_args = ["type"] list_url_args = ["type"]
@ -91,13 +94,12 @@ class ObjectList(ListView):
return self.render_to_response(context) return self.render_to_response(context)
class ObjectCreate(CreateView): class ObjectCreate(ObjectNameMixin, CreateView):
allowed_types = ["modal", "widget", "window", "page"] allowed_types = ["modal", "widget", "window", "page"]
window_content = "window-content/object-form.html" window_content = "window-content/object-form.html"
parser_classes = [FormParser] parser_classes = [FormParser]
model = None model = None
context_object_name = "objects"
submit_url_name = None submit_url_name = None
list_url_name = None list_url_name = None
@ -159,21 +161,19 @@ class ObjectCreate(CreateView):
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)
class ObjectRead(DetailView): class ObjectRead(ObjectNameMixin, DetailView):
allowed_types = ["modal", "widget", "window", "page"] allowed_types = ["modal", "widget", "window", "page"]
window_content = "window-content/object.html" window_content = "window-content/object.html"
model = None model = None
context_object_name = "object"
class ObjectUpdate(UpdateView): class ObjectUpdate(ObjectNameMixin, UpdateView):
allowed_types = ["modal", "widget", "window", "page"] allowed_types = ["modal", "widget", "window", "page"]
window_content = "window-content/object-form.html" window_content = "window-content/object-form.html"
parser_classes = [FormParser] parser_classes = [FormParser]
model = None model = None
context_object_name = "objects"
submit_url_name = None submit_url_name = None
request = None request = None
@ -225,9 +225,8 @@ class ObjectUpdate(UpdateView):
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)
class ObjectDelete(DeleteView): class ObjectDelete(ObjectNameMixin, DeleteView):
model = None model = None
context_object_name_singular = "object"
template_name = "partials/notify.html" template_name = "partials/notify.html"
# Overriden to prevent success URL from being used # Overriden to prevent success URL from being used

View File

@ -39,14 +39,7 @@ class AccountInfo(LoginRequiredMixin, View):
} }
return render(request, template_name, context) return render(request, template_name, context)
success, live_info = account.client.get_account() live_info = account.client.get_account()
if not success:
context = {
"message": "Could not get account info",
"class": "danger",
"window_content": self.window_content,
}
return render(request, template_name, context)
live_info = live_info live_info = live_info
account_info = account.__dict__ account_info = account.__dict__
account_info = { account_info = {
@ -70,10 +63,6 @@ class AccountInfo(LoginRequiredMixin, View):
class AccountList(LoginRequiredMixin, ObjectList): class AccountList(LoginRequiredMixin, ObjectList):
list_template = "partials/account-list.html" list_template = "partials/account-list.html"
model = Account model = Account
context_object_name = "accounts"
context_object_name_singular = "account"
title = "Accounts"
title_singular = "Account"
page_title = "List of accounts" page_title = "List of accounts"
list_url_name = "accounts" list_url_name = "accounts"
@ -85,8 +74,6 @@ class AccountList(LoginRequiredMixin, ObjectList):
class AccountCreate(LoginRequiredMixin, ObjectCreate): class AccountCreate(LoginRequiredMixin, ObjectCreate):
model = Account model = Account
form_class = AccountForm form_class = AccountForm
context_object_name = "accounts"
context_object_name_singular = "account"
list_url_name = "accounts" list_url_name = "accounts"
list_url_args = ["type"] list_url_args = ["type"]
@ -110,8 +97,6 @@ class AccountCreate(LoginRequiredMixin, ObjectCreate):
class AccountUpdate(LoginRequiredMixin, ObjectUpdate): class AccountUpdate(LoginRequiredMixin, ObjectUpdate):
model = Account model = Account
form_class = AccountForm form_class = AccountForm
context_object_name = "accounts"
context_object_name_singular = "account"
list_url_name = "accounts" list_url_name = "accounts"
list_url_args = ["type"] list_url_args = ["type"]
@ -121,8 +106,6 @@ class AccountUpdate(LoginRequiredMixin, ObjectUpdate):
class AccountDelete(LoginRequiredMixin, ObjectDelete): class AccountDelete(LoginRequiredMixin, ObjectDelete):
model = Account model = Account
context_object_name = "accounts"
context_object_name_singular = "account"
list_url_name = "accounts" list_url_name = "accounts"
list_url_args = ["type"] list_url_args = ["type"]

View File

@ -106,10 +106,6 @@ class HookList(LoginRequiredMixin, ObjectList):
# window_content = "window-content/hooks.html" # window_content = "window-content/hooks.html"
list_template = "partials/hook-list.html" list_template = "partials/hook-list.html"
model = Hook model = Hook
context_object_name = "hooks"
context_object_name_singular = "hook"
title = "Hooks"
title_singular = "Hook"
page_title = "List of active URL endpoints for receiving hooks." page_title = "List of active URL endpoints for receiving hooks."
page_subtitle = ( page_subtitle = (
"Add URLs here to receive Drakdoo callbacks. " "Add URLs here to receive Drakdoo callbacks. "
@ -125,8 +121,6 @@ class HookList(LoginRequiredMixin, ObjectList):
class HookCreate(LoginRequiredMixin, ObjectCreate): class HookCreate(LoginRequiredMixin, ObjectCreate):
model = Hook model = Hook
form_class = HookForm form_class = HookForm
context_object_name = "hooks"
context_object_name_singular = "hook"
list_url_name = "hooks" list_url_name = "hooks"
list_url_args = ["type"] list_url_args = ["type"]
@ -137,8 +131,6 @@ class HookCreate(LoginRequiredMixin, ObjectCreate):
class HookUpdate(LoginRequiredMixin, ObjectUpdate): class HookUpdate(LoginRequiredMixin, ObjectUpdate):
model = Hook model = Hook
form_class = HookForm form_class = HookForm
context_object_name = "hooks"
context_object_name_singular = "hook"
list_url_name = "hooks" list_url_name = "hooks"
list_url_args = ["type"] list_url_args = ["type"]
@ -148,8 +140,6 @@ class HookUpdate(LoginRequiredMixin, ObjectUpdate):
class HookDelete(LoginRequiredMixin, ObjectDelete): class HookDelete(LoginRequiredMixin, ObjectDelete):
model = Hook model = Hook
context_object_name = "hooks"
context_object_name_singular = "hook"
list_url_name = "hooks" list_url_name = "hooks"
list_url_args = ["type"] list_url_args = ["type"]

View File

@ -70,16 +70,14 @@ class PositionAction(LoginRequiredMixin, View):
info = account.client.get_position_info(symbol) info = account.client.get_position_info(symbol)
print("ACCT INFO", info) print("ACCT INFO", info)
items = get_positions(request.user, account_id)
if type == "page": if type == "page":
type = "modal" type = "modal"
context = { context = {
"title": f"Hooks ({type})", "title": f"Hooks ({type})",
"unique": unique, "unique": unique,
"window_content": self.window_content, "window_content": self.window_content,
"items": items,
"type": type, "type": type,
"items": info,
} }
context["items"] = info
return render(request, template_name, context) return render(request, template_name, context)

View File

@ -14,10 +14,6 @@ log = logs.get_logger(__name__)
class StrategyList(LoginRequiredMixin, ObjectList): class StrategyList(LoginRequiredMixin, ObjectList):
list_template = "partials/strategy-list.html" list_template = "partials/strategy-list.html"
model = Strategy model = Strategy
context_object_name = "strategies"
context_object_name_singular = "strategy"
title = "Strategies"
title_singular = "Strategy"
page_title = "List of strategies" page_title = "List of strategies"
list_url_name = "strategies" list_url_name = "strategies"
@ -29,9 +25,6 @@ class StrategyList(LoginRequiredMixin, ObjectList):
class StrategyCreate(LoginRequiredMixin, ObjectCreate): class StrategyCreate(LoginRequiredMixin, ObjectCreate):
model = Strategy model = Strategy
form_class = StrategyForm form_class = StrategyForm
context_object_name = "strategies"
context_object_name_singular = "strategy"
list_url_name = "strategies" list_url_name = "strategies"
list_url_args = ["type"] list_url_args = ["type"]
@ -41,8 +34,6 @@ class StrategyCreate(LoginRequiredMixin, ObjectCreate):
class StrategyUpdate(LoginRequiredMixin, ObjectUpdate): class StrategyUpdate(LoginRequiredMixin, ObjectUpdate):
model = Strategy model = Strategy
form_class = StrategyForm form_class = StrategyForm
context_object_name = "strategies"
context_object_name_singular = "strategy"
list_url_name = "strategies" list_url_name = "strategies"
list_url_args = ["type"] list_url_args = ["type"]
@ -52,8 +43,6 @@ class StrategyUpdate(LoginRequiredMixin, ObjectUpdate):
class StrategyDelete(LoginRequiredMixin, ObjectDelete): class StrategyDelete(LoginRequiredMixin, ObjectDelete):
model = Strategy model = Strategy
context_object_name = "strategies"
context_object_name_singular = "strategy"
list_url_name = "strategies" list_url_name = "strategies"
list_url_args = ["type"] list_url_args = ["type"]

View File

@ -5,7 +5,13 @@ from django.views import View
from core.forms import TradeForm from core.forms import TradeForm
from core.models import Trade from core.models import Trade
from core.util import logs from core.util import logs
from core.views import ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate from core.views import (
ObjectCreate,
ObjectDelete,
ObjectList,
ObjectNameMixin,
ObjectUpdate,
)
log = logs.get_logger(__name__) log = logs.get_logger(__name__)
@ -13,10 +19,6 @@ log = logs.get_logger(__name__)
class TradeList(LoginRequiredMixin, ObjectList): class TradeList(LoginRequiredMixin, ObjectList):
list_template = "partials/trade-list.html" list_template = "partials/trade-list.html"
model = Trade model = Trade
context_object_name = "trades"
context_object_name_singular = "trade"
title = "Trades"
title_singular = "Trade"
page_title = ( page_title = (
"List of bot and manual trades. This may not reflect actual live trades." "List of bot and manual trades. This may not reflect actual live trades."
) )
@ -33,9 +35,6 @@ class TradeList(LoginRequiredMixin, ObjectList):
class TradeCreate(LoginRequiredMixin, ObjectCreate): class TradeCreate(LoginRequiredMixin, ObjectCreate):
model = Trade model = Trade
form_class = TradeForm form_class = TradeForm
context_object_name = "trades"
context_object_name_singular = "trade"
list_url_name = "trades" list_url_name = "trades"
list_url_args = ["type"] list_url_args = ["type"]
@ -49,9 +48,6 @@ class TradeCreate(LoginRequiredMixin, ObjectCreate):
class TradeUpdate(LoginRequiredMixin, ObjectUpdate): class TradeUpdate(LoginRequiredMixin, ObjectUpdate):
model = Trade model = Trade
form_class = TradeForm form_class = TradeForm
context_object_name = "trades"
context_object_name_singular = "trade"
list_url_name = "trades" list_url_name = "trades"
list_url_args = ["type"] list_url_args = ["type"]
@ -60,15 +56,14 @@ class TradeUpdate(LoginRequiredMixin, ObjectUpdate):
class TradeDelete(LoginRequiredMixin, ObjectDelete): class TradeDelete(LoginRequiredMixin, ObjectDelete):
model = Trade model = Trade
context_object_name = "trades"
context_object_name_singular = "trade"
list_url_name = "trades" list_url_name = "trades"
list_url_args = ["type"] list_url_args = ["type"]
class TradeDeleteAll(LoginRequiredMixin, View): class TradeDeleteAll(LoginRequiredMixin, ObjectNameMixin, View):
template_name = "partials/notify.html" template_name = "partials/notify.html"
model = Trade
def delete(self, request): def delete(self, request):
""" """