-
Notifications
You must be signed in to change notification settings - Fork 9
doge-serverless #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sodogemuchwow
wants to merge
6
commits into
master
Choose a base branch
from
doge-serverless
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,652
−0
Open
doge-serverless #116
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
8c1a60b
first prototype, deployed on AWS and tested: strategy gets data from …
sodogemuchwow 7b210dc
mock genetic program deployed and running on AWS
sodogemuchwow a681d60
fixes context-> event, strategies load indicators from the invoking S…
sodogemuchwow baf3559
fixed logging on CloudWatch, strategies send their name with the sign…
sodogemuchwow 60b71b1
PR fixes.
sodogemuchwow 81ce5ee
Merge branch 'master' into doge-serverless
tomcounsell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import operator | ||
import types | ||
from abc import ABC, abstractmethod | ||
from deap import gp | ||
|
||
class GeneticProgram(ABC): | ||
""" | ||
Abstract base class for genetic programs. Derived classes need to implement indicator computation functions. | ||
""" | ||
def __init__(self): | ||
self.build_grammar() | ||
|
||
@abstractmethod | ||
def rsi(self, input): | ||
pass | ||
|
||
@abstractmethod | ||
def sma50(self, input): | ||
pass | ||
|
||
@abstractmethod | ||
def ema50(self, input): | ||
pass | ||
|
||
@abstractmethod | ||
def sma200(self, input): | ||
pass | ||
|
||
@abstractmethod | ||
def ema200(self, input): | ||
pass | ||
|
||
@abstractmethod | ||
def price(self, input): | ||
pass | ||
|
||
def buy(self): | ||
pass | ||
|
||
def sell(self): | ||
pass | ||
|
||
def ignore(self): | ||
pass | ||
|
||
def identity(self, x): | ||
return x | ||
|
||
def if_then_else(self, input, output1, output2): | ||
try: | ||
return output1 if input else output2 | ||
except: | ||
return output1 | ||
|
||
|
||
def build_grammar(self): | ||
pset = gp.PrimitiveSetTyped("main", [list], types.FunctionType) | ||
pset.addPrimitive(operator.lt, [float, float], bool) | ||
pset.addPrimitive(operator.gt, [float, float], bool) | ||
pset.addPrimitive(operator.or_, [bool, bool], bool) | ||
pset.addPrimitive(operator.and_, [bool, bool], bool) | ||
pset.addPrimitive(self.if_then_else, [bool, types.FunctionType, types.FunctionType], types.FunctionType) | ||
pset.addPrimitive(self.rsi, [list], float) | ||
pset.addPrimitive(self.sma50, [list], float) | ||
pset.addPrimitive(self.ema50, [list], float) | ||
pset.addPrimitive(self.sma200, [list], float) | ||
pset.addPrimitive(self.ema200, [list], float) | ||
pset.addPrimitive(self.price, [list], float) | ||
pset.addTerminal(False, bool) | ||
pset.addTerminal(True, bool) | ||
pset.addTerminal(self.buy, types.FunctionType) | ||
pset.addTerminal(self.sell, types.FunctionType) | ||
pset.addTerminal(self.ignore, types.FunctionType) | ||
pset.addPrimitive(self.identity, [bool], bool, name="identity_bool") | ||
pset.addPrimitive(self.identity, [list], list, name="identity_list") | ||
pset.addPrimitive(self.identity, [float], float, name="identity_float") | ||
# TODO: make sure ephemerals can be left out | ||
# see https://github.com/DEAP/deap/issues/108 | ||
# pset.addEphemeralConstant("rsi_overbought_threshold", lambda: random.uniform(70, 100), float) | ||
# pset.addEphemeralConstant("rsi_oversold_threshold", lambda: random.uniform(0, 30), float) | ||
self.pset = pset | ||
|
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,80 @@ | ||
from StrategyHandler import AbstractStrategyHandler | ||
import logging | ||
from deap import gp | ||
|
||
from GeneticProgram import GeneticProgram | ||
logger = logging.getLogger('TradingStrategy123') | ||
logger.setLevel(logging.INFO) | ||
|
||
|
||
class GeneticStrategy(AbstractStrategyHandler, GeneticProgram): | ||
"""" | ||
A lambda doge baby. | ||
""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
GeneticProgram.__init__(self) | ||
self.individual = kwargs['individual'] | ||
|
||
|
||
def make_signal(self) -> int: | ||
from StrategyHandler import BUY, SELL, IGNORE | ||
doge = gp.compile(self.individual, self.pset) | ||
outcome = doge([]) | ||
if outcome == self.buy: | ||
self.signal = BUY | ||
elif outcome == self.sell: | ||
self.signal = SELL | ||
elif outcome == self.ignore: | ||
self.signal = IGNORE | ||
else: | ||
raise Exception("Unknown outcome produced by doge baby... singularity?!?") | ||
|
||
return self.signal | ||
|
||
@property | ||
def used_indicators(self): | ||
return ["RSI", "SMA"] # TODO: add logic to auto-populate from individual | ||
|
||
def rsi(self, input): | ||
return self.get_indicator("RSI") | ||
|
||
def sma50(self, input): | ||
return self.get_indicator("SMA") | ||
|
||
def price(self, input): | ||
return self.get_indicator("price") | ||
|
||
# TODO: implement the methods below once the API supports them | ||
def ema50(self, input): | ||
pass | ||
|
||
def sma200(self, input): | ||
pass | ||
|
||
def ema200(self, input): | ||
pass | ||
|
||
|
||
def check_strategy_gp(event, context): | ||
logger.info("\n-------------------\n" + | ||
"Trading Strategy GP" + | ||
"\n-------------------") | ||
logger.info('Event: {e}\nContext: {c}'.format(e=event, c=context)) | ||
try: | ||
logger.info("initiating objects...............") | ||
mock_individual = 'if_then_else(gt(price(ARG0), sma50(ARG0)), if_then_else(gt(17.64793885409774, 84.48578956778925), ' \ | ||
'if_then_else(True, ignore, ignore), sell), if_then_else(and_(True, False), ' \ | ||
'if_then_else(True, sell, ignore), if_then_else(True, buy, ignore)))' | ||
mock_individual = 'if_then_else(gt(rsi(ARG0), sma50(ARG0)), buy, sell)' | ||
this_trading_strategy = GeneticStrategy(sns_event=event, individual=mock_individual) | ||
logger.info("running..........................") | ||
this_trading_strategy.run() | ||
logger.info("saving...........................") | ||
this_trading_strategy.save() | ||
except Exception as e: | ||
logger.warning("Exception: {}".format(e)) | ||
return | ||
|
||
|
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,108 @@ | ||
import logging | ||
logger = logging.getLogger() | ||
logger.setLevel(logging.INFO) | ||
|
||
import json | ||
from abc import ABC | ||
import boto3 | ||
import os | ||
SNS_ARN = os.environ.get('SNS_ARN', "") | ||
|
||
|
||
|
||
class SNSEventException(Exception): | ||
pass | ||
|
||
class SNSMessageException(Exception): | ||
pass | ||
|
||
class ITFSNSException(Exception): | ||
pass | ||
|
||
|
||
class AbstractSNSEventHandler(ABC): | ||
''' | ||
Base class for ITF Events and Strategies | ||
triggered by an SNS message | ||
required | ||
''' | ||
|
||
sns_event = {} | ||
sns_subscribe_arn = "" | ||
sns_publish_topic_prefix = "" | ||
sns_publish_topic = "" # automatically set, but can be overwritten manually | ||
sns_client_response = None | ||
_parameters = None | ||
|
||
|
||
def __init__(self, *args, **kwargs): | ||
if 'sns_event' in kwargs: | ||
self._sns_event = kwargs['sns_event'] # SNS event that triggered the function | ||
self.sns_event = self._sns_event # trigger setter to parse incoming msgs | ||
# don't really like this, but left it for now | ||
self.sns_publish_topic_prefix = "itf-sns-sls-event-" | ||
|
||
|
||
@property | ||
def sns_event(self): | ||
return self._sns_event # or not the droids we're looking for? | ||
|
||
@sns_event.setter | ||
def sns_event(self, sns_event): # former "context" -> all the data is actually in event, not context | ||
try: | ||
self.raw_sns_message = sns_event['Records'][0]['Sns']['Message'] | ||
except KeyError as e: | ||
logger.error("SNS messages not found! Check code and configurations.") | ||
|
||
|
||
def emit_sns_message(self, message_data={}): | ||
# message data must be json serializable and not empty | ||
if not message_data: | ||
raise SNSMessageException("message_data is empty or missing!") | ||
try: | ||
json.dumps(message_data) | ||
except Exception as e: | ||
raise SNSMessageException(str(e)) | ||
|
||
sns_subscribed_arn = self.sns_event['Records'][0]['Sns']['TopicArn'] # TODO: perhaps not good to assume we were | ||
# invoked via SNS, remove in production? | ||
logger.info("Received message from SNS ARN {}".format(sns_subscribed_arn)) | ||
|
||
if not self.sns_publish_topic.startswith("itf-sns-"): | ||
# use instance class name | ||
self.sns_publish_topic = self.sns_publish_topic_prefix | ||
self.sns_publish_topic += str(self.__class__.__name__).lower() | ||
|
||
if not self.sns_publish_topic.startswith("itf-sns-"): | ||
raise ITFSNSException("SNS topic must start with 'itf-sns-'") | ||
|
||
logger.debug("using " + SNS_ARN + " as ARN base") | ||
# sns_publishing_arn = ":".join(SNS_ARN.split(":")[0:-1].append(self.sns_publish_topic)) | ||
sns_publishing_arn = SNS_ARN # TODO: this is set temporarily, for debug purposes | ||
logger.info("using " + sns_publishing_arn + " as ARN for publishing") | ||
|
||
logger.info("Publishing message to SNS ARN {}".format(sns_publishing_arn)) | ||
|
||
sns_client = boto3.client('sns') | ||
self.sns_client_response = sns_client.publish( | ||
TargetArn=sns_publishing_arn, | ||
Message=json.dumps({'default': json.dumps(message_data)}), | ||
MessageStructure='json' | ||
) | ||
|
||
|
||
# | ||
# EXAMPLE SNS MESSAGE FROM DATA APP ON HEROKU | ||
# {'Records': [{ | ||
# 'EventSource': 'aws:sns', | ||
# 'EventVersion': '1.0', | ||
# 'EventSubscriptionArn': 'arn:aws:sns:us-east-1:983584755688:itt-sns-data-core-stage:493ba9a2-52f0-4c12-b1a4-7bdaee803690' | ||
# , | ||
# 'Sns': { | ||
# 'Type': 'Notification', | ||
# 'MessageId': 'dc693f13-b8d8-5445-88b1-6c9954471f92', | ||
# 'TopicArn': 'arn:aws:sns:us-east-1:983584755688:itt-sns-data-core-stage' | ||
# , | ||
# 'Subject': None, | ||
# 'Message': '[{"source": "binance", "category": "price", "symbol": "ETH/BTC", "value": 0.077133, "timestamp": 1527835230.836}, | ||
# |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😂 👍