diff --git a/freqtrade/aws/backtesting_lambda.py b/freqtrade/aws/backtesting_lambda.py index 43730d025..47e353298 100644 --- a/freqtrade/aws/backtesting_lambda.py +++ b/freqtrade/aws/backtesting_lambda.py @@ -1,3 +1,4 @@ +import datetime import logging import os import boto3 @@ -37,101 +38,105 @@ def backtest(event, context): no return """ - if 'body' in event: - event['body'] = json.loads(event['body']) - name = event['body']['name'] - user = event['body']['user'] + if 'Records' in event: + for x in event['Records']: + if 'Sns' in x and 'Message' in x['Sns']: - # technically we can get all these from teh strategy table - stake_currency = event['body']['stake_currency'].upper() - asset = event['body']['asset'] - exchange = event['body']['exchange'] + event['body'] = json.loads(x['Sns']['Message']) + name = event['body']['name'] + user = event['body']['user'] - assets = list(map(lambda x: "{}/{}".format(x, stake_currency).upper(), asset)) + trade_table = get_trade_table() + table = get_strategy_table() - trade_table = get_trade_table() - table = get_strategy_table() + response = table.query( + KeyConditionExpression=Key('user').eq(user) & + Key('name').eq(name) - response = table.query( - KeyConditionExpression=Key('user').eq(user) & - Key('name').eq(name) + ) - ) + print(response) + if "Items" in response and len(response['Items']) > 0: - print(response) - if "Items" in response and len(response['Items']) > 0: + today = datetime.datetime.today() + yesterday = today - datetime.timedelta(days=1) - content = response['Items'][0]['content'] - configuration = { - "max_open_trades": 1, - "stake_currency": stake_currency, - "stake_amount": 1, - "fiat_display_currency": "USD", - "unfilledtimeout": 600, - "bid_strategy": { - "ask_last_balance": 0.0 - }, - "exchange": { - "name": exchange, - "enabled": True, - "key": "key", - "secret": "secret", - "pair_whitelist": assets - }, - "telegram": { - "enabled": False, - "token": "token", - "chat_id": "0" - }, - "initial_state": "running", - "datadir": ".", - "experimental": { - "use_sell_signal": True, - "sell_profit_only": True - }, - "internals": { - "process_throttle_secs": 5 - }, - 'realistic_simulation': True, - "loglevel": logging.DEBUG, - "strategy": "{}:{}".format(name, content) + content = response['Items'][0]['content'] + configuration = { + "max_open_trades": 1, + "stake_currency": response['Items'][0]['stake_currency'], + "stake_amount": 1, + "fiat_display_currency": "USD", + "unfilledtimeout": 600, + "trailing_stop": response['Items'][0]['trailing_stop'], + "bid_strategy": { + "ask_last_balance": 0.0 + }, + "exchange": { + "name": response['Items'][0]['exchange'], + "enabled": True, + "key": "key", + "secret": "secret", + "pair_whitelist": list( + map(lambda x: "{}/{}".format(x, response['Items'][0]['stake_currency']).upper(), + response['Items'][0]['assets'])) + }, + "telegram": { + "enabled": False, + "token": "token", + "chat_id": "0" + }, + "initial_state": "running", + "datadir": ".", + "experimental": { + "use_sell_signal": response['Items'][0]['use_sell'], + "sell_profit_only": True + }, + "internals": { + "process_throttle_secs": 5 + }, + 'realistic_simulation': True, + "loglevel": logging.DEBUG, + "strategy": "{}:{}".format(name, content), + "timerange": "{}-{}".format(yesterday.strftime('%Y%m%d'), today.strftime('%Y%m%d')), + "refresh_pairs": True - } + } - print("generated configuration") - print(configuration) + print("generated configuration") + print(configuration) - print("initialized backtesting") - backtesting = Backtesting(configuration) - result = backtesting.start() - print("finished test") + print("initialized backtesting") + backtesting = Backtesting(configuration) + result = backtesting.start() + print("finished test") - print("persist data in dynamo") + print("persist data in dynamo") - print(result) - for index, row in result.iterrows(): - data = { - "id": "{}.{}:{}".format(user, name, row['currency']), - "trade": "{} to {}".format(row['entry'].strftime('%Y-%m-%d %H:%M:%S'), - row['exit'].strftime('%Y-%m-%d %H:%M:%S')), - "pair": row['currency'], - "duration": row['duration'], - "profit_percent": row['profit_percent'], - "profit_stake": row['profit_BTC'], - "entry_date": row['entry'].strftime('%Y-%m-%d %H:%M:%S'), - "exit_date": row['exit'].strftime('%Y-%m-%d %H:%M:%S') - } + print(result) + for index, row in result.iterrows(): + data = { + "id": "{}.{}:{}".format(user, name, row['currency']), + "trade": "{} to {}".format(row['entry'].strftime('%Y-%m-%d %H:%M:%S'), + row['exit'].strftime('%Y-%m-%d %H:%M:%S')), + "pair": row['currency'], + "duration": row['duration'], + "profit_percent": row['profit_percent'], + "profit_stake": row['profit_BTC'], + "entry_date": row['entry'].strftime('%Y-%m-%d %H:%M:%S'), + "exit_date": row['exit'].strftime('%Y-%m-%d %H:%M:%S') + } - data = json.dumps(data, use_decimal=True) - data = json.loads(data, use_decimal=True) - print(data) - # persist data - trade_table.put_item(Item=data) - else: - raise Exception( - "sorry we did not find any matching strategy for user {} and name {}".format(user, name)) + data = json.dumps(data, use_decimal=True) + data = json.loads(data, use_decimal=True) + print(data) + # persist data + trade_table.put_item(Item=data) + else: + raise Exception( + "sorry we did not find any matching strategy for user {} and name {}".format(user, name)) else: - raise Exception("no body provided") + raise Exception("not a valid event: {}".format(event)) def cron(event, context): diff --git a/freqtrade/aws/strategy.py b/freqtrade/aws/strategy.py index 361b96f94..9227bc36c 100644 --- a/freqtrade/aws/strategy.py +++ b/freqtrade/aws/strategy.py @@ -186,6 +186,22 @@ def __evaluate(data): data['stoploss'] = strat.stoploss data['ticker'] = strat.ticker_interval + # default variables if not provided + if 'trailing_stop' not in data: + data['trailing_stop'] = False + + if 'stake_currency' not in data: + data['stake_currency'] = "USDT" + + if 'use_sell' not in data: + data['use_sell'] = True + + if 'exchange' not in data: + data['exchange'] = 'binance' + + if 'assets' not in data: + data['assets'] = ["BTC", "ETH", "LTC"] + # force serialization to deal with decimal number data = json.dumps(data, use_decimal=True) data = json.loads(data, use_decimal=True) diff --git a/freqtrade/aws/tables.py b/freqtrade/aws/tables.py index 059783728..ecae65f57 100644 --- a/freqtrade/aws/tables.py +++ b/freqtrade/aws/tables.py @@ -13,6 +13,9 @@ def get_trade_table(): :return: """ + if 'tradeTable' not in os.environ: + os.environ['tradeTable'] = "FreqTradeTable" + table_name = os.environ['tradeTable'] existing_tables = boto3.client('dynamodb').list_tables()['TableNames'] if table_name not in existing_tables: @@ -55,6 +58,9 @@ def get_strategy_table(): provides us access to the strategy table and if it doesn't exists creates it for us :return: """ + if 'strategyTable' not in os.environ: + os.environ['strategyTable'] = "FreqStrategyTable" + table_name = os.environ['strategyTable'] existing_tables = boto3.client('dynamodb').list_tables()['TableNames'] @@ -62,7 +68,7 @@ def get_strategy_table(): if table_name not in existing_tables: try: db.create_table( - TableName=os.environ[table_name], + TableName=table_name, KeySchema=[ { 'AttributeName': 'user', diff --git a/freqtrade/tests/aws/test_backtest.py b/freqtrade/tests/aws/test_backtest.py index a3447c07b..770339e6c 100644 --- a/freqtrade/tests/aws/test_backtest.py +++ b/freqtrade/tests/aws/test_backtest.py @@ -1,5 +1,7 @@ +import os from base64 import urlsafe_b64encode +import boto3 import pytest import simplejson as json from freqtrade.aws.backtesting_lambda import backtest, cron @@ -64,15 +66,21 @@ class MyFancyTestStrategy(IStrategy): "body": json.dumps(request) }, {}) + # build sns request request = { "user": "GCU4LW2XXZW3A3FM2XZJTEJHNWHTWDKY2DIJLCZJ5ULVZ4K7LZ7D23TG", - "name": "MyFancyTestStrategy", - "stake_currency": "usdt", - "asset": ["ETH", "BTC", "XRP", "LTC"], - "exchange": "binance" + "name": "MyFancyTestStrategy" } - backtest({"body": json.dumps(request)}, {}) + backtest({ + "Records": [ + { + "Sns": { + "Subject": "backtesting", + "Message": json.dumps(request) + } + }] + }, {}) def test_cron(lambda_context): @@ -135,4 +143,7 @@ class MyFancyTestStrategy(IStrategy): }, {}) print("evaluating cron job") + cron({}, {}) + + #TODO test receiving of message some how diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 8d645116d..5e8ac29c6 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -628,13 +628,15 @@ def lambda_context(): lamb.start() session = boto3.session.Session() - - client = session.client('sns') - dynamodb = boto3.resource('dynamodb') os.environ["strategyTable"] = "StrategyTable" os.environ["tradeTable"] = "TradeTable" os.environ["topic"] = "UnitTestTopic" + client = session.client('sns') + client.create_topic(Name=os.environ["topic"]) + + dynamodb = boto3.resource('dynamodb') + import responses # do not mock requests to these urls diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index f17a0115e..620242d1f 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -490,7 +490,7 @@ def test_processed(default_conf, mocker) -> None: def test_backtest_pricecontours(default_conf, fee, mocker) -> None: mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee) - tests = [['raise', 17], ['lower', 0], ['sine', 17]] + tests = [['raise', 17], ['lower', 0], ['sine', 16]] for [contour, numres] in tests: simple_backtest(default_conf, contour, numres, mocker)