图像接缝剪裁SeamCarving的实现

源码 https://github.com/Gaobx/SeamCarving

@Author : ydk & gaojx

内容

  1. 使用seam-carving实现图像resize
  2. 使用seam-carving实现带保护protect的resize
  3. 使用seam-carving实现物体remove
  4. 人脸识别,进而对人脸进行保护
  5. 研究不同能量函数完成的seam-carving效果

原理

  1. 使用事先定义好的能量函数,针对图像上每个像素点计算其对应能力值,然后使用动态规划算法寻找当前图片中能量最小的一条线,根据所需要实现功能的不同进行不同操作。
  2. 采用了两个不同的能量函数,一种是论文使用的基于灰度扫描计算梯度的E1-Error。另一种是RGB-Error,即对R,G,B三个色彩通道分别计算梯度,求和作为能量。

步骤

  1. resize的删除部分
  2. resize的拉伸部分
  3. remove 标记(mask)

功能及结果

  1. 缩放效果图
  2. Remove效果图
  3. seams show
    • 我们通过记录seams_index,可以多次重放resize及remove的操作,并显示算法找到的能量线。
  4. face detection
  5. 指定区域保护(mask protect)
  6. 指定区域擦除(mask remove)
    • 根据mask图想的标记部分对指定图像进行remove
    • 指定矩形框内的部分进行remove
  7. 软件截图

参考文献

\[1\] Daniel Vaquero, et al. "A survey of image retargeting techniques," Proc. SPIE 7798, Applications of Digital Image Processing XXXIII, 779814 (7 September 2010).

源码参考

  1. https://github.com/vivianhylee/seam-carving

使用Sklearn训练模型预测台风路径

运用Sklearn的多种线性回归策略提高台风路径预测的准确性


内容

  • 使用Sklearn中线性回归、岭回归、ElasticNet回归以及Lasso回归模型对台风路径数据进行训练、预测,进行误差分析,并比较各个模型的优劣。
  • 比较训练数据量对结果准确性的影响。
  • 找出预测台风路径最优的回归模型,并使用模型对目标数据进行预测。

数据描述

台风训练数据typhoon.dat, 共计500条,共18列,前16列为预报因子,最后2列分别是经纬度。 每行数据是间隔12个小时采集到的台风预报因子,数据的任务是预测台风的路径,即预测台风的经纬度,最后两列的值。 LON .V1: 起报时刻经度 lon+00 LON .V2: 起报时刻前12小时纬向移速 vlat-12 LON .V3: 起报时刻前24小时纬向移速 vlat-24(可能是经向) LON .V4: 起报时刻前24 小时所在经度 lon-24 LON .V5: 起报时刻前12 小时至前24小时纬向移速 vlat-24 LON .V6: 起报时刻与前12小时的经度差 (lon+00)-(lon-12) LON .V7: 起报时刻前6 小时所在经度 lon-06 LON .V8: 起报时刻前18 小时所在经度 lon-18 Lat.v9: 起报时刻纬度 lat+00 Lat.v10: 起报时刻前12小时经向移速 vlon-12 Lat.v11: 起报时刻前24小时经向移速的平方 (vlon-24)^2 Lat.v12: 起报时刻前12 小时所在纬度 lat-12 Lat.v13: 起报时刻与前24小时的经度差 (lon+00)-(lon-24) Lat.v14: 起报时刻前6 小时所在纬度 lat-06 Lat.v15: 起报时刻前18 小时所在纬度 lat-18 Lat.v16: 起报时刻前6 小时地面附近最大风速 wind-06 LON . t: 要预报的24小时后的经度(预报量) lon+24 Lat.t: 要预报的24小时后的纬度(预报量) lat+24 台风测试数据为 typhoon200Test.dat,共计200条,每条有16列预报因子,与训练数据的前16列数据格式顺序相同,但是没有最后的两列经纬度数据。 测试的任务就是对这200条台风数据预测每条记录的经度和纬度值。

分析

(分析过程代码见最后)

首先分析一下各个回归模型训练数据量对预测结果准确性的影响。

上图是训练样本量对各个模型预测结果与真实结果的距离误差的影响。其中,横坐标是训练数据集的大小,纵坐标是真实的经纬度与预测的经纬度之间的距离。 由图可见,随着训练数据集中数据数量的增多,预测误差是逐渐减小的。 上图是训练样本量对各个模型预测结果与真实结果的经纬度误差的影响。其中,横坐标是训练数据集的大小,纵坐标是真实的经纬度与预测的经纬度之间的误差大小。 由图亦可看出,随着训练数据集中数据数量的增多,预测误差是逐渐减小的。并且各个模型预测纬度的误差要小于对经度进行预测的误差。

使用50到300个预测样本分析不同的回归模型对台风路径预测误差的影响。

上图是截取了训练样本数据量在50到300时预测的距离误差随训练数据量的变化趋势。 由图可见,在训练样本数据量稍大的情况下,岭回归与线性回归对台风路径的预测的误差较小。 上图是截取了训练样本数据量在50到300时预测的经纬度误差随训练数据量的变化趋势。 由图可见,总体来说,各个回归模型对台风路径纬度的预测误差要小于对经度的预测误差。 在训练样本数据量稍大的情况下,岭回归对台风路径经度的预测的误差相对较小,线性回归对台风路径纬度的预测的误差相对较小。


由上述分析可知,在此数量级的训练样本下,线性回归对纬度的预测要较为准确,而岭回归对经度的预测较为准确。所以,可以对台风路径的经纬度分别使用这两种回归模型进行预测。 上图是训练样本量对预测经纬度与真实经纬度之间相关系数的影响。由图可见,在数据量为150到300之间时,岭回归对经度的预测结果与真实结果相关性最强,线性回归对纬度的预测结果与真实结果相关性最强。 所以在此实验中,可以使用岭回归来预测台风路径的经度值,使用线性回归来预测台风路径的纬度值。

结论

上图中红线是使用岭回归来预测台风路径的经度值,使用线性回归来预测台风路径的纬度值,进而得到的距离误差。 可见,采用此方法预测在数据量大于150时要比其他方法准确。 所以,本实验最终使用岭回归来预测台风路径的经度值,使用线性回归来预测台风路径的纬度值,对200条测试数据进行预测。

预测结果

typhoon200Results.dat

预测代码

from __future__ import division
import os, sys
import numpy as np
# from numpy import
from sklearn import linear_model
from math import

import pandas as pd
from pandas import DataFrame
from pandas_datareader import data
import pandas_datareader as pdr

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl

mpl.rcParams[‘font.sans-serif’] = [‘SimHei’]
mpl.rcParams[‘font.serif’] = [‘SimHei’]
import seaborn as sns
sns.set_style(“darkgrid”,{“font.sans-serif”:[‘simhei’, ‘Arial’]})

train_num = 50

#加载数据
def loadDataSet(fileName): #general function to parse tab -delimited floats
numFeat = len(open(fileName).readline().split(‘\t’)) - 2 #get number of fields
dataArr = []
lonArr = []
latArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
lineArr.append(1.0) # 增加这一列是为了进行线性回归,存放常量x0
curLine = line.strip().split(‘\t’)
for i in range(numFeat):
lineArr.append(float(curLine[i]))
dataArr.append(lineArr)
lonArr.append(float(curLine[-2])) # 倒数第二项是lon经度
latArr.append(float(curLine[-1])) # 最后一项是lat纬度
return dataArr,lonArr, latArr

# 计算误差
def absError(yArr,yHatArr): #yArr and yHatArr both need to be arrays
return abs(yArr-yHatArr).sum() / len(yArr)

# 加载数据,分隔为train和test
dataArr,lonArr,latArr= loadDataSet(‘typhoon.dat’)

#加载测试数据
def loadTestDataSet(fileName): #general function to parse tab -delimited floats
numFeat = len(open(fileName).readline().split(‘\t’)) #get number of fields
dataArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
lineArr.append(1.0) # 增加这一列是为了进行线性回归,存放常量x0
curLine = line.strip().split(‘\t’)
for i in range(numFeat):
lineArr.append(float(curLine[i]))
dataArr.append(lineArr)
return dataArr

trainX=dataArr[0:500]; lonTrainY=lonArr[0:500]; latTrainY=latArr[0:500]
# testX=dataArr[300:]; lonTestY=lonArr[300:]; latTestY=latArr[300:]
testX=loadTestDataSet(‘typhoon200Test.dat’)

# 构建模型
clf = linear_model.LinearRegression() # 线性回归
clf1 = linear_model.Ridge (alpha = 0.8) # 岭回归
# clf2 = linear_model.ElasticNet(alpha = 1.0) # ElasticNet线性
# clf3 = linear_model.Lasso()

clf.fit (trainX, lonTrainY) # 训练经度模型
lonyHat = clf.predict(testX) # 使用经度模型去预测经度
clf.fit (trainX, latTrainY) # 训练纬度模型
latyHat = clf.predict(testX) # 使用纬度模型去预测纬度
# # 经度误差
# absErrorLon = absError(lonTestY,lonyHat)
# # 纬度误差
# absErrorLat = absError(latTestY,latyHat)
# absErrorLen = 110 sqrt(absErrorLon\*2 + absErrorLat**2)

clf1.fit (trainX, lonTrainY) # 训练经度模型
lonyHat1 = clf1.predict(testX) # 使用经度模型去预测经度
clf1.fit (trainX, latTrainY) # 训练纬度模型
latyHat1 = clf1.predict(testX) # 使用纬度模型去预测纬度

# print(lonyHat1)
# print(latyHat)

data = []

for i in range(0, 200):
data1 = []
data1.append(lonyHat1[i])
data1.append(latyHat[i])
data.append(data1)

df = DataFrame(data)
df.to_csv(‘typhoon200Results.dat’, sep=’\t’, header=False, index=False)

# # 经度误差
# absErrorLon1 = absError(lonTestY,lonyHat1)
# # 纬度误差
# absErrorLat1 = absError(latTestY,latyHat1)
# absErrorLen1 = 110 sqrt(absErrorLon1\*2 + absErrorLat1**2)

# absErrorLen3333 = 110 sqrt(absErrorLon1\*2 + absErrorLat**2)

# print( absErrorLen, absErrorLen1, absErrorLen3333)

分析过程代码

from __future__ import division
import os, sys
import numpy as np
# from numpy import
from sklearn import linear_model
from math import

import pandas as pd
from pandas import DataFrame
from pandas_datareader import data
import pandas_datareader as pdr

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl

mpl.rcParams[‘font.sans-serif’] = [‘SimHei’]
mpl.rcParams[‘font.serif’] = [‘SimHei’]
import seaborn as sns
sns.set_style(“darkgrid”,{“font.sans-serif”:[‘simhei’, ‘Arial’]})

train_num = 50

#加载数据
def loadDataSet(fileName): #general function to parse tab -delimited floats
numFeat = len(open(fileName).readline().split(‘\t’)) - 2 #get number of fields
dataArr = []
lonArr = []
latArr = []
fr = open(fileName)
for line in fr.readlines():
lineArr =[]
lineArr.append(1.0) # 增加这一列是为了进行线性回归,存放常量x0
curLine = line.strip().split(‘\t’)
for i in range(numFeat):
lineArr.append(float(curLine[i]))
dataArr.append(lineArr)
lonArr.append(float(curLine[-2])) # 倒数第二项是lon经度
latArr.append(float(curLine[-1])) # 最后一项是lat纬度
return dataArr,lonArr, latArr

# 计算误差
def absError(yArr,yHatArr): #yArr and yHatArr both need to be arrays
return abs(yArr-yHatArr).sum() / len(yArr)

# 加载数据,分隔为train和test
dataArr,lonArr,latArr= loadDataSet(‘typhoon.dat’)

data_all = {‘data size’:[], ‘线性回归’:[], ‘岭回归’:[], ‘ElasticNet回归’:[], ‘Lasso回归’:[]}
data_LonLat_Err = {‘data size’:[], ‘线性回归Lon’:[], ‘岭回归Lon’:[], ‘ElasticNet回归Lon’:[],
‘Lasso回归Lon’:[],
‘线性回归Lat’:[], ‘岭回归Lat’:[], ‘ElasticNet回归Lat’:[],’Lasso回归Lat’:[]}
data_LonLat_corr = {‘data size’:[], ‘线性回归Lon’:[], ‘岭回归Lon’:[], ‘ElasticNet回归Lon’:[],
‘Lasso回归Lon’:[],
‘线性回归Lat’:[], ‘岭回归Lat’:[], ‘ElasticNet回归Lat’:[],’Lasso回归Lat’:[]}
for train_num in range(50, 300, 1):
trainX=dataArr[0:train_num]; lonTrainY=lonArr[0:train_num]; latTrainY=latArr[0:train_num]
testX=dataArr[300:]; lonTestY=lonArr[300:]; latTestY=latArr[300:]

data\_all\['data size'\].append(train\_num)
data\_LonLat\_Err\['data size'\].append(train_num)
data\_LonLat\_corr\['data size'\].append(train_num)

# 构建模型
clf = linear_model.LinearRegression() # 线性回归
clf1 = linear_model.Ridge (alpha = 0.8) # 岭回归
clf2 = linear_model.ElasticNet(alpha = 1.0) # ElasticNet线性
clf3 = linear_model.Lasso()


clf.fit (trainX, lonTrainY) # 训练经度模型
lonyHat = clf.predict(testX) # 使用经度模型去预测经度
clf.fit (trainX, latTrainY) # 训练纬度模型
latyHat = clf.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon = absError(lonTestY,lonyHat)
# 纬度误差
absErrorLat = absError(latTestY,latyHat)
absErrorLen = 110 * sqrt(absErrorLon\*\*2 + absErrorLat\*\*2)

data_all\['线性回归'\].append(absErrorLen)

clf1.fit (trainX, lonTrainY) # 训练经度模型
lonyHat1 = clf1.predict(testX) # 使用经度模型去预测经度
clf1.fit (trainX, latTrainY) # 训练纬度模型
latyHat1 = clf1.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon1 = absError(lonTestY,lonyHat1)
# 纬度误差
absErrorLat1 = absError(latTestY,latyHat1)
absErrorLen1 = 110 * sqrt(absErrorLon1\*\*2 + absErrorLat1\*\*2)

data_all\['岭回归'\].append(absErrorLen1)


clf2.fit (trainX, lonTrainY) # 训练经度模型
lonyHat2 = clf2.predict(testX) # 使用经度模型去预测经度
clf2.fit (trainX, latTrainY) # 训练纬度模型
latyHat2 = clf2.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon2 = absError(lonTestY,lonyHat2)
# 纬度误差
absErrorLat2 = absError(latTestY,latyHat2)
absErrorLen2 = 110 * sqrt(absErrorLon2\*\*2 + absErrorLat2\*\*2)

data_all\['ElasticNet回归'\].append(absErrorLen2)


clf3.fit (trainX, lonTrainY) # 训练经度模型
lonyHat3= clf3.predict(testX) # 使用经度模型去预测经度
clf3.fit (trainX, latTrainY) # 训练纬度模型
latyHat3 = clf3.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon3 = absError(lonTestY,lonyHat3)
# 纬度误差
absErrorLat3 = absError(latTestY,latyHat3)
absErrorLen3 = 110 * sqrt(absErrorLon3\*\*2 + absErrorLat3\*\*2)

data_all\['Lasso回归'\].append(absErrorLen3)

data\_LonLat\_Err\['线性回归Lon'\].append(absErrorLon)
data\_LonLat\_Err\['岭回归Lon'\].append(absErrorLon1)
data\_LonLat\_Err\['ElasticNet回归Lon'\].append(absErrorLon2)
data\_LonLat\_Err\['Lasso回归Lon'\].append(absErrorLon3)

data\_LonLat\_Err\['线性回归Lat'\].append(absErrorLat)
data\_LonLat\_Err\['岭回归Lat'\].append(absErrorLat1)
data\_LonLat\_Err\['ElasticNet回归Lat'\].append(absErrorLat2)
data\_LonLat\_Err\['Lasso回归Lat'\].append(absErrorLat3)

data\_LonLat\_corr\['线性回归Lon'\].append(np.corrcoef(lonyHat, lonArr\[300:\])\[0\]\[1\])
data\_LonLat\_corr\['岭回归Lon'\].append(np.corrcoef(lonyHat1, lonArr\[300:\])\[0\]\[1\])
data\_LonLat\_corr\['ElasticNet回归Lon'\].append(np.corrcoef(lonyHat2, lonArr\[300:\])\[0\]\[1\])
data\_LonLat\_corr\['Lasso回归Lon'\].append(np.corrcoef(lonyHat3, lonArr\[300:\])\[0\]\[1\])

data\_LonLat\_corr\['线性回归Lat'\].append(np.corrcoef(latyHat, latArr\[300:\])\[0\]\[1\])
data\_LonLat\_corr\['岭回归Lat'\].append(np.corrcoef(latyHat1, latArr\[300:\])\[0\]\[1\])
data\_LonLat\_corr\['ElasticNet回归Lat'\].append(np.corrcoef(latyHat2, latArr\[300:\])\[0\]\[1\])
data\_LonLat\_corr\['Lasso回归Lat'\].append(np.corrcoef(latyHat3, latArr\[300:\])\[0\]\[1\])



#计算距离公式: 110*sqrt(lon^2+lat^2)

# print(absErrorLen,absErrorLen1,absErrorLen2 )

# 计算经度预测值与真实值的相关系数
# print(np.corrcoef(lonyHat, lonArr\[400:\])\[0\]\[1\])
# 计算纬度预测值与真实值的相关系数
# print(np.corrcoef(latyHat, latArr\[400:\])\[0\]\[1\])

# print(data_all)
data_all = DataFrame(data_all)
data_all.set_index([‘data size’], inplace=True)
data_all.plot(kind=’line’, figsize=(12,8),
title=’训练样本量对预测距离误差的影响’)
plt.legend(bbox_to_anchor=(1.0, 1))

data_LonLat_Err = DataFrame(data_LonLat_Err)
data_LonLat_Err.set_index([‘data size’], inplace=True)
data_LonLat_Err.plot(kind=’line’, figsize=(12,8),
title=’训练样本量对预测经纬度误差的影响’)
plt.legend(bbox_to_anchor=(1.0, 1))

data_LonLat_corr = DataFrame(data_LonLat_corr)
data_LonLat_corr.set_index([‘data size’], inplace=True)
data_LonLat_corr.plot(kind=’line’, figsize=(12,8),
title=’训练样本量对预测经纬度与真实经纬度之间相关系数的影响’)
plt.legend(bbox_to_anchor=(1.0, 1))

data_all = {‘data size’:[], ‘线性回归’:[], ‘岭回归’:[], ‘ElasticNet回归’:[], ‘Lasso回归’:[], ‘线性-岭回归’:[]}
for train_num in range(50, 300, 1):
trainX=dataArr[0:train_num]; lonTrainY=lonArr[0:train_num]; latTrainY=latArr[0:train_num]
testX=dataArr[300:]; lonTestY=lonArr[300:]; latTestY=latArr[300:]

data\_all\['data size'\].append(train\_num)

# data_LonLat_Err[‘data size’].append(train_num)
# data_LonLat_corr[‘data size’].append(train_num)

# 构建模型
clf = linear_model.LinearRegression() # 线性回归
clf1 = linear_model.Ridge (alpha = 0.8) # 岭回归
clf2 = linear_model.ElasticNet(alpha = 1.0) # ElasticNet线性
clf3 = linear_model.Lasso()


clf.fit (trainX, lonTrainY) # 训练经度模型
lonyHat = clf.predict(testX) # 使用经度模型去预测经度
clf.fit (trainX, latTrainY) # 训练纬度模型
latyHat = clf.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon = absError(lonTestY,lonyHat)
# 纬度误差
absErrorLat = absError(latTestY,latyHat)
absErrorLen = 110 * sqrt(absErrorLon\*\*2 + absErrorLat\*\*2)

data_all\['线性回归'\].append(absErrorLen)

clf1.fit (trainX, lonTrainY) # 训练经度模型
lonyHat1 = clf1.predict(testX) # 使用经度模型去预测经度
clf1.fit (trainX, latTrainY) # 训练纬度模型
latyHat1 = clf1.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon1 = absError(lonTestY,lonyHat1)
# 纬度误差
absErrorLat1 = absError(latTestY,latyHat1)
absErrorLen1 = 110 * sqrt(absErrorLon1\*\*2 + absErrorLat1\*\*2)

data_all\['岭回归'\].append(absErrorLen1)



clf2.fit (trainX, lonTrainY) # 训练经度模型
lonyHat2 = clf2.predict(testX) # 使用经度模型去预测经度
clf2.fit (trainX, latTrainY) # 训练纬度模型
latyHat2 = clf2.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon2 = absError(lonTestY,lonyHat2)
# 纬度误差
absErrorLat2 = absError(latTestY,latyHat2)
absErrorLen2 = 110 * sqrt(absErrorLon2\*\*2 + absErrorLat2\*\*2)

data_all\['ElasticNet回归'\].append(absErrorLen2)


clf3.fit (trainX, lonTrainY) # 训练经度模型
lonyHat3= clf3.predict(testX) # 使用经度模型去预测经度
clf3.fit (trainX, latTrainY) # 训练纬度模型
latyHat3 = clf3.predict(testX) # 使用纬度模型去预测纬度
# 经度误差
absErrorLon3 = absError(lonTestY,lonyHat3)
# 纬度误差
absErrorLat3 = absError(latTestY,latyHat3)
absErrorLen3 = 110 * sqrt(absErrorLon3\*\*2 + absErrorLat3\*\*2)

data_all\['Lasso回归'\].append(absErrorLen3)


absErrorLen333 = 110 * sqrt(absErrorLon1\*\*2 + absErrorLat\*\*2)

data_all\['线性-岭回归'\].append(absErrorLen333)

data_all = DataFrame(data_all)
data_all.set_index([‘data size’], inplace=True)
data_all.plot(kind=’line’, figsize=(12,8),
title=’训练样本量对预测距离误差的影响’)
plt.legend(bbox_to_anchor=(1.0, 1))

关于通过脚本实现虚拟货币量化交易的部分交易数据(17w+条)

通过脚本在币安网(binance.com)实现的虚拟货币自动交易的交易数据(量化交易) 通过程序进行的量化交易,一个月盈利500%左右 包含我本人大约一个月的交易数据,总共17w+条 (期间因改进代码、币安停服升级等原因中断过数天) 详见https://github.com/Gaobx/virtual-currency-automatic-trading-data 如有使用价值,请告知i@gaojx.cn 后使用

关于mysql和python跨年周以及当前周的计算问题

在写某个后端时,需要使用python的pymysql从数据库获取上周的数据 然而数据总是不对,bug找了两三个小时才找出来,下面是bug出现的原因,以及关于mysql和python周数的计算方法的总结。


python代码中sql语句的where部分:

“WHERE YEARWEEK(date_format(commit_time,’%%Y-%%m-%%d’)) = YEARWEEK(now())- 1”

语句意思是,把当前时间转换为周数,再把数据库中相应datetime字段也转换为周数,两者之间相差1即可。 当前日期是2018/4/15星期日。 在python中通过datetime.datetime.now().isocalendar()[1] 可以得到当前周数是15; 通过上述sql语句select出上周数据后,在python中选出了mysql返回的数据中时间最靠后的一条数据,对该数据的时间项利用isocalendar()求周数。 然而上述python程序返回的结果也是15(本应该是上周的周数14)。 所以就考虑到很可能是python和mysql计算周数的方式不同,而今天恰巧是周日,有可能是SQL将今天算作了新的一周,即第16周,所以返回的上周数据在python中计算的周数为15。 在mysql中运行语句SELECT YEARWEEK(now()); 结果j截图如下 即显示的是当前时间为2018年第15周。 如果按照周日算作新的一周,当前应该是第16周才对。 进一步查阅网上的万年历 也显示今天为第十五周。 很让人费解。 那么下面通过查看sql语句查询结果的数据情况发现,数据中最靠后的日期的2018-4-14 23:17:32, 即为前一天。 在mysql中执行SELECT YEARWEEK(‘2018-4-14 23:17:32’); 结果是201814, 即第14周。 后来经过不断尝试发现,mysql不仅把周日当做了新的一周,而且是把每年的第一个周日后的那一周当做第一周。 举个例子 这是2018年1月的日历,在mysql中,1月7号到1月13号才算做是2018年第1周,1月1号到1月6号还算作是2017年的第53周。 而在python中,1月1号到1月7号算作是第1周。 所以在python代码中执行上述语句返回结果是4月8号(星期日)到4月14号(星期六)的数据,而python取了最后一条数据来计算周数,即计算4月14号的周数。 所以会导致在上述bug的产生。

使用pandas和matplotlib分析各类股票数据

本篇内容:网络数据采集美交所、沪深交所的多种不同题材股票交易信息,使用python的pandas和matplotlib分析趋势、相关性、可视化等。


以下均在ipython notebook中进行分析。 从Yahoo! Finance下载美交所特斯拉、58同城、网易、百度以及亚马逊的股票数据; 下载豫能控股、申能股份、东材科技、盾安环境以及博威合金等沪深交所新能源类股票; 下载中国太保、中国人寿、天茂集团、中国平安以及新华保险等沪深交所保险类股票; 下载游族网络、浪潮软件、皖通科技、北纬科技以及浙大网新等沪深交所互联网类股票。

import pandas as pd
from pandas import DataFrame
import numpy as np
from pandas_datareader import data
import pandas_datareader as pdr

%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl

mpl.rcParams[‘font.sans-serif’] = [‘SimHei’]
mpl.rcParams[‘font.serif’] = [‘SimHei’]
import seaborn as sns
sns.set_style(“darkgrid”,{“font.sans-serif”:[‘simhei’, ‘Arial’]})

stocks = [‘TSLA’, ‘WUBA’, ‘NTES’, ‘BIDU’, ‘AMZN’,
‘001896.SZ’, ‘600642.SS’, ‘601208.SS’, ‘002011.SZ’, ‘601137.SS’,
‘601601.SS’, ‘601628.SS’, ‘000627.SZ’, ‘601318.SS’, ‘601336.SS’,
‘002174.SZ’, ‘600756.SS’, ‘002331.SZ’, ‘002148.SZ’, ‘600797.SS’,]
names = [ ‘特斯拉’, ‘58同城’, ‘网易’, ‘百度’, ‘亚马逊’,
‘豫能控股’, ‘申能股份’, ‘东材科技’, ‘盾安环境’, ‘博威合金’,
‘中国太保’, ‘中国人寿’, ‘天茂集团’, ‘中国平安’, ‘新华保险’,
‘游族网络’, ‘浪潮软件’, ‘皖通科技’, ‘北纬科技’, ‘浙大网新’]
for i in range(len(stocks)):
name = pdr.get_data_yahoo(stocks[i])
path = ‘data/‘ + names[i] + ‘.csv’
out_file = open(path, ‘w’)
name.to_csv(out_file)
out_file.close()

下面是对股票数据进行整理,并且绘制图表的过程: 对原始数据按各项指标(开盘价、收盘价、最高价以及最低价等)进行分类,并且有必要对数据进行归一化处理,消除价格因素对股票趋势判断的影响;并对股票进行趋势分析、相关性分析等。

要点说明

  • 在选取股票时,为了方便分析,尽量选取记录超过1000个交易日的股票,并且取今天以前1000个交易日的数据进行分析
  • 在对多支股票的趋势进行分析时,股票价格会影响趋势的判断,需要对股价进行归一化处理。
    • 常见的归一化方法(链接):
      • min-max标准化
      • Z-score标准化方法
    • 这里采用min-max标准化方法
  • 采用pearson相关系数进行相关性分析

股价总览

data_set = {}
data_set[‘美股科技类’] = [ names[i] for i in range(0, 5)]
data_set[‘新能源类’] = [ names[i] for i in range(5, 10)]
data_set[‘保险类’] = [ names[i] for i in range(10, 15)]
data_set[‘互联网类’] = [ names[i] for i in range(15, 20)]

data_all = {}
for i in names:
file_name = ‘data/‘ + i + ‘.csv’
f = open(file_name)
data = pd.read_csv(f)
data = data.head(1000)
# print(i, data[‘close’].median())
data_all[i] = data[‘adjclose’]
data_all[‘date’] = data[‘date’]
data_all = DataFrame(data_all)
data_all.set_index([‘date’], inplace=True)
data_all.index = pd.to_datetime(data_all.index)

data_all.plot(kind=’line’, figsize=(12,8),
title=’所有股票原始数据(1000个交易日)’)
plt.legend(bbox_to_anchor=(1.0, 1))

# data_all.corr()

df = data_all
for i in data_all.columns:
df[i] = df[i].rolling(window=42, center=False).mean()

df.plot(kind=’line’, figsize=(12,8),
title=’所有股票移动平均值42d(1000个交易日)’)
plt.legend(bbox_to_anchor=(1.0, 1))

# data_all.corr()

此图表显示了所有股票在过去1000个交易日内的收盘价格 此图表显示了所有股票在过去1000个交易日中42d移动平均值 由此可见,除个别股票趋势明显外,大多数股票趋势难以判断,需要分类别进行后续分析。

美股科技类

data_all_1 = {}
for i in data_set[‘美股科技类’]:
file_name = ‘data/‘ + i + ‘.csv’
f = open(file_name)
data = pd.read_csv(f)
data = data.head(1000)
data_all_1[i] = data[‘adjclose’]
# data_all_1[i] = (data[‘adjclose’] - data[‘adjclose’].min())/(data[‘adjclose’].max() - data[‘adjclose’].min())
data_all_1[‘date’] = data[‘date’]
data_all_1 = DataFrame(data_all_1)
data_all_1.set_index([‘date’], inplace=True)
data_all_1.index = pd.to_datetime(data_all_1.index)
data_all_1.plot(kind=’line’, figsize=(10,6), title=’美股科技类股票原始数据(1000个交易日)’)
# plt.legend(bbox_to_anchor=(1.0, 1))

df = data_all_1
for i in data_all_1.columns:
df[i] = df[i].rolling(window=42, center=False).mean()

df.plot(kind=’line’, figsize=(10,6),
title=’美股科技类移动平均值42d(1000个交易日)’)
# plt.legend(bbox_to_anchor=(1.0, 1))

data_all_1 = {}
for i in data_set[‘美股科技类’]:
file_name = ‘data/‘ + i + ‘.csv’
f = open(file_name)
data = pd.read_csv(f)
data = data.head(1000)
data_all_1[i] = data[‘adjclose’]
data_all_1[i] = (data[‘adjclose’] - data[‘adjclose’].min())/(data[‘adjclose’].max() - data[‘adjclose’].min())
data_all_1[‘date’] = data[‘date’]
data_all_1 = DataFrame(data_all_1)
data_all_1.set_index([‘date’], inplace=True)
data_all_1.index = pd.to_datetime(data_all_1.index)
data_all_1.plot(kind=’line’, figsize=(10,6), title=’美股科技类股票数据(1000个交易日)’)
# plt.legend(bbox_to_anchor=(1.0, 1))

df = data_all_1
for i in data_all_1.columns:
df[i] = df[i].rolling(window=42, center=False).mean()

df.plot(kind=’line’, figsize=(10,6),
title=’经归一化处理的美股科技类移动平均值42d(1000个交易日)’)
# plt.legend(bbox_to_anchor=(1.0, 1))

data_all_1.corr()

此图表显示了科技类美股在过去1000个交易日内的收盘价格 此图显示了科技类美股在过去1000个交易日内42d移动平均值 通过该图表可以看到,亚马逊股票在此期间上升趋势明显,并且价格明显高于其他股票。 为了消除价格因素对股票趋势分析的影响,需要对股票数据采用归一化处理。 此图表显示了经归一化处理后的科技类美股1000个交易日内42d移动平均值 通过此图表可以看出亚马逊和网易股价呈明显上升趋势;特斯拉股价在2016年末以前在特定区间内呈震荡走势,过后呈明显上升趋势;58同城和百度股价处于波动状态。 此表显示了科技类美股Pearson相关系数矩阵 从表中可以看出亚马逊和网易股价相关系数为0.95,相关性很强;其次特斯拉与网易之间、特斯拉与亚马逊之间相关系数皆约为0.7,相关性较强;并且所有股票价格均呈正相关。

沪深交所新能源类

代码略。 此图表显示了科技类美股在过去1000个交易日内的收盘价格 可以看出,所选新能源类股票走势基本相同,可以通过42d移动平均值来进一步验证结论。 此图显示了沪深交所新能源类股票在过去1000个交易日内42d移动平均值 通过该图表可以看到,所有新能源类股票在2015年有较大幅度的上涨,并且在下半年股价有所回调;2016年至今,所有新能源类股票都呈震荡趋势。 为了消除价格因素对股票趋势分析的影响,下面对股票数据采用归一化处理。 此图表显示了经归一化处理后的沪深交所新能源类股票1000个交易日内42d移动平均值 通过此图表可以看出所有股票在2015年四月之前呈上涨趋势,并且在2015年下半年有较大幅度的回调;其中博威合金在回调后走出了第二轮上涨趋势,其余股票在波动后都有所回落。 此表显示了沪深交所新能源类股票Pearson相关系数矩阵 从表中可以看出东财科技与除博威合金之外的其他所有股票相关性都比较强,豫能控股、申能股份、盾安环境也是如此;博威合金与其他所有股票相关性都比较弱。

沪深交所保险类

代码略。 此图表显示了沪深交所保险类股票在过去1000个交易日内的收盘价格 由图可见,各个股票走势大致相同,可以通过下面的42d移动平均值进行验证。 此图显示了沪深交所保险类股票在过去1000个交易日内42d移动平均值 通过该图表可以看到,所有股票在2015年和2018年都有个阶段性上涨阶段,随后回落;总体趋势是类似的,股价在震荡上涨。 为了消除价格因素对股票趋势分析的影响,需要对股票数据采用归一化处理。 此图表显示了经归一化处理后的沪深交所保险类股票1000个交易日内42d移动平均值 通过此图表可以看出所有股票在2015年与2018年出现了大幅上涨的趋势,并且在过后都有所回落,与之前讨论结果相同。 此表显示了科技类美股Pearson相关系数矩阵 从表中可以看出大部分保险类股票之间是强相关的;相关性最弱的天茂集团与中国平安之间的相关系数也为0.53,说明该行业之间股票的走势是联动的,相关性很强。

沪深交所互联网类

代码略。 此图显示了沪深交所互联网类股票在过去1000个交易日内的收盘价格 此图显示了沪深交所互联网类股票在过去1000个交易日内42d移动平均值 通过该图表可以看到,皖通科技、浪潮软件和游族网络股价波动较大;北纬科技和浙大网新股价波动相对较小;总体呈区间内震荡趋势。 为了消除价格因素对股票趋势分析的影响,需要对股票数据采用归一化处理。 此图表显示了经归一化处理后的沪深交所互联网类股票1000个交易日内42d移动平均值 通过此图表可以看出所有股票皆为波动趋势;2015年上半年除浪潮软件外所有股票大幅上涨,随后回落,浪潮软件上涨较为滞后。 此表显示了科技类美股Pearson相关系数矩阵 从表中可以看出游族网络、浙大网新以及皖通科技三者之间相关性较强;其他股票相关性较弱,且北纬科技与浪潮软件之间呈现较弱的负相关状态。

  • 沪深交所绝大部分股票在2015年出现了大幅度的上涨趋势,随后有较大的回落。美交所股票并无明显的此种现象。
  • 沪深交所同题材下的股票价格走势有比较强的相关性。由此猜测,国内一些政策性的利好、利空消息会对股票价格产生较大的影响。

在服务器上装Anaconda给自己挖的坑

服务器用的是centOS系统,装Anaconda的步骤如下 第一步,下载Anaconda 如果直接在官网下会很慢,可以用清华镜像下载。清华镜像 如,我下载的是Anaconda3-5.1.0,64位linux版:

wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-5.1.0-Linux-x86_64.sh

第二步,安装

bash Anaconda3-5.1.0-Linux-x86_64.sh

在安装过程中会有个Cryptography Notice,一直按回车才会继续。 在下面这个地方不要回车,静静等就行,不然到最后会直接跳过添加环境变量的环节 安装到最后有一个Do you wish the installer to prepend the Anaconda3 install location to PATH in your /root/.bashrc ? [yes|no] 关于设置Anaconda环境变量,这里填yes。 如果没及时填yes,会默认no,只能后面手动添加环境变量。 在我安装的时候,由于多按了一次回车,导致没有添加到环境变量。 手动添加环境变量后也还是不能用,因为也不是很着急用,陆陆续续一星期最后发现, tm添加环境变量后忘记重启了!!! 最近在三台服务器上配置anaconda,每次都出了这个错误,orz…………………….. 写一下算是警醒自己吧…..

Learn Blockchains by Building One

出处:https://pythoncaff.com/topics/82/build-your-own-block-chain-step-by-step-with-python


你是否会和我一样,对加密数字货币底层的区块链技术非常感兴趣,特别想了解他们的运行机制。 但是学习区块链技术并非一帆风顺,我看多了大量的视频教程还有各种课程,最终的感觉就是真正可用的实战课程太少。 我喜欢在实践中学习,尤其喜欢以代码为基础去了解整个工作机制。如果你我一样喜欢这种学习方式,当你学完本教程时,你将会知道区块链技术是如何工作的。

写在开始之前

记住,区块链是一个 不可变的、有序的 被称为块的记录链。它们可以包含交易、文件或任何您喜欢的数据。但重要的是,他们用_哈希_ 一起被链接在一起。 如果你不熟悉哈希, 这里是一个解释. 该指南的目的是什么? 你可以舒服地阅读和编写基础的Python,因为我们将通过HTTP与区块链进行讨论,所以你也要了解HTTP的工作原理。 我需要准备什么? 确定安装了 Python 3.6+ (还有 pip) ,你还需要安装 Flask、 Requests 库:

pip install Flask==0.12.2 requests==2.18.4

对了, 你还需要一个支持HTTP的客户端, 比如 Postman 或者 cURL,其他也可以。 源码 可以点击这里


Step 1: 创建一个区块链

打开你最喜欢的文本编辑器或者IDE, 我个人比较喜欢 PyCharm. 新建一个名为blockchain.py的文件。 我们将只用这一个文件就可以。但是如果你还是不太清楚, 你也可以参考 源码.

描述区块链

我们要创建一个 Blockchain 类 ,他的构造函数创建了一个初始化的空列表(要存储我们的区块链),并且另一个存储交易。下面是我们这个类的实例: blockchain.py

class Blockchain(object):
def __init__(self):
self.chain = []
self.current_transactions = []

def new_block(self):
    # Creates a new Block and adds it to the chain
    pass

def new_transaction(self):
    # Adds a new transaction to the list of transactions
    pass

@staticmethod
def hash(block):
    # Hashes a Block
    pass

@property
def last_block(self):
    # Returns the last Block in the chain
    pass

我们的 Blockchain 类负责管理链式数据,它会存储交易并且还有添加新的区块到链式数据的Method。让我们开始扩充更多Method


块是什么样的 ?

每个块都有一个 索引,一个 时间戳(Unix时间戳),一个事务列表, 一个 校验(稍后详述) 和 前一个块的散列 。 下面是一个Block的例子 : blockchain.py

block = {
‘index’: 1,
‘timestamp’: 1506057125.900785,
‘transactions’: [
{
‘sender’: “8527147fe1f5426f9dd545de4b27ee00”,
‘recipient’: “a77f5cdfa2934df3954a5c7c7da5df1f”,
‘amount’: 5,
}
],
‘proof’: 324984774000,
‘previous_hash’: “2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”
}

在这一点上,一个 区块链 的概念应该是明显的 - 每个新块都包含在其内的前一个块的 散列 。 这是至关重要的,因为这是 区块链 不可改变的原因:如果攻击者损坏 区块链 中较早的块,则所有后续块将包含不正确的哈希值。 这有道理吗? 如果你还没有想通,花点时间仔细思考一下 - 这是区块链背后的核心理念


添加交易到区块

我们将需要一个添加交易到区块的方式。我们的 new_transaction()方法的责任就是这个, 并且它非常的简单: blockchain.py

class Blockchain(object):

def new_transaction(self, sender, recipient, amount):
    """
    Creates a new transaction to go into the next mined Block
    :param sender: <str> Address of the Sender
    :param recipient: <str> Address of the Recipient
    :param amount: <int> Amount
    :return: <int> The index of the Block that will hold this transaction
    """

    self.current_transactions.append({
        'sender': sender,
        'recipient': recipient,
        'amount': amount,
    })

    return self.last_block\['index'\] + 1

new_transaction() 方法添加了交易到列表,它返回了交易将被添加到的区块的_索引---讲开采下一个_这对稍后对提交交易的用户有用。


创建新的区块

当我们的 Blockchain 被实例化后,我们需要将 _创世_ 区块(一个没有前导区块的区块)添加进去进去。我们还需要向我们的起源块添加一个 _证明_,这是挖矿的结果(或工作证明)。 我们稍后会详细讨论挖矿。 除了在构造函数中创建 _创世_ 区块外,我们还会补全 new_block()new_transaction()hash()函数: blockchain.py

import hashlib
import json
from time import time

class Blockchain(object):
def __init__(self):
self.current_transactions = []
self.chain = []

    # 创建创世区块
    self.new\_block(previous\_hash=1, proof=100)

def new\_block(self, proof, previous\_hash=None):
    """
    创建一个新的区块到区块链中
    :param proof: <int> 由工作证明算法生成的证明
    :param previous_hash: (Optional) <str> 前一个区块的 hash 值
    :return: <dict> 新区块
    """

    block = {
        'index': len(self.chain) + 1,
        'timestamp': time(),
        'transactions': self.current_transactions,
        'proof': proof,
        'previous\_hash': previous\_hash or self.hash(self.chain\[-1\]),
    }

    # 重置当前交易记录
    self.current_transactions = \[\]

    self.chain.append(block)
    return block

def new_transaction(self, sender, recipient, amount):
    """
    创建一笔新的交易到下一个被挖掘的区块中
    :param sender: <str> 发送人的地址
    :param recipient: <str> 接收人的地址
    :param amount: <int> 金额
    :return: <int> 持有本次交易的区块索引
    """
    self.current_transactions.append({
        'sender': sender,
        'recipient': recipient,
        'amount': amount,
    })

    return self.last_block\['index'\] + 1

@property
def last_block(self):
    return self.chain\[-1\]

@staticmethod
def hash(block):
    """
    给一个区块生成 SHA-256 值
    :param block: <dict> Block
    :return: <str>
    """

    # 我们必须确保这个字典(区块)是经过排序的,否则我们将会得到不一致的散列
    block\_string = json.dumps(block, sort\_keys=True).encode()
    return hashlib.sha256(block_string).hexdigest()

上面的代码应该是直白的 --- 为了让代码清晰,我添加了一些注释和文档说明。 我们差不多完成了我们的区块链。 但在这个时候你一定很疑惑新的块是怎么被创建、锻造或挖掘的。


工作量证明算法

使用工作量证明(PoW)算法,来证明是如何在区块链上创建或挖掘新的区块。PoW 的目标是计算出一个符合特定条件的数字,这个数字对于所有人而言必须在计算上非常困难,但易于验证。这是工作证明背后的核心思想。 我们将看到一个简单的例子帮助你理解: 假设一个整数 x 乘以另一个整数 y 的积的 Hash 值必须以 0 结尾,即 hash(x * y) = ac23dc…0。设 x = 5,求 y ?用 Python 实现:

from hashlib import sha256
x = 5
y = 0 # We don’t know what y should be yet…
while sha256(f’{x*y}’.encode()).hexdigest()[-1] != “0”:
y += 1
print(f’The solution is y = {y}’)

结果是: y = 21。因为,生成的 Hash 值结尾必须为 0。

hash(5 * 21) = 1253e9373e…5e3600155e860

在比特币中,工作量证明算法被称为 Hashcash ,它和上面的问题很相似,只不过计算难度非常大。这就是矿工们为了争夺创建区块的权利而争相计算的问题。 通常,计算难度与目标字符串需要满足的特定字符的数量成正比,矿工算出结果后,就会获得一定数量的比特币奖励(通过交易)。 验证结果,当然非常容易。


实现工作量证明 让我们来实现一个相似 PoW 算法。规则类似上面的例子:

  • [mark]找到一个数字 P ,使得它与前一个区块的 proof 拼接成的字符串的 Hash 值以 4 个零开头。[/mark]

blockchain.py

import hashlib
import json

from time import time
from uuid import uuid4

class Blockchain(object):

def proof\_of\_work(self, last_proof):
    """
    Simple Proof of Work Algorithm:
     \- Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
     \- p is the previous proof, and p' is the new proof
    :param last_proof: <int>
    :return: <int>
    """

    proof = 0
    while self.valid\_proof(last\_proof, proof) is False:
        proof += 1

    return proof

@staticmethod
def valid\_proof(last\_proof, proof):
    """
    Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
    :param last_proof: <int> Previous Proof
    :param proof: <int> Current Proof
    :return: <bool> True if correct, False if not.
    """

    guess = f'{last_proof}{proof}'.encode()
    guess_hash = hashlib.sha256(guess).hexdigest()
    return guess_hash\[:4\] == "0000"

衡量算法复杂度的办法是修改零开头的个数。使用 4 个来用于演示,你会发现多一个零都会大大增加计算出结果所需的时间。 现在 Blockchain 类基本已经完成了,接下来使用 HTTP requests 来进行交互。


Step 2: Blockchain 作为 API 接口

我们将使用 Python Flask 框架,这是一个轻量 Web 应用框架,它方便将网络请求映射到 Python 函数,现在我们来让 Blockchain 运行在基于 Flask web 上。 我们将创建三个接口:

  • /transactions/new 创建一个交易并添加到区块
  • /mine 告诉服务器去挖掘新的区块
  • /chain 返回整个区块链

创建节点

我们的“Flask 服务器”将扮演区块链网络中的一个节点。我们先添加一些框架代码: blockchain.py

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask

class Blockchain(object):

# Instantiate our Node
app = Flask(__name__)

# Generate a globally unique address for this node
node_identifier = str(uuid4()).replace(‘-‘, ‘’)

# Instantiate the Blockchain
blockchain = Blockchain()

@app.route(‘/mine’, methods=[‘GET’])
def mine():
return “We’ll mine a new Block”

@app.route(‘/transactions/new’, methods=[‘POST’])
def new_transaction():
return “We’ll add a new transaction”

@app.route(‘/chain’, methods=[‘GET’])
def full_chain():
response = {
‘chain’: blockchain.chain,
‘length’: len(blockchain.chain),
}
return jsonify(response), 200

if __name__ == ‘__main__‘:
app.run(host=’0.0.0.0’, port=5000)

简单的说明一下以上代码:

  • 第 15 行:实例化节点。阅读更多关于 Flask 内容。
  • 第 18 行:为节点创建一个随机的名称。.
  • 第 21 行:实例化 Blockchain 类。
  • 第 24–26 行:创建 /mine 接口,GET 方式请求。
  • 第 28–30 行:创建 /transactions/new 接口,POST 方式请求,可以给接口发送交易数据。
  • 第 32–38 行:创建 /chain 接口,返回整个区块链。
  • 第 40–41 行:服务器运行端口 5000 。

发送交易

发送到节点的交易数据结构如下:

{
“sender”: “my address”,
“recipient”: “someone else’s address”,
“amount”: 5
}

因为我们已经有了添加交易的方法,所以基于接口来添加交易就很简单了。让我们为添加事务写函数: blockchain.py

import hashlib
import json
from textwrap import dedent
from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

@app.route(‘/transactions/new’, methods=[‘POST’])
def new_transaction():
values = request.get_json()

# Check that the required fields are in the POST'ed data
required = \['sender', 'recipient', 'amount'\]
if not all(k in values for k in required):
    return 'Missing values', 400

# Create a new Transaction
index = blockchain.new_transaction(values\['sender'\], values\['recipient'\], values\['amount'\])

response = {'message': f'Transaction will be added to Block {index}'}
return jsonify(response), 201

挖矿

挖矿正是神奇所在,它很简单,做了一下三件事:

  1. 计算工作量证明 PoW
  2. 通过新增一个交易授予矿工(自己)一个币
  3. 构造新区块并将其添加到链中

blockchain.py

import hashlib
import json

from time import time
from uuid import uuid4

from flask import Flask, jsonify, request

@app.route(‘/mine’, methods=[‘GET’])
def mine():

# We run the proof of work algorithm to get the next proof...
last\_block = blockchain.last\_block
last\_proof = last\_block\['proof'\]
proof = blockchain.proof\_of\_work(last_proof)

# We must receive a reward for finding the proof.
# The sender is "0" to signify that this node has mined a new coin.
blockchain.new_transaction(
    sender="0",
    recipient=node_identifier,
    amount=1,
)

# Forge the new Block by adding it to the chain
previous\_hash = blockchain.hash(last\_block)
block = blockchain.new\_block(proof, previous\_hash)

response = {
    'message': "New Block Forged",
    'index': block\['index'\],
    'transactions': block\['transactions'\],
    'proof': block\['proof'\],
    'previous\_hash': block\['previous\_hash'\],
}
return jsonify(response), 200

注意交易的接收者是我们自己的服务器节点,我们做的大部分工作都只是围绕 Blockchain 类方法进行交互。到此,我们的区块链就算完成了,我们来实际运行下。


Step 3: 运行区块链

你可以使用 cURL 或 Postman 去和 API 进行交互 启动 Server:

$ python blockchain.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

让我们通过请求 http://localhost:5000/mine ( GET )来进行挖矿: 挖矿 用 Postman 发起一个 GET 请求. 创建一个交易请求,请求 http://localhost:5000/transactions/new (POST),如图 如果不是使用 Postman,则用一下的 cURL 语句也是一样的:

$ curl -X POST -H “Content-Type: application/json” -d ‘{
“sender”: “d4ee26eee15148ee92c6cd394edd974e”,
“recipient”: “someone-other-address”,
“amount”: 5
}’ “http://localhost:5000/transactions/new"

在挖了两次矿之后,就有 3 个块了,通过请求 http://localhost:5000/chain 可以得到所有的块信息

{
“chain”: [
{
“index”: 1,
“previous_hash”: 1,
“proof”: 100,
“timestamp”: 1506280650.770839,
“transactions”: []
},
{
“index”: 2,
“previous_hash”: “c099bc…bfb7”,
“proof”: 35293,
“timestamp”: 1506280664.717925,
“transactions”: [
{
“amount”: 1,
“recipient”: “8bbcb347e0634905b0cac7955bae152b”,
“sender”: “0”
}
]
},
{
“index”: 3,
“previous_hash”: “eff91a…10f2”,
“proof”: 35089,
“timestamp”: 1506280666.1086972,
“transactions”: [
{
“amount”: 1,
“recipient”: “8bbcb347e0634905b0cac7955bae152b”,
“sender”: “0”
}
]
}
],
“length”: 3
}


Step 4: 一致性(共识)

我们已经有了一个基本的区块链可以接受交易和挖矿。但是区块链系统应该是分布式的。既然是分布式的,那么我们究竟拿什么保证所有节点有同样的链呢?这就是一致性问题,我们要想在网络上有多个节点,就必须实现一个一致性的算法

注册节点

在实现一致性算法之前,我们需要找到一种方式让一个节点知道它相邻的节点。每个节点都需要保存一份包含网络中其它节点的记录。因此让我们新增几个接口:

  1. /nodes/register 接收 URL 形式的新节点列表.
  2. /nodes/resolve 执行一致性算法,解决任何冲突,确保节点拥有正确的链.

我们修改下 Blockchain 的 init 函数并提供一个注册节点方法: blockchain.py


from urllib.parse import urlparse

class Blockchain(object):
def __init__(self):

self.nodes = set()

def register_node(self, address):
    """
    Add a new node to the list of nodes
    :param address: <str> Address of node. Eg. 'http://192.168.0.5:5000'
    :return: None
    """

    parsed_url = urlparse(address)
    self.nodes.add(parsed_url.netloc)

我们用 set 来储存节点,这是一种避免重复添加节点的简单方法.


实现共识算法

就像先前讲的那样,当一个节点与另一个节点有不同的链时,就会产生冲突。 为了解决这个问题,我们将制定最长的有效链条是最权威的规则。换句话说就是:在这个网络里最长的链就是最权威的。 我们将使用这个算法,在网络中的节点之间达成共识。 blockchain.py


import requests

class Blockchain(object)

def valid_chain(self, chain):
    """
    Determine if a given blockchain is valid
    :param chain: <list> A blockchain
    :return: <bool> True if valid, False if not
    """

    last_block = chain\[0\]
    current_index = 1

    while current_index < len(chain):
        block = chain\[current_index\]
        print(f'{last_block}')
        print(f'{block}')
        print("\\n-----------\\n")
        # Check that the hash of the block is correct
        if block\['previous\_hash'\] != self.hash(last\_block):
            return False

        # Check that the Proof of Work is correct
        if not self.valid\_proof(last\_block\['proof'\], block\['proof'\]):
            return False

        last_block = block
        current_index += 1

    return True

def resolve_conflicts(self):
    """
    This is our Consensus Algorithm, it resolves conflicts
    by replacing our chain with the longest one in the network.
    :return: <bool> True if our chain was replaced, False if not
    """

    neighbours = self.nodes
    new_chain = None

    # We're only looking for chains longer than ours
    max_length = len(self.chain)

    # Grab and verify the chains from all the nodes in our network
    for node in neighbours:
        response = requests.get(f'http://{node}/chain')

        if response.status_code == 200:
            length = response.json()\['length'\]
            chain = response.json()\['chain'\]

            # Check if the length is longer and the chain is valid
            if length > max\_length and self.valid\_chain(chain):
                max_length = length
                new_chain = chain

    # Replace our chain if we discovered a new, valid chain longer than ours
    if new_chain:
        self.chain = new_chain
        return True

    return False

第一个方法 valid_chain() 负责检查一个链是否有效,方法是遍历每个块并验证散列和证明。 resolve_conflicts() 是一个遍历我们所有邻居节点的方法,下载它们的链并使用上面的方法验证它们。 如果找到一个长度大于我们的有效链条,我们就取代我们的链条。 我们将两个端点注册到我们的API中,一个用于添加相邻节点,另一个用于解决冲突: blockchain.py

@app.route(‘/nodes/register’, methods=[‘POST’])
def register_nodes():
values = request.get_json()

nodes = values.get('nodes')
if nodes is None:
    return "Error: Please supply a valid list of nodes", 400

for node in nodes:
    blockchain.register_node(node)

response = {
    'message': 'New nodes have been added',
    'total_nodes': list(blockchain.nodes),
}
return jsonify(response), 201

@app.route(‘/nodes/resolve’, methods=[‘GET’])
def consensus():
replaced = blockchain.resolve_conflicts()

if replaced:
    response = {
        'message': 'Our chain was replaced',
        'new_chain': blockchain.chain
    }
else:
    response = {
        'message': 'Our chain is authoritative',
        'chain': blockchain.chain
    }

return jsonify(response), 200

在这一点上,如果你喜欢,你可以使用一台不同的机器,并在你的网络上启动不同的节点。 或者使用同一台机器上的不同端口启动进程。 我在我的机器上,不同的端口上创建了另一个节点,并将其注册到当前节点。 因此,我有两个节点:http://localhost:5000http://localhost:5001。 注册一个新节点: 然后我在节点 2 上挖掘了一些新的块,以确保链条更长。 之后,我在节点1上调用 GET /nodes/resolve,其中链由一致性算法取代: 这是一个包,去找一些朋友一起,以帮助测试你的区块链。


我之所以对数字货币入迷,是因为我相信区块链会很快改变我们看待事物的方式,包括经济、政府、档案管理等。在我看来,目前的区块链就像90年代的互联网一样,正处于萌芽期,在不远的将来,区块链必将颠覆许多行业的传统运作方式。

一个不幸夭折的虚拟货币自动套利策略

2017年无疑是虚拟货币的元年,整体市值已经突破了四万亿人民币。上涨几百倍几千倍的山寨币比比皆是,即使这种行情,能拿到这么多倍数的人少之又少,而且也还有很多人是亏损的,毕竟虚拟货币的波动太大,分分钟让人怀疑人生。 所以虚拟货币是一种收益高风险也高的投资。除了投资和投机之外,在虚拟货币的市场也产生了许多其他获利的方式,比如搬砖、量化交易等。这里介绍一种在聚币网用程序自动交易的策略。 聚币网(jubi.com,在2017年九月之前,在国内币圈号称“聚币小赌场”。每天零点整,平台上都会有几个山寨币被暴力拉升,少则几十点,多则数倍。所以每天都有人埋伏这些各种山寨币,碰运气,小赌场的名号由此而来。然而,不幸的是九月份ZF开始管制国内的虚拟货币交易,关停所有交易所,小赌场也未能幸免,所以这个策略也随之夭折了。 小赌场每晚暴力拉升的时间非常短,几秒几十秒就能完成,庄家出货也非常快。如果每晚守在电脑前,看见有极速拉升的币种立即买入然后出货,八成会被套。。。 既然无法手动交易,那么不妨设计一个策略让程序去自动买卖。这个策略分为三个部分,检测被拉升的币种、挂单买入、出货。

一、检测被拉升的币种

首先介绍一下聚币的API,我们可以通过调用API来获取行情、查询账户信息以及进行交易等。 [caption id=”” align=”aligncenter” width=”647”] 图 聚币API介绍[/caption] 这里使用python来调用API以实现各种功能,后面附详细代码。 聚币网涨跌幅参照每晚零点的价格计算的,所以可以通过每晚零点的实时涨跌幅来检测是否有被拉升的币种。 判断条件是:

  • 涨幅最高的币在5%-10%之间
  • 有多于10个币种涨跌幅为0
  • 当前时间在0:00 - 0:01之间

满足上述条件,基本可以认为有狗庄在拉升当前涨幅最高的币种。 当然,在具体操作的时候并没有想象的那么容易,就比如聚币的API有一些坑:

二、挂单买入

经过第一步获取到可以投机的币种,接下来便是快速买入。 应该以什么价格买入?我这里采用的策略是挂高于当前价格20%的单买入,原因如下:

  • 鉴于狗庄的拉升是迅速的,若我获取到当前的价格后,立即以该价格挂单,这样八成是买不到的,因为这中间有一来一回的数据请求过程,网络延迟就有几十甚至上百毫秒,价格肯定已经更高了。
  • 平台交易系统的成交规则是买一卖一价成交。举个例子,btc当前的卖一价是$3000、数量1个,卖二价是$3100、数量1个,卖三价是$4000、数量2个,那么当以$4000的价格挂单买入1.5个btc时,最终成交情况是$3000成交1个、$3100成交0.5个,而非$4000成交1.5个。所以,挂高于当前价格20%的单能以当前的最低卖价成交,而又不至于买不到。

[caption id=”” align=”aligncenter” width=”690”] 测试时的一次自动交易记录(自动买入,手动卖出)[/caption]

三、出货

考虑到庄家出货需要一定的时间,最初是考虑人工出货的,不使用程序。 测试时发现,人工手动出货容易受到情绪以及心态的影响,一不小心就能从盈利变为亏损,所以后面决定采用程序自动出货(最终并没有投入使用)。 出货条件是一旦检测到买入的币种价格开始下跌便全部卖出,挂单价格是第二步的成交价格。

总结

这个策略是17年八月底到九月初萌生并开始实现的(拖了半年才写出这篇23333),起初只实现了前两个部分。经过几个晚上的测试,程序能够很好的按照策略执行,还没等实现第三个部分,zf发了个文件《中国人民银行 中央网信办 工业和信息化部 工商总局 银监会 证监会 保监会关于防范代币发行融资风险的公告》,监管虚拟货币市场,并关停国内各大交易所,聚币网也就这样被关停了。。。 虽然这个策略被扼杀在了摇篮中,但我奔向温饱的梦想是永远无法被扼杀的,哈哈哈。 最近一直在琢磨量化交易的策略,也封装了一些平台的原始API,并实现了一个量化交易,每天自动交易2w到3w次左右,但盈利率比较低。 所以如果有什么想法和策略尽情联系我,我们一起走向温饱哈哈。

代码

下面是一些用python封装的API。 login.py

# -- coding: utf-8 --

“””
成功请求的参数顺序:
signature,nonce,key
nonce, ,key
(nonce参数一定要在key前面)
“””

import requests
import time
import hashlib
import hmac
import collections
import urllib
import urllib2

class Jubi(object):

def \_\_init\_\_(self, public\_key, private\_key):
    self.public\_key = public\_key
    self.private\_key = private\_key

def get_nonce(self):
    curr_stamp = time.time()*100
    nonce = int(curr_stamp)
    return nonce

def get_md5(self,s):
    m = hashlib.md5()
    m.update(s.encode())
    return m.hexdigest()

def get_params(self):
    nonce\_value = self.get\_nonce()
    key\_value = self.public\_key
    private\_key = self.private\_key
    string = ('nonce=' + str(nonce\_value) + '&' + 'key=' + key\_value).encode('utf-8')
    private\_key\_md5 = self.get\_md5(private\_key).encode('utf-8')
    signature = hmac.new(private\_key\_md5, string, digestmod=hashlib.sha256).hexdigest()
    dict_ordered = collections.OrderedDict()
    dict_ordered\['signature'\] = signature
    dict\_ordered\['nonce'\] = nonce\_value
    dict\_ordered\['key'\] = key\_value
    return dict_ordered

def get\_trade\_list_params(self, since, coin, type):
    nonce\_value = self.get\_nonce()
    key\_value = self.public\_key
    private\_key = self.private\_key
    string = ('nonce=' + str(nonce\_value) + '&' + 'key=' + key\_value + '&since=' + str(since) + '&coin=' + coin + '&type=' +type).encode('utf-8')
    private\_key\_md5 = self.get\_md5(private\_key).encode('utf-8')
    signature = hmac.new(private\_key\_md5, string, digestmod=hashlib.sha256).hexdigest()
    dict_ordered = collections.OrderedDict()
    dict_ordered\['signature'\] = signature
    dict\_ordered\['nonce'\] = nonce\_value
    dict\_ordered\['key'\] = key\_value
    dict_ordered\['since'\] = since
    dict_ordered\['coin'\] = coin
    dict_ordered\['type'\] = type
    return dict_ordered

def get\_trade\_view_params(self, id, coin):
    nonce\_value = self.get\_nonce()
    key\_value = self.public\_key
    private\_key = self.private\_key
    string = ('nonce=' + str(nonce\_value) + '&' + 'key=' + key\_value + '&id=' + str(id) + '&coin=' + coin).encode('utf-8')
    private\_key\_md5 = self.get\_md5(private\_key).encode('utf-8')
    signature = hmac.new(private\_key\_md5, string, digestmod=hashlib.sha256).hexdigest()
    dict_ordered = collections.OrderedDict()
    dict_ordered\['signature'\] = signature
    dict\_ordered\['nonce'\] = nonce\_value
    dict\_ordered\['key'\] = key\_value
    dict_ordered\['id'\] = id
    dict_ordered\['coin'\] = coin
    return dict_ordered

def get\_trade\_add_params(self, amount, price, type, coin):
    nonce\_value = self.get\_nonce()
    key\_value = self.public\_key
    private\_key = self.private\_key
    string = ('nonce=' + str(nonce\_value) + '&key=' + key\_value + '&amount=' + str(amount) +'&price=' +str(price) +'&type=' +type +'&coin='+coin).encode('utf-8')
    #string = "amount="+str(amount)+"&nonce="+str(nonce\_value)+"&type="+type+"&key="+key\_value+"&price="+str(price)+"&coin="+coin
    private\_key\_md5 = self.get\_md5(private\_key).encode('utf-8')
    signature = hmac.new(private\_key\_md5, string, digestmod=hashlib.sha256).hexdigest()

    dict_ordered = collections.OrderedDict()
    dict_ordered\['signature'\] = signature
    dict\_ordered\['nonce'\] = nonce\_value
    dict\_ordered\['key'\] = key\_value
    dict_ordered\['amount'\] = amount
    dict_ordered\['price'\] = price
    dict_ordered\['type'\] = type
    dict_ordered\['coin'\] = coin

    return dict_ordered

ticker.py

# -- coding: utf-8 --

import requests
import time
import login
import urllib2
import json

class ticker(object):

def \_\_init\_\_(self, coin):
    self.url = 'https://www.jubi.com/api/v1/ticker/'
    self.coin = coin

def get\_coin\_ticker(self):
    self.resp = requests.post(url = self.url, data = {'coin': self.coin}).json()
    response = urllib2.urlopen('https://www.jubi.com/coin/trends')
    html = response.read().decode("utf8","ignore").encode("gbk","ignore")
    trends = json.loads(html)

    self.resp\['yprice'\] = trends\[self.coin\]\['yprice'\]

    last = self.resp\['last'\].encode("utf-8")    #unicode to string
    self.resp\['last'\] = float(last)                #string to float
    buy = self.resp\['buy'\].encode("utf-8")
    self.resp\['buy'\] = float(buy)
    sell = self.resp\['sell'\].encode("utf-8")
    self.resp\['sell'\] = float(sell)
    high = self.resp\['high'\].encode("utf-8")
    self.resp\['high'\] = float(high)
    low = self.resp\['low'\].encode("utf-8")
    self.resp\['low'\] = float(low)

    if self.resp\['yprice'\] != 0:        
        self.resp\['rate'\] = (self.resp\['last'\]/self.resp\['yprice'\]) - 1
    else:
        self.resp\['rate'\] = -1.0
    return self.resp

def calc\_all\_rate(self):
    allTicker = self.get_allTicker()
    #print allTicker
    self.zero_num = 0
    headers = {'User-agent' : 'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0'}

    request = urllib2.Request('https://www.jubi.com/coin/trends', headers = headers)
    response = urllib2.urlopen(request)
    #response = urllib2.urlopen('https://www.jubi.com/coin/trends')
    html = response.read().decode("utf8","ignore").encode("gbk","ignore")
    trends = json.loads(html)
    for coin in trends:
        coin_yprice = trends\[coin\]\['yprice'\]
        allTicker\[coin\]\['yprice'\] = coin_yprice

        if coin_yprice != 0:
            allTicker\[coin\]\['rate'\] = round(allTicker\[coin\]\['last'\]/coin_yprice - 1, 4)
        else:
            allTicker\[coin\]\['rate'\] = -1.0000
        if allTicker\[coin\]\['rate'\] == 0:
            self.zero_num += 1
    #print self.zero_num
        #print coin , allTicker\[coin\]\['rate'\]\
    return allTicker

def sort\_by\_rate(self):
    allTicker = self.calc\_all\_rate()
    #print allTicker
    #for coin in allTicker:
    #    backitems = \[allTicker\[coin\]\['rate'\], coin\]
    self.backitems=\[\[allTicker\[coin\]\['rate'\],coin\] for coin in allTicker\]
    self.backitems.sort(reverse = True)
    '''for i in range(0, len(backitems)):
        print backitems\[i\]'''
    return self.backitems

def print_ticker(self):
    for i in range(0, len(self.backitems)):
        print self.backitems\[i\]

def get_allTicker(self):
    url = 'https://www.jubi.com/api/v1/allticker/'
    result = requests.post(url = url).json()
    return result

def get_orders(self):
    url = 'https://www.jubi.com/api/v1/orders/'
    result = requests.post(url = url, data = self.coin).json()
    return result

trade.py

# -- coding: utf-8 --

import requests
import time
import login

class trade(object):

def \_\_init\_\_(self, account):
    self.account = account

def get\_trade\_list(self, since, coin, type):
    params = self.account.get\_trade\_list_params(since, coin, type)
    url = 'https://www.jubi.com/api/v1/trade_list/'
    self.list = requests.post(url = url, data = params).json()
    return self.list

def get\_trade\_view(self, id, coin):
    params = self.account.get\_trade\_view_params(id, coin)
    url = 'https://www.jubi.com/api/v1/trade_view/'
    self.view = requests.post(url = url, data = params).json()
    return self.view

def trade_cancel(self, id, coin):
    params = self.account.get\_trade\_view_params(id, coin)
    url = 'https://www.jubi.com/api/v1/trade_cancel/'
    self.cancel = requests.post(url = url, data = params).json()
    return self.cancel

def trade_add(self, amount, price, type, coin):
    params = self.account.get\_trade\_add_params(amount, price, type, coin)
    #print params
    url = 'https://www.jubi.com/api/v1/trade_add/'
    self.add = requests.post(url = url, data = params).json()
    #print self.add
    return self.add

balance.py

# -- coding: utf-8 --

import requests
import time
import login

class balance(object):

def \_\_init\_\_(self, account):
    self.account = account
    self.url = 'https://www.jubi.com/api/v1/balance/'
    self.data = self.account.get_params()
    self.resp = requests.post(url = self.url, data = self.data).json()

def get\_all\_balances(self):
    return self.resp

def get_asset(self):
    return self.resp\['asset'\]

def get\_coin\_balances(self, coin):
    #Coin\_balance = coin + '\_balance'
    #Coin\_lock = coin + '\_lock'
    coin\_balances = {'balance' : self.get\_coin\_balance(coin), 'lock' : self.get\_coin_lock(coin)}
    return coin_balances                

def get\_coin\_balance(self, coin):
    Coin = coin + '_balance'
    return self.resp\[Coin\]

def get\_coin\_lock(self, coin):
    Coin = coin + '_lock'
    return self.resp\[Coin\]

其他交易平台API的使用方法类似于此

关于华师大图书馆创意空间预约系统

学校图书馆创意空间预约系统 ↓↓ 从URL中看到使用的是GET方法,可以分析出API格式。 写个脚本,处理下服务器返回的数据,能提取出如下数据。 [caption id=”” align=”alignnone” width=”676”] (20171122全天的预约情况,预约人、时间、房间号等一览无余)[/caption] [caption id=”” align=”alignnone” width=”675”] (上图是20171119 21:30左右查询的当天的预约情况,未显示已失效的预约)[/caption] 这些API照理说应该有个身份认证过程,没有权限的用户无法获取数据,结果…… 同样,改一下GET的数据就能写个自动抢小包间的脚本了,不再赘述。 (另外,经过很长一段时间对预约情况的观察,发现其实有人每天都在用脚本抢23333) 仔细深究下去,这网站的bug还有很多…比如通过这个预约系统可以根据一位同学或者老师的姓名(或姓名的一部分)查到对应的学号、工号,进而就有了全校每个人的联系方式等等…2333 纯粹出于兴趣和吐槽….