Implement strategies and posting trades
This commit is contained in:
147
core/models.py
147
core/models.py
@@ -1,14 +1,13 @@
|
||||
import stripe
|
||||
from alpaca.common.exceptions import APIError
|
||||
from alpaca.trading.client import TradingClient
|
||||
from alpaca.trading.enums import OrderSide, TimeInForce
|
||||
from alpaca.trading.requests import LimitOrderRequest, MarketOrderRequest
|
||||
from alpaca.trading.requests import GetAssetsRequest
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
from serde import ValidationError
|
||||
|
||||
from core.lib import trades
|
||||
from core.lib.customers import get_or_create, update_customer_fields
|
||||
from core.lib.serde import ccxt_s
|
||||
from core.util import logs
|
||||
|
||||
log = logs.get_logger(__name__)
|
||||
@@ -78,12 +77,46 @@ class Account(models.Model):
|
||||
api_key = models.CharField(max_length=255)
|
||||
api_secret = models.CharField(max_length=255)
|
||||
sandbox = models.BooleanField(default=False)
|
||||
supported_symbols = models.JSONField(default=list)
|
||||
|
||||
def get_account(self):
|
||||
def __str__(self):
|
||||
name = f"{self.name} ({self.exchange})"
|
||||
if self.sandbox:
|
||||
name += " (sandbox)"
|
||||
return name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""
|
||||
Override the save function to update supported symbols.
|
||||
"""
|
||||
try:
|
||||
request = GetAssetsRequest(status="active", asset_class="crypto")
|
||||
assets = self.client.get_all_assets(filter=request)
|
||||
except APIError as e:
|
||||
log.error(f"Could not get asset list: {e}")
|
||||
return False
|
||||
asset_list = [x["symbol"] for x in assets if "symbol" in x]
|
||||
self.supported_symbols = asset_list
|
||||
print("Supported symbols", self.supported_symbols)
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_client(self):
|
||||
trading_client = TradingClient(
|
||||
self.api_key, self.api_secret, paper=self.sandbox
|
||||
self.api_key, self.api_secret, paper=self.sandbox, raw_data=True
|
||||
)
|
||||
return trading_client.get_account()
|
||||
return trading_client
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
"""
|
||||
Convenience property for one-off API calls.
|
||||
"""
|
||||
return self.get_client()
|
||||
|
||||
@classmethod
|
||||
def get_by_id(cls, account_id, user):
|
||||
return cls.objects.get(id=account_id, user=user)
|
||||
|
||||
|
||||
class Session(models.Model):
|
||||
@@ -95,17 +128,21 @@ class Session(models.Model):
|
||||
|
||||
|
||||
class Hook(models.Model):
|
||||
DIRECTION_CHOICES = (
|
||||
("buy", "Buy"),
|
||||
("sell", "Sell"),
|
||||
)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=1024, null=True, blank=True, unique=True)
|
||||
hook = models.CharField(max_length=255, unique=True)
|
||||
direction = models.CharField(choices=DIRECTION_CHOICES, max_length=255)
|
||||
received = models.IntegerField(default=0)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.hook})"
|
||||
|
||||
|
||||
class Trade(models.Model):
|
||||
SYMBOL_CHOICES = (
|
||||
("BTC/USD", "Bitcoin/US Dollar"),
|
||||
("LTC/USD", "Litecoin/US Dollar"),
|
||||
)
|
||||
TYPE_CHOICES = (
|
||||
("market", "Market"),
|
||||
("limit", "Limit"),
|
||||
@@ -114,11 +151,13 @@ class Trade(models.Model):
|
||||
("buy", "Buy"),
|
||||
("sell", "Sell"),
|
||||
)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
account = models.ForeignKey(Account, on_delete=models.CASCADE)
|
||||
hook = models.ForeignKey(Hook, on_delete=models.CASCADE, null=True, blank=True)
|
||||
symbol = models.CharField(choices=SYMBOL_CHOICES, max_length=255)
|
||||
symbol = models.CharField(max_length=255)
|
||||
type = models.CharField(choices=TYPE_CHOICES, max_length=255)
|
||||
amount = models.FloatField()
|
||||
amount = models.FloatField(null=True, blank=True)
|
||||
amount_usd = models.FloatField(null=True, blank=True)
|
||||
price = models.FloatField(null=True, blank=True)
|
||||
stop_loss = models.FloatField(null=True, blank=True)
|
||||
take_profit = models.FloatField(null=True, blank=True)
|
||||
@@ -134,56 +173,8 @@ class Trade(models.Model):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._original = self
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""
|
||||
Override the save function to place the trade.
|
||||
"""
|
||||
if self.response is None:
|
||||
# the trade is not placed yet
|
||||
if self.account.exchange == "alpaca":
|
||||
trading_client = TradingClient(
|
||||
self.account.api_key,
|
||||
self.account.api_secret,
|
||||
paper=self.account.sandbox,
|
||||
)
|
||||
if self.direction == "buy":
|
||||
direction = OrderSide.BUY
|
||||
elif self.direction == "sell":
|
||||
direction = OrderSide.SELL
|
||||
else:
|
||||
raise Exception("Unknown direction")
|
||||
if self.type == "market":
|
||||
|
||||
market_order_data = MarketOrderRequest(
|
||||
symbol=self.symbol,
|
||||
qty=self.amount,
|
||||
side=OrderSide.BUY,
|
||||
time_in_force=TimeInForce.IOC,
|
||||
)
|
||||
order = trading_client.submit_order(order_data=market_order_data)
|
||||
elif self.type == "limit":
|
||||
limit_order_data = LimitOrderRequest(
|
||||
symbol=self.symbol,
|
||||
limit_price=self.price,
|
||||
qty=self.amount,
|
||||
side=direction,
|
||||
time_in_force=TimeInForce.IOC,
|
||||
)
|
||||
order = trading_client.submit_order(order_data=limit_order_data)
|
||||
else:
|
||||
raise Exception("Unknown order type")
|
||||
|
||||
print("ORDER", order)
|
||||
|
||||
# self.status = parsed.status
|
||||
# self.response = order
|
||||
else:
|
||||
# there is a trade open
|
||||
# get trade
|
||||
# update trade
|
||||
pass
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
def post(self):
|
||||
return trades.post_trade(self)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
# close the trade
|
||||
@@ -195,12 +186,30 @@ class Callback(models.Model):
|
||||
title = models.CharField(max_length=1024, null=True, blank=True)
|
||||
message = models.CharField(max_length=1024, null=True, blank=True)
|
||||
period = models.CharField(max_length=255, null=True, blank=True)
|
||||
timestamp_sent = models.BigIntegerField(null=True, blank=True)
|
||||
timestamp_trade = models.BigIntegerField(null=True, blank=True)
|
||||
market_exchange = models.CharField(max_length=255, null=True, blank=True)
|
||||
market_item = models.CharField(max_length=255, null=True, blank=True)
|
||||
market_currency = models.CharField(max_length=255, null=True, blank=True)
|
||||
market_contract = models.CharField(max_length=255, null=True, blank=True)
|
||||
sent = models.BigIntegerField(null=True, blank=True)
|
||||
trade = models.BigIntegerField(null=True, blank=True)
|
||||
exchange = models.CharField(max_length=255, null=True, blank=True)
|
||||
base = models.CharField(max_length=255, null=True, blank=True)
|
||||
quote = models.CharField(max_length=255, null=True, blank=True)
|
||||
contract = models.CharField(max_length=255, null=True, blank=True)
|
||||
price = models.FloatField(null=True, blank=True)
|
||||
symbol = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class Strategy(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(null=True, blank=True)
|
||||
account = models.ForeignKey(Account, on_delete=models.CASCADE)
|
||||
hooks = models.ManyToManyField(Hook)
|
||||
enabled = models.BooleanField(default=False)
|
||||
take_profit_percent = models.FloatField(default=3.0)
|
||||
stop_loss_percent = models.FloatField(default=1.0)
|
||||
price_slippage_percent = models.FloatField(default=2.5)
|
||||
trade_size_percent = models.FloatField(default=2.5)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# class Perms(models.Model):
|
||||
|
||||
Reference in New Issue
Block a user