PPTP客户端连接VPN的详细记录

为了工作方便,利用动态域名最近在公司里做了个VPN服务器,这样回到家后可以继续和公司同处一个局域网下,方便一些应急的时候不至于急急忙忙往公司跑。

系统继续是把做成路由的Debian,尽管它本身也已经是个VPN服务器了,但奈何现在坑爹的运营商,分配不到一个公网IP,所以这个服务器歇菜了。

首先安装客户端了,命令:aptitude install pptp-linux;安装完创建连接:pptpsetup –create 连接名称 –server 你的服务器 –username 你的账户 –password 你的密码 –encrypt。这一步具体可以用命令:/usr/sbin/pptpsetup –help,这个可以看到详细的说明格式。执行完这一步之后在/etc/ppp/chap-secrets这个文件会多出两行,但多出的第一行是注释掉的说明,第二行才是参数,格式如此:你的账户 pptp "你的密码" *,注意别掉了星号。还有在/etc/ppp/peers/这个目录下多出一个以连接名称命名的文件,里面的格式基本如此:

# written by pptpsetup
pty "pptp 你的服务器 --nolaunchpppd"
lock
noauth
nobsdcomp
nodeflate
name 你的用户名
remotename 你的连接名称
ipparam 你的连接名称
require-mppe-128

现在已经可以使用pon 你的连接名称来连接vpn服务器了。

快速在Debian上部署路由功能

用了一段时间的Ubuntu后,对于它四个桌面的切换,老是经常卡死,并且在换到17.04版本后,即使在固态硬盘和16G内存的帮助下用着N3150的处理器,感觉还是有点卡,于是乎就切换到了Debian阵营来了。也正因为装系统的次数太频繁了,所以重新整理了一下迅速部署路由功能,现在可以说在重新安装系统的情况下也可以在三分钟分钟内重新建立路由。

废话少说,步骤开始,切换到管理员账户,安装软件:

aptitude install hostapd bridge-utils dnsmasq

配置网络接口:mousepad /etc/network/interfaces

auto lo
iface lo inet loopback
auto lan0   #有线网卡名称
iface lan0 inet manual
auto wlan0  #无线网卡名称
iface wlan0 inet manual
auto br0 #自定义的接口
iface br0 inet static
address 192.168.147.1  本机局域网地址 别的机器网关就是它了
netmask 255.255.255.0
broadcast 192.168.147.255
bridge-ports lan0 wlan0 #把两真实网卡桥连
bridge_fd 9
bridge_hello 2
bridge_maxage 12
bridge_stp off

配置无线控制:mousepad /etc/hostapd/hostapd.conf

interface=wlan0  #无线网卡名称
bridge=br0   # 自定义的
driver=nl80211
ssid=Ubuntu  # 本热点的名称
# country_code=US
hw_mode=g  # 模式
channel=11  # 频道
dtim_period=1
rts_threshold=2347
fragm_threshold=2346
macaddr_acl=0
# accept_mac_file=/etc/hostapd/hostapd.accept #MAC地址限制,需要建立这个文件
# deny_mac_flie=/etc/hostapd/hostapd.deny
auth_algs=3
ieee80211n=1
ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40]
wpa=2
wpa_passphrase=88888888  # 热点密码
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP CCMP
rsn_pairwise=CCMP

还有一个要改的:mousepad /etc/default/hostapd

RUN_DAEMON="yes"
DAEMON_CONF="/etc/hostapd/hostapd.conf"

配置DNS服务器和DHCP服务器:mousepad /etc/dnsmasq.conf

cache-size=4096
listen-address=192.168.147.1,127.0.0.1
resolv-file=/etc/resolv.dnsmasq.conf
conf-dir=/etc/dnsmasq.d

interface=br0
expand-hosts
domain=example.com
dhcp-range=192.168.147.50,192.168.147.150,12h
dhcp-option=3,192.168.147.1
dhcp-option=6,192.168.147.1

创建resolv.dnsmasq.conf文件:mousepad /etc/resolv.dnsmasq.conf

nameserver 58.20.127.170
nameserver 58.20.127.238

打开数据包转发:mousepad /etc/sysctl.conf

net.ipv4.ip_forward = 1

最后,还有一个问题,如果Debian在9之前的版本里,还有rc.local文件,那么直接可以rc.local文件里加上:mousepad /etc/rc.local

iptables -t nat -A POSTROUTING -o wlan1 -j MASQUERADE

但是,在9之后的版本里,已经没有了rc.local文件,所以就要把这个规则加到文件里了。创建自启动配置文件在网络启动之前的文件夹中,并赋予可执行权限:

touch /etc/network/if-pre-up.d/iptables
chmod +x /etc/network/if-pre-up.d/iptables

编辑该文件:mousepad /etc/network/if-pre-up.d/iptables

#!/bin/sh
/sbin/iptables-restore < /etc/iptables

执行规则定义,然后保存成文件:

iptables -t nat -A POSTROUTING -o wlan1 -j MASQUERADE
iptables-save > /etc/iptables

也可以使用:mousepad /etc/iptables,如下内容:

# Generated by iptables-save v1.6.0 on Mon Mar 26 10:32:21 2018
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [20:1340]
:POSTROUTING ACCEPT [20:1340]
-A POSTROUTING -o ppp0 -j MASQUERADE
COMMIT
# Completed on Mon Mar 26 10:32:21 2018

重启完成~

行情软件里的平均函数以及Python的实现

在行情软件里,有很多的平均公式,一直没有深入的去了解,用得也是糊里糊涂的,现结合百度谷歌,对各种公式做个总结记录。

MA(x,n)–移动平均,是最简单的n日内的平均值;

计算公式:(X1+X2+X3+X4+…+Xn)/n

Python代码:

def getMA(df,n):
    for i in range(len(df)):
        if i >= n:
            df.ix[i,'ma'] = df.close.values[i-n:i].mean()
    return df

EMA(x,n)–指数移动平均,这个函数以相关周期为权重进行计算;

计算公式:[2*X+(n-1)*Y]/(n+1) 其中'Y'表示上一周期的Y值

例如:每天X值不同,由远到近标示分别为X1、X2、X3、X4……Xn

如果n=1,那么EMA(X,1) 则等于 [2*X1+(1-1)*Y]/(1+1)=X1

如果n=2,那么EMA(X,2) 则等于 [2*X2+(2-1)*Y]/(2+1)=(2/3)*X2+(1/3)*X1

如果n=3,那么EMA(X,3) 则等于 [2*X3+(3-1)*Y]/(3+1)

                                                  =[2*X3+2*((2/3)*X2+(1/3)*X1)]/4

                                                  =(1/2)*X3+(1/3)*X2+(1/6)*X1

以此类推,往下循环……

Python代码:

def getEMA(df,n):  
    for i in range(len(df)):  
        if i==0:  
            df.ix[i,'ema']=df.ix[i,'close']  
        if i>0:  
            df.ix[i,'ema']=(1-n)*df.ix[i-1,'close']+n*df.ix[i,'close']  
    return df

SMA(x,n,m)–简单移动平均,m为当日的权重,是个0~1之间的值;

计算公式:(X*M+Y'*(N-M))/N 其中Y表示上一周期值

SMA 就是把EMA(X,N) 中的权重2,变成了一个可自己定义的变数。要求M<N,M/N, (N-M)/N 就是一个加起来等于1的小数,于是定义动态平均值。

Python代码:

def getSMA(df,n,m):
    for i in range(len(df)):
        if i==0:
            df.ix[i,'sma'] = df.ix[i,'close']*m/n
        else:
            df.ix[i,'sma'] = [df.ix[i,'close']*m + (n-m)*df.ix[i-1,'sma']]/n
    return df

DMA(x,m)–动态移动平均,这个函数以动态设定的权重m进行计算;

计算公式:m*X+(1-m)*Y  其中Y表示上一周期值,A必须小于1

Python代码:

def getDMA(df,m):
    for i in range(len(df)):
        if i == 0:
            df.ix[i,'dma'] = df.ix[i,'close']/m
        else:
            df.ix[i,'dma'] = df.ix[i,'close']/m + (1-m)*df.ix[i-1,'dma']
    return df

TMA(x,n,m)–递归移动平均,这个函数可以完全控制当前周期的权重和上一次值的权重;

计算公示:m*X+n*Y 其中Y表示上一周期值,初值为m*X。

Python代码:

def getTMA(df,n,m):
    for i in range(len(df)):
        if i==0:
            df.ix[i,'sma'] = df.ix[i,'close']*m
        else:
            df.ix[i,'sma'] = df.ix[i,'close']*m + df.ix[i-1,'sma']*n
    return df

WMA(x,n)–加权移动平均,这个函数对于近日的权重会比其它函数敏感。

计算公式:n*X0+(n-1)*X1+(n- 2)*X2)+…+1*Xn)/(n+(n-1)+(n-2)+…+1)

X0表示本周期值,X1表示上一周期值。

Python代码:

def getWMA(df,n):
    weight = 0
    for i in range(n):
        weight += i
    for i in range(len(df)):
        if i >= n:
            sum = 0
            for j in range(n):
                 sum += (j+1)*df.ix[i-n+j,'close']
            df.ix[i,'wma'] = sum/weight

ps: 关于加权移动平均,有多个加权的计算方式,代码里的仅仅只是其中一种,如果需求不同,也可以换做其他计算方式。

1、末日加权移动平均线: 

计算公式=(C1+C2+……+Cn×2)/(n+1)

我们用C代表收盘价。末日指的是最后一日哦,可不是世界末日。我们看到只有最后一天的收盘价(Cn)乘了一个2。这样,原来的n个价格就变成了(n+1)个,所以在求的平均的时候要除以(n+1)。

2、线性加权移动平均线:

计算公式=(C1×1+C2×2+……+Cn×n)/(1+2+…+n)

这一种均线计算起来有一点点麻烦,就是计算时每个价格都乘以一个权值,这个权值刚好是它的编号。对于线性这个词,如果大家不理解,那么请继续关注慧济,以后我会为大家详细解释。

3、梯型加权移动平均线:计算方法如下(5日为例):

计算公式=((C1+C2)×1+(C2+C3)×2+(C3+C4)×3+(C4+C5)×4)/(2×1+2×2+2×3+2×4)

是不是有点像梯形的面积公式啊?梯形面积=(上底+下底)×高/2

4、平方系数加权移动平均线:计算方法如下(5日为例):

MA=((C1×1×1)+( C2×2×2)+(C3×3×3)+(C4×4×4)+( C5×5×5))/(1×1+2×2+3×3+4×4+5×5)

两个好用的FreeBSD国内源

国内的FreeBSD镜像貌似不多,在网上浏览的时候发现了一个,赶紧修改/etc/pkg/FreeBSD.conf文件,记录下来。

FreeBSD: {
    url: "pkg+http://pkg1.chinafreebsd.cn/${ABI}/latest",
    mirror_type: "srv",  signature_type: "fingerprints",
    fingerprints: "/usr/share/keys/pkg",
    enabled: yes
}

还有一个貌似开通不久的中科大的源:

FreeBSD: {
    url: "pkg+http://mirrors.ustc.edu.cn/freebsd-pkg/${ABI}/latest",
    mirror_type: "srv",  signature_type: "fingerprints",
    fingerprints: "/usr/share/keys/pkg",
    enabled: yes
}

地址后面latest 表示滚动更新的版本库,如果想要稳定些的版本换成quarterly即可。

PHPcms与百度编辑器UEditor深度结合

记忆力是严重衰退了,关于百度编辑器还有个地方没提,那就是把PHP文件夹里的config.json文件得拷贝出来。我是放在了根目录下,然后为了系统好管理,需要打开这个文件修改编辑器上传后的目录。

“imagePathFormat”:"/ueditor/php/upload/..."

改成了:

“imagePathFormat”:"upload/image/..."

关于这里还有个小细节,就是分三种文件夹需要改三个地方,分别为图片image、视频video、文件file,注意分开别漏了就好。剩下的就是系统的存储目录修改了,打开/phpcms/libs/classes/attachment.class.php文件,在47行左右做如下改动:

$this->savepath = $this->upload_root.$this->upload_dir.date('Y/m/d');
$this->savepath = 'uploadfile/image/'.$this->upload_dir.date('Y/m/d');

把109行注释掉,第118行做如下改动:

$uploadedfile = array('filename'=>$file['name'],'filepath‘=>$filepath,....
$uploadedfile = array('filename'=>$file['name'],'filepath‘=>$savefile,....

这个文件最后把区分用户上传和管理员上传的字段补上,在add方法里,208行和220行之间随便哪个地方加上一句:

$uploadedfile['isuser'] = $_SESSION['roleid'] ? 0 : 1;

在这里我是用1表示为是注册用户0表示为管理员,做完这些就用phpMyAdmin或者Navicat之类的工具,在数据库附件表里手工加上这个字段。

再接着就是改系统的表单类了,文件是在/phpcms/libs/classes/form.class.php,改的代码很多,直接替换editor方法,代码如下:

public static function editor($textareaid = 'content',$module = '', $catid = '', $allowbrowser = 1,$alowuploadexts = '',$disabled_page = 0, $allowuploadnum = '10',$setting) {
		$editor = $setting['editor'] ? $setting['editor'] : 'ueditor' ;
		$str ='';
		if($editor == 'ueditor'){
			if(!defined('EDITOR_INIT')) {
			$str = '<script type="text/javascript" src="'.JS_PATH.'ueditor.full.1.4.3.2/ueditor.config.js"></script>';
			$str .= '<script type="text/javascript" src="'.JS_PATH.'ueditor.full.1.4.3.2/ueditor.all.min.js"></script>';
			define('EDITOR_INIT', 1);
			}
			if($setting['toolbar'] == 'basic') {
				$admin = defined('IN_ADMIN') ? "'Source'," : '';
				$toolbar = ",toolbars:[[".$admin."'FullScreen', 'Undo', 'Redo','Bold','italic','underline','strikethrough','superscript','subscript','formatmatch','removeformat','justifyleft','justifyright','justifycenter','justifyjustify','pasteplain','date','selectall','forecolor','insertorderedlist','insertunorderedlist','fontsize']]";
			} elseif($setting['toolbar'] == 'full') {
				if(defined('IN_ADMIN')) {
					$admin = "'Source',";
				} else {
					$admin = '';
				}
				$toolbar = ", toolbars: [[
				'fullscreen', ".$admin." '|', 'undo', 'redo', '|',
				'bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc', '|',
				'rowspacingtop', 'rowspacingbottom', 'lineheight', '|',
				'customstyle', 'paragraph', 'fontfamily', 'fontsize', '|',
				'directionalityltr', 'directionalityrtl', 'indent', '|',
				'justifyleft', 'justifycenter', 'justifyright', 'justifyjustify', '|', 'touppercase', 'tolowercase', '|',
				'link', 'unlink', 'anchor', '|', 'imagenone', 'imageleft', 'imageright', 'imagecenter', '|',
				'simpleupload', 'insertimage', 'emotion', 'scrawl', 'insertvideo', 'music', 'attachment', 'map', 'gmap', 'insertframe', 'insertcode', 'webapp', 'pagebreak', 'template', 'background', '|',
				'horizontal', 'date', 'time', 'spechars', 'snapscreen', 'wordimage', '|',
				'inserttable', 'deletetable', 'insertparagraphbeforetable', 'insertrow', 'deleterow', 'insertcol', 'deletecol', 'mergecells', 'mergeright', 'mergedown', 'splittocells', 'splittorows', 'splittocols', 'charts', '|',
				'print', 'preview', 'searchreplace', 'help', 'drafts'
			]]";
			}
			$str .= "<script type=\"text/javascript\">\r\n";
			$str .= "UE.getEditor('$textareaid',{";
			$str .= "serverUrl: '".APP_PATH."index.php?m=attachment&c=ueditupload&a=upload'";
			if($setting['height']) $str .= ',initialFrameHeight:'.$setting['height'];
			$str .= $toolbar;
			$str .= '})';
			$str .= '</script>';
		}else if($editor == 'ckeditor'){
			if(!defined('EDITOR_INIT')) {
				$str = '<script type="text/javascript" src="'.JS_PATH.'ckeditor4.5.9/ckeditor.js"></script>';
				define('EDITOR_INIT', 1);
			}
			if($setting['toolbar'] == 'basic') {
				$toolbar = defined('IN_ADMIN') ? "['Source']," : '';
				$toolbar .= "['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink' ],['Maximize'],\r\n";
			} elseif($setting['toolbar'] == 'full') {
				if(defined('IN_ADMIN')) {
					$toolbar = "['Source',";
				} else {
					$toolbar = '[';
				}
				$toolbar .= "'-','Templates'],
				['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print'],
				['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],['ShowBlocks'],['Image','Capture','Flash','flashplayer','MyVideo'],['Maximize'],
				'/',
				['Bold','Italic','Underline','Strike','-'],
				['Subscript','Superscript','-'],
				['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
				['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
				['Link','Unlink','Anchor'],
				['Table','HorizontalRule','Smiley','SpecialChar','PageBreak'],
				'/',
				['Styles','Format','Font','FontSize'],
				['TextColor','BGColor'],
				['attachment'],\r\n";
			} elseif($setting['toolbar'] == 'desc') {
				$toolbar = "['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink', '-', 'Image', '-','Source'],['Maximize'],\r\n";
			} else {
				$toolbar = '';
			}
			$str .= "<script type=\"text/javascript\">\r\n";
			$str .= "CKEDITOR.replace( '$textareaid',{";
			$str .= "height:{$setting['height']},";
		
			$show_page = ($module == 'content' && !$disabled_page) ? 'true' : 'false';
			$str .="pages:$show_page,subtitle:$show_page,textareaid:'".$textareaid."',module:'".$module."',catid:'".$catid."',\r\n";
			if($setting['allowupload']) {
				$authkey = upload_key("$allowuploadnum,$alowuploadexts,$allowbrowser");
				$str .="flashupload:true,alowuploadexts:'".$alowuploadexts."',allowbrowser:'".$allowbrowser."',allowuploadnum:'".$allowuploadnum."',authkey:'".$authkey."',\r\n";
			}
			if($setting['allowupload']) $str .= "filebrowserUploadUrl : 'index.php?m=attachment&c=attachments&a=upload&module=".$module."&catid=".$catid."&dosubmit=1',\r\n";
			if($color) {
				$str .= "extraPlugins : 'uicolor',uiColor: '$color',";
			}
			$str .= "toolbar :\r\n";
			$str .= "[\r\n";
			$str .= $toolbar;
			$str .= "]\r\n";
			$str .= "});\r\n";
			$str .= '</script>';
			$ext_str = "<div class='editor_bottom'>";
			if(!defined('IMAGES_INIT')) {
				$ext_str .= '<script type="text/javascript" src="'.JS_PATH.'swfupload/swf2ckeditor.js"></script>';
				define('IMAGES_INIT', 1);
			}
			$ext_str .= "<div id='page_title_div'>
			<table cellpadding='0' cellspacing='1' border='0'><tr><td class='title'>".L('subtitle')."<span id='msg_page_title_value'></span></td><td>
			<a class='close' href='javascript:;' onclick='javascript:$(\"#page_title_div\").hide();'><span>×</span></a></td>
			<tr><td colspan='2'><input name='page_title_value' id='page_title_value' class='input-text' value='' size='30'>&nbsp;<input type='button' class='button' value='".L('submit')."' onclick=insert_page_title(\"$textareaid\",1)></td></tr>
			</table></div>";
			$ext_str .= "</div>";
			if(is_ie()) $ext_str .= "<div style='display:none'><OBJECT id='PC_Capture' classid='clsid:021E8C6F-52D4-42F2-9B36-BCFBAD3A0DE4'><PARAM NAME='_Version' VALUE='0'><PARAM NAME='_ExtentX' VALUE='0'><PARAM NAME='_ExtentY' VALUE='0'><PARAM NAME='_StockProps' VALUE='0'></OBJECT></div>";
			$str .= $ext_str;
		}
		
		
		return $str;
	}

最后一步了,在模型管理里字段编辑时添加上编辑器选择项,文件在/phpcms/modules/content/fields/editor/目录下面,一共有field_add_form.inc.php、field_edit_form.inc.php、form.inc.php三个文件,头两个文件在第二行加入:

<tr> 
      <td width="100">编辑器选择:</td>
      <td><input type="radio" name="setting[editor]" value="ueditor" checked> 百度编辑器 <input type="radio" name="setting[editor]" value="ckeditor"> CK编辑器 </td>
</tr>

第三个文件在第6行后加入:

$setting = string2array($setting);

把倒数第二行的return内容改成如下:

return "<div id='{$field}_tip'></div>".'<textarea name="info['.$field.']" id="'.$field.'" boxid="'.$field.'">'.$value.'</textarea>'.form::editor($field,'content',$this->catid,1,'',$disabled_page,$allowuploadnum,$setting);

好了,至此大功告成,百度编辑器就算完美的和PHPcms结合了,整个过程步骤很多,需要细心调整,这样的结果是还可以往里面添加各类的编辑器,在管理员后台随时的更换。回顾整个过程最麻烦的主要在修改上传设置方面,为了和系统统一改了太多东西,如果不需要这功能的情况下,那就只要改form表单类和content目录下的字段设置文件这两地方就可以实现自由切换编辑器了。

Ubuntu 16.04 安装 LAMP

近日用重装了系统,用了几个一键安装包,但使用起来特别的不爽,数据库的速度很慢很慢,在测试的结果发现一个简单的查询几乎是原生安装的三十分之一,在Python里查询一键安装包的数据库需要1.12秒时间的同样代码在原生安装的数据库里只需要0.003秒,具体原因我没查出来,但测试了几台电脑都是同样情况,所以,干脆还是自己配置环境吧,不能偷懒了。

1.安装之前先更新系统

sudo apt update

2.安装Apache2

sudo apt install apache2

3.安装PHP

sudo apt install php
sudo apt-get install libapache2-mod-php

4.安装MySQL

sudo apt install mysql-server php7.0-mysql
sudo apt-get install mysql-client
mysql_secure_installation

5.安装phpmyadmin

sudo apt-get install phpmyadmin
sudo apt-get install php-mbstring
sudo apt-get install php-gettext
sudo ln -s /usr/share/phpmyadmin /var/www/html/phpmyadmin

配置phpmyadmin

sudo gedit /etc/php/7.0/apache2/php.ini

去掉注释extension=php_mbstring.dll (开启mbstring)

部分的国内PyPi源,存以备用

对于Python开发用户来讲,PIP安装软件包是家常便饭。但国外的源下载速度实在太慢,浪费时间。而且经常出现下载后安装出错问题。所以把PIP安装源替换成国内镜像,可以大幅提升下载速度,还可以提高安装成功率。

国内源(新版ubuntu要求使用https源,要注意):

清华:https://pypi.tuna.tsinghua.edu.cn/simple/

豆瓣:http://pypi.douban.com/simple/

阿里云:http://mirrors.aliyun.com/pypi/simple/

中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/

华中理工大学:http://pypi.hustunique.com/

山东理工大学:http://pypi.sdutlinux.org/

临时使用:

可以在使用pip的时候加参数-i https://pypi.tuna.tsinghua.edu.cn/simple。例如:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspider,这样就会从清华这边的镜像去安装pyspider库。

永久修改,一劳永逸:

Linux下,修改 ~/.pip/pip.conf (没有就创建一个文件夹及文件。文件夹必须要加“.”,表示是隐藏文件夹)

内容如下:

[global]
trusted-host =  pypi.douban.com
index-url = http://pypi.douban.com/simple

windows下,直接在user目录中创建一个pip目录,如:C:\Users\xx\pip,新建文件pip.ini。内容同上。

用Pandas计算前复权数据

得到大智慧的除权数据琢磨了好些日子,最后发现还是用Pandas来解决是最便捷的,后复权还没做,先在这里做个笔记,备忘。

以股票600725为例,除权数据赋予变量ea,数据为dataframe格式,结构如下:

            present  bonus  price  rationed
date                                       
2016-12-28     10.0   0.00    0.0       0.0
2012-06-04      0.0   0.50    0.0       0.0
2011-05-20      0.0   1.00    0.0       0.0
2010-05-13      8.0   0.23    0.0       0.0
2009-05-15      0.0   2.00    0.0       0.0
2008-04-09      0.0   3.00    0.0       0.0
2007-04-12      0.0   1.00    0.0       0.0
2006-05-22      0.0   1.00    0.0       0.0
2006-02-10      3.5   0.00    0.0       0.0
2005-03-28      5.0   0.50    0.0       0.0
2003-03-26      0.0   2.00    0.0       0.0
2002-04-15      0.0   2.00    0.0       0.0
1997-06-05      0.0   2.00    0.0       0.0

如果做2016年11月1日到12月31日的复权,则对数据做切片:

ea = ea.ix[datetime.datetime(2016,12,31,0,0,0):datetime.datetime(2016,11,1,0,0,0)]

这样除权数据就只剩一条了:

            present  bonus  price  rationed
date                                       
2016-12-28     10.0    0.0    0.0       0.0

接着取出2016年11月1日到12月31日期间600725的日K数据赋予变量df,同样是dataframe格式,虽然我这里现在只需要一条除权数据,但做成循环更好一些,拿出每条除权数据去计算。里面再嵌套K线数据的字段循环,完成所有字段的前复权处理:

for key,val in ea.iterrows():
    date = key - datetime.timedelta(days=1)
    for field in df.columns.values:
        if field != 'volume' and field != 'amount':
            df.ix[date:,field] -= val.bonus/10
            df.ix[date:,field] += val.price*(val.rationed/10)
            df.ix[date:,field] /= 1 + val.present/10 + val.rationed/10

PS:交易量和交易金额不需要做复权,所以加了个判断排除掉。后复权公式有点不同,但方法是类似的,方法上需要注意的就是时间切片这个问题。另外我喜欢用数据倒序使用,所以在前复权时key需要减一天,如果是正序是不用的。另在动态的情况下,对于除权数据的切片可以使用K线数据的头尾日期去切片,切完之后再做处理节约计算机资源。

PHPcms添加UEditor百度编辑器

其实对于修改百度编辑器的文章,在网路上一搜一大把,在没有深入读PHPcms代码的时候也照着这些文章改过,但来来去去这些文章都仿佛是一个人写的,连截图都是一模一样,这也是现在网络文章的一大现状,搜到很多的资料都是一样的。重要的是改得并不彻底,存在很多问题,基本上都仅仅只是解决了文字编辑而已。

去年的时候在基于PHPcms的框架上深入的DIY了一个网站,应该可以说是很完美的把两者结合了吧,系统可以在管理员的后台自由的切换管理员使用的编辑器和用户使用的编辑器,在UEditor和原本CKEditor之间方便的切换,依照这个方式也可以继续添加更多的编辑器,在你愿意的情况下。另外根据百度编辑器的上传功能,完善了附件的功能,使之每个用户只能看到自己上传的图片或者文件。当然,这在单一用户的网站上是毫无意义的。所以,我是为了记录下这个流程而已,免得下次再来又要研究一下,而各位就挑着看吧。

我下载的版本是ueditor.full.1.4.3.2,下载地址是http://ueditor.baidu.com/build/build_down.php?n=ueditor&v=1_4_3_2-src,现在最新版本已经是http://ueditor.baidu.com/build/build_down.php?n=ueditor&v=1_4_3_3-utf8-php,各位也可以试试最新版,我没对比过,相信应该变动不大。

下载之后解压放在./statics/js/下,系统的JS扩展包都在这里了;另外把config.json文件放在网站的根目录下,这个文件标注的是上传浏览之类动作后台的具体操作动作。

首先修改它的URL地址,打开ueditor.config.js文件,添加一句:

var URL = window.UEDITOR_HOME_URL || getUEBasePath();

在window.UEDITOR_CONFIG里添加一句:

UEDITOR_HOME_URL: URL

再接着修改上传后台接收地址:

serverUrl: '?m=attachment&c=ueditupload&a=upload'

注:attachment是系统附件管理的模块,ueditupload和upload都是我自建的,各位可以按照自己的喜好来

然后当然是在./phpcms/modules/attachment/目录下建立ueditupload.php文件了,代码如下:

<?
defined('IN_PHPCMS') or exit('No permission resources.'); 
$session_storage = 'session_'.pc_base::load_config('system','session_storage');
pc_base::load_sys_class($session_storage);


class ueditupload {
    private $userid,$isuser,$isadmin;
    function __construct(){
        $this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE'));
        $this->isuser = $_SESSION['roleid'] ? 0 : 1;
        $this->isadmin =  $_SESSION['roleid'] ? 1 : 0;
        $this->att_db = pc_base::load_model('attachment_model');
    }
    private function updata($fileinfo){
        if($fileinfo['state'] == 'SUCCESS'){
            $data['module'] = 'content';
            $data['catid'] = 0;
            $data['filename'] = $fileinfo['original'];
            $data['filepath'] = $fileinfo['url'];
            $data['filesize'] = $fileinfo['size'];
            $data['fileext'] = substr($fileinfo['type'],1);
            $data['isimage'] = in_array($fileinfo['type'],array(".png", ".jpg", ".jpeg", ".gif", ".bmp")) ? 1 : 0;
            $data['isthumb'] = 0;
            $data['downloads'] = 0;
            $data['userid'] = $this->userid;
            $data['uploadtime'] = time();
            $data['uploadip'] = ip();
            $data['status'] = 0;
            $data['authcode'] = md5($fileinfo['url']);
            $data['siteid'] = 1;
            $data['isuser'] = $this->isuser;
            $this->att_db->insert($data);
        }
    }

    public function upload(){
        error_reporting(E_ERROR);
        header("Content-Type: text/html; charset=utf-8");
        $CONFIG = json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "", file_get_contents("config.json")), true);
        $action = htmlspecialchars($_GET['action']);
        $base64 = 'upload';
        $uploader = pc_base::load_app_class('uploader');
        
        switch ($action) {
            case 'config':
                $result =  json_encode($CONFIG);
                break;
            /* 上传图片 */
            case 'uploadimage':
                $config = array(
                    "pathFormat" => $CONFIG['imagePathFormat'],
                    "maxSize" => $CONFIG['imageMaxSize'],
                    "allowFiles" => $CONFIG['imageAllowFiles']
                );
                $fieldName = $CONFIG['imageFieldName'];
                $uploader->upload($fieldName, $config, $base64);
                $result = json_encode($uploader->getFileInfo());
                $this -> updata($uploader->getFileInfo());
                break;
            /* 上传涂鸦 */
            case 'uploadscrawl':
                $config = array(
                    "pathFormat" => $CONFIG['scrawlPathFormat'],
                    "maxSize" => $CONFIG['scrawlMaxSize'],
                    "allowFiles" => $CONFIG['scrawlAllowFiles'],
                    "oriName" => "scrawl.png"
                );
                $fieldName = $CONFIG['scrawlFieldName'];
                $base64 = "base64";
                $uploader->upload($fieldName, $config, $base64);
                $result = json_encode($uploader->getFileInfo());
                $this -> updata($uploader->getFileInfo());
                break;
            /* 上传视频 */
            case 'uploadvideo':
                $config = array(
                    "pathFormat" => $CONFIG['videoPathFormat'],
                    "maxSize" => $CONFIG['videoMaxSize'],
                    "allowFiles" => $CONFIG['videoAllowFiles']
                );
                $fieldName = $CONFIG['videoFieldName'];
                $uploader->upload($fieldName, $config, $base64);
                $result = json_encode($uploader->getFileInfo());
                $this -> updata($uploader->getFileInfo());
                break;
            /* 上传文件 */
            case 'uploadfile':
                $config = array(
                    "pathFormat" => $CONFIG['filePathFormat'],
                    "maxSize" => $CONFIG['fileMaxSize'],
                    "allowFiles" => $CONFIG['fileAllowFiles']
                );
                $fieldName = $CONFIG['fileFieldName'];
                $uploader->upload($fieldName, $config, $base64);
                $result = json_encode($uploader->getFileInfo());
                $this -> updata($uploader->getFileInfo());
                break;

            /* 列出图片 */
            case 'listimage':
                $pagesize = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : 20;
                $startpage = isset($_GET['start']) ? intval(htmlspecialchars($_GET['start'])) / $pagesize + 1 : 0;
                if($this->isadmin == 1){
                    //如果是管理员
                    $countwhere = 'isimage = 1';
                    $listwhere = 'isimage = 1';             
                }else{
                    $countwhere = 'isimage = 1 AND isuser = 1 AND userid = '.$this->userid;
                    $listwhere = 'isimage = 1 AND isuser = 1 AND userid = '.$this->userid;
                }
                $count = $this->att_db->count($countwhere);
                $row = $this->att_db->listinfo($listwhere,'uploadtime DESC',$startpage,$pagesize);
                $list = array();
                foreach($row as $key => $val){
                    $list[$key]['url'] = APP_PATH.$row[$key]['filepath'];
                    $list[$key]['mtime'] = $row[$key]['uploadtime'];
                }
                $result = json_encode(array(
                    "state" => "SUCCESS",
                    "list" => $list,
                    "start" => $_GET['start'] == 0 ? 0 : $pagesize * ($startpage - 1),
                    "total" => $count
                ));
                break;
            /* 列出文件 */
            case 'listfile':
                $pagesize = isset($_GET['size']) ? htmlspecialchars($_GET['size']) : 20;
                $startpage = isset($_GET['start']) ? intval(htmlspecialchars($_GET['start'])) / $pagesize + 1 : 0;
                if($this->isadmin == 1){
                    //如果是管理员
                    $countwhere = 'isimage = 1';
                    $listwhere = 'isimage = 1';             
                }else{
                    $countwhere = 'isimage = 0 AND isuser = 1 AND userid = '.$this->userid;
                    $listwhere = 'isimage = 0 AND isuser = 1 AND userid = '.$this->userid;
                }
                $count = $this->att_db->count($countwhere);
                $row = $this->att_db->listinfo($listwhere,'uploadtime DESC',$startpage,$pagesize);
                $list = array();
                foreach($row as $key => $val){
                    $list[$key]['url'] = $row[$key]['filepath'];
                    $list[$key]['mtime'] = $row[$key]['uploadtime'];
                }
                $result = json_encode(array(
                    "state" => "SUCCESS",
                    "list" => $list,
                    "start" => $_GET['start'] == 0 ? 0 : $pagesize * ($startpage - 1),
                    "total" => $count
                ));
                break;

            /* 抓取远程文件 */
            case 'catchimage':
                $config = array(
                    "pathFormat" => $CONFIG['catcherPathFormat'],
                    "maxSize" => $CONFIG['catcherMaxSize'],
                    "allowFiles" => $CONFIG['catcherAllowFiles'],
                    "oriName" => "remote_".time().".png"
                );
                $fieldName = $CONFIG['catcherFieldName'];
                /* 抓取远程图片 */
                $list = array();
                if (isset($_POST[$fieldName])) {
                    $source = $_POST[$fieldName];
                } else {
                    $source = $_GET[$fieldName];
                }
                foreach ($source as $imgUrl) {
                    $uploader->upload($imgUrl, $config, 'remote');
                    $info = $uploader->getFileInfo();
                    $this -> updata($uploader->getFileInfo());
                    array_push($list, array(
                        "state" => $info["state"],
                        "url" => APP_PATH.$info["url"],
                        "size" => $info["size"],
                        "title" => htmlspecialchars($info["title"]),
                        "original" => htmlspecialchars($info["original"]),
                        "source" => htmlspecialchars($imgUrl)
                    ));
                }
                /* 返回抓取数据 */
                $result = json_encode(array(
                    'state'=> count($list) ? 'SUCCESS':'ERROR',
                    'list'=> $list
                ));
                break;
            default:
                $result = json_encode(array(
                    'state'=> '请求地址出错'
                ));
                break;
        }       
        /* 输出结果 */
        if (isset($_GET["callback"])) {
            if (preg_match("/^[\w_]+$/", $_GET["callback"])) {
                echo htmlspecialchars($_GET["callback"]) . '(' . $result . ')';
            } else {
                echo json_encode(array(
                    'state'=> 'callback参数不合法'
                ));
            }
        } else {
            echo $result;
        }
        //print_r($_POST);
    }
}
?>

晕,貌似提示说字数超出最大值了,只好分篇了吧,在完成上述的步骤后,上传已经分用户存入数据库了,获取也会按照会员分别列出。

用Python抓取新浪的股票数据

最近做数据分析,先是找到了Tushare这个免费开源的第三方财经包,但后来用了几天之后发现,它的日交易历史数据有时候有不准确的情况,查看源代码发现,这个包的数据源是凤凰财经,而对比凤凰网站其站点的数据本身就是有出入的,所以到也不是Tushare的问题。于是百度了一圈,发现很多网友都是获取新浪的股票数据,包括其历史数据和实时数据。于是乎试了一下,发现速度还挺快,没有具体去测时间但从感官上要比Tushare获取的凤凰数据要快得多。并且数据也很丰富,囊括了每只票自上市以来的所有数据,对此Tushare貌似只有三年数据。当然,新浪数据也有不足的地方,细节上没凤凰数据那么丰富,没有价MA5、MA10以及量MA5、MA10等等,最重要的还是缺少每天的交易额。所幸我目前计算所需的数据里还不包括每天交易额。

新浪财经的数据接口地址是:http://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/var=/CN_MarketData.getKLineData?symbol=sz000001&scale=240&ma=no&datalen=60。在地址里symbol指的是股票代码,这里需要注意的是不能只填数字代码,还需要把交易市场的前缀加上去,比如sz000001指的是平安银行,而sh000001则是上证指数;scale表示的是时间长度,以分钟为基本单位,输入240就表示下载日K线数据,60就是小时K线数据,貌似最短时间是5分钟,并没有提供分钟数据;datalen则是获取数据的条数,在日K线的时间长度了,datalen就是获取60天日K数据,当然也可以获取60小时K数据。

人生苦短,我用Python,所以代码就用它了,其实以前一直是用世界上最好的语言PHP 的,这是为了做数据分析才开始学着用Python,代码粗糙了些,返回的是个列表,每笔数据则是字典,将就着看吧。

import urllib.request
links = 'http://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/var=/CN_MarketData.getKLineData?symbol=' + code + '&scale=' + str(scale) + '&ma=no&datalen='+str(datalen)
histData = urllib.request.urlopen(links).read()
histData = str(histData).split('[')[1]
histData = histData[1:len(histData) - 4].split('},{')
datas = []
for i in range(0, len(histData)):
    column = {}
    dayData = histData[i].split(',')
    for j in range(0, len(dayData)):
       field = dayData[j].split(':"')
       if field[0] == 'day':
          column['date'] = field[1].replace('"', '')
       else:
          column[field[0]] = field[1].replace('"', '')
    datas.append(column)
return datas

PS:这里要说明的是新浪默认字段里,日期字段名称是day,个人觉得很是膈应,如果是小时数据或者5分钟数据还叫day岂不难受?所以改成了 date,其实没什么大的意义,个人喜好而已。

实时数据获取方式和历史数据差别不大,需要的也是完整代码,地址是:http://hq.sinajs.cn/list=sz000001,不同的是实时数据可以多支同时获取的,代码之间用逗号隔开就可以了,经过实验,貌似最多一次可以获取100只票的实时数据。

links = 'http://hq.sinajs.cn/list=' + codes
realTimeData = urllib.request.urlopen(links).read()
realTimeData = realTimeData.decode('gbk').replace('"','').split('\n')
data = {}
for i in range(len(realTimeData)-1):
    if len(realTimeData[i]) > 0:
       data[realTimeData[i].split('=')[0].split('_')[2][2:]] = realTimeData[i].split('=')[1].split(',')[:-1]
return data

PS:大家获取可别太多线程,我试过,会被封。