finterion-investing-algorithm-framework 0.20.0__tar.gz → 0.22.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of finterion-investing-algorithm-framework might be problematic. Click here for more details.
- finterion_investing_algorithm_framework-0.22.0/PKG-INFO +61 -0
- finterion_investing_algorithm_framework-0.22.0/README.md +46 -0
- finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/__init__.py +9 -0
- finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/order_executor.py +112 -0
- finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/ping_hook.py +39 -0
- finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/portfolio_provider.py +123 -0
- finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/utils.py +56 -0
- {finterion_investing_algorithm_framework-0.20.0 → finterion_investing_algorithm_framework-0.22.0}/pyproject.toml +3 -3
- finterion_investing_algorithm_framework-0.20.0/PKG-INFO +0 -51
- finterion_investing_algorithm_framework-0.20.0/README.md +0 -35
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/__init__.py +0 -5
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/configuration/__init__.py +0 -3
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/configuration/constants.py +0 -2
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/create_app.py +0 -68
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/exceptions.py +0 -5
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/market_service.py +0 -183
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/models/__init__.py +0 -6
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/models/portfolio_configuration.py +0 -24
- finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/validation.py +0 -14
- {finterion_investing_algorithm_framework-0.20.0 → finterion_investing_algorithm_framework-0.22.0}/LICENSE +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: finterion-investing-algorithm-framework
|
|
3
|
+
Version: 0.22.0
|
|
4
|
+
Summary: This is the official finterion plugin for the investing algorithm framework open source project.
|
|
5
|
+
Author: Marc van Duyn
|
|
6
|
+
Author-email: marc@finterion.com
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Requires-Dist: finterion (>=0.8.8,<0.9.0)
|
|
13
|
+
Requires-Dist: investing-algorithm-framework (>=6.5.2)
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# Finterion Investing Algorithm Framework Plugin
|
|
17
|
+
This is the official plugin for the [investing algorithm framework](https://investing-algorithm-framework.com) open source project.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
You can install the plugin with pip.
|
|
21
|
+
```shell
|
|
22
|
+
pip install finterion-investing-algorithm-framework
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
In order to use the plugin you must register the following components in your investing algorithm framework application:
|
|
27
|
+
- **FinterionOrderExecutor**: This component is responsible for executing orders on the finterion platform.
|
|
28
|
+
- **FinterionPortfolioProvider**: This component is responsible for connecting the portfolio and syncing positions.
|
|
29
|
+
- **FinterionPingHook**: This component is responsible for pinging the finterion platform.
|
|
30
|
+
|
|
31
|
+
> **Note:** You must provide the API key of your algorithm in order to use
|
|
32
|
+
> the plugin. You can find your API keys in the developer dashboard of
|
|
33
|
+
> your algorithm on the finterion platform.
|
|
34
|
+
|
|
35
|
+
## Example
|
|
36
|
+
```python
|
|
37
|
+
import logging.config
|
|
38
|
+
from dotenv import load_dotenv
|
|
39
|
+
|
|
40
|
+
from investing_algorithm_framework import create_app, DEFAULT_LOGGING_CONFIG
|
|
41
|
+
|
|
42
|
+
from finterion_investing_algorithm_framework import \
|
|
43
|
+
FinterionPortfolioProvider, FinterionOrderExecutor, FinterionPingAction
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
load_dotenv()
|
|
47
|
+
logging.config.dictConfig(DEFAULT_LOGGING_CONFIG)
|
|
48
|
+
|
|
49
|
+
app = create_app()
|
|
50
|
+
app.on_strategy_run(FinterionPingAction)
|
|
51
|
+
app.add_order_executor(FinterionOrderExecutor)
|
|
52
|
+
app.add_portfolio_provider(FinterionPortfolioProvider)
|
|
53
|
+
app.add_market(
|
|
54
|
+
market="Finterion",
|
|
55
|
+
api_key="<FINTERION_API_KEY>", # Or set the environment variable FINTERION_API_KEY
|
|
56
|
+
trading_symbol="EUR",
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Documentation
|
|
61
|
+
You can find the official documentation at our [documentation website](https://docs.finterion.com/)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Finterion Investing Algorithm Framework Plugin
|
|
2
|
+
This is the official plugin for the [investing algorithm framework](https://investing-algorithm-framework.com) open source project.
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
You can install the plugin with pip.
|
|
6
|
+
```shell
|
|
7
|
+
pip install finterion-investing-algorithm-framework
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
In order to use the plugin you must register the following components in your investing algorithm framework application:
|
|
12
|
+
- **FinterionOrderExecutor**: This component is responsible for executing orders on the finterion platform.
|
|
13
|
+
- **FinterionPortfolioProvider**: This component is responsible for connecting the portfolio and syncing positions.
|
|
14
|
+
- **FinterionPingHook**: This component is responsible for pinging the finterion platform.
|
|
15
|
+
|
|
16
|
+
> **Note:** You must provide the API key of your algorithm in order to use
|
|
17
|
+
> the plugin. You can find your API keys in the developer dashboard of
|
|
18
|
+
> your algorithm on the finterion platform.
|
|
19
|
+
|
|
20
|
+
## Example
|
|
21
|
+
```python
|
|
22
|
+
import logging.config
|
|
23
|
+
from dotenv import load_dotenv
|
|
24
|
+
|
|
25
|
+
from investing_algorithm_framework import create_app, DEFAULT_LOGGING_CONFIG
|
|
26
|
+
|
|
27
|
+
from finterion_investing_algorithm_framework import \
|
|
28
|
+
FinterionPortfolioProvider, FinterionOrderExecutor, FinterionPingAction
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
load_dotenv()
|
|
32
|
+
logging.config.dictConfig(DEFAULT_LOGGING_CONFIG)
|
|
33
|
+
|
|
34
|
+
app = create_app()
|
|
35
|
+
app.on_strategy_run(FinterionPingAction)
|
|
36
|
+
app.add_order_executor(FinterionOrderExecutor)
|
|
37
|
+
app.add_portfolio_provider(FinterionPortfolioProvider)
|
|
38
|
+
app.add_market(
|
|
39
|
+
market="Finterion",
|
|
40
|
+
api_key="<FINTERION_API_KEY>", # Or set the environment variable FINTERION_API_KEY
|
|
41
|
+
trading_symbol="EUR",
|
|
42
|
+
)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
You can find the official documentation at our [documentation website](https://docs.finterion.com/)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework import OrderExecutor, OrderType, \
|
|
4
|
+
OrderSide, Order, OperationalException
|
|
5
|
+
from finterion import Finterion
|
|
6
|
+
|
|
7
|
+
from .utils import convert_finterion_order_to_order
|
|
8
|
+
|
|
9
|
+
logger = getLogger("investing_algorithm_framework")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FinterionOrderExecutor(OrderExecutor):
|
|
13
|
+
"""
|
|
14
|
+
FinterionOrderExecutor is a class that implements the OrderExecutor
|
|
15
|
+
interface for executing and canceling orders using the Finterion API.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def execute_order(self, portfolio, order, market_credential) -> Order:
|
|
19
|
+
"""
|
|
20
|
+
Executes an order for a given portfolio on Finterion.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
order: The order to be executed
|
|
24
|
+
portfolio: The portfolio in which the order will be executed
|
|
25
|
+
market_credential: The market credential to use for the order
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Order: Instance of the executed order. The order instance
|
|
29
|
+
should copy the id of the order that has been provided as a
|
|
30
|
+
"""
|
|
31
|
+
finterion_client = self.initialize(market_credential)
|
|
32
|
+
target_symbol = order.get_target_symbol()
|
|
33
|
+
amount = order.get_amount()
|
|
34
|
+
price = order.get_price()
|
|
35
|
+
order_type = order.get_order_type()
|
|
36
|
+
order_side = order.get_order_side()
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
if OrderType.LIMIT.equals(order_type):
|
|
40
|
+
if OrderSide.BUY.equals(order_side):
|
|
41
|
+
external_order = finterion_client.create_limit_order(
|
|
42
|
+
order_side="buy",
|
|
43
|
+
amount=str(amount),
|
|
44
|
+
target_symbol=target_symbol,
|
|
45
|
+
price=price
|
|
46
|
+
)
|
|
47
|
+
external_order = (
|
|
48
|
+
convert_finterion_order_to_order(external_order)
|
|
49
|
+
)
|
|
50
|
+
else:
|
|
51
|
+
external_order = finterion_client.create_limit_order(
|
|
52
|
+
order_side="sell",
|
|
53
|
+
amount=str(amount),
|
|
54
|
+
target_symbol=target_symbol,
|
|
55
|
+
price=price
|
|
56
|
+
)
|
|
57
|
+
external_order = (
|
|
58
|
+
convert_finterion_order_to_order(external_order)
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
raise OperationalException(
|
|
62
|
+
f"Order type {order_type} not supported "
|
|
63
|
+
f"by CCXT OrderExecutor"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
external_order.id = order.id
|
|
67
|
+
return external_order
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.exception(e)
|
|
70
|
+
raise OperationalException("Could not create finterion order")
|
|
71
|
+
|
|
72
|
+
def cancel_order(self, portfolio, order, market_credential) -> Order:
|
|
73
|
+
"""
|
|
74
|
+
Cancels an order for a given portfolio on a CCXT exchange.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
order: The order to be canceled
|
|
78
|
+
portfolio: The portfolio in which the order was executed
|
|
79
|
+
market_credential: The market credential to use for the order
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Order: Instance of the canceled order.
|
|
83
|
+
"""
|
|
84
|
+
raise OperationalException("Cancel order not supported")
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def initialize(market_credential) -> Finterion:
|
|
88
|
+
"""
|
|
89
|
+
Function to initialize the finterion api client.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
market_credential (MarketCredential): The market credential to use
|
|
93
|
+
with the finterion api client
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Finterion: Instance of the Finterion API client
|
|
97
|
+
"""
|
|
98
|
+
api_key = market_credential.api_key
|
|
99
|
+
return Finterion(api_key)
|
|
100
|
+
|
|
101
|
+
def supports_market(self, market):
|
|
102
|
+
"""
|
|
103
|
+
Function to check if the market is supported by the portfolio
|
|
104
|
+
provider.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
market: Market object
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
bool: True if the market is supported, False otherwise
|
|
111
|
+
"""
|
|
112
|
+
return market.lower() == "finterion"
|
finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/ping_hook.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
from investing_algorithm_framework import AppHook
|
|
3
|
+
from finterion import Finterion
|
|
4
|
+
|
|
5
|
+
logger = getLogger("investing_algorithm_framework")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FinterionPingAction(AppHook):
|
|
9
|
+
"""
|
|
10
|
+
A class that implements the AppHook interface to perform a ping action
|
|
11
|
+
on the Finterion API.
|
|
12
|
+
"""
|
|
13
|
+
def on_run(self, context):
|
|
14
|
+
"""
|
|
15
|
+
Method to be called when the algorithm is run.
|
|
16
|
+
It pings the Finterion API and prints the response.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
context: The context of the framework
|
|
20
|
+
"""
|
|
21
|
+
logger.info("Pinging Finterion")
|
|
22
|
+
market_credential = context.get_market_credential("finterion")
|
|
23
|
+
finterion_client = self.initialize(market_credential)
|
|
24
|
+
finterion_client.ping()
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def initialize(market_credential) -> Finterion:
|
|
28
|
+
"""
|
|
29
|
+
Function to initialize the finterion api client.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
market_credential (MarketCredential): The market credential to use
|
|
33
|
+
with the finterion api client
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Finterion: Instance of the Finterion API client
|
|
37
|
+
"""
|
|
38
|
+
api_key = market_credential.api_key
|
|
39
|
+
return Finterion(api_key)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from investing_algorithm_framework.domain import PortfolioProvider, \
|
|
5
|
+
OperationalException, Order, Position
|
|
6
|
+
from finterion import Finterion
|
|
7
|
+
|
|
8
|
+
from .utils import convert_finterion_order_to_order, \
|
|
9
|
+
convert_finterion_position_to_position
|
|
10
|
+
|
|
11
|
+
logger = getLogger("investing_algorithm_framework")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FinterionPortfolioProvider(PortfolioProvider):
|
|
15
|
+
"""
|
|
16
|
+
Implementation of Portfolio Provider for Finterion.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def get_order(
|
|
20
|
+
self, portfolio, order, market_credential
|
|
21
|
+
) -> Union[Order, None]:
|
|
22
|
+
"""
|
|
23
|
+
Method to check if there are any pending orders for the portfolio.
|
|
24
|
+
This method will retrieve the open orders from the exchange and
|
|
25
|
+
check if there are any pending orders for the portfolio.
|
|
26
|
+
|
|
27
|
+
!IMPORTANT: This function should return None if the order is
|
|
28
|
+
not found or if the order is not available on the
|
|
29
|
+
exchange or broker. Please do not throw an exception if the
|
|
30
|
+
order is not found.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
portfolio: Portfolio object
|
|
34
|
+
order: Order object from the database
|
|
35
|
+
market_credential: Market credential object
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
None
|
|
39
|
+
"""
|
|
40
|
+
finterion_client = self.initialize(market_credential)
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
external_order = finterion_client.get_order(
|
|
44
|
+
order.get_external_id()
|
|
45
|
+
)
|
|
46
|
+
external_order = (
|
|
47
|
+
convert_finterion_order_to_order(external_order)
|
|
48
|
+
)
|
|
49
|
+
external_order.id = order.id
|
|
50
|
+
return external_order
|
|
51
|
+
except Exception as e:
|
|
52
|
+
logger.exception(e)
|
|
53
|
+
raise OperationalException(
|
|
54
|
+
"Could not retrieve order from Finterion"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def get_position(
|
|
58
|
+
self, portfolio, symbol, market_credential
|
|
59
|
+
) -> Union[Position, None]:
|
|
60
|
+
"""
|
|
61
|
+
Function to get the position for a given symbol in the portfolio.
|
|
62
|
+
The returned position should be an object that reflects the current
|
|
63
|
+
state of the position on the exchange or broker.
|
|
64
|
+
|
|
65
|
+
!IMPORTANT: This function should return None if the position is
|
|
66
|
+
not found or if the position is not available on the
|
|
67
|
+
exchange or broker. Please do not throw an exception if the
|
|
68
|
+
position is not found.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
portfolio (Portfolio): Portfolio object
|
|
72
|
+
symbol (str): Symbol object
|
|
73
|
+
market_credential (MarketCredential): MarketCredential object
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Position: Position for the given symbol in the portfolio
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
finterion_client = self.initialize(market_credential)
|
|
80
|
+
symbol = symbol
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
external_positions = finterion_client.get_positions(
|
|
84
|
+
symbol=symbol
|
|
85
|
+
)
|
|
86
|
+
external_position = (
|
|
87
|
+
convert_finterion_position_to_position(external_positions[0])
|
|
88
|
+
)
|
|
89
|
+
external_position.portfolio_id = portfolio.id
|
|
90
|
+
return external_position
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.exception(e)
|
|
93
|
+
raise OperationalException(
|
|
94
|
+
"Could not retrieve position from Finterion"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def initialize(market_credential) -> Finterion:
|
|
99
|
+
"""
|
|
100
|
+
Function to initialize the finterion api client.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
market_credential (MarketCredential): The market credential to use
|
|
104
|
+
with the finterion api client
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Finterion: Instance of the Finterion API client
|
|
108
|
+
"""
|
|
109
|
+
api_key = market_credential.api_key
|
|
110
|
+
return Finterion(api_key)
|
|
111
|
+
|
|
112
|
+
def supports_market(self, market):
|
|
113
|
+
"""
|
|
114
|
+
Function to check if the market is supported by the portfolio
|
|
115
|
+
provider.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
market: Market object
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
bool: True if the market is supported, False otherwise
|
|
122
|
+
"""
|
|
123
|
+
return market.lower() == "finterion"
|
finterion_investing_algorithm_framework-0.22.0/finterion_investing_algorithm_framework/utils.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from dateutil import parser
|
|
2
|
+
from investing_algorithm_framework import Order, Position
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def convert_finterion_order_to_order(finterion_order):
|
|
6
|
+
"""
|
|
7
|
+
Convert a Finterion order to an Order object from the
|
|
8
|
+
investing_algorithm_framework.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
finterion_order (dict): The Finterion order data.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
Order: An Order object populated with the data
|
|
15
|
+
from the Finterion order.
|
|
16
|
+
"""
|
|
17
|
+
order = Order(
|
|
18
|
+
external_id=finterion_order.get("id"),
|
|
19
|
+
order_type=finterion_order.get("order_type"),
|
|
20
|
+
order_side=finterion_order.get("order_side"),
|
|
21
|
+
status=finterion_order.get("status"),
|
|
22
|
+
amount=float(finterion_order.get("amount")),
|
|
23
|
+
target_symbol=finterion_order.get("target_symbol"),
|
|
24
|
+
trading_symbol=finterion_order.get("trading_symbol"),
|
|
25
|
+
price=float(finterion_order.get("price")),
|
|
26
|
+
filled=float(finterion_order.get("filled")),
|
|
27
|
+
remaining=float(finterion_order.get("remaining")),
|
|
28
|
+
cost=float(finterion_order.get("cost")),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if finterion_order.get("created_at") is not None:
|
|
32
|
+
order.created_at = parser.parse(finterion_order.get("created_at"))
|
|
33
|
+
|
|
34
|
+
if finterion_order.get("updated_at") is not None:
|
|
35
|
+
order.updated_at = parser.parse(finterion_order.get("updated_at"))
|
|
36
|
+
|
|
37
|
+
return order
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def convert_finterion_position_to_position(finterion_position) -> Position:
|
|
41
|
+
"""
|
|
42
|
+
Convert a Finterion position to a Position object from the
|
|
43
|
+
investing_algorithm_framework.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
finterion_position (dict): The Finterion position data.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Position: An Position object populated with the data
|
|
50
|
+
from the Finterion position.
|
|
51
|
+
"""
|
|
52
|
+
return Position(
|
|
53
|
+
symbol=finterion_position.get("symbol"),
|
|
54
|
+
amount=float(finterion_position.get("amount")),
|
|
55
|
+
cost=float(finterion_position.get("cost")),
|
|
56
|
+
)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "finterion-investing-algorithm-framework"
|
|
3
|
-
version = "v0.
|
|
3
|
+
version = "v0.22.0"
|
|
4
4
|
description = "This is the official finterion plugin for the investing algorithm framework open source project."
|
|
5
5
|
authors = ["Marc van Duyn <marc@finterion.com>"]
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
|
|
8
8
|
[tool.poetry.dependencies]
|
|
9
|
-
python = "^3.
|
|
10
|
-
investing-algorithm-framework = "
|
|
9
|
+
python = "^3.10"
|
|
10
|
+
investing-algorithm-framework = ">=6.5.2"
|
|
11
11
|
finterion = "^0.8.8"
|
|
12
12
|
|
|
13
13
|
[tool.poetry.group.test.dependencies]
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: finterion-investing-algorithm-framework
|
|
3
|
-
Version: 0.20.0
|
|
4
|
-
Summary: This is the official finterion plugin for the investing algorithm framework open source project.
|
|
5
|
-
Author: Marc van Duyn
|
|
6
|
-
Author-email: marc@finterion.com
|
|
7
|
-
Requires-Python: >=3.8.1,<4.0.0
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Requires-Dist: finterion (>=0.8.8,<0.9.0)
|
|
14
|
-
Requires-Dist: investing-algorithm-framework (>=3.5.1,<4.0.0)
|
|
15
|
-
Description-Content-Type: text/markdown
|
|
16
|
-
|
|
17
|
-
# Finterion Investing Algorithm Framework Plugin
|
|
18
|
-
This is the official plugin for the [investing algorithm framework](https://investing-algorithm-framework.com) open source project.
|
|
19
|
-
|
|
20
|
-
## Installation
|
|
21
|
-
You can install the plugin with pip.
|
|
22
|
-
```shell
|
|
23
|
-
pip install finterion-investing-algorithm-framework
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
In order to use the plugin you must use the 'create_app' function provided
|
|
28
|
-
by the plugin. This function will return an instance of the investing
|
|
29
|
-
algorithm framework configured with the finterion platform.
|
|
30
|
-
|
|
31
|
-
> **Note:** You must provide the API key of your algorithm in order to use
|
|
32
|
-
> the plugin. You can find your API keys in the developer dashboard of
|
|
33
|
-
> your algorithm on the finterion platform.
|
|
34
|
-
|
|
35
|
-
```python
|
|
36
|
-
from finterion_investing_algorithm_framework import create_app
|
|
37
|
-
|
|
38
|
-
app = create_app(api_key="<YOUR_TRADING_BOT_FINTERION_API_KEY>")
|
|
39
|
-
|
|
40
|
-
# Add your investing algorithm framework market data sources
|
|
41
|
-
# .....
|
|
42
|
-
|
|
43
|
-
# Add your investing algorithm framework trading strategies
|
|
44
|
-
# ....
|
|
45
|
-
|
|
46
|
-
if __name__ == "__main__":
|
|
47
|
-
app.run()
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Documentation
|
|
51
|
-
You can find the official documentation at our [documentation website](https://docs.finterion.com/)
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Finterion Investing Algorithm Framework Plugin
|
|
2
|
-
This is the official plugin for the [investing algorithm framework](https://investing-algorithm-framework.com) open source project.
|
|
3
|
-
|
|
4
|
-
## Installation
|
|
5
|
-
You can install the plugin with pip.
|
|
6
|
-
```shell
|
|
7
|
-
pip install finterion-investing-algorithm-framework
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
## Usage
|
|
11
|
-
In order to use the plugin you must use the 'create_app' function provided
|
|
12
|
-
by the plugin. This function will return an instance of the investing
|
|
13
|
-
algorithm framework configured with the finterion platform.
|
|
14
|
-
|
|
15
|
-
> **Note:** You must provide the API key of your algorithm in order to use
|
|
16
|
-
> the plugin. You can find your API keys in the developer dashboard of
|
|
17
|
-
> your algorithm on the finterion platform.
|
|
18
|
-
|
|
19
|
-
```python
|
|
20
|
-
from finterion_investing_algorithm_framework import create_app
|
|
21
|
-
|
|
22
|
-
app = create_app(api_key="<YOUR_TRADING_BOT_FINTERION_API_KEY>")
|
|
23
|
-
|
|
24
|
-
# Add your investing algorithm framework market data sources
|
|
25
|
-
# .....
|
|
26
|
-
|
|
27
|
-
# Add your investing algorithm framework trading strategies
|
|
28
|
-
# ....
|
|
29
|
-
|
|
30
|
-
if __name__ == "__main__":
|
|
31
|
-
app.run()
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Documentation
|
|
35
|
-
You can find the official documentation at our [documentation website](https://docs.finterion.com/)
|
finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/create_app.py
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from finterion import Finterion
|
|
4
|
-
from investing_algorithm_framework import create_app as framework_create_app, \
|
|
5
|
-
Task, TimeUnit, App, MarketCredential, AppHook, Algorithm
|
|
6
|
-
|
|
7
|
-
from finterion_investing_algorithm_framework.market_service import \
|
|
8
|
-
FinterionMarketService
|
|
9
|
-
from finterion_investing_algorithm_framework.models.portfolio_configuration \
|
|
10
|
-
import FinterionPortfolioConfiguration
|
|
11
|
-
from .validation import check_portfolio_active
|
|
12
|
-
|
|
13
|
-
logger = logging.getLogger("finterion_investing_algorithm_framework_plugin")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def create_app(
|
|
17
|
-
api_key,
|
|
18
|
-
config={},
|
|
19
|
-
stateless=False,
|
|
20
|
-
web=False,
|
|
21
|
-
base_url=None,
|
|
22
|
-
) -> App:
|
|
23
|
-
client = Finterion(api_key=api_key, base_url=base_url)
|
|
24
|
-
client.ping()
|
|
25
|
-
model = client.get_algorithm_model()
|
|
26
|
-
|
|
27
|
-
# Check that the portfolio is active
|
|
28
|
-
check_portfolio_active(client)
|
|
29
|
-
|
|
30
|
-
# Add a finterion portfolio configuration
|
|
31
|
-
portfolio_configuration = FinterionPortfolioConfiguration(
|
|
32
|
-
trading_symbol=model['profile']['trading_symbol'],
|
|
33
|
-
market_data_markets=model['profile']['markets'],
|
|
34
|
-
)
|
|
35
|
-
app = framework_create_app(config=config, web=web, stateless=stateless)
|
|
36
|
-
app.add_market_credential(
|
|
37
|
-
MarketCredential(
|
|
38
|
-
market="finterion",
|
|
39
|
-
api_key=api_key,
|
|
40
|
-
secret_key=None,
|
|
41
|
-
)
|
|
42
|
-
)
|
|
43
|
-
market_credential_service = app.container.market_credential_service()
|
|
44
|
-
app.container.market_service.override(
|
|
45
|
-
FinterionMarketService(
|
|
46
|
-
api_key=api_key,
|
|
47
|
-
base_url=base_url,
|
|
48
|
-
market_credential_service=market_credential_service
|
|
49
|
-
)
|
|
50
|
-
)
|
|
51
|
-
app.add_portfolio_configuration(portfolio_configuration)
|
|
52
|
-
|
|
53
|
-
class PingTask(Task):
|
|
54
|
-
interval = 30
|
|
55
|
-
time_unit = TimeUnit.MINUTE
|
|
56
|
-
|
|
57
|
-
def run(self, algorithm):
|
|
58
|
-
logger.debug("Pinging Finterion platform")
|
|
59
|
-
client.ping()
|
|
60
|
-
|
|
61
|
-
class PingAppHook(AppHook):
|
|
62
|
-
|
|
63
|
-
def on_run(self, app, algorithm: Algorithm):
|
|
64
|
-
algorithm.add_task(PingTask)
|
|
65
|
-
|
|
66
|
-
# Register the ping task to the algorithm
|
|
67
|
-
app.after_initialize(PingAppHook())
|
|
68
|
-
return app
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
|
|
3
|
-
from dateutil import parser
|
|
4
|
-
from finterion import Finterion
|
|
5
|
-
from investing_algorithm_framework import Order
|
|
6
|
-
from investing_algorithm_framework.infrastructure import CCXTMarketService
|
|
7
|
-
from finterion_investing_algorithm_framework.exceptions import \
|
|
8
|
-
FinterionInvestingAlgorithmFrameworkException
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class FinterionMarketService(CCXTMarketService):
|
|
12
|
-
"""
|
|
13
|
-
Finterion market service implementation.
|
|
14
|
-
"""
|
|
15
|
-
def cancel_order(self, order, market):
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
def get_open_orders(
|
|
19
|
-
self, market, target_symbol: str = None, trading_symbol: str = None
|
|
20
|
-
):
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
def get_closed_orders(
|
|
24
|
-
self, market, target_symbol: str = None, trading_symbol: str = None
|
|
25
|
-
):
|
|
26
|
-
pass
|
|
27
|
-
|
|
28
|
-
def get_ticker(self, symbol, market):
|
|
29
|
-
"""
|
|
30
|
-
Finterion does not support get_ticker, please specify a ticker market
|
|
31
|
-
data source for symbol {symbol}. Finterion can't provide this data.
|
|
32
|
-
|
|
33
|
-
Users should register their own ticker data sources to obtain ticker
|
|
34
|
-
data.
|
|
35
|
-
"""
|
|
36
|
-
raise FinterionInvestingAlgorithmFrameworkException(
|
|
37
|
-
f"Finterion does not support get_ticker, please specify "
|
|
38
|
-
f"a ticker market data source for symbol {symbol}. Finterion "
|
|
39
|
-
f"can't provide this data."
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
def get_order_book(self, symbol, market):
|
|
43
|
-
raise FinterionInvestingAlgorithmFrameworkException(
|
|
44
|
-
f"Finterion does not support get_order_book, please specify "
|
|
45
|
-
f"an order book market data source for symbol {symbol}. Finterion "
|
|
46
|
-
f"can't provide this data."
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
def get_ohlcv(
|
|
50
|
-
self,
|
|
51
|
-
symbol,
|
|
52
|
-
time_frame,
|
|
53
|
-
from_timestamp,
|
|
54
|
-
market,
|
|
55
|
-
to_timestamp=None
|
|
56
|
-
):
|
|
57
|
-
raise FinterionInvestingAlgorithmFrameworkException(
|
|
58
|
-
f"Finterion does not support get_ohlcv, please specify "
|
|
59
|
-
f"an ohlcv market data source for symbol {symbol}. Finterion "
|
|
60
|
-
f"can't provide this data."
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
def __init__(
|
|
64
|
-
self,
|
|
65
|
-
api_key,
|
|
66
|
-
market_credential_service,
|
|
67
|
-
base_url=None,
|
|
68
|
-
initialize=True
|
|
69
|
-
):
|
|
70
|
-
super().__init__(market_credential_service)
|
|
71
|
-
self._api_key = api_key
|
|
72
|
-
self._market = "finterion"
|
|
73
|
-
|
|
74
|
-
if initialize:
|
|
75
|
-
if base_url is not None:
|
|
76
|
-
self._finterion = Finterion(api_key, base_url=base_url)
|
|
77
|
-
else:
|
|
78
|
-
self._finterion = Finterion(api_key)
|
|
79
|
-
|
|
80
|
-
def initialize(self, portfolio_configuration):
|
|
81
|
-
pass
|
|
82
|
-
|
|
83
|
-
def get_order(self, order, market):
|
|
84
|
-
order = self._finterion.get_order(order.get_external_id())
|
|
85
|
-
return self._convert_order(order)
|
|
86
|
-
|
|
87
|
-
def get_orders(self, symbol, market, since: datetime = None):
|
|
88
|
-
orders = self._finterion.get_orders(target_symbol=symbol.split("/")[0])
|
|
89
|
-
return [self._convert_order(order) for order in orders]
|
|
90
|
-
|
|
91
|
-
def get_balance(self, market):
|
|
92
|
-
"""
|
|
93
|
-
Finterion implementation of get_balance.
|
|
94
|
-
"""
|
|
95
|
-
positions = self._finterion.get_positions()
|
|
96
|
-
entries = {}
|
|
97
|
-
|
|
98
|
-
for position in positions:
|
|
99
|
-
entries[position["symbol"]] = float(position["amount"])
|
|
100
|
-
|
|
101
|
-
return entries
|
|
102
|
-
|
|
103
|
-
def create_market_sell_order(
|
|
104
|
-
self,
|
|
105
|
-
target_symbol: str,
|
|
106
|
-
trading_symbol: str,
|
|
107
|
-
amount: float,
|
|
108
|
-
market
|
|
109
|
-
):
|
|
110
|
-
order = self._finterion.create_market_order(
|
|
111
|
-
order_side="sell",
|
|
112
|
-
amount=str(amount),
|
|
113
|
-
target_symbol=target_symbol,
|
|
114
|
-
)
|
|
115
|
-
return self._convert_order(order)
|
|
116
|
-
|
|
117
|
-
def create_limit_sell_order(
|
|
118
|
-
self,
|
|
119
|
-
target_symbol: str,
|
|
120
|
-
trading_symbol: str,
|
|
121
|
-
amount,
|
|
122
|
-
price,
|
|
123
|
-
market
|
|
124
|
-
):
|
|
125
|
-
order = self._finterion.create_limit_order(
|
|
126
|
-
order_side="sell",
|
|
127
|
-
amount=str(amount),
|
|
128
|
-
target_symbol=target_symbol,
|
|
129
|
-
price=price
|
|
130
|
-
)
|
|
131
|
-
return self._convert_order(order)
|
|
132
|
-
|
|
133
|
-
def create_limit_buy_order(
|
|
134
|
-
self,
|
|
135
|
-
target_symbol: str,
|
|
136
|
-
trading_symbol: str,
|
|
137
|
-
amount: float,
|
|
138
|
-
price: float,
|
|
139
|
-
market
|
|
140
|
-
):
|
|
141
|
-
order = self._finterion.create_limit_order(
|
|
142
|
-
order_side="buy",
|
|
143
|
-
amount=str(amount),
|
|
144
|
-
target_symbol=target_symbol,
|
|
145
|
-
price=str(price)
|
|
146
|
-
)
|
|
147
|
-
return self._convert_order(order)
|
|
148
|
-
|
|
149
|
-
def _convert_order(self, finterion_order):
|
|
150
|
-
order = Order(
|
|
151
|
-
external_id=finterion_order.get("id"),
|
|
152
|
-
order_type=finterion_order.get("order_type"),
|
|
153
|
-
order_side=finterion_order.get("order_side"),
|
|
154
|
-
status=finterion_order.get("status"),
|
|
155
|
-
amount=finterion_order.get("amount"),
|
|
156
|
-
target_symbol=finterion_order.get("target_symbol"),
|
|
157
|
-
trading_symbol=finterion_order.get("trading_symbol"),
|
|
158
|
-
price=finterion_order.get("price"),
|
|
159
|
-
trade_closed_price=finterion_order.get("trade_closed_price", None),
|
|
160
|
-
trade_closed_at=finterion_order.get("trade_closed_at", None),
|
|
161
|
-
trade_closed_amount=finterion_order.get(
|
|
162
|
-
"trade_closed_amount", None
|
|
163
|
-
),
|
|
164
|
-
filled=finterion_order.get("filled"),
|
|
165
|
-
remaining=finterion_order.get("remaining"),
|
|
166
|
-
cost=finterion_order.get("cost"),
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
if finterion_order.get("trade_closed_at") is not None:
|
|
170
|
-
order.trade_closed_at = parser.parse(
|
|
171
|
-
finterion_order.get("trade_closed_at")
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
if finterion_order.get("created_at") is not None:
|
|
175
|
-
order.created_at = parser.parse(finterion_order.get("created_at"))
|
|
176
|
-
|
|
177
|
-
if finterion_order.get("updated_at") is not None:
|
|
178
|
-
order.updated_at = parser.parse(finterion_order.get("updated_at"))
|
|
179
|
-
|
|
180
|
-
return order
|
|
181
|
-
|
|
182
|
-
def get_symbols(self, market):
|
|
183
|
-
return self._finterion.get_supported_symbols()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from investing_algorithm_framework import PortfolioConfiguration
|
|
2
|
-
from investing_algorithm_framework.domain import ImproperlyConfigured
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class FinterionPortfolioConfiguration(PortfolioConfiguration):
|
|
6
|
-
|
|
7
|
-
def __init__(self, trading_symbol, market_data_markets):
|
|
8
|
-
try:
|
|
9
|
-
super().__init__(
|
|
10
|
-
market="finterion",
|
|
11
|
-
trading_symbol=trading_symbol,
|
|
12
|
-
)
|
|
13
|
-
except ImproperlyConfigured:
|
|
14
|
-
pass
|
|
15
|
-
|
|
16
|
-
if market_data_markets is None:
|
|
17
|
-
raise ImproperlyConfigured(
|
|
18
|
-
"Your algorithm has no markets configured. Please add at "
|
|
19
|
-
"least one market to your algorithm on the finterion platform."
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
@property
|
|
23
|
-
def identifier(self):
|
|
24
|
-
return "finterion"
|
finterion_investing_algorithm_framework-0.20.0/finterion_investing_algorithm_framework/validation.py
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
from finterion import Finterion
|
|
2
|
-
from finterion_investing_algorithm_framework.exceptions import \
|
|
3
|
-
FinterionInvestingAlgorithmFrameworkException
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def check_portfolio_active(finterion_client: Finterion):
|
|
7
|
-
portfolio = finterion_client\
|
|
8
|
-
.get_portfolio(query_params={"ShowMetrics": "False"})
|
|
9
|
-
|
|
10
|
-
if portfolio['active'] is False:
|
|
11
|
-
raise FinterionInvestingAlgorithmFrameworkException(
|
|
12
|
-
"Cannot run on in-active portfolio, "
|
|
13
|
-
"please activate your portfolio"
|
|
14
|
-
)
|
|
File without changes
|