From 473339f740d1b1528091d060d2c2f95440886376 Mon Sep 17 00:00:00 2001 From: Gert Wohlgemuth Date: Tue, 5 Jun 2018 16:04:43 -0700 Subject: [PATCH] added some more testing as well as cron expression is now scheduling larger intervals --- freqtrade/aws/backtesting_lambda.py | 61 ++++++++---------- freqtrade/aws/strategy.py | 53 --------------- freqtrade/aws/trade.py | 68 ++++++++++++++++++++ freqtrade/tests/aws/test_backtest.py | 96 +++++++++++++++++++++++++--- freqtrade/tests/conftest.py | 26 ++++++++ 5 files changed, 209 insertions(+), 95 deletions(-) diff --git a/freqtrade/aws/backtesting_lambda.py b/freqtrade/aws/backtesting_lambda.py index a45b2c21c..3e705a6ad 100644 --- a/freqtrade/aws/backtesting_lambda.py +++ b/freqtrade/aws/backtesting_lambda.py @@ -72,7 +72,7 @@ def backtest(event, context): refresh = True if 'refresh' in event['body']: - refresh = event['body'] + refresh = event['body']['refresh'] print("time range between dates is: {} days".format(timerange)) @@ -96,15 +96,13 @@ def backtest(event, context): if "local" in event['body'] and event['body']['local']: print("running in local mode") run_backtest(configuration, name, user, ticker, timerange) - return { - "statusCode": 200 - } else: print("running in remote mode") - return { - "statusCode": 200, - "body": json.dumps(_submit_job(configuration, user, ticker, timerange)) - } + json.dumps(_submit_job(configuration, user, ticker, timerange)) + + return { + "statusCode": 200 + } else: return { "statusCode": 404, @@ -340,34 +338,31 @@ def cron(event, context): for i in response['Items']: # fire a message to our queue - message = { - "user": i['user'], - "name": i['name'], - "assets": i['assets'], - "stake_currency": i['stake_currency'] - } + # we want to evaluate several time spans for the strategy + for day in [1, 7, 30, 90]: - # triggered over html, let's provide - # a date range for the backtesting - if 'pathParameters' in event: - if 'from' in event['pathParameters']: - message['from'] = event['pathParameters']['from'] - else: - message['from'] = datetime.datetime.today().strftime('%Y%m%d') - if 'till' in event['pathParameters']: - message['till'] = event['pathParameters']['till'] - else: - message['till'] = (datetime.datetime.today() - datetime.timedelta(days=1)).strftime('%Y%m%d') + # we want to evaluate several time intervals for each strategy + for interval in ['5m', '15m', '30m', '1h']: + message = { + "user": i['user'], + "name": i['name'], + "assets": i['assets'], + "stake_currency": i['stake_currency'], + "local": False, + "refresh": True, + "ticker": interval, + "days": day + } - serialized = json.dumps(message, use_decimal=True) - # submit item to queue for routing to the correct persistence + serialized = json.dumps(message, use_decimal=True) + # submit item to queue for routing to the correct persistence - result = client.publish( - TopicArn=topic_arn, - Message=json.dumps({'default': serialized}), - Subject="schedule", - MessageStructure='json' - ) + result = client.publish( + TopicArn=topic_arn, + Message=json.dumps({'default': serialized}), + Subject="schedule", + MessageStructure='json' + ) if 'LastEvaluatedKey' in response: return table.scan( diff --git a/freqtrade/aws/strategy.py b/freqtrade/aws/strategy.py index 03436bf6a..74d5b425d 100644 --- a/freqtrade/aws/strategy.py +++ b/freqtrade/aws/strategy.py @@ -278,56 +278,3 @@ def submit_github(event, context): "statusCode": 404, "body": json.dumps({"error": result}) } - - -def get_trades(event, context): - """ - this function retuns all the knowns trades for a user, strategy and pair - :param event: - :param context: - :return: - """ - - assert 'pathParameters' in event - assert 'user' in event['pathParameters'] - assert 'name' in event['pathParameters'] - assert 'stake' in event['pathParameters'] - assert 'asset' in event['pathParameters'] - - table = get_trade_table() - - response = table.query( - KeyConditionExpression=Key('id').eq( - "{}.{}:{}/{}".format( - event['pathParameters']['user'], - event['pathParameters']['name'], - event['pathParameters']['asset'].upper(), - event['pathParameters']['stake'].upper() - ) - ) - ) - - if "Items" in response and len(response['Items']) > 0: - - # preparation for pagination - # TODO include in parameters an optional - # start key ExclusiveStartKey=response['LastEvaluatedKey'] - - data = { - "result": response['Items'], - "paginationKey": response.get('LastEvaluatedKey') - } - - return { - "statusCode": response['ResponseMetadata']['HTTPStatusCode'], - "body": json.dumps(data) - } - - else: - return { - "statusCode": 404, - "body": json.dumps({ - "error": "sorry this query did not produce any results", - "event": event - }) - } diff --git a/freqtrade/aws/trade.py b/freqtrade/aws/trade.py index 0dbb822e8..d262608bf 100644 --- a/freqtrade/aws/trade.py +++ b/freqtrade/aws/trade.py @@ -2,6 +2,7 @@ import boto3 import simplejson as json import os from freqtrade.aws.tables import get_trade_table +from boto3.dynamodb.conditions import Key, Attr def store(event, context): @@ -41,3 +42,70 @@ def submit(event, context): "statusCode": 200, "body": json.dumps(result) } + +def get_aggregated_trades(event, context): + """ + returns the aggregated trades for the given key combination + :param event: + :param context: + :return: + """ + + assert 'pathParameters' in event + assert 'user' in event['pathParameters'] + assert 'name' in event['pathParameters'] + assert 'ticker' in event['pathParameters'] + assert 'days' in event['pathParameters'] + + +def get_trades(event, context): + """ + this function returns all the known trades for a user, strategy and pair + :param event: + :param context: + :return: + """ + + assert 'pathParameters' in event + assert 'user' in event['pathParameters'] + assert 'name' in event['pathParameters'] + assert 'stake' in event['pathParameters'] + assert 'asset' in event['pathParameters'] + + table = get_trade_table() + + response = table.query( + KeyConditionExpression=Key('id').eq( + "{}.{}:{}/{}".format( + event['pathParameters']['user'], + event['pathParameters']['name'], + event['pathParameters']['asset'].upper(), + event['pathParameters']['stake'].upper() + ) + ) + ) + + if "Items" in response and len(response['Items']) > 0: + + # preparation for pagination + # TODO include in parameters an optional + # start key ExclusiveStartKey=response['LastEvaluatedKey'] + + data = { + "result": response['Items'], + "paginationKey": response.get('LastEvaluatedKey') + } + + return { + "statusCode": response['ResponseMetadata']['HTTPStatusCode'], + "body": json.dumps(data) + } + + else: + return { + "statusCode": 404, + "body": json.dumps({ + "error": "sorry this query did not produce any results", + "event": event + }) + } diff --git a/freqtrade/tests/aws/test_backtest.py b/freqtrade/tests/aws/test_backtest.py index c56f2655a..2176d04cb 100644 --- a/freqtrade/tests/aws/test_backtest.py +++ b/freqtrade/tests/aws/test_backtest.py @@ -1,13 +1,92 @@ -import os from base64 import urlsafe_b64encode -import boto3 import pytest import simplejson as json -from mock import Mock from freqtrade.aws.backtesting_lambda import backtest, cron -from freqtrade.aws.strategy import submit, get_trades +from freqtrade.aws.strategy import submit + + +@pytest.mark.skip(reason="no way of currently testing this") +def test_backtest_remote(lambda_context): + content = """# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from hyperopt import hp +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +class MyFancyTestStrategy(IStrategy): + minimal_roi = { + "0": 0.5 + } + stoploss = -0.2 + ticker_interval = '5m' + + def populate_indicators(self, dataframe: DataFrame) -> DataFrame: + macd = ta.MACD(dataframe) + dataframe['maShort'] = ta.EMA(dataframe, timeperiod=8) + dataframe['maMedium'] = ta.EMA(dataframe, timeperiod=21) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['maShort'], dataframe['maMedium']) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame: + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['maMedium'], dataframe['maShort']) + ), + 'sell'] = 1 + return dataframe + + + """ + + request = { + "user": "GCU4LW2XXZW3A3FM2XZJTEJHNWHTWDKY2DIJLCZJ5ULVZ4K7LZ7D23TG", + "description": "simple test strategy", + "name": "MyFancyTestStrategy", + "content": urlsafe_b64encode(content.encode('utf-8')), + "public": False + } + + # now we add an entry + submit({ + "body": json.dumps(request) + }, {}) + + # build sns request + request = { + "user": "GCU4LW2XXZW3A3FM2XZJTEJHNWHTWDKY2DIJLCZJ5ULVZ4K7LZ7D23TG", + "name": "MyFancyTestStrategy", + "from": "20180401", + "till": "20180501", + "stake_currency": "usdt", + "assets": ["ltc"], + "local": False + + } + + assert backtest({ + "Records": [ + { + "Sns": { + "Subject": "backtesting", + "Message": json.dumps(request) + } + }] + }, {})['statusCode'] == 200 def test_backtest_time_frame(lambda_context): @@ -60,10 +139,7 @@ class MyFancyTestStrategy(IStrategy): "description": "simple test strategy", "name": "MyFancyTestStrategy", "content": urlsafe_b64encode(content.encode('utf-8')), - "public": False, - "days": 1, - "local": True - + "public": False } # now we add an entry @@ -78,7 +154,8 @@ class MyFancyTestStrategy(IStrategy): "from": "20180401", "till": "20180501", "stake_currency": "usdt", - "assets": ["ltc"] + "assets": ["ltc"], + "local": True } @@ -159,6 +236,7 @@ class MyFancyTestStrategy(IStrategy): "stake_currency": "usdt", "assets": ["ltc"], "days": 2, + "ticker": '15m', "local": True } diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index f0f5c85ae..43455603f 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -627,6 +627,31 @@ def lambda_context(): lamb = moto.mock_lambda() lamb.start() + ecs = moto.mock_ecs() + ecs.start() + + cluster = boto3.client('ecs') + cluster.create_cluster(clusterName='fargate') + + cluster.register_task_definition( + containerDefinitions=[ + { + 'name': 'freqtrade-backtesting', + 'command': [ + 'sleep', + '360', + ], + 'cpu': 10, + 'essential': True, + 'image': 'busybox', + 'memory': 10, + }, + ], + family='sleep360', + taskRoleArn='', + volumes=[ + ], + ) session = boto3.session.Session() os.environ["strategyTable"] = "StrategyTable" os.environ["tradeTable"] = "TradeTable" @@ -649,3 +674,4 @@ def lambda_context(): sns.stop() dynamo.stop() lamb.stop() + ecs.stop()