1- import logging
21import decimal
2+ import logging
33from typing import List
44
55from investing_algorithm_framework .domain import OrderStatus , OrderFee , \
66 Position , Order , Portfolio , OrderType , OrderSide , ApiException , \
7- BACKTESTING_FLAG , BACKTESTING_INDEX_DATETIME , Trade , PeekableQueue , \
8- MarketService
7+ BACKTESTING_FLAG , BACKTESTING_INDEX_DATETIME , MarketService
98from investing_algorithm_framework .services import MarketCredentialService , \
10- MarketDataSourceService
9+ MarketDataSourceService , PortfolioService , PositionService , TradeService , \
10+ OrderService , ConfigurationService , StrategyOrchestratorService , \
11+ PortfolioConfigurationService
1112
1213logger = logging .getLogger ("investing_algorithm_framework" )
1314
@@ -24,21 +25,26 @@ def __init__(
2425 market_service ,
2526 strategy_orchestrator_service ,
2627 market_credential_service ,
27- market_data_source_service
28+ market_data_source_service ,
29+ trade_service
2830 ):
29- self .portfolio_service = portfolio_service
30- self .position_service = position_service
31- self .order_service = order_service
32- self ._market_service : MarketService = market_service
33- self .configuration_service = configuration_service
34- self .portfolio_configuration_service = portfolio_configuration_service
35- self .strategy_orchestrator_service = strategy_orchestrator_service
31+ self .portfolio_service : PortfolioService = portfolio_service
32+ self .position_service : PositionService = position_service
33+ self .order_service : OrderService = order_service
34+ self .market_service : MarketService = market_service
35+ self .configuration_service : ConfigurationService \
36+ = configuration_service
37+ self .portfolio_configuration_service : PortfolioConfigurationService \
38+ = portfolio_configuration_service
39+ self .strategy_orchestrator_service : StrategyOrchestratorService \
40+ = strategy_orchestrator_service
3641 self ._market_data_sources = {}
3742 self ._strategies = []
3843 self ._market_credential_service : MarketCredentialService \
3944 = market_credential_service
4045 self ._market_data_source_service : MarketDataSourceService \
4146 = market_data_source_service
47+ self .trade_service : TradeService = trade_service
4248
4349 def start (self , number_of_iterations = None , stateless = False ):
4450
@@ -158,14 +164,14 @@ def create_limit_order(
158164 )
159165
160166 def create_market_order (
161- self ,
162- target_symbol ,
163- order_side ,
164- amount ,
165- market = None ,
166- execute = False ,
167- validate = False ,
168- sync = True
167+ self ,
168+ target_symbol ,
169+ order_side ,
170+ amount ,
171+ market = None ,
172+ execute = False ,
173+ validate = False ,
174+ sync = True
169175 ):
170176
171177 if market is None :
@@ -221,13 +227,13 @@ def reset(self):
221227 self ._running_workers = []
222228
223229 def get_order (
224- self ,
225- reference_id = None ,
226- market = None ,
227- target_symbol = None ,
228- trading_symbol = None ,
229- order_side = None ,
230- order_type = None
230+ self ,
231+ reference_id = None ,
232+ market = None ,
233+ target_symbol = None ,
234+ trading_symbol = None ,
235+ order_side = None ,
236+ order_type = None
231237 ) -> Order :
232238 query_params = {}
233239
@@ -256,12 +262,12 @@ def get_order(
256262 return self .order_service .find (query_params )
257263
258264 def get_orders (
259- self ,
260- target_symbol = None ,
261- status = None ,
262- order_type = None ,
263- order_side = None ,
264- market = None
265+ self ,
266+ target_symbol = None ,
267+ status = None ,
268+ order_type = None ,
269+ order_side = None ,
270+ market = None
265271 ) -> List [Order ]:
266272
267273 if market is None :
@@ -284,13 +290,13 @@ def get_order_fee(self, order_id) -> OrderFee:
284290 return self .order_service .get_order_fee (order_id )
285291
286292 def get_positions (
287- self ,
288- market = None ,
289- identifier = None ,
290- amount_gt = None ,
291- amount_gte = None ,
292- amount_lt = None ,
293- amount_lte = None
293+ self ,
294+ market = None ,
295+ identifier = None ,
296+ amount_gt = None ,
297+ amount_gte = None ,
298+ amount_lt = None ,
299+ amount_lte = None
294300 ) -> List [Position ]:
295301 query_params = {}
296302
@@ -346,14 +352,14 @@ def get_position(self, symbol, market=None, identifier=None) -> Position:
346352 return None
347353
348354 def has_position (
349- self ,
350- symbol ,
351- market = None ,
352- identifier = None ,
353- amount_gt = 0 ,
354- amount_gte = None ,
355- amount_lt = None ,
356- amount_lte = None
355+ self ,
356+ symbol ,
357+ market = None ,
358+ identifier = None ,
359+ amount_gt = 0 ,
360+ amount_gte = None ,
361+ amount_lt = None ,
362+ amount_lte = None
357363 ):
358364 return self .position_exists (
359365 symbol ,
@@ -675,55 +681,10 @@ def check_pending_orders(self):
675681 self .order_service .check_pending_orders ()
676682
677683 def get_trades (self , market = None ):
678- portfolios = self .portfolio_service .get_all ()
679- trades = []
680-
681- for portfolio in portfolios :
682- buy_orders = self .order_service .get_all ({
683- "status" : OrderStatus .CLOSED .value ,
684- "order_side" : OrderSide .BUY .value ,
685- "portfolio_id" : portfolio .id
686- })
687-
688- for buy_order in buy_orders :
689- symbol = buy_order .get_symbol ()
690- ticker = self ._market_data_source_service .get_ticker (
691- symbol = symbol , market = market
692- )
693- trades .append (
694- Trade (
695- buy_order_id = buy_order .id ,
696- target_symbol = buy_order .get_target_symbol (),
697- trading_symbol = buy_order .get_trading_symbol (),
698- amount = buy_order .get_amount (),
699- open_price = buy_order .get_price (),
700- closed_price = buy_order .get_trade_closed_price (),
701- closed_at = buy_order .get_trade_closed_at (),
702- opened_at = buy_order .get_created_at (),
703- current_price = ticker ["bid" ]
704- )
705- )
706-
707- return trades
684+ return self .trade_service .get_trades (market )
708685
709686 def get_closed_trades (self ):
710- buy_orders = self .order_service .get_all ({
711- "status" : OrderStatus .CLOSED .value ,
712- "order_side" : OrderSide .BUY .value
713- })
714- return [
715- Trade (
716- buy_order_id = order .id ,
717- target_symbol = order .get_target_symbol (),
718- trading_symbol = order .get_trading_symbol (),
719- amount = order .get_amount (),
720- open_price = order .get_price (),
721- closed_price = order .get_trade_closed_price (),
722- closed_at = order .get_trade_closed_at (),
723- opened_at = order .get_created_at ()
724- ) for order in buy_orders
725- if order .get_trade_closed_at () is not None
726- ]
687+ return self .trade_service .get_closed_trades ()
727688
728689 def round_down (self , value , amount_of_decimals ):
729690
@@ -743,140 +704,10 @@ def count_decimals(self, number):
743704 return 0
744705
745706 def get_open_trades (self , target_symbol = None , market = None ):
746- portfolios = self .portfolio_service .get_all ()
747- trades = []
748-
749- for portfolio in portfolios :
750-
751- if target_symbol is not None :
752- buy_orders = self .order_service .get_all ({
753- "status" : OrderStatus .CLOSED .value ,
754- "order_side" : OrderSide .BUY .value ,
755- "portfolio_id" : portfolio .id ,
756- "target_symbol" : target_symbol
757- })
758- sell_orders = self .order_service .get_all ({
759- "status" : OrderStatus .OPEN .value ,
760- "order_side" : OrderSide .SELL .value ,
761- "portfolio_id" : portfolio .id ,
762- "target_symbol" : target_symbol
763- })
764- else :
765- buy_orders = self .order_service .get_all ({
766- "status" : OrderStatus .CLOSED .value ,
767- "order_side" : OrderSide .BUY .value ,
768- "portfolio_id" : portfolio .id
769- })
770- sell_orders = self .order_service .get_all ({
771- "status" : OrderStatus .OPEN .value ,
772- "order_side" : OrderSide .SELL .value ,
773- "portfolio_id" : portfolio .id
774- })
775-
776- buy_orders = [
777- buy_order for buy_order in buy_orders
778- if buy_order .get_trade_closed_at () is None
779- ]
780- sell_amount = sum ([order .amount for order in sell_orders ])
781-
782- # Subtract the amount of the open sell orders
783- # from the amount of the buy orders
784- buy_orders_queue = PeekableQueue ()
785-
786- for buy_order in buy_orders :
787- buy_orders_queue .enqueue (buy_order )
788-
789- while sell_amount > 0 and not buy_orders_queue .is_empty ():
790- first_buy_order = buy_orders_queue .peek ()
791- available = first_buy_order .get_filled () \
792- - first_buy_order .get_trade_closed_amount ()
793-
794- if available > sell_amount :
795- remaining = available - sell_amount
796- sell_amount = 0
797- first_buy_order .set_filled (remaining )
798- else :
799- sell_amount = sell_amount - available
800- buy_orders_queue .dequeue ()
801-
802- for buy_order in buy_orders_queue :
803- symbol = buy_order .get_symbol ()
804-
805- try :
806- ticker = self ._market_data_source_service .get_ticker (
807- symbol = symbol , market = market
808- )
809- except Exception as e :
810- logger .error (e )
811- raise ApiException (
812- f"Error getting ticker data for "
813- f"trade { buy_order .get_target_symbol ()} "
814- f"-{ buy_order .get_trading_symbol ()} . Make sure you "
815- f"have registered a ticker market data source for "
816- f"{ buy_order .get_target_symbol ()} "
817- f"-{ buy_order .get_trading_symbol ()} "
818- f"for market { portfolio .market } "
819- )
820-
821- amount = buy_order .get_filled ()
822- closed_amount = buy_order .get_trade_closed_amount ()
823-
824- if closed_amount is not None :
825- amount = amount - closed_amount
826-
827- trades .append (
828- Trade (
829- buy_order_id = buy_order .id ,
830- target_symbol = buy_order .get_target_symbol (),
831- trading_symbol = buy_order .get_trading_symbol (),
832- amount = amount ,
833- open_price = buy_order .get_price (),
834- opened_at = buy_order .get_created_at (),
835- current_price = ticker ["bid" ]
836- )
837- )
838-
839- return trades
707+ return self .trade_service .get_open_trades (target_symbol , market )
840708
841709 def close_trade (self , trade , market = None ):
842-
843- if trade .closed_at is not None :
844- raise ApiException ("Trade already closed." )
845-
846- order = self .order_service .get (trade .buy_order_id )
847-
848- if order .get_filled () <= 0 :
849- raise ApiException (
850- "Buy order belonging to the trade has no amount."
851- )
852-
853- portfolio = self .portfolio_service \
854- .find ({"position" : order .position_id })
855- position = self .position_service .find (
856- {"portfolio" : portfolio .id , "symbol" : order .get_target_symbol ()}
857- )
858- amount = order .get_amount ()
859-
860- if position .get_amount () < amount :
861- logger .warning (
862- f"Order amount { amount } is larger then amount "
863- f"of available { position .symbol } "
864- f"position: { position .get_amount ()} , "
865- f"changing order amount to size of position"
866- )
867- amount = position .get_amount ()
868-
869- symbol = f"{ order .get_target_symbol ().upper ()} " \
870- f"/{ order .get_trading_symbol ().upper ()} "
871- ticker = self ._market_data_source_service .get_ticker (
872- symbol = symbol , market = market
873- )
874- self .create_limit_order (
875- target_symbol = order .target_symbol ,
876- amount = amount ,
877- order_side = OrderSide .SELL .value ,
878- price = ticker ["bid" ],
879- )
710+ self .trade_service .close_trade (trade , market )
880711
881712 def get_number_of_positions (self ):
882713 """
0 commit comments