溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

從無(wú)到用寫個(gè)股票分析APP(一)

發(fā)布時(shí)間:2020-08-08 01:23:48 來(lái)源:網(wǎng)絡(luò) 閱讀:22217 作者:youerning 欄目:系統(tǒng)運(yùn)維

前言:再給自己挖個(gè)坑吧。


我想寫個(gè)什么東西呢?

一:可以瀏覽當(dāng)下相關(guān)資訊,以及大盤指數(shù)實(shí)時(shí)更新。

二:添加自選股票,可以查看該股票的走勢(shì)圖,相關(guān)資訊以及基本數(shù)據(jù)。

三:通過(guò) server 端定義相關(guān)指標(biāo)及常用策略,手機(jī)上可以直接添加已定義的技術(shù)指標(biāo)及策略用以組合,然后在在 server 端得到結(jié)果,手機(jī)端查看。


項(xiàng)目地址:https://github.com/youerning/pstock


所用技術(shù):

● nodejs:socket.io

● golang

● javascript:angularjs,chartjs

● css.

● Python:tushare,PyAlgoTrade,tornado,flask

● 打包:ionic


然后預(yù)覽一下兩天做的 demo


從無(wú)到用寫個(gè)股票分析APP(一)從無(wú)到用寫個(gè)股票分析APP(一)從無(wú)到用寫個(gè)股票分析APP(一)

從無(wú)到用寫個(gè)股票分析APP(一)


文章目錄:

一:布局

二:部分細(xì)節(jié)說(shuō)明

三:獲取數(shù)據(jù)

四:繪圖

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

六:優(yōu)化細(xì)節(jié) //等待填坑

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

注:為了使文章不會(huì)過(guò)于冗長(zhǎng),代碼細(xì)節(jié)可能有所刪減,詳情參考項(xiàng)目源碼:


(一)

1. 環(huán)境搭建參考:從無(wú)到有寫一個(gè)運(yùn)維APP(一)


2. 創(chuàng)建項(xiàng)目

ionic start pstock blank


3. 編寫index.html。

<ion-tabs class="tabs-icon-top">
<!-- 首頁(yè) -->
<ion-tab title="首頁(yè)" 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>
<!-- 回測(cè) -->
<ion-tab title="回測(cè)" icon="ion-clock" href="#/backtest">
<ion-nav-view name="tab-backtest"></ion-nav-view>
</ion-tab>
</ion-tabs>


4. 創(chuàng)建相應(yīng)模板文件,結(jié)構(gòu)大致如下


從無(wú)到用寫個(gè)股票分析APP(一)


5. 創(chuàng)建路由

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


至此,基本結(jié)構(gòu)確定。


(二)

1. 上拉,下拉。

按住屏幕上下拖動(dòng),用以刷新數(shù)據(jù)以及加載數(shù)據(jù)在 ionic 的 JavaScript 組件已經(jīng)有現(xiàn)成的了,所以可以直接拿過(guò)來(lái)用


代碼如下:

<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`">
<h3 class="news-title">`n`.`title`</h3>
<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>


然后在相應(yīng)的 controller 里面定義指定的執(zhí)行函數(shù) loadNewer(),loadOlder()


2. 自選股票的數(shù)據(jù)保存。

因?yàn)闆](méi)有打算將自選的股票放在 server 端,所以數(shù)據(jù)應(yīng)該保存在本地,即 localStorage 里面


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


(三)

1. 獲取新聞數(shù)據(jù)

在國(guó)內(nèi)獲取數(shù)據(jù)時(shí)間很難過(guò)的事情,為什么難過(guò)就不說(shuō)了,當(dāng)然可以自己爬,但是那樣太不優(yōu)雅了。


這里我們今日頭條的新聞數(shù)據(jù)(今日頭條不是沒(méi)有公開(kāi)過(guò)自己的API么?)


首先我們打開(kāi)以下今日頭條的網(wǎng)站


從無(wú)到用寫個(gè)股票分析APP(一)

從無(wú)到用寫個(gè)股票分析APP(一)

從無(wú)到用寫個(gè)股票分析APP(一)

然后數(shù)據(jù)就出現(xiàn)了,就是這么有尿性,其實(shí)還有很多網(wǎng)站也這樣,大家可以自己試試。


參考:

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. 獲取股票數(shù)據(jù)

這里用 tushare,當(dāng)然了也可以用其他的 API。

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


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

PyAlgoTrade 策略。

其實(shí)直接用 tushare 的數(shù)據(jù)會(huì)報(bào)錯(cuò),不過(guò),也就是少了個(gè) Adj Close,加個(gè)字段也不會(huì)那么難得。。。


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":
# 獲取當(dāng)前價(jià)格
code = code.split(",")
df = ts.get_realtime_quotes(code)
ret = df.to_json()
elif app == "stock":
# 獲取股票歷史數(shù)據(jù)
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":
# 獲取股票基本數(shù)據(jù)
# 公司名稱
# 所屬地域
# 公司簡(jiǎn)介
# 經(jīng)營(yíng)范圍
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.lh34")[-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: "趨勢(shì)圖"
},
elements: {
point:{
radius: 0
}
},
xAxis: {
display:true,
axisLabel: 'X Axis',
rotateLabels: 90
}
};


(四)

用 echarts 或者 chartjs,其實(shí)這沒(méi)有技術(shù)含量的來(lái)著。。。主要查 API。

不過(guò)似乎手機(jī)端顯示有問(wèn)題,可能數(shù)據(jù)量過(guò)大或者不兼容之類的,待排查。。。


5,6,7 待填坑


自問(wèn)自答:

Q:明明沒(méi)用 golang,socket.io,tornado,為毛在所用技術(shù)中寫出來(lái)。

A:我構(gòu)思了,可是還沒(méi)寫完。


Q:寫一個(gè) web 的不也挺好的么。

A:寫完了 app 自然會(huì)寫 web 的。。。


后記:值得一說(shuō)的事,好像也沒(méi)想象中的那么簡(jiǎn)單,預(yù)想是三天就寫完的來(lái)著,在下一篇之前,我應(yīng)該先寫 pyalgotrade 源碼解讀。


如果覺(jué)得不錯(cuò),并有所收獲,請(qǐng)我喝杯茶唄


從無(wú)到用寫個(gè)股票分析APP(一)從無(wú)到用寫個(gè)股票分析APP(一)


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI