Skip to content

Commit

Permalink
Refactor to decimal support
Browse files Browse the repository at this point in the history
  • Loading branch information
MDUYN committed Sep 2, 2023
1 parent a5bf4bf commit 86ad9d7
Show file tree
Hide file tree
Showing 50 changed files with 1,807 additions and 1,505 deletions.
210 changes: 180 additions & 30 deletions investing_algorithm_framework/app/algorithm.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import logging
from math import floor
from typing import List
from decimal import Decimal

from investing_algorithm_framework.domain import OrderStatus, OrderFee, \
Position, Order, Portfolio, OrderType, OrderSide, ApiException
Position, Order, Portfolio, OrderType, OrderSide, ApiException, \
parse_decimal_to_string, PositionCost

logger = logging.getLogger("investing_algorithm_framework")

Expand All @@ -14,13 +17,15 @@ def __init__(
portfolio_configuration_service,
portfolio_service,
position_service,
position_cost_service,
order_service,
market_service,
market_data_service,
strategy_orchestrator_service
):
self.portfolio_service = portfolio_service
self.position_service = position_service
self.position_cost_service = position_cost_service
self.order_service = order_service
self.market_service = market_service
self._config = None
Expand Down Expand Up @@ -68,8 +73,8 @@ def create_order(
{
"target_symbol": target_symbol,
"price": price,
"amount": amount,
"type": type,
"amount": parse_decimal_to_string(amount),
"order_type": type,
"side": side,
"portfolio_id": portfolio.id,
"status": OrderStatus.CREATED.value,
Expand All @@ -96,8 +101,8 @@ def create_limit_order(
portfolio = self.portfolio_service.find({"market": market})

if percentage_of_portfolio is not None and OrderSide.BUY.equals(side):
size = portfolio.net_size * (percentage_of_portfolio / 100)
amount = size / price
size = float(portfolio.net_size) * (percentage_of_portfolio / 100)
amount = floor(size / price)

if percentage_of_position is not None and OrderSide.SELL.equals(side):
position = self.position_service.find(
Expand All @@ -106,14 +111,15 @@ def create_limit_order(
"portfolio": portfolio.id
}
)
amount = position.amount * (percentage_of_position / 100)
amount = position.get_amount() * \
(Decimal(percentage_of_position) / 100)

return self.order_service.create(
{
"target_symbol": target_symbol,
"price": price,
"amount": amount,
"type": OrderType.LIMIT.value,
"order_type": OrderType.LIMIT.value,
"side": OrderSide.from_value(side).value,
"portfolio_id": portfolio.id,
"status": OrderStatus.CREATED.value,
Expand All @@ -139,7 +145,7 @@ def create_market_order(
{
"target_symbol": target_symbol,
"amount": amount,
"type": OrderType.MARKET.value,
"order_type": OrderType.MARKET.value,
"side": OrderSide.from_value(side).value,
"portfolio_id": portfolio.id,
"status": OrderStatus.CREATED.value,
Expand Down Expand Up @@ -167,7 +173,7 @@ def get_unallocated(self, market=None) -> Position:
trading_symbol = portfolio.trading_symbol
return self.position_service.find(
{"portfolio": portfolio.id, "symbol": trading_symbol}
).amount
).get_amount()

def reset(self):
self._workers = []
Expand Down Expand Up @@ -236,7 +242,15 @@ def get_orders(
def get_order_fee(self, order_id) -> OrderFee:
return self.order_service.get_order_fee(order_id)

def get_positions(self, market=None, identifier=None) -> List[Position]:
def get_positions(
self,
market=None,
identifier=None,
amount_gt=None,
amount_gte=None,
amount_lt=None,
amount_lte=None
) -> List[Position]:
query_params = {}

if market is not None:
Expand All @@ -245,6 +259,18 @@ def get_positions(self, market=None, identifier=None) -> List[Position]:
if identifier is not None:
query_params["identifier"] = identifier

if amount_gt is not None:
query_params["amount_gt"] = amount_gt

if amount_gte is not None:
query_params["amount_gte"] = amount_gte

if amount_lt is not None:
query_params["amount_lt"] = amount_lt

if amount_lte is not None:
query_params["amount_lte"] = amount_lte

portfolios = self.portfolio_service.get_all(query_params)

if not portfolios:
Expand Down Expand Up @@ -278,7 +304,16 @@ def get_position(self, symbol, market=None, identifier=None) -> Position:
except ApiException:
return None

def position_exists(self, symbol, market=None, identifier=None) -> bool:
def position_exists(
self,
symbol,
market=None,
identifier=None,
amount_gt=None,
amount_gte=None,
amount_lt=None,
amount_lte=None
) -> bool:
query_params = {}

if market is not None:
Expand All @@ -287,6 +322,18 @@ def position_exists(self, symbol, market=None, identifier=None) -> bool:
if identifier is not None:
query_params["identifier"] = identifier

if amount_gt is not None:
query_params["amount_gt"] = amount_gt

if amount_gte is not None:
query_params["amount_gte"] = amount_gte

if amount_lt is not None:
query_params["amount_lt"] = amount_lt

if amount_lte is not None:
query_params["amount_lte"] = amount_lte

query_params["symbol"] = symbol
return self.position_service.exists(query_params)

Expand Down Expand Up @@ -317,38 +364,34 @@ def get_position_percentage(
self.get_allocated(identifier=portfolio.identifier)) * 100

def close_position(self, symbol, market=None, identifier=None):
query_params = {}

if market is not None:
query_params["market"] = market

if identifier is not None:
query_params["identifier"] = identifier

position = self.get_position(symbol, market, identifier)

if position is None:
raise ApiException("No position found.")
portfolio = self.portfolio_service.find(
{"market": market, "identifier": identifier}
)
portfolio_config = self.portfolio_configuration_service.find(
{"portfolio": portfolio.id}
)
self.market_service.initialize(portfolio_config)
position = self.position_service.find(
{"portfolio": portfolio.id, "symbol": symbol}
)

if position.amount == 0:
if position.get_amount() == 0:
return

for order in self.order_service\
.get_all({
"position": position.id, "status": OrderStatus.OPEN.value
}):
self.order_service.cancel_order(order.id)
self.market_service.cancel_order(order.id)

portfolio = self.portfolio_service.find({"position": position.id})
self.market_service.market = portfolio.market
ticker = self.market_service.get_ticker(
symbol=f"{symbol.upper()}/{portfolio.trading_symbol.upper()}"
)
self.create_limit_order(
target_symbol=position.symbol,
amount=position.amount,
amount=position.get_amount(),
side=OrderSide.SELL.value,
price=ticker["bid"],
price=parse_decimal_to_string(ticker["bid"]),
)

def add_strategies(self, strategies):
Expand Down Expand Up @@ -402,9 +445,116 @@ def get_allocated(self, market=None, identifier=None) -> float:
f"{portfolio.trading_symbol.upper()}"
self.market_service.initialize(portfolio.configuration)
price = self.market_service.get_ticker(symbol)
allocated = allocated + (position.amount * price["bid"])
allocated = allocated + \
(position.get_amount() * Decimal(price["bid"]))

return allocated

def get_unfilled(self, market=None, identifier=None) -> float:

if self.portfolio_configuration_service.count() > 1 \
and identifier is None and market is None:
raise ApiException(
"Multiple portfolios found. Please specify a "
"portfolio identifier."
)

if market is not None and identifier is not None:
portfolio_configurations = self.portfolio_configuration_service\
.get_all()

else:
query_params = {
"market": market,
"identifier": identifier
}
portfolio_configurations = [self.portfolio_configuration_service
.find(query_params)]

portfolios = []

for portfolio_configuration in portfolio_configurations:
portfolio = self.portfolio_service.find(
{"identifier": portfolio_configuration.identifier}
)
portfolios.append(portfolio)

unfilled = 0

for portfolio in portfolios:
orders = self.order_service.get_all(
{"status": OrderStatus.OPEN.value, "portfolio": portfolio.id}
)
unfilled = unfilled \
+ sum([order.get_amount() * order.get_price() for order in orders])

return unfilled

def get_portfolio_configurations(self):
return self.portfolio_configuration_service.get_all()

def has_open_buy_orders(self, target_symbol, identifier=None, market=None):
query_params = {}

if identifier is not None:
portfolio = self.portfolio_service.find(
{"identifier": identifier}
)
query_params["portfolio"] = portfolio.id

if market is not None:
portfolio = self.portfolio_service.find(
{"market": market}
)
query_params["portfolio"] = portfolio.id

query_params["target_symbol"] = target_symbol
query_params["side"] = OrderSide.BUY.value
query_params["status"] = OrderStatus.OPEN.value
return self.order_service.exists(query_params)

def has_open_sell_orders(self, target_symbol, identifier=None, market=None):
query_params = {}

if identifier is not None:
portfolio = self.portfolio_service.find(
{"identifier": identifier}
)
query_params["portfolio"] = portfolio.id

if market is not None:
portfolio = self.portfolio_service.find(
{"market": market}
)
query_params["portfolio"] = portfolio.id

query_params["target_symbol"] = target_symbol
query_params["side"] = OrderSide.SELL.value
query_params["status"] = OrderStatus.OPEN.value
return self.order_service.exists(query_params)

def has_open_orders(self, target_symbol, identifier=None, market=None):
query_params = {}

if identifier is not None:
portfolio = self.portfolio_service.find(
{"identifier": identifier}
)
query_params["portfolio"] = portfolio.id

if market is not None:
portfolio = self.portfolio_service.find(
{"market": market}
)
query_params["portfolio"] = portfolio.id

query_params["target_symbol"] = target_symbol
query_params["status"] = OrderStatus.OPEN.value
return self.order_service.exists(query_params)

def get_position_costs(self, symbol, market=None, identifier=None) -> List[PositionCost]:
position = self.get_position(symbol, market, identifier)
return self.position_cost_service.get_all({"position": position.id, "itemized": True})

def check_pending_orders(self):
self.order_service.check_pending_orders()
23 changes: 13 additions & 10 deletions investing_algorithm_framework/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ def initialize(self):
setup_sqlalchemy(self)
create_all_tables()

self.algorithm = self.container.algorithm()
self.algorithm.add_strategies(self.strategies)
self.algorithm.add_tasks(self.tasks)
portfolio_configuration_service = self.container\
.portfolio_configuration_service()

if portfolio_configuration_service.count() == 0:
raise OperationalException("No portfolios configured")

self.create_portfolios()

def _initialize_management_commands(self):

if not Environment.TEST.equals(self.config.get(ENVIRONMENT)):
Expand All @@ -69,17 +80,9 @@ def run(
number_of_iterations: int = None,
sync=True
):
self.algorithm = self.container.algorithm()
self.algorithm.add_strategies(self.strategies)
self.algorithm.add_tasks(self.tasks)
portfolio_configuration_service = self.container\
.portfolio_configuration_service()
portfolio_service = self.container.portfolio_service()
self.initialize()

if portfolio_configuration_service.count() == 0:
raise OperationalException("No portfolios configured")

self.create_portfolios()
portfolio_service = self.container.portfolio_service()

if sync:
portfolio_service.sync_portfolios()
Expand Down
5 changes: 0 additions & 5 deletions investing_algorithm_framework/create_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,12 @@ def create_app(
config={},
stateless=False,
web=False,
initialize=True
):
app = App(config=config, web=web, stateless=stateless)
app = setup_dependency_container(
app,
["investing_algorithm_framework"],
["investing_algorithm_framework"]
)

if initialize:
app.initialize()

logger.info( "Investing algoritm framework app created")
return app
Loading

0 comments on commit 86ad9d7

Please sign in to comment.