added support for backtest ranges to collect more data and initial support for pagination

This commit is contained in:
Gert Wohlgemuth 2018-05-24 14:05:29 -07:00
parent f2b8609127
commit e37e7bd914
5 changed files with 145 additions and 8 deletions

View File

@ -57,13 +57,17 @@ def backtest(event, context):
) )
print(response)
try:
if "Items" in response and len(response['Items']) > 0:
today = datetime.datetime.today() today = datetime.datetime.today()
yesterday = today - datetime.timedelta(days=1) yesterday = today - datetime.timedelta(days=1)
if 'from' in event['body']:
yesterday = datetime.datetime.strptime(event['body']['from'], '%Y%m%d')
if 'till' in event['body']:
yesterday = datetime.datetime.strptime(event['body']['till'], '%Y%m%d')
try:
if "Items" in response and len(response['Items']) > 0:
content = response['Items'][0]['content'] content = response['Items'][0]['content']
configuration = { configuration = {
"max_open_trades": 1, "max_open_trades": 1,
@ -189,6 +193,14 @@ def cron(event, context):
"name": i['name'] "name": i['name']
} }
# 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']
if 'till' in event['pathParameters']:
message['till'] = event['pathParameters']['till']
serialized = json.dumps(message, use_decimal=True) serialized = json.dumps(message, use_decimal=True)
# submit item to queue for routing to the correct persistence # submit item to queue for routing to the correct persistence

View File

@ -26,6 +26,7 @@ def names(event, context):
response = table.scan() response = table.scan()
result = response['Items'] result = response['Items']
# no pagination here
while 'LastEvaluatedKey' in response: while 'LastEvaluatedKey' in response:
for i in response['Items']: for i in response['Items']:
result.append(i) result.append(i)
@ -36,6 +37,10 @@ def names(event, context):
# map results and hide informations # map results and hide informations
data = list(map(lambda x: {'name': x['name'], 'public': x['public'], 'user': x['user']}, result)) data = list(map(lambda x: {'name': x['name'], 'public': x['public'], 'user': x['user']}, result))
# keep in a result object, so we can later add pagination to it
data = {
"result": data
}
return { return {
"statusCode": 200, "statusCode": 200,
"body": json.dumps(data) "body": json.dumps(data)
@ -304,13 +309,25 @@ def get_trades(event, context):
if "Items" in response and len(response['Items']) > 0: 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 { return {
"statusCode": response['ResponseMetadata']['HTTPStatusCode'], "statusCode": response['ResponseMetadata']['HTTPStatusCode'],
"body": json.dumps(response['Items']) "body": json.dumps(data)
} }
else: else:
return { return {
"statusCode": response['ResponseMetadata']['HTTPStatusCode'], "statusCode": 404,
"body": json.dumps(response) "body": json.dumps({
"error": "sorry this query did not produce any results",
"event": event
})
} }

View File

@ -8,6 +8,100 @@ from freqtrade.aws.backtesting_lambda import backtest, cron
from freqtrade.aws.strategy import submit, get_trades from freqtrade.aws.strategy import submit, get_trades
def test_backtest_time_frame(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": "20180501",
"till": "20180401"
}
data = json.loads(backtest({
"Records": [
{
"Sns": {
"Subject": "backtesting",
"Message": json.dumps(request)
}
}]
}, {})['body'])
# evaluate that we now have trades in the database
# sadly not always a given at this tage
# due to the dynamic nature. Should pick a strategy for testing
# which generates a lot of trades
if len(data) > 0:
data = get_trades({
'pathParameters': {
'user': "GCU4LW2XXZW3A3FM2XZJTEJHNWHTWDKY2DIJLCZJ5ULVZ4K7LZ7D23TG",
"name": "MyFancyTestStrategy",
'stake': "USDT",
'asset': "{}".format(data[0]['pair'].split("/")[0])
}
}, {})['body']
print(data)
assert len(json.loads(data)) > 0
def test_backtest(lambda_context): def test_backtest(lambda_context):
content = """# --- Do not remove these libs --- content = """# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy

View File

@ -87,7 +87,7 @@ class TestStrategy(IStrategy):
"body": json.dumps(request) "body": json.dumps(request)
}, {}) }, {})
assert (len(json.loads(aws.names({}, {})['body'])) == 2) assert (len(json.loads(aws.names({}, {})['body']['result'])) == 2)
# able to add a duplicated strategy, which should overwrite the existing strategy # able to add a duplicated strategy, which should overwrite the existing strategy

View File

@ -223,6 +223,7 @@ functions:
events: events:
- sns: ${self:custom.snsTopic} - sns: ${self:custom.snsTopic}
environment: environment:
topic: ${self:custom.snsTopic} topic: ${self:custom.snsTopic}
tradeTable: ${self:custom.tradeTable} tradeTable: ${self:custom.tradeTable}
@ -241,6 +242,19 @@ functions:
- schedule: - schedule:
rate: rate(5 minutes) rate: rate(5 minutes)
enabled: false enabled: false
# manual trigger for a longer range
# TODO protect with api key
- http:
path: strategies/backtest/{from}/{till}
method: post
cors: true
request:
parameter:
paths:
from: true
till: true
environment: environment:
topic: ${self:custom.snsTopic} topic: ${self:custom.snsTopic}
tradeTable: ${self:custom.tradeTable} tradeTable: ${self:custom.tradeTable}