Implement strategies and posting trades

This commit is contained in:
2022-10-27 18:08:40 +01:00
parent 7e4f3f52d1
commit 061c6f6ca7
32 changed files with 1060 additions and 178 deletions

View File

@@ -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):