從無到用寫個股票分析APP(一)

前言:再給自己挖個坑吧。


我想寫個什麼東西呢?

一:可以瀏覽當下相關資訊,以及大盤指數實時更新。

二:添加自選股票,可以查看該股票的走勢圖,相關資訊以及基本數據。

三:通過 server 端定義相關指標及常用策略,手機上可以直接添加已定義的技術指標及策略用以組合,然後在在 server 端得到結果,手機端查看。


項目地址:https://github.com/youerning/pstock


所用技術:

● nodejs:socket.io

● golang

● javascript:angularjs,chartjs

● css.

● Python:tushare,PyAlgoTrade,tornado,flask

● 打包:ionic


然後預覽一下兩天做的 demo


wKioL1lU21bQGX12AADR-YWIVUc817.png-wh_50wKioL1lU21eTCF7wAAAzOCZkoa4391.png-wh_50wKiom1lU21ngdN7QAABAYXoEmBs877.png-wh_50

wKioL1lU21yDE5RHAABcM4LbGYM181.png-wh_50


文章目錄:

一:佈局

二:部分細節說明

三:獲取數據

四:繪圖

五:編寫策略 //等待填坑

六:優化細節 //等待填坑

七:美化,收尾 //等待填坑

注:爲了使文章不會過於冗長,代碼細節可能有所刪減,詳情參考項目源碼:


(一)

1. 環境搭建參考:從無到有寫一個運維APP(一)


2. 創建項目

ionic start pstock blank


3. 編寫index.html。

<ion-tabs class="tabs-icon-top">
<!-- 首頁 -->
<ion-tab title="首頁" icon="ion-home" href="#/home">
<ion-nav-view name="tab-home"></ion-nav-view>
</ion-tab>
<!-- 自選 -->
<ion-tab title="自選" icon="ion-person-add" href="#/user">
<ion-nav-view name="tab-user"></ion-nav-view>
</ion-tab>
<!-- 回測 -->
<ion-tab title="回測" icon="ion-clock" href="#/backtest">
<ion-nav-view name="tab-backtest"></ion-nav-view>
</ion-tab>
</ion-tabs>


4. 創建相應模板文件,結構大致如下


wKioL1lU3qDzdV6fAABJzR37VWQ807.png-wh_50


5. 創建路由

app.config(function($stateProvider, $urlRouterProvider, $ionicConfigProvider) {
$ionicConfigProvider.tabs.position('bottom');
$stateProvider
.state("home", {
url:"/home",
views:{
"tab-home":{
controller:"homeCtrl",
templateUrl: "tpls/home.html"
}
}
});
略...


至此,基本結構確定。


(二)

1. 上拉,下拉。

按住屏幕上下拖動,用以刷新數據以及加載數據在 ionic 的 JavaScript 組件已經有現成的了,所以可以直接拿過來用


代碼如下:

<ion-refresher
pulling-text="Pull to refresh..."
on-refresh="loadNewer()">
</ion-refresher>
<a class="item item-thumbnail-left"
ng-repeat="n in news track by n.item_id"
ng-click="openLink(n.article_url)">
<img ng-src="`n`.`media_avatar_url`">
<h2 class="news-title">`n`.`title`</h2>
<p><span am-time-ago="n.behot_time | amFromUnix"></span> - `n`.`media_name`</p>
</a>
</div>
<ion-infinite-scroll
on-infinite="loadOlder()"
distance="1%">
</ion-infinite-scroll>


然後在相應的 controller 裏面定義指定的執行函數 loadNewer(),loadOlder()


2. 自選股票的數據保存。

因爲沒有打算將自選的股票放在 server 端,所以數據應該保存在本地,即 localStorage 裏面


$scope.userCode = angular.fromJson(window.localStorage["userCode"] || "{}");
function persist() {
window.localStorage["userCode"] = angular.toJson($scope.userCode)
};


(三)

1. 獲取新聞數據

在國內獲取數據時間很難過的事情,爲什麼難過就不說了,當然可以自己爬,但是那樣太不優雅了。


這裏我們今日頭條的新聞數據(今日頭條不是沒有公開過自己的API麼?)


首先我們打開以下今日頭條的網站


wKiom1lU38nxiyKMAAISoG_S-cM776.png-wh_50

wKiom1lU38vReJKCAABd6qf5Fvo834.png-wh_50

spacer.gif

然後數據就出現了,就是這麼有尿性,其實還有很多網站也這樣,大家可以自己試試。


參考:

https://github.com/iMeiji/Toutiao/wiki/%E4%BB%8A%E6%97%A5%E5%A4%B4%E6%9D%A1Api%E5%88%86%E6%9E%90


2. 獲取股票數據

這裏用 tushare,當然了也可以用其他的 API。

參考:http://tushare.org/trading.html#id2


3. 策略數據(待填坑。。。)

PyAlgoTrade 策略。

其實直接用 tushare 的數據會報錯,不過,也就是少了個 Adj Close,加個字段也不會那麼難得。。。


4. server端代碼

#coding: utf8
from flask import Flask
from flask import Response, request, abort
import urlparse
import requests
import json
import tushare as ts
from random import randint
from bs4 import BeautifulSoup
import pandas as pd
# import sys
# reload(sys)
# sys.setdefaultencoding('utf-8')
app = Flask(__name__)
# sinaApi = "http://hq.sinajs.cn/list="
detailUrl = "http://stockpage.10jqka.com.cn/%s/company/"
toutiao = "http://www.toutiao.com/api/article/recent/?source=2&category=%s&as=A105177907376A5&cp=5797C7865AD54E1&count=5&offset=0&_=%s"
def getUserAgent():
userAgent = ["Mozilla/5.0 (compatible, MSIE 10.0, Windows NT, DigExt)",
"Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, 360SE)",
"Mozilla/4.0 (compatible, MSIE 8.0, Windows NT 6.0, Trident/4.0)",
"Mozilla/5.0 (compatible, MSIE 9.0, Windows NT 6.1, Trident/5.0,",
"Opera/9.80 (Windows NT 6.1, U, en) Presto/2.8.131 Version/11.11",
"Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, TencentTraveler 4.0)",
"Mozilla/5.0 (Windows, U, Windows NT 6.1, en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Macintosh, Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh, U, Intel Mac OS X 10_6_8, en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
"Mozilla/5.0 (Linux, U, Android 3.0, en-us, Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
"Mozilla/5.0 (iPad, U, CPU OS 4_3_3 like Mac OS X, en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"Mozilla/4.0 (compatible, MSIE 7.0, Windows NT 5.1, Trident/4.0, SE 2.X MetaSr 1.0, SE 2.X MetaSr 1.0, .NET CLR 2.0.50727, SE 2.X MetaSr 1.0)",
"Mozilla/5.0 (iPhone, U, CPU iPhone OS 4_3_3 like Mac OS X, en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
"MQQBrowser/26 Mozilla/5.0 (Linux, U, Android 2.3.7, zh-cn, MB200 Build/GRJ22, CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"]
return userAgent[randint(0,len(userAgent)-1)]
@app.route("/<app>/", methods=["GET","POST"])
def index(app):
headers = {"User-Agent": getUserAgent()}
code = request.args["code"]
data = {}
error = ""
if app == "now":
# 獲取當前價格
code = code.split(",")
df = ts.get_realtime_quotes(code)
ret = df.to_json()
elif app == "stock":
# 獲取股票歷史數據
df = ts.get_hist_data(code)
df = df.sort_index()
df["date"] = df.index
df.index = range(len(df.index))
ret = df.to_json()
elif app == "detail":
# 獲取股票基本數據
# 公司名稱
# 所屬地域
# 公司簡介
# 經營範圍
ret = {}
url = detailUrl % code
page = requests.get(url, headers=headers)
soup = BeautifulSoup(page.content, "html.parser")
name = soup.select("td span")[0].text
bussines = soup.select("td span")[3].text
region = soup.select("td span")[1].text
intro = soup.select("p.tip.lh24")[-2].text[:-3]
ret["name"] = name
ret["bussines"] = bussines
ret["region"] = region
ret["intro"] = intro
elif app == "bt":
ret = [{"status":"ok"}]
elif app == "news":
# 反向代理今日頭條
catelog = request.args["catelog"]
time = request.args["now"]
url = toutiao % (catelog, time)
page = requests.get(url, headers=headers)
ret = [{"status":"ok"}]
else:
ret = ""
error = "incorrect url"
try:
data["data"] = json.loads(ret)
except Exception as e:
data["data"] = ret
data["error"] = error
# print data
resp = Response(json.dumps(data))
if error:
abort(500)
resp.headers["Content-Type"] = "application/json; charset=UTF-8"
resp.headers["access-control-allow-origin"] = "*"
return resp
if __name__ == "__main__":
app.run(port=80,debug=True, host="0.0.0.0")


5. client 端代碼

$http.get(surl)
.success(function(resp) {
$scope.labelsline = Object.values(resp.data.date);
$scope.seriesline = ["ma5", "ma10", "ma20", "close"];
$scope.dataline = [
Object.values(resp.data.ma5),
Object.values(resp.data.ma10),
Object.values(resp.data.ma20),
Object.values(resp.data.close)];
$scope.optionsline = {
title: {
display:true,
text: "趨勢圖"
},
elements: {
point:{
radius: 0
}
},
xAxis: {
display:true,
axisLabel: 'X Axis',
rotateLabels: 90
}
};


(四)

用 echarts 或者 chartjs,其實這沒有技術含量的來着。。。主要查 API。

不過似乎手機端顯示有問題,可能數據量過大或者不兼容之類的,待排查。。。


5,6,7 待填坑


自問自答:

Q:明明沒用 golang,socket.io,tornado,爲毛在所用技術中寫出來。

A:我構思了,可是還沒寫完。


Q:寫一個 web 的不也挺好的麼。

A:寫完了 app 自然會寫 web 的。。。


後記:值得一說的事,好像也沒想象中的那麼簡單,預想是三天就寫完的來着,在下一篇之前,我應該先寫 pyalgotrade 源碼解讀。


如果覺得不錯,並有所收穫,請我喝杯茶唄


wKioL1lU4MXwELckAADg-gB3Tsc583.jpg-wh_50wKiom1lU4Mqg8rxIAADzypnX0FU518.jpg-wh_50


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章