-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #380 from mraniki/dev
💄♻️ Code refactor to support multiple parser
- Loading branch information
Showing
11 changed files
with
668 additions
and
236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
""" | ||
Supported Handlers | ||
""" | ||
|
||
from .basic import BasicHandler | ||
from .standard import StandardHandler | ||
|
||
__all__ = ["StandardHandler", "BasicHandler"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
""" | ||
Basic Parser | ||
""" | ||
|
||
from pyparsing import ( | ||
Optional, | ||
Word, | ||
alphas, | ||
nums, | ||
one_of, | ||
pyparsing_common, | ||
) | ||
|
||
from .handler import ParserClient | ||
|
||
|
||
class BasicHandler(ParserClient): | ||
|
||
def __init__(self, **kwargs): | ||
""" | ||
Initialize the Handler object | ||
""" | ||
|
||
super().__init__(**kwargs) | ||
self.client = "basic" | ||
|
||
async def identify_order( | ||
self, | ||
my_string: str, | ||
) -> dict: | ||
""" | ||
Identify an order and return a dictionary | ||
with the order parameters | ||
Args: | ||
my_string (str): Message | ||
Returns: | ||
dict with the order parameters: | ||
action, instrument | ||
""" | ||
action = ( | ||
one_of(self.action_identifier, caseless=True) | ||
.set_results_name("action") | ||
.set_parse_action(pyparsing_common.upcase_tokens) | ||
) | ||
instrument = Word(alphas + nums).set_results_name("instrument") | ||
order_grammar = action("action") + Optional(instrument, default=None) | ||
order = order_grammar.parse_string(instring=my_string, parse_all=False) | ||
return order.asDict() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
from datetime import datetime, timezone | ||
|
||
from loguru import logger | ||
|
||
|
||
class ParserClient: | ||
""" | ||
Parser Handler Base Class | ||
Args: | ||
**kwargs: | ||
Methods: | ||
search(self) | ||
identify_order(self) | ||
get_order(self) | ||
replace_instrument(self) | ||
""" | ||
|
||
def __init__(self, **kwargs): | ||
""" | ||
Initialize the chat client. | ||
""" | ||
self.name = kwargs.get("name", None) | ||
self.client = None | ||
self.enabled = kwargs.get("enabled", None) | ||
self.action_identifier = kwargs.get("action_identifier", "BUY SELL") | ||
self.action_identifier = self.action_identifier.lower() | ||
self.stop_loss_identifier = kwargs.get("stop_loss_identifier", None) | ||
self.take_profit_identifier = kwargs.get("take_profit_identifier", None) | ||
self.quantity_identifier = kwargs.get("quantity_identifier", None) | ||
self.order_type_identifier = kwargs.get("order_type_identifier", None) | ||
self.leverage_type_identifier = kwargs.get("leverage_type_identifier", None) | ||
self.comment_identifier = kwargs.get("comment_identifier", None) | ||
self.stop_loss = kwargs.get("stop_loss", None) | ||
self.take_profit = kwargs.get("take_profit", None) | ||
self.quantity = kwargs.get("quantity", None) | ||
self.instrument_mapping = kwargs.get("instrument_mapping", None) | ||
self.mapping = kwargs.get("mapping", None) | ||
self.ignore_instrument = kwargs.get("ignore_instrument", None) | ||
|
||
async def identify_order( | ||
self, | ||
my_string: str, | ||
) -> dict: | ||
""" | ||
Identify an order and return a dictionary | ||
with the order parameters to be implemented in | ||
the child class | ||
""" | ||
|
||
async def search(self, message: str) -> bool: | ||
""" | ||
Search an order. | ||
Args: | ||
message (str): Message | ||
Returns: | ||
bool | ||
""" | ||
if message: | ||
order_identifier = message.split()[0].lower() | ||
# logger.debug("Order identifier: {}", order_identifier) | ||
# logger.debug("Action identifiers: {}", self.action_identifiers) | ||
if order_identifier in self.action_identifier: | ||
|
||
# logger.debug("Order identifier found in {}", order_identifier) | ||
return True | ||
|
||
return False | ||
|
||
async def replace_instrument(self, order): | ||
""" | ||
Replace instrument by an alternative instrument, if the | ||
instrument is not in the mapping, it will be ignored. | ||
Args: | ||
order (dict): | ||
Returns: | ||
dict | ||
""" | ||
instrument = order["instrument"] | ||
for item in self.mapping: | ||
if item["id"] == instrument: | ||
order["instrument"] = item["alt"] | ||
break | ||
logger.debug("Instrument symbol changed", order) | ||
return order | ||
|
||
async def get_order( | ||
self, | ||
msg: str, | ||
): | ||
""" | ||
Get an order from a message. The message can be | ||
an order or an order identifier | ||
Args: | ||
msg (str): Message | ||
Returns: | ||
dict | ||
""" | ||
if not await self.search(msg): | ||
logger.debug("No order identified") | ||
return None | ||
order = await self.identify_order(msg) | ||
if isinstance(order, dict): | ||
order["timestamp"] = datetime.now(timezone.utc).strftime( | ||
"%Y-%m-%dT%H:%M:%SZ" | ||
) | ||
if self.instrument_mapping: | ||
logger.debug("mapping") | ||
await self.replace_instrument(order) | ||
if order["instrument"] in self.ignore_instrument: | ||
logger.debug("Ignoring instrument {}", order["instrument"]) | ||
return | ||
logger.debug("Order identified {}", order) | ||
return order |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
""" | ||
Standard Parser | ||
""" | ||
|
||
from loguru import logger | ||
from pyparsing import ( | ||
Combine, | ||
Optional, | ||
Suppress, | ||
Word, | ||
alphas, | ||
nums, | ||
one_of, | ||
pyparsing_common, | ||
) | ||
|
||
from .handler import ParserClient | ||
|
||
|
||
class StandardHandler(ParserClient): | ||
|
||
def __init__(self, **kwargs): | ||
""" | ||
Initialize the Handler object | ||
""" | ||
|
||
super().__init__(**kwargs) | ||
self.client = "standard" | ||
|
||
async def identify_order( | ||
self, | ||
my_string: str, | ||
) -> dict: | ||
""" | ||
Identify an order and return a dictionary | ||
with the order parameters | ||
Args: | ||
my_string (str): Message | ||
Returns: | ||
dict | ||
""" | ||
if not await self.search(my_string): | ||
logger.debug("No order identified") | ||
return None | ||
else: | ||
action = ( | ||
one_of(self.action_identifier, caseless=True) | ||
.set_results_name("action") | ||
.set_parse_action(pyparsing_common.upcase_tokens) | ||
) | ||
instrument = Word(alphas + nums).set_results_name("instrument") | ||
stop_loss = Combine( | ||
Suppress(self.stop_loss_identifier) + Word(nums) | ||
).set_results_name("stop_loss") | ||
take_profit = Combine( | ||
Suppress(self.take_profit_identifier) + Word(nums) | ||
).set_results_name("take_profit") | ||
quantity = Combine( | ||
Suppress(self.quantity_identifier) | ||
+ Word(nums) | ||
+ Optional(Suppress("%")) | ||
).set_results_name("quantity") | ||
order_type = one_of( | ||
self.order_type_identifier, caseless=True | ||
).set_results_name("order_type") | ||
leverage_type = one_of( | ||
self.leverage_type_identifier, caseless=True | ||
).set_results_name("leverage_type") | ||
comment = Combine( | ||
Suppress(self.comment_identifier) + Word(alphas) | ||
).set_results_name("comment") | ||
|
||
order_grammar = ( | ||
action("action") | ||
+ Optional(instrument, default=None) | ||
+ Optional(stop_loss, default=self.stop_loss) | ||
+ Optional(take_profit, default=self.take_profit) | ||
+ Optional(quantity, default=self.quantity) | ||
+ Optional(order_type, default=None) | ||
+ Optional(leverage_type, default=None) | ||
+ Optional(comment, default=None) | ||
) | ||
|
||
order = order_grammar.parse_string(instring=my_string, parse_all=False) | ||
logger.debug("Order parsed {}", order) | ||
return order.asDict() |
Oops, something went wrong.