working on backtesting BASE64 encoded strategies

This commit is contained in:
Gert Wohlgemuth 2018-05-21 20:48:35 -07:00
parent 4e31b4c9ee
commit c25aa22690
5 changed files with 184 additions and 77 deletions

View File

@ -1,8 +1,15 @@
import logging import logging
import boto3
import os
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
import simplejson as json
from boto3.dynamodb.conditions import Key, Attr
db = boto3.resource('dynamodb')
def backtest(event, context): def backtest(event, context):
@ -29,25 +36,39 @@ def backtest(event, context):
no return no return
""" """
name = "TestStrategy" if 'body' in event:
user = "12345678" event['body'] = json.loads(event['body'])
stake_currency = "USDT" name = event['body']['name']
asset = ["ETH", "BTC"] user = event['body']['user']
exchange = "binance" stake_currency = event['body']['stake_currency'].upper()
asset = event['body']['asset']
exchange = event['body']['exchange']
assets = list(map(lambda x: "{}/{}".format(x, stake_currency).upper(), asset)) assets = list(map(lambda x: "{}/{}".format(x, stake_currency).upper(), asset))
table = db.Table(os.environ['strategyTable'])
response = table.query(
KeyConditionExpression=Key('user').eq(user) &
Key('name').eq(name)
)
print(response)
if "Items" in response and len(response['Items']) > 0:
content = response['Items'][0]['content']
configuration = { configuration = {
"max_open_trades": 1, "max_open_trades": 1,
"stake_currency": stake_currency, "stake_currency": stake_currency,
"stake_amount": 0.001, "stake_amount": 1,
"fiat_display_currency": "USD", "fiat_display_currency": "USD",
"unfilledtimeout": 600, "unfilledtimeout": 600,
"bid_strategy": { "bid_strategy": {
"ask_last_balance": 0.0 "ask_last_balance": 0.0
}, },
"exchange": { "exchange": {
"name": "bittrex", "name": exchange,
"enabled": True, "enabled": True,
"key": "key", "key": "key",
"secret": "secret", "secret": "secret",
@ -68,7 +89,8 @@ def backtest(event, context):
"process_throttle_secs": 5 "process_throttle_secs": 5
}, },
'realistic_simulation': True, 'realistic_simulation': True,
"loglevel": logging.DEBUG "loglevel": logging.DEBUG,
"strategy": "{}:{}".format(name, content)
} }
@ -80,23 +102,26 @@ def backtest(event, context):
result = backtesting.start() result = backtesting.start()
print("finished test") print("finished test")
print(result)
print("persist data in dynamo") print("persist data in dynamo")
for index, row in result.iterrows(): for index, row in result.iterrows():
if row['loss'] > 0 or row['profit'] > 0:
item = { item = {
"id": "{}.{}:{}".format(user, name, row['pair']), "id": "{}.{}:{}".format(user, name, row['pair']),
"pair": row['pair'], "pair": row['pair'],
"profit": row['profit'], "count_profit": row['profit'],
"loss": row['loss'], "count_loss": row['loss'],
"duration": row['avg duration'], "avg_duration": row['avg duration'],
"avg profit": row['avg profit %'], "avg profit": row['avg profit %'],
"total profit": row['total profit {}'.format(stake_currency)] "total profit": row['total profit {}'.format(stake_currency)]
} }
print(item) print(item)
pass else:
raise Exception("sorry we did not find any matching strategy for user {} and name {}".format(user, name))
else:
raise Exception("no body provided")
def submit(event, context): def submit(event, context):

View File

@ -95,11 +95,6 @@ def code(event, context):
:return: :return:
""" """
print("event")
print(event)
print("context")
print(context)
user = "" user = ""
name = "" name = ""

View File

@ -6,6 +6,7 @@ This module load custom strategies
import importlib.util import importlib.util
import inspect import inspect
import logging import logging
from base64 import urlsafe_b64decode
from collections import OrderedDict from collections import OrderedDict
from typing import Optional, Dict, Type from typing import Optional, Dict, Type
@ -88,8 +89,26 @@ class StrategyResolver(object):
# Add extra strategy directory on top of search paths # Add extra strategy directory on top of search paths
abs_paths.insert(0, extra_dir) abs_paths.insert(0, extra_dir)
try: # check if the given strategy is provided as name, value pair
# where the value is the strategy encoded in base 64
if ":" in strategy_name:
strat = strategy_name.split(":")
if len(strat) == 2:
temp = Path(tempfile.mkdtemp("freq", "strategy"))
name = strat[0] + ".py"
temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))
temp.joinpath("__init__.py").touch()
strategy_name = os.path.splitext(name)[0]
# register temp path with the bot
abs_paths.insert(0, temp.absolute())
# check if given strategy matches an url # check if given strategy matches an url
else:
try:
logger.debug("requesting remote strategy from {}".format(strategy_name)) logger.debug("requesting remote strategy from {}".format(strategy_name))
resp = requests.get(strategy_name, stream=True) resp = requests.get(strategy_name, stream=True)
if resp.status_code == 200: if resp.status_code == 200:

View File

@ -1,7 +1,75 @@
import pytest from base64 import urlsafe_b64encode
import pytest
import simplejson as json
from freqtrade.aws.backtesting_lambda import backtest from freqtrade.aws.backtesting_lambda import backtest
from freqtrade.aws.strategy import submit
def test_backtest(lambda_context): def test_backtest(lambda_context):
backtest({}, {}) 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)
}, {})
request = {
"user": "GCU4LW2XXZW3A3FM2XZJTEJHNWHTWDKY2DIJLCZJ5ULVZ4K7LZ7D23TG",
"name": "MyFancyTestStrategy",
"stake_currency": "usdt",
"asset": ["ETH", "BTC", "XRP", "LTC"],
"exchange": "binance"
}
backtest({"body": json.dumps(request)}, {})

View File

@ -642,7 +642,7 @@ def lambda_context():
# do not mock requests to these urls # do not mock requests to these urls
responses.add_passthru('https://api.github.com') responses.add_passthru('https://api.github.com')
responses.add_passthru('https://bittrex.com') responses.add_passthru('https://bittrex.com')
responses.add_passthru('https://api.binance.com')
# here we will define required tables later # here we will define required tables later
yield yield
sns.stop() sns.stop()