1
- import logging
2
1
import decimal
2
+ import logging
3
3
from typing import List
4
4
5
5
from investing_algorithm_framework .domain import OrderStatus , OrderFee , \
6
6
Position , Order , Portfolio , OrderType , OrderSide , ApiException , \
7
- BACKTESTING_FLAG , BACKTESTING_INDEX_DATETIME , Trade , PeekableQueue , \
8
- MarketService
7
+ BACKTESTING_FLAG , BACKTESTING_INDEX_DATETIME , MarketService
9
8
from investing_algorithm_framework .services import MarketCredentialService , \
10
- MarketDataSourceService
9
+ MarketDataSourceService , PortfolioService , PositionService , TradeService , \
10
+ OrderService , ConfigurationService , StrategyOrchestratorService , \
11
+ PortfolioConfigurationService
11
12
12
13
logger = logging .getLogger ("investing_algorithm_framework" )
13
14
@@ -24,21 +25,26 @@ def __init__(
24
25
market_service ,
25
26
strategy_orchestrator_service ,
26
27
market_credential_service ,
27
- market_data_source_service
28
+ market_data_source_service ,
29
+ trade_service
28
30
):
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
36
41
self ._market_data_sources = {}
37
42
self ._strategies = []
38
43
self ._market_credential_service : MarketCredentialService \
39
44
= market_credential_service
40
45
self ._market_data_source_service : MarketDataSourceService \
41
46
= market_data_source_service
47
+ self .trade_service : TradeService = trade_service
42
48
43
49
def start (self , number_of_iterations = None , stateless = False ):
44
50
@@ -158,14 +164,14 @@ def create_limit_order(
158
164
)
159
165
160
166
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
169
175
):
170
176
171
177
if market is None :
@@ -221,13 +227,13 @@ def reset(self):
221
227
self ._running_workers = []
222
228
223
229
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
231
237
) -> Order :
232
238
query_params = {}
233
239
@@ -256,12 +262,12 @@ def get_order(
256
262
return self .order_service .find (query_params )
257
263
258
264
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
265
271
) -> List [Order ]:
266
272
267
273
if market is None :
@@ -284,13 +290,13 @@ def get_order_fee(self, order_id) -> OrderFee:
284
290
return self .order_service .get_order_fee (order_id )
285
291
286
292
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
294
300
) -> List [Position ]:
295
301
query_params = {}
296
302
@@ -346,14 +352,14 @@ def get_position(self, symbol, market=None, identifier=None) -> Position:
346
352
return None
347
353
348
354
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
357
363
):
358
364
return self .position_exists (
359
365
symbol ,
@@ -675,55 +681,10 @@ def check_pending_orders(self):
675
681
self .order_service .check_pending_orders ()
676
682
677
683
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 )
708
685
709
686
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 ()
727
688
728
689
def round_down (self , value , amount_of_decimals ):
729
690
@@ -743,140 +704,10 @@ def count_decimals(self, number):
743
704
return 0
744
705
745
706
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 )
840
708
841
709
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 )
880
711
881
712
def get_number_of_positions (self ):
882
713
"""
0 commit comments