Skip to content

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
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions serverless/python-template/GeneticProgram.py
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

80 changes: 80 additions & 0 deletions serverless/python-template/GeneticStrategy.py
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


108 changes: 108 additions & 0 deletions serverless/python-template/SNSEventHandler.py
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?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😂 👍


@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},
#
Loading