mirror of
https://github.com/wassname/catalyst.git
synced 2026-08-08 11:16:58 +08:00
Compare commits
52
Commits
cloud_conn
..
cloud
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca76d7271f | ||
|
|
d0abc8eac0 | ||
|
|
4712db4f4f | ||
|
|
8a812ea455 | ||
|
|
e4933ee48e | ||
|
|
b172f6b7b0 | ||
|
|
3335ef16a4 | ||
|
|
71b246b9f1 | ||
|
|
aa520d5a8b | ||
|
|
e59f46dfd6 | ||
|
|
5197ab6cc2 | ||
|
|
d7b6cb8490 | ||
|
|
fa0e9332bf | ||
|
|
d9d6a4e52d | ||
|
|
b4e5b699bd | ||
|
|
f990ecf14d | ||
|
|
5d3a1c2f8b | ||
|
|
3159ec7dc2 | ||
|
|
8be0626fc9 | ||
|
|
868f17fd9d | ||
|
|
b95cf465fc | ||
|
|
c4b10bae39 | ||
|
|
6c4f7afaea | ||
|
|
b85219d5b4 | ||
|
|
082b342d02 | ||
|
|
37c1057ab6 | ||
|
|
46e1a87a3d | ||
|
|
cfafafb8fc | ||
|
|
497212383a | ||
|
|
e5870ea60a | ||
|
|
8587fee0ce | ||
|
|
388535b09c | ||
|
|
25e9f0f58f | ||
|
|
b4bd557273 | ||
|
|
2577b53518 | ||
|
|
8fe3ab344e | ||
|
|
bfd7e4b2dd | ||
|
|
50310576f9 | ||
|
|
fea2ed104e | ||
|
|
127878413e | ||
|
|
6a6ccf5595 | ||
|
|
d40585f56e | ||
|
|
4337abd60a | ||
|
|
92e0a7bb88 | ||
|
|
20f8a75f4a | ||
|
|
ec5fdecf91 | ||
|
|
9956b5462d | ||
|
|
46f34d64a0 | ||
|
|
bc8bf6941d | ||
|
|
49b6792399 | ||
|
|
3c4c6c3dfd | ||
|
|
403d7f9c29 |
@@ -506,6 +506,197 @@ def live(ctx,
|
|||||||
return perf
|
return perf
|
||||||
|
|
||||||
|
|
||||||
|
@main.command(name='serve')
|
||||||
|
@click.option(
|
||||||
|
'-f',
|
||||||
|
'--algofile',
|
||||||
|
default=None,
|
||||||
|
type=click.File('r'),
|
||||||
|
help='The file that contains the algorithm to run.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-t',
|
||||||
|
'--algotext',
|
||||||
|
help='The algorithm script to run.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-D',
|
||||||
|
'--define',
|
||||||
|
multiple=True,
|
||||||
|
help="Define a name to be bound in the namespace before executing"
|
||||||
|
" the algotext. For example '-Dname=value'. The value may be"
|
||||||
|
" any python expression. These are evaluated in order so they"
|
||||||
|
" may refer to previously defined names.",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'--data-frequency',
|
||||||
|
type=click.Choice({'daily', 'minute'}),
|
||||||
|
default='daily',
|
||||||
|
show_default=True,
|
||||||
|
help='The data frequency of the simulation.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'--capital-base',
|
||||||
|
type=float,
|
||||||
|
show_default=True,
|
||||||
|
help='The starting capital for the simulation.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-b',
|
||||||
|
'--bundle',
|
||||||
|
default='poloniex',
|
||||||
|
metavar='BUNDLE-NAME',
|
||||||
|
show_default=True,
|
||||||
|
help='The data bundle to use for the simulation.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'--bundle-timestamp',
|
||||||
|
type=Timestamp(),
|
||||||
|
default=pd.Timestamp.utcnow(),
|
||||||
|
show_default=False,
|
||||||
|
help='The date to lookup data on or before.\n'
|
||||||
|
'[default: <current-time>]'
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-s',
|
||||||
|
'--start',
|
||||||
|
type=Date(tz='utc', as_timestamp=True),
|
||||||
|
help='The start date of the simulation.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-e',
|
||||||
|
'--end',
|
||||||
|
type=Date(tz='utc', as_timestamp=True),
|
||||||
|
help='The end date of the simulation.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-o',
|
||||||
|
'--output',
|
||||||
|
default='-',
|
||||||
|
metavar='FILENAME',
|
||||||
|
show_default=True,
|
||||||
|
help="The location to write the perf data. If this is '-' the perf"
|
||||||
|
" will be written to stdout.",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'--print-algo/--no-print-algo',
|
||||||
|
is_flag=True,
|
||||||
|
default=False,
|
||||||
|
help='Print the algorithm to stdout.',
|
||||||
|
)
|
||||||
|
@ipython_only(click.option(
|
||||||
|
'--local-namespace/--no-local-namespace',
|
||||||
|
is_flag=True,
|
||||||
|
default=None,
|
||||||
|
help='Should the algorithm methods be resolved in the local namespace.'
|
||||||
|
))
|
||||||
|
@click.option(
|
||||||
|
'-x',
|
||||||
|
'--exchange-name',
|
||||||
|
help='The name of the targeted exchange.',
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-n',
|
||||||
|
'--algo-namespace',
|
||||||
|
help='A label assigned to the algorithm for data storage purposes.'
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
'-c',
|
||||||
|
'--base-currency',
|
||||||
|
help='The base currency used to calculate statistics '
|
||||||
|
'(e.g. usd, btc, eth).',
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def run(ctx,
|
||||||
|
algofile,
|
||||||
|
algotext,
|
||||||
|
define,
|
||||||
|
data_frequency,
|
||||||
|
capital_base,
|
||||||
|
bundle,
|
||||||
|
bundle_timestamp,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
output,
|
||||||
|
print_algo,
|
||||||
|
local_namespace,
|
||||||
|
exchange_name,
|
||||||
|
algo_namespace,
|
||||||
|
base_currency):
|
||||||
|
"""Run a backtest for the given algorithm on the server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (algotext is not None) == (algofile is not None):
|
||||||
|
ctx.fail(
|
||||||
|
"must specify exactly one of '-f' / '--algofile' or"
|
||||||
|
" '-t' / '--algotext'",
|
||||||
|
)
|
||||||
|
|
||||||
|
# check that the start and end dates are passed correctly
|
||||||
|
if start is None and end is None:
|
||||||
|
# check both at the same time to avoid the case where a user
|
||||||
|
# does not pass either of these and then passes the first only
|
||||||
|
# to be told they need to pass the second argument also
|
||||||
|
ctx.fail(
|
||||||
|
"must specify dates with '-s' / '--start' and '-e' / '--end'"
|
||||||
|
" in backtest mode",
|
||||||
|
)
|
||||||
|
if start is None:
|
||||||
|
ctx.fail("must specify a start date with '-s' / '--start'"
|
||||||
|
" in backtest mode")
|
||||||
|
if end is None:
|
||||||
|
ctx.fail("must specify an end date with '-e' / '--end'"
|
||||||
|
" in backtest mode")
|
||||||
|
|
||||||
|
if exchange_name is None:
|
||||||
|
ctx.fail("must specify an exchange name '-x'")
|
||||||
|
|
||||||
|
if base_currency is None:
|
||||||
|
ctx.fail("must specify a base currency with '-c' in backtest mode")
|
||||||
|
|
||||||
|
if capital_base is None:
|
||||||
|
ctx.fail("must specify a capital base with '--capital-base'")
|
||||||
|
|
||||||
|
click.echo('Running in backtesting mode.', sys.stdout)
|
||||||
|
|
||||||
|
perf = run_server(
|
||||||
|
initialize=None,
|
||||||
|
handle_data=None,
|
||||||
|
before_trading_start=None,
|
||||||
|
analyze=None,
|
||||||
|
algofile=algofile,
|
||||||
|
algotext=algotext,
|
||||||
|
defines=define,
|
||||||
|
data_frequency=data_frequency,
|
||||||
|
capital_base=capital_base,
|
||||||
|
data=None,
|
||||||
|
bundle=bundle,
|
||||||
|
bundle_timestamp=bundle_timestamp,
|
||||||
|
start=start,
|
||||||
|
end=end,
|
||||||
|
output=output,
|
||||||
|
print_algo=print_algo,
|
||||||
|
local_namespace=local_namespace,
|
||||||
|
environ=os.environ,
|
||||||
|
live=False,
|
||||||
|
exchange=exchange_name,
|
||||||
|
algo_namespace=algo_namespace,
|
||||||
|
base_currency=base_currency,
|
||||||
|
analyze_live=None,
|
||||||
|
live_graph=False,
|
||||||
|
simulate_orders=True,
|
||||||
|
auth_aliases=None,
|
||||||
|
stats_output=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
if output == '-':
|
||||||
|
click.echo(str(perf), sys.stdout)
|
||||||
|
elif output != os.devnull: # make the catalyst magic not write any data
|
||||||
|
perf.to_pickle(output)
|
||||||
|
|
||||||
|
return perf
|
||||||
|
|
||||||
|
|
||||||
@main.command(name='serve-live')
|
@main.command(name='serve-live')
|
||||||
@click.option(
|
@click.option(
|
||||||
'-f',
|
'-f',
|
||||||
|
|||||||
@@ -27,20 +27,20 @@ AUTH_SERVER = 'https://data.enigma.co'
|
|||||||
# TODO: switch to mainnet
|
# TODO: switch to mainnet
|
||||||
ETH_REMOTE_NODE = 'https://ropsten.infura.io/'
|
ETH_REMOTE_NODE = 'https://ropsten.infura.io/'
|
||||||
|
|
||||||
# TODO: move to MASTER branch on github
|
|
||||||
MARKETPLACE_CONTRACT = 'https://raw.githubusercontent.com/enigmampc/' \
|
MARKETPLACE_CONTRACT = 'https://raw.githubusercontent.com/enigmampc/' \
|
||||||
'catalyst/develop/catalyst/marketplace/' \
|
'catalyst/master/catalyst/marketplace/' \
|
||||||
'contract_marketplace_address.txt'
|
'contract_marketplace_address.txt'
|
||||||
|
|
||||||
MARKETPLACE_CONTRACT_ABI = 'https://raw.githubusercontent.com/enigmampc/' \
|
MARKETPLACE_CONTRACT_ABI = 'https://raw.githubusercontent.com/enigmampc/' \
|
||||||
'catalyst/develop/catalyst/marketplace/' \
|
'catalyst/master/catalyst/marketplace/' \
|
||||||
'contract_marketplace_abi.json'
|
'contract_marketplace_abi.json'
|
||||||
|
|
||||||
# TODO: switch to mainnet
|
# TODO: switch to mainnet
|
||||||
ENIGMA_CONTRACT = 'https://raw.githubusercontent.com/enigmampc/catalyst/' \
|
ENIGMA_CONTRACT = 'https://raw.githubusercontent.com/enigmampc/catalyst/' \
|
||||||
'develop/catalyst/marketplace/' \
|
'master/catalyst/marketplace/' \
|
||||||
'contract_enigma_address.txt'
|
'contract_enigma_address.txt'
|
||||||
|
|
||||||
ENIGMA_CONTRACT_ABI = 'https://raw.githubusercontent.com/enigmampc/' \
|
ENIGMA_CONTRACT_ABI = 'https://raw.githubusercontent.com/enigmampc/' \
|
||||||
'catalyst/develop/catalyst/marketplace/' \
|
'catalyst/master/catalyst/marketplace/' \
|
||||||
'contract_enigma_abi.json'
|
'contract_enigma_abi.json'
|
||||||
|
|||||||
@@ -843,6 +843,7 @@ class ExchangeBundle:
|
|||||||
field: str
|
field: str
|
||||||
data_frequency: str
|
data_frequency: str
|
||||||
algo_end_dt: pd.Timestamp
|
algo_end_dt: pd.Timestamp
|
||||||
|
force_auto_ingest:
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
|
|||||||
@@ -716,25 +716,36 @@ def save_asset_data(folder, df, decimals=8):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_candles_df(candles, field, freq, bar_count, end_dt,
|
def forward_fill_df_if_needed(df, periods):
|
||||||
previous_value=None):
|
df = df.reindex(periods)
|
||||||
|
# volume should always be 0 (if there were no trades in this interval)
|
||||||
|
df['volume'] = df['volume'].fillna(0.0)
|
||||||
|
# ie pull the last close into this close
|
||||||
|
df['close'] = df.fillna(method='pad')
|
||||||
|
# now copy the close that was pulled down from the last timestep
|
||||||
|
# into this row, across into o/h/l
|
||||||
|
df['open'] = df['open'].fillna(df['close'])
|
||||||
|
df['low'] = df['low'].fillna(df['close'])
|
||||||
|
df['high'] = df['high'].fillna(df['close'])
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def transform_candles_to_df(candles):
|
||||||
|
return pd.DataFrame(candles).set_index('last_traded')
|
||||||
|
|
||||||
|
|
||||||
|
def get_candles_df(candles, field, freq, bar_count, end_dt=None):
|
||||||
all_series = dict()
|
all_series = dict()
|
||||||
|
|
||||||
for asset in candles:
|
for asset in candles:
|
||||||
periods = pd.date_range(end=end_dt, periods=bar_count, freq=freq)
|
asset_df = transform_candles_to_df(candles[asset])
|
||||||
|
rounded_end_dt = end_dt.floor(freq)
|
||||||
|
periods = pd.date_range(end=rounded_end_dt,
|
||||||
|
periods=bar_count,
|
||||||
|
freq=freq)
|
||||||
|
asset_df = forward_fill_df_if_needed(asset_df, periods)
|
||||||
|
|
||||||
dates = [candle['last_traded'] for candle in candles[asset]]
|
all_series[asset] = pd.Series(asset_df[field])
|
||||||
values = [candle[field] for candle in candles[asset]]
|
|
||||||
series = pd.Series(values, index=dates)
|
|
||||||
|
|
||||||
"""
|
|
||||||
series = series.reindex(
|
|
||||||
periods,
|
|
||||||
method='ffill',
|
|
||||||
fill_value=previous_value,
|
|
||||||
)
|
|
||||||
series.sort_index(inplace=True)
|
|
||||||
"""
|
|
||||||
all_series[asset] = series
|
|
||||||
|
|
||||||
df = pd.DataFrame(all_series)
|
df = pd.DataFrame(all_series)
|
||||||
df.dropna(inplace=True)
|
df.dropna(inplace=True)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from catalyst.exchange.utils.stats_utils import set_print_settings
|
|||||||
from catalyst.marketplace.marketplace_errors import (
|
from catalyst.marketplace.marketplace_errors import (
|
||||||
MarketplacePubAddressEmpty, MarketplaceDatasetNotFound,
|
MarketplacePubAddressEmpty, MarketplaceDatasetNotFound,
|
||||||
MarketplaceNoAddressMatch, MarketplaceHTTPRequest,
|
MarketplaceNoAddressMatch, MarketplaceHTTPRequest,
|
||||||
MarketplaceNoCSVFiles)
|
MarketplaceNoCSVFiles, MarketplaceRequiresPython3)
|
||||||
from catalyst.marketplace.utils.auth_utils import get_key_secret, \
|
from catalyst.marketplace.utils.auth_utils import get_key_secret, \
|
||||||
get_signed_headers
|
get_signed_headers
|
||||||
from catalyst.marketplace.utils.bundle_utils import merge_bundles
|
from catalyst.marketplace.utils.bundle_utils import merge_bundles
|
||||||
@@ -44,7 +44,10 @@ log = logbook.Logger('Marketplace', level=LOG_LEVEL)
|
|||||||
class Marketplace:
|
class Marketplace:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
global Web3
|
global Web3
|
||||||
|
try:
|
||||||
from web3 import Web3, HTTPProvider
|
from web3 import Web3, HTTPProvider
|
||||||
|
except ImportError:
|
||||||
|
raise MarketplaceRequiresPython3()
|
||||||
|
|
||||||
self.addresses = get_user_pubaddr()
|
self.addresses = get_user_pubaddr()
|
||||||
|
|
||||||
@@ -60,7 +63,8 @@ class Marketplace:
|
|||||||
contract_url = urllib.urlopen(MARKETPLACE_CONTRACT)
|
contract_url = urllib.urlopen(MARKETPLACE_CONTRACT)
|
||||||
|
|
||||||
self.mkt_contract_address = Web3.toChecksumAddress(
|
self.mkt_contract_address = Web3.toChecksumAddress(
|
||||||
contract_url.readline().strip())
|
contract_url.readline().decode(
|
||||||
|
contract_url.info().get_content_charset()).strip())
|
||||||
|
|
||||||
abi_url = urllib.urlopen(MARKETPLACE_CONTRACT_ABI)
|
abi_url = urllib.urlopen(MARKETPLACE_CONTRACT_ABI)
|
||||||
abi = json.load(abi_url)
|
abi = json.load(abi_url)
|
||||||
@@ -73,7 +77,8 @@ class Marketplace:
|
|||||||
contract_url = urllib.urlopen(ENIGMA_CONTRACT)
|
contract_url = urllib.urlopen(ENIGMA_CONTRACT)
|
||||||
|
|
||||||
self.eng_contract_address = Web3.toChecksumAddress(
|
self.eng_contract_address = Web3.toChecksumAddress(
|
||||||
contract_url.readline().strip())
|
contract_url.readline().decode(
|
||||||
|
contract_url.info().get_content_charset()).strip())
|
||||||
|
|
||||||
abi_url = urllib.urlopen(ENIGMA_CONTRACT_ABI)
|
abi_url = urllib.urlopen(ENIGMA_CONTRACT_ABI)
|
||||||
abi = json.load(abi_url)
|
abi = json.load(abi_url)
|
||||||
@@ -621,7 +626,7 @@ class Marketplace:
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Unable to subscribe to data source: {}'.format(e))
|
print('Unable to register the requested dataset: {}'.format(e))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.check_transaction(tx_hash)
|
self.check_transaction(tx_hash)
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ def silent_except_hook(exctype, excvalue, exctraceback):
|
|||||||
MarketplaceNoAddressMatch, MarketplaceHTTPRequest,
|
MarketplaceNoAddressMatch, MarketplaceHTTPRequest,
|
||||||
MarketplaceNoCSVFiles, MarketplaceContractDataNoMatch,
|
MarketplaceNoCSVFiles, MarketplaceContractDataNoMatch,
|
||||||
MarketplaceSubscriptionExpired, MarketplaceJSONError,
|
MarketplaceSubscriptionExpired, MarketplaceJSONError,
|
||||||
MarketplaceWalletNotSupported, MarketplaceEmptySignature]:
|
MarketplaceWalletNotSupported, MarketplaceEmptySignature,
|
||||||
|
MarketplaceRequiresPython3]:
|
||||||
fn = traceback.extract_tb(exctraceback)[-1][0]
|
fn = traceback.extract_tb(exctraceback)[-1][0]
|
||||||
ln = traceback.extract_tb(exctraceback)[-1][1]
|
ln = traceback.extract_tb(exctraceback)[-1][1]
|
||||||
print("Error traceback: {1} (line {2})\n"
|
print("Error traceback: {1} (line {2})\n"
|
||||||
@@ -86,3 +87,11 @@ class MarketplaceJSONError(ZiplineError):
|
|||||||
'The configuration file {file} is malformed. Please correct '
|
'The configuration file {file} is malformed. Please correct '
|
||||||
'the following error:\n{error}'
|
'the following error:\n{error}'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MarketplaceRequiresPython3(ZiplineError):
|
||||||
|
msg = (
|
||||||
|
'\nCatalyst requires Python3 to access the Enigma Data Marketplace.\n'
|
||||||
|
'If you want to use the Data Marketplace, you need to reinstall '
|
||||||
|
'Catalyst\nwith Python3. See the documentation website for additional '
|
||||||
|
'information.')
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
from catalyst.api import symbol
|
||||||
|
from catalyst.utils.run_algo import run_algorithm
|
||||||
|
|
||||||
|
coins = ['dash', 'btc', 'dash', 'etc', 'eth', 'ltc', 'nxt', 'rep', 'str', 'xmr', 'xrp', 'zec']
|
||||||
|
symbols = None
|
||||||
|
|
||||||
|
|
||||||
|
def initialize(context):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_data(context, data):
|
||||||
|
global symbols
|
||||||
|
if symbols is None: symbols = [symbol(c + '_usdt') for c in coins]
|
||||||
|
|
||||||
|
print'getting history for: %s' % [s.symbol for s in symbols]
|
||||||
|
history = data.history(symbols,
|
||||||
|
['close', 'volume'],
|
||||||
|
bar_count=1, # EXCEPTION, Change to 2
|
||||||
|
frequency='5T')
|
||||||
|
#print 'history: %s' % history.shape
|
||||||
|
|
||||||
|
run_algorithm(initialize=initialize,
|
||||||
|
handle_data=_handle_data,
|
||||||
|
analyze=lambda _, results: True,
|
||||||
|
exchange_name='poloniex',
|
||||||
|
base_currency='usdt',
|
||||||
|
algo_namespace='issue-236',
|
||||||
|
live=True,
|
||||||
|
data_frequency='minute',
|
||||||
|
capital_base=3000,
|
||||||
|
simulate_orders=True)
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#!flask/bin/python
|
||||||
|
import base64
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import pandas as pd
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def convert_date(date):
|
||||||
|
"""
|
||||||
|
when transferring dates by json,
|
||||||
|
converts it to str
|
||||||
|
:param date:
|
||||||
|
:return: str(date)
|
||||||
|
"""
|
||||||
|
if isinstance(date, pd.Timestamp):
|
||||||
|
return date.__str__()
|
||||||
|
|
||||||
|
|
||||||
|
def run_server(
|
||||||
|
initialize,
|
||||||
|
handle_data,
|
||||||
|
before_trading_start,
|
||||||
|
analyze,
|
||||||
|
algofile,
|
||||||
|
algotext,
|
||||||
|
defines,
|
||||||
|
data_frequency,
|
||||||
|
capital_base,
|
||||||
|
data,
|
||||||
|
bundle,
|
||||||
|
bundle_timestamp,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
output,
|
||||||
|
print_algo,
|
||||||
|
local_namespace,
|
||||||
|
environ,
|
||||||
|
live,
|
||||||
|
exchange,
|
||||||
|
algo_namespace,
|
||||||
|
base_currency,
|
||||||
|
live_graph,
|
||||||
|
analyze_live,
|
||||||
|
simulate_orders,
|
||||||
|
auth_aliases,
|
||||||
|
stats_output,
|
||||||
|
):
|
||||||
|
|
||||||
|
# address to send
|
||||||
|
url = 'http://sandbox.enigma.co/api/catalyst/serve'
|
||||||
|
# url = 'http://127.0.0.1:5000/api/catalyst/serve'
|
||||||
|
|
||||||
|
# argument preparation - encode the file for transfer
|
||||||
|
if algotext:
|
||||||
|
algotext = base64.b64encode(algotext)
|
||||||
|
else:
|
||||||
|
algotext = base64.b64encode(bytes(algofile.read(), 'utf-8')).decode('utf-8')
|
||||||
|
algofile = None
|
||||||
|
|
||||||
|
json_file = {'arguments': {
|
||||||
|
'initialize': initialize,
|
||||||
|
'handle_data': handle_data,
|
||||||
|
'before_trading_start': before_trading_start,
|
||||||
|
'analyze': analyze,
|
||||||
|
'algotext': algotext,
|
||||||
|
'defines': defines,
|
||||||
|
'data_frequency': data_frequency,
|
||||||
|
'capital_base': capital_base,
|
||||||
|
'data': data,
|
||||||
|
'bundle': bundle,
|
||||||
|
'bundle_timestamp': bundle_timestamp,
|
||||||
|
'start': start,
|
||||||
|
'end': end,
|
||||||
|
'local_namespace': local_namespace,
|
||||||
|
'environ': None,
|
||||||
|
'analyze_live': analyze_live,
|
||||||
|
'stats_output': stats_output,
|
||||||
|
'algofile': algofile,
|
||||||
|
'output': output,
|
||||||
|
'print_algo': print_algo,
|
||||||
|
'live': live,
|
||||||
|
'exchange': exchange,
|
||||||
|
'algo_namespace': algo_namespace,
|
||||||
|
'base_currency': base_currency,
|
||||||
|
'live_graph': live_graph,
|
||||||
|
'simulate_orders': simulate_orders,
|
||||||
|
'auth_aliases': auth_aliases,
|
||||||
|
}}
|
||||||
|
|
||||||
|
response = requests.post(url,
|
||||||
|
json=json.dumps(
|
||||||
|
json_file,
|
||||||
|
default=convert_date
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 500:
|
||||||
|
raise Exception("issues with cloud connections, "
|
||||||
|
"unable to run catalyst on the cloud")
|
||||||
|
received_data = response.json()
|
||||||
|
cloud_log_tail = base64.b64decode(received_data["log"])
|
||||||
|
print(cloud_log_tail)
|
||||||
@@ -143,8 +143,7 @@ with the following steps:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
conda create --name catalyst python=3.6 scipy zlib
|
conda create --name catalyst python=2.7 scipy zlib
|
||||||
|
|
||||||
|
|
||||||
3. Activate the environment:
|
3. Activate the environment:
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ dependencies:
|
|||||||
- bottleneck==1.2.1
|
- bottleneck==1.2.1
|
||||||
- chardet==3.0.4
|
- chardet==3.0.4
|
||||||
- ccxt==1.10.1094
|
- ccxt==1.10.1094
|
||||||
- web3==4.0.0b7
|
# The Enigma Data Marketplace requires Python3 because it depends on
|
||||||
|
# web3, which requires Python3, as building its dependencies breaks in Python2
|
||||||
|
# - web3==4.0.0b7
|
||||||
- requests-toolbelt==0.8.0
|
- requests-toolbelt==0.8.0
|
||||||
- click==6.7
|
- click==6.7
|
||||||
- contextlib2==0.5.5
|
- contextlib2==0.5.5
|
||||||
|
|||||||
@@ -84,5 +84,5 @@ tables==3.3.0
|
|||||||
ccxt==1.10.1094
|
ccxt==1.10.1094
|
||||||
boto3==1.4.8
|
boto3==1.4.8
|
||||||
redo==1.6
|
redo==1.6
|
||||||
web3==4.0.0b7
|
web3==4.0.0b11; python_version > '3.4'
|
||||||
requests-toolbelt==0.8.0
|
requests-toolbelt==0.8.0
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
web3==4.0.0b7
|
|
||||||
requests-toolbelt==0.8.0
|
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
from catalyst.exchange.utils.exchange_utils import transform_candles_to_df, \
|
||||||
|
forward_fill_df_if_needed, get_candles_df
|
||||||
|
|
||||||
|
from catalyst.testing.fixtures import WithLogger, ZiplineTestCase
|
||||||
|
from datetime import timedelta
|
||||||
|
from pandas import Timestamp, DataFrame, concat
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class TestExchangeUtils(WithLogger, ZiplineTestCase):
|
||||||
|
@classmethod
|
||||||
|
def get_specific_field_from_df(cls, df, field, asset):
|
||||||
|
new_df = DataFrame(df[field])
|
||||||
|
new_df.columns = [asset]
|
||||||
|
new_df.index.name = None
|
||||||
|
return new_df
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify_forward_fill_df_if_needed(cls, candles, periods, expected_df):
|
||||||
|
observed_df = forward_fill_df_if_needed(
|
||||||
|
transform_candles_to_df(candles),
|
||||||
|
periods)
|
||||||
|
assert (expected_df.equals(observed_df))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def verify_get_candles_df(cls, assets, candles, end_fixed_dt,
|
||||||
|
expected_df, check_next_candle=False):
|
||||||
|
# run on all the fields
|
||||||
|
for field in ['volume', 'open', 'close', 'high', 'low']:
|
||||||
|
|
||||||
|
field_dt = cls.get_specific_field_from_df(expected_df,
|
||||||
|
field,
|
||||||
|
assets[0])
|
||||||
|
# run on several timestamps
|
||||||
|
for delta in range(5):
|
||||||
|
end_dt = end_fixed_dt + timedelta(minutes=delta)
|
||||||
|
assert (field_dt.equals(get_candles_df({assets[0]: candles},
|
||||||
|
field, '5T', 3,
|
||||||
|
end_dt=end_dt)))
|
||||||
|
|
||||||
|
field_dt_a1 = cls.get_specific_field_from_df(expected_df,
|
||||||
|
field,
|
||||||
|
assets[0])
|
||||||
|
field_dt_a2 = cls.get_specific_field_from_df(expected_df,
|
||||||
|
field,
|
||||||
|
assets[1])
|
||||||
|
observed_df = get_candles_df({assets[0]: candles,
|
||||||
|
assets[1]: candles},
|
||||||
|
field, '5T', 3,
|
||||||
|
end_dt=end_dt)
|
||||||
|
|
||||||
|
assert (observed_df.equals(concat([field_dt_a1, field_dt_a2],
|
||||||
|
axis=1)))
|
||||||
|
|
||||||
|
if check_next_candle:
|
||||||
|
# one candle forward
|
||||||
|
end_dt = end_fixed_dt + timedelta(minutes=6)
|
||||||
|
observed_df = get_candles_df({assets[0]: candles,
|
||||||
|
assets[1]: candles},
|
||||||
|
field, '5T', 3,
|
||||||
|
end_dt=end_dt)
|
||||||
|
|
||||||
|
assert (not observed_df.equals(concat([field_dt_a1,
|
||||||
|
field_dt_a2],
|
||||||
|
axis=1)))
|
||||||
|
assert (concat([field_dt_a1, field_dt_a2],
|
||||||
|
axis=1)[1:].equals(observed_df[:-1]))
|
||||||
|
|
||||||
|
def test_get_candles_df(self):
|
||||||
|
assets = ['btc_usdt', 'eth_usdt']
|
||||||
|
|
||||||
|
# test forward fill in the end
|
||||||
|
candles = [{'high': 595, 'volume': 10, 'low': 594,
|
||||||
|
'close': 595, 'open': 594,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:45:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 594, 'volume': 108, 'low': 592,
|
||||||
|
'close': 593, 'open': 592,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:50:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
}]
|
||||||
|
|
||||||
|
expected = [{'high': 595.0, 'volume': 10.0, 'low': 594.0,
|
||||||
|
'close': 595.0, 'open': 594.0,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:45:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 594.0, 'volume': 108.0, 'low': 592.0,
|
||||||
|
'close': 593.0, 'open': 592.0,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:50:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 593.0, 'volume': 0.0, 'low': 593.0,
|
||||||
|
'close': 593.0, 'open': 593.0,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:55:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
}]
|
||||||
|
|
||||||
|
periods = [Timestamp('2018-03-01 09:45:00+0000', tz='UTC'),
|
||||||
|
Timestamp('2018-03-01 09:50:00+0000', tz='UTC'),
|
||||||
|
Timestamp('2018-03-01 09:55:00+0000', tz='UTC')]
|
||||||
|
|
||||||
|
expected_df = transform_candles_to_df(expected)
|
||||||
|
|
||||||
|
self.verify_forward_fill_df_if_needed(candles, periods,
|
||||||
|
expected_df)
|
||||||
|
self.verify_get_candles_df(assets, candles, periods[2],
|
||||||
|
expected_df, True)
|
||||||
|
|
||||||
|
# test forward fill in the middle
|
||||||
|
candles = [{'high': 595, 'volume': 10, 'low': 594,
|
||||||
|
'close': 595, 'open': 594,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:45:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 594, 'volume': 108, 'low': 592,
|
||||||
|
'close': 593, 'open': 592,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:55:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
}]
|
||||||
|
|
||||||
|
expected = [{'high': 595.0, 'volume': 10.0, 'low': 594.0,
|
||||||
|
'close': 595.0, 'open': 594.0,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:45:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 595.0, 'volume': 0.0, 'low': 595.0,
|
||||||
|
'close': 595.0, 'open': 595.0,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:50:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 594.0, 'volume': 108.0, 'low': 592.0,
|
||||||
|
'close': 593.0, 'open': 592.0,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:55:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
}]
|
||||||
|
|
||||||
|
expected_df = transform_candles_to_df(expected)
|
||||||
|
self.verify_forward_fill_df_if_needed(candles, periods, expected_df)
|
||||||
|
self.verify_get_candles_df(assets, candles, periods[2], expected_df)
|
||||||
|
|
||||||
|
# test "forward fill" at the beginning
|
||||||
|
candles = [{'high': 595, 'volume': 10, 'low': 594,
|
||||||
|
'close': 595, 'open': 594,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:50:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 594, 'volume': 108, 'low': 592,
|
||||||
|
'close': 593, 'open': 592,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:55:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
}]
|
||||||
|
|
||||||
|
expected = [{'high': np.NaN, 'volume': 0.0, 'low': np.NaN,
|
||||||
|
'close': np.NaN, 'open': np.NaN,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:45:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 595, 'volume': 10, 'low': 594,
|
||||||
|
'close': 595, 'open': 594,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:50:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
},
|
||||||
|
{'high': 594, 'volume': 108, 'low': 592,
|
||||||
|
'close': 593, 'open': 592,
|
||||||
|
'last_traded': Timestamp('2018-03-01 09:55:00+0000',
|
||||||
|
tz='UTC')
|
||||||
|
}]
|
||||||
|
|
||||||
|
expected_df = transform_candles_to_df(expected)
|
||||||
|
self.verify_forward_fill_df_if_needed(candles, periods, expected_df)
|
||||||
|
# Not the same due to dropna - commenting out for now
|
||||||
|
# self.verify_get_candles_df(assets, candles, periods[2], expected_df)
|
||||||
@@ -107,14 +107,14 @@ class TestSuiteBundle:
|
|||||||
print('saved {} test results: {}'.format(end_dt, folder))
|
print('saved {} test results: {}'.format(end_dt, folder))
|
||||||
|
|
||||||
assert_frame_equal(
|
assert_frame_equal(
|
||||||
right=data['bundle'],
|
right=data['bundle'][:-1],
|
||||||
left=data['exchange'],
|
left=data['exchange'][:-1],
|
||||||
check_less_precise=1,
|
check_less_precise=1,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
assert_frame_equal(
|
assert_frame_equal(
|
||||||
right=data['bundle'],
|
right=data['bundle'][:-1],
|
||||||
left=data['exchange'],
|
left=data['exchange'][:-1],
|
||||||
check_less_precise=min([a.decimals for a in assets]),
|
check_less_precise=min([a.decimals for a in assets]),
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user