import random
from datetime import datetime
1. 要会见binance API,只需从呼吁行pip install binance client。其他依赖项安装在其各自的导入行上注释。
cols = [‘time’,’Open’,’High’,’Low’,f'{ratio}-USD_close’,f'{ratio}-USD_volume’,’CloseTime’,’QuoteAssetVolume’,’NumberOfTrades’,’TBBAV’,’TBQAV’,’null’]
Memory
self.clear()
#log after each episode
2. binance客户端请求API密钥时,这些密钥仅对付将生意业务订单及时发送到币安生意业务所是必须的,而对付读取数据则不是必须的。我保存了名目,以防这是任何读者所期望的用例。
self.iloc+=1
class Env:
data = client.get_historical_klines(symbol=f'{ratio}USDT’,interval=Client.KLINE_INTERVAL_1MINUTE,start_str=START_TIME,end_str=END_TIME)
for ratio in self.ratios:
self.balances[‘USD’] = (self.balances[self.money_in]*self.episode_df[f'{self.money_in}-USD_close’][self.iloc])*TRADING_FEE_MULTIPLIER
self.start_time = self.episode_df[‘time’].iloc[0]
在我看来,利用我们在这个例子中的情况是一个很好的要领,可以将动态算法生意业务先容给像我以前一样(也许此刻照旧我此刻的本身)劈面向工具编程常识有限的人。就我小我私家而言,我利用这个东西往返溯测试很多差异的计策,这些计策包括了价值数据的更多特性,还答允一些附加成果;即可以或许将加密钱币生意业务(譬喻BTC/LTC对等),而不只仅是加密钱币对美元,反之亦然。 self.money_in = ‘USD’
env.reset()
print(f’average market change: {average_market_change}’)
def __init__(self, ratios, df):
memory.clear()
df.to_csv(’12-coins-Mar18_Jun20′)
average_market_change_collect = []#buy that ratio (self.to_buy)
self.balances[‘USD’] = 0.0
for i_episode in range(NUM_EPISODES):
api_secret=”
self.average_market_change = self.average_start_price / self.average_end_price
此刻,我不确定是否有人相信这个计策真的有效(在某些环境下大概是这样),可是为了演示回溯测试情况(而且因为实现更巨大的计策将从修改您本身的想法中去除一些兴趣),让我们思量一下。如下面的代码所示,我们有两个要害的if语句,这些语句查抄移动平均线变革并抉择是否记录买入或卖出(或都不记录)。需要熟悉的几个局部变量:money_in和to_buy。这些是字符串变量,用于跟踪您当前持有的资金以及购置开始时资金的去向。输入买入和卖出if语句后,这些变量将相应更新;因此,,余额会按照您当前持有的价值举办更新。请留意,每次交易都将相应的余额乘以TRADING_FEE_MULTIPLIER,在这种环境下为0.99925。该值基于利用BNB令牌(币安生意业务所可用的最自制的选择)付出用度时的0.075%币安生意业务用度,但可以按照您特定生意业务所的生意业务用度举办变动。
self.average_start_price += self.episode_df[f'{ratio}-USD_close’].iloc[0]
NUM_EPISODES = 100
if self.episode_df[f'{self.money_in}_{SMA_LOW}’][self.iloc] < self.episode_df[f'{self.money_in}_{SMA_HIGH}’][self.iloc]:
#——-CALCULATE PERFORMANCE METRICS HERE——-
MINUTES_PER_EPISODE = 1440*DAYS_PER_EPISODE
for ratio in ratios:
return sma
episode: 99
memory.add_to_memory(f’Sell {self.money_in}: {self.sell_price}’)
#convert binance timestamp to datetime
time.sleep(60) #sleep for a bit so the binance api doesn’t kick you out for too many data asks
self.average_start_price /= len(ratios)
TRADING_FEE_MULTIPLIER = .99925 #this is the trading fee on binance VIP level 0 if using BNB to pay fees
self.net_worth += self.balances[ratio]*self.episode_df[f'{ratio}-USD_close’][self.iloc]
average market change: 0.9984598063740531
#——-CALCULATE PERFORMANCE METRICS HERE——-
df = temp_df
DAYS_PER_EPISODE = 1
self.reset()
net_worth_collect.append(net_worth)
我将这种要领分为两部门:实施生意业务计策和计较结果指标。
merge = True
self.actions = []
print(f’Gathering {ratio} data…’)
self.average_start_price = 0
关于数据读取的一些值得留意的工作:
print(‘complete’)
def clear(self):
break
#if low sma crosses above high sma
收集数据
client = Client(api_key=api_key,api_secret=api_secret)
self.sell_price = self.episode_df[f'{self.money_in}-USD_close’][self.iloc]
SMA_LOW = 40
net worth after 1 day(s): 1.0221672847272814
for i in range(len(env.episode_df)-1):
def reset(self):
self.average_end_price += self.episode_df[f'{ratio}-USD_close’].iloc[-1]
df[col] = df[col].astype(np.float64)
SMA_HIGH = 150
在此示例中,我没有利用更深入的技能阐明接头中大概碰着的更巨大的生意业务计策,而是要实验我们在网上看到的一种常见计策的有效性:移动平均线交错计策。假如你不熟悉,这个想法的要点是,当一个短窗口移动平均线越过一个较长的窗口移动平均线时→买入;当短窗口移动平均线回到较长窗口移动平均线以下时→卖出。
#——-IMPLEMENT STRATEGY HERE——–
def compute_sma(data, window):
df = df[SMA_HIGH:]
memory = Memory()
我们的情况尚有另两个要领:reset()和step()。
for col in df.columns:
print(f’net worth average after {NUM_EPISODES} backtest episodes: {np.mean(net_worth_collect)}’)
太好了,此刻我们有了一个复杂的加密钱币价值数据集。在进入生意业务情况之前,让我们建设一个简朴的memory 类,这样我们可以存储关于每个回测事件的信息。
[‘Buy XRP: 0.30917’, ‘Sell XRP: 0.31171’, ‘Buy BTC: 9641.57’, ‘Sell BTC: 9635.6’, ‘Buy TRX: 0.02252’, ‘Sell TRX: 0.02238’, ‘Buy XLM: 0.08462’, ‘Sell XLM: 0.08457’, ‘Buy LINK: 2.2158’, ‘Sell LINK: 2.211’, ‘Buy EOS: 4.2811’, ‘Sell EOS: 4.2527’, ‘Buy ETH: 212.07’, ‘Sell ETH: 211.26’, ‘Buy IOTA: 0.2835’, ‘Sell IOTA: 0.2833’, ‘Buy XLM: 0.08375’, ‘Sell XLM: 0.08317999999999999’, ‘Buy LTC: 89.2’, ‘Sell LTC: 89.34’, ‘Buy XMR: 79.39’, ‘Sell XMR: 79.15’, ‘Buy BTC: 9570.01’, ‘Sell BTC: 9548.48’, ‘Buy LINK: 2.1819’, ‘Sell LINK: 2.1595’, ‘Buy LTC: 89.33’, ‘Sell LTC: 89.4’, ‘Buy EOS: 4.1803’, ‘Sell EOS: 4.1903’, ‘Buy BTC: 9540.07’, ‘Sell BTC: 9559.46’, ‘Buy ETH: 210.83’, ‘Sell ETH: 208.78’, ‘Buy LTC: 90.68’, ‘Sell LTC: 90.39’, ‘Buy LTC: 90.76’, ‘Sell LTC: 90.44’, ‘Buy XRP: 0.31008’, ‘Sell XRP: 0.30991’, ‘Buy BTC: 9504.64’, ‘Sell BTC: 9490.15’, ‘Buy ETH: 209.7’, ‘Sell ETH: 209.77’, ‘Buy IOTA: 0.2853’]
if merge == False:
在我们有了数据和Memory工具,让我们开始进入生意业务情况。
print(f’episode: {i_episode}’)
我们做得怎么样?
net worth average after 100 backtest episodes: 0.9656146271760888
#Running net worth
for ratio in self.ratios:
print(memory.actions)
self.balances = {‘USD’:1.0}
api_key=”
df[f'{ratio}_{SMA_LOW}’] = compute_sma(df[f'{ratio}-USD_close’], SMA_LOW)
在启动情况时(在__init__中),将挪用reset()要领。这将在第一次挪用Env工具时配置所有情况变量。
self.average_end_price = 0
self.episode_df = self.main_df[self.iloc:self.iloc+MINUTES_PER_EPISODE+2]
self.end_time = self.episode_df[‘time’].iloc[-1]
#sell money_in/USD
df[f'{ratio}_{SMA_HIGH}’] = compute_sma(df[f'{ratio}-USD_close’], SMA_HIGH)
self.ratios = ratios
df = pd.read_csv(’12-coins-Mar18_Jun20′)
self.actions.append(new_action)
下面是一个生意业务情况,在这个情况中,所有大概的生意业务计策都可以以一种很是动态的方法举办测试,纵然是初学者python措施员也可以建设和回溯测试本身的生意业务想法,并最终办理他们的疑问。利用情况并实现您本身的想法的独一先决条件是:对python布局(如for轮回和if语句)有根基的相识,对pandas有足够的相识,以便从dataframe的正确列/索引位置获取适当的价值dataframe,大概劈面向工具编程有较低条理的领略,这取决于您的计策的巨大性。
self.balances[self.money_in] = 0.0
#——-IMPLEMENT STRATEGY HERE——–
else:
from binance.client import Client #pip install python-binance
ratios = [‘BTC’,’ETH’,’LTC’,’XLM’,’XRP’,’XMR’,’TRX’,’LINK’,’IOTA’,’EOS’,’DASH’,’ZRX’]
sma = data.rolling(window=window).mean()
interval: 2019-07-28 16:39:00 – 2019-07-29 16:40:00
if col != ‘time’:
self.average_end_price /= len(ratios)
[‘Buy BTC: 5255.99’, ‘Sell BTC: 5227.76’, ‘Buy ETH: 155.86’, ‘Sell ETH: 155.41’, ‘Buy LTC: 67.46’, ‘Sell LTC: 67.45’, ‘Buy IOTA: 0.3167’, ‘Sell IOTA: 0.3176’, ‘Buy IOTA: 0.3167’, ‘Sell IOTA: 0.3151’, ‘Buy LTC: 67.67’, ‘Sell LTC: 67.42’, ‘Buy XMR: 61.38’, ‘Sell XMR: 61.59’, ‘Buy EOS: 4.5225’, ‘Sell EOS: 4.5076’, ‘Buy ZRX: 0.2743’, ‘Sell ZRX: 0.2723’, ‘Buy ETH: 155.2’, ‘Sell ETH: 156.98’, ‘Buy XLM: 0.09605’, ‘Sell XLM: 0.09597’, ‘Buy LINK: 0.439’, ‘Sell LINK: 0.439’, ‘Buy LINK: 0.4418’, ‘Sell LINK: 0.4672’, ‘Buy BTC: 5308.08’, ‘Sell BTC: 5304.94’, ‘Buy XLM: 0.09966’, ‘Sell XLM: 0.09965’, ‘Buy XMR: 62.29’, ‘Sell XMR: 62.22’, ‘Buy TRX: 0.02312’, ‘Sell TRX: 0.02307’, ‘Buy LTC: 72.82’]
#if high sma crosses below low sma
average, average market change over 100 episodes: 1.0021440510159623
self.money_in = self.to_buy
import pandas as pd #pip install pandas
self.balances[ratio] = 0.0
在这里,我们一次一分钟地遍历情况,凭据计策的指示举办交易,并跟踪机能指标以及将其输出到日志的进程。
3. 在收集了每个代币的数据之后,我们需要让剧本遏制运行60秒。这是为了防备binance API因为我们请求太多的数据而把我们踢出去。我今朝没有碰着过这种问题,但假如碰着错误,这个时间大概需要增加。
#Net_worth had you owned all ratios over episode_df –> ‘average_market_change’
merge = False
Env.step()要领
for ratio in self.ratios:
机能指标
interval: 2019-04-29 12:25:00 – 2019-04-30 12:26:00
def add_to_memory(self, new_action):
#log overall
请留意,每次重置城市占用主要dataframe的一小部门,并将其称为“ episode_df”,而不是在回测期间遍历整个600,000多个数据实例。这是该计策将针对每个周期举办回测的时距离断。可以利用常量MINUTES_PER_EPISODE或DAYS_PER_EPISODE来指定此周期dataframe的一连时间。在此示例中,我们利用1天或1440分钟的周期巨细。
self.buy_price = self.episode_df[f'{self.to_buy}-USD_close’][self.iloc]
return self.net_worth, self.average_market_change, self.start_time, self.end_time
net_worth, average_market_change, start_time, end_time = env.step()
for ratio in self.ratios:
self.main_df = df
temp_df = pd.DataFrame(data,columns=cols)
if self.money_in == ‘USD’:
显示的是100会合仅有两期间的输出。为了简捷起见,我选择了一个有利可图的期间(96个)和一个不赚钱的期间(99个)来显示,而实际输出显示所有100期间的功效。不外我们真正体贴的不是单个事件的功效,而是问题:“我的计策平均表示如何?”. 这将显示在最终平均事件日志中。
print(f’net worth after {DAYS_PER_EPISODE} day(s): {net_worth}’)
if self.money_in != ‘USD’: #can’t sell if money_in usd
df = pd.merge(df,temp_df,how=’inner’,on=’time’)
self.to_buy = ratio
class Memory:
执行情况
#Yes, average of the average market changes
average market change: 0.988959335567251
#select cryptocurrencies you’d like to gather and time interval
from tqdm import tqdm #pip install tqdm
5. 卷数据也可用,但在本例中未利用。假如您感乐趣,请将其添加到第32行。
for ratio in ratios:
def step(self):
这是另一个无需过多表明的项目:它只是将生意业务情况重置为其默认状态。这样做的目标是在运行多个周期的回测时,我们将在每个周期之后挪用env.reset()来重置所有局部变量,譬喻代币余额,周期dataframe的索引位置 跟踪您的特定计策以及大概对日志记录有用的任何其他信息。情况的默认状态是USD余额为1.0,每种加密钱币的余额为0.0。有关reset()要领的一些重要留意事项:
其他一些想法
self.iloc = random.randint(0,len(self.main_df)-MINUTES_PER_EPISODE-1)
python日志记录包在这里对付运行很多差异计策并登录到单独文件以较量功效的人大概有用。
首先读取数据。在此剧本中,我们利用了binance API,该API答允从生意业务所中列出的各类中收集数据。给出的示例是一个单独的剧本,从2019年3月28日到2020年6月1日,读取12种差异加密钱币的每种加密钱币的价值变革,总共618290个实例的价值,从而发生211 MB的dataframe。在我的呆板上,这个从binance API读取的进程约莫需要5个小时才气完成,可是通过一个简朴的csv写操纵,在今后利用剧本时险些可以当即检索到。
df[‘time’][i] = datetime.fromtimestamp(int(df[‘time’][i]/1000))
if self.episode_df[f'{ratio}_{SMA_LOW}’][self.iloc] > self.episode_df[f'{ratio}_{SMA_HIGH}’][self.iloc] and self.episode_df[f'{ratio}_{SMA_LOW}’][self.iloc-1] > self.episode_df[f'{ratio}_{SMA_HIGH}’][self.iloc-1]:
这个类很容易表明;它就是一个简朴的Memory工具,它答允我们附加购置/出售操纵,还可以在每周期之后排除Memory。
for i in tqdm(range(len(df))):
END_TIME = ‘1 Jun, 2020’
temp_df = temp_df[[‘time’,f'{ratio}-USD_close’]]
print(‘\n’)
print(‘\n’)
我们将利用两个指标来评预计策在回测隔断内的功效:回测隔断后的资产净值和回测隔断的平均市场变革。您的余额净值是按照您持有的钱币的当前价值和您所持钱币的金额将您持有的资产转换为美元来计较的。区间的平均市场变革是通过计较事件开始和竣事时所有选定钱币的价值平均值,然后将这些值相除得出的。基于这些指标,最终方针是以比拥有平均市场份额时要高的净资产来完本钱集(显然要比开始时拥有的净资产高)。
net worth after 1 day(s): 0.9218801235080804
print(f’interval: {start_time} – {end_time}’)
average_market_change_collect.append(average_market_change)
memory.add_to_memory(f’Buy {self.to_buy}: {self.buy_price}’)
我们的情况回收两个参数:我们方才建设的dataframe以及您但愿在回溯测试进程中包括的钱币比率(譬喻“BTC”、“LTC”等)。
df = df.reset_index(drop=True)
self.net_worth = self.balances[‘USD’]
self.money_in = ‘USD’
net_worth_collect = []
print(f’average, average market change over {NUM_EPISODES} episodes: {np.mean(average_market_change_collect)}’)
env = Env(ratios, df)
episode: 96
最终显示日生意业务是具有很是大的风险。我但愿阅读此文的人会发明此东西有助于摸索他们的生意业务思路,使他们可以或许在将其计策推向市场之前能本身对决定举办摸索。对编程和算法生意业务常识有限的人来说,好像缺乏资源来做出明智的生意业务决定;
self.balances[self.to_buy] = (self.balances[‘USD’]/self.episode_df[f'{self.to_buy}-USD_close’][self.iloc])*TRADING_FEE_MULTIPLIER
START_TIME = ’28 Mar, 2019′
4. 这里包罗一个简朴的移动平均函数,它展示了如何从价值数据中设计特性并将其添加到dataframe中的示例。这可以是您选择的自界说函数,也可以只是一个在网上找到的通用指示器的函数,如图所示。
计策
6. csv文件的自界说定名可以在第72行的引号中编辑。
#clip NaNs
import numpy as np #pip install numpy
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。