Showing
9 changed files
with
154 additions
and
28 deletions
LICENSE
0 → 100644
1 | +Copyright (c) [2021] 김다빈,이가원 | ||
2 | + | ||
3 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
4 | +of this software and associated documentation files (the "Software"), to deal | ||
5 | +in the Software without restriction, including without limitation the rights | ||
6 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
7 | +copies of the Software, and to permit persons to whom the Software is | ||
8 | +furnished to do so, subject to the following conditions: | ||
9 | + | ||
10 | +The above copyright notice and this permission notice shall be included in all | ||
11 | +copies or substantial portions of the Software. | ||
12 | + | ||
13 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
14 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
15 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
16 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
17 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
18 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
19 | +SOFTWARE. | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -#Trading Service | 1 | +<div align="center"> |
2 | + <a href="https://github.com/othneildrew/Best-README-Template"> | ||
3 | + <img src="images/logo.png" alt="Logo" width="80" height="80"> | ||
4 | + </a> | ||
2 | 5 | ||
3 | -./config/dev.js 생성 필요 | 6 | + <h3 align="center">업비트 암호화폐 자동 매매 서비스</h3> |
4 | -module.exports = { | 7 | + </p> |
5 | - mongoURI:{mongoURI} | 8 | +</div> |
6 | -} | 9 | + |
7 | -.env 파일 생성 후 access_key와 secret_key 할당 필요 | 10 | +## About The Project |
11 | +<img src="images/about01.jpg" alt="about" width="80" height="80"> | ||
12 | +<h4>24시간 시세가 변하는 암호화폐를 위한 자동 매매 서비스<br/></h4> | ||
13 | +해당 서비스를 만든 이유 <br/> | ||
14 | +● 24시간 가격변동이 있지만 사람이 하루종일 보고 있을 수는 없다.<br/> | ||
15 | +● 매매 기준을 정해두어도 이성적인 판단을 못하고 감정적으로 매매를 할 수 있다.<br/> | ||
16 | +● 일상샐활을 하면서 고정적인 수익을 얻을 수 있다.<br/> | ||
17 | + | ||
18 | +#### DB Schema | ||
19 | +* User | ||
20 | + - uid : 유저의 고유 아이디 | ||
21 | + - krw_balance : 보유 원화 | ||
22 | + - market : 보유 암호화폐 이름 | ||
23 | + - count : 매수 횟수 | ||
24 | + - avg_buy_price : 매수 평균가 | ||
25 | + - volume : 보유 암호화폐 갯수 | ||
26 | + | ||
27 | +* Coin | ||
28 | + - tid : 거래량별 암호화폐 순위 | ||
29 | + - name : 암호화폐 이름 | ||
30 | + - korean_name : 암호화폐 한글 이름 | ||
31 | + - acc_trade_price_24h : 24시간 거래대금 | ||
32 | + - current_price : 현재 암호화폐 가격 | ||
33 | + - up_count : 5분 간격 연속 상승 횟수 | ||
34 | + - down_count : 5분 간격 연속 하락 횟수 | ||
35 | + | ||
36 | +### Built With | ||
37 | +* [Node.js](https://nodejs.org/) | ||
38 | + | ||
39 | +### Installation | ||
40 | +1. repository를 clone한다. | ||
41 | +```sh | ||
42 | + git clone http://khuhub.khu.ac.kr/Crypto/Crypto_trade_bot.git | ||
43 | +``` | ||
44 | +2. NPM package들을 설치한다. | ||
45 | +```sh | ||
46 | + npm install | ||
47 | +``` | ||
48 | +3. 해당 프로젝트는 aws ec2와 ssl인증서를 사용한다. 개인 PC에서 작동하려면 domain과 sslport를 지우고 express.app을 사용해서 서버를 작동시키면 된다.(이 경우 4번은 생략) | ||
49 | +4. 최상위 폴더의 index.js의 domain과 sslport를 본인 domain과 sslport로 변경한다. | ||
50 | +```js | ||
51 | +const domain = "{your domain}"; | ||
52 | +const sslport = {your port}; | ||
53 | +``` | ||
54 | +5. https://cloud.mongodb.com/ 가입 후 db connect url을 복사한다. | ||
55 | +6. /config 폴더에 dev.js 파일 추가 | ||
56 | +```js | ||
57 | + module.exports = { | ||
58 | + mongoURI:"복사한 connect url" | ||
59 | + } | ||
60 | +``` | ||
61 | +7. https://upbit.com/service_center/open_api_guide 에서 access_key와 secret_key를 발급받는다. | ||
62 | +8. https://developers.line.biz/en/services/messaging-api/ 에서 line message-api를 생성하고 channel access token을 복사한다. | ||
63 | +9. 3번에서 입력한 도메인을 webhook url로 설정해준다.(4번을 생략했다면 webhook url에 본인 네트워크망IP를 입력) | ||
64 | +10. 프로젝트 최상위 폴더에 .env 파일 추가 | ||
65 | +```env | ||
66 | + access_key={upbit에서 발급 받은 access_key} | ||
67 | + secret_key={upbit에서 발급 받은 secret_key} | ||
68 | + token={line message-api 의 channel access token} | ||
69 | + userid={테스트 알림을 받을 line userid} | ||
70 | +``` | ||
71 | +## Usage | ||
72 | +프로젝트 최상위 폴더에서 명령어 실행 | ||
73 | +```sh | ||
74 | + nodemon | ||
75 | +``` | ||
76 | +<img src="images/first.jpg" alt="first" width="80" height="80"> | ||
77 | +<img src="images/second.jpg" alt="second" width="80" height="80"> | ||
78 | + | ||
79 | +## Roadmap | ||
80 | +- [x] Add get_marketname | ||
81 | +- [x] Add get_marketInfo | ||
82 | +- [x] Add price_comparison | ||
83 | +- [x] Add transaction_coin | ||
84 | +- [x] Add latest_repeat | ||
85 | +- [x] Add send the order breakdown to the user | ||
86 | +- [x] Add send the asset to the user | ||
87 | +- [x] Add Whenever a sale is signed, the details of the signing are sent. | ||
88 | + | ||
89 | +## Contributing | ||
90 | + | ||
91 | +1. 프로젝트를 fork 하세요. | ||
92 | +2. 여러분의 feature 브랜치를 만들어 주세요.(`git checkout -b feature/{function}`) | ||
93 | +3. 변경사항을 commit 해주세요.(`git commit -m 'Add some function`) | ||
94 | +4. 원격 브랜치로 push 해주세요.(`git push origin feature/{function}`) | ||
95 | +5. pull request를 보내주세요. | ||
96 | + | ||
97 | +## License | ||
98 | +[MIT License](http://khuhub.khu.ac.kr/Crypto/Crypto_trade_bot/blob/master/LICENSE) | ||
99 | + | ||
100 | +<!-- CONTACT --> | ||
101 | +## Contact | ||
102 | + | ||
103 | +이름 - [@김다빈,이가원](https://github.com/dogsoft0937) - dogsoft0937@khu.ac.kr,juneee0864@gmail.com <br/> | ||
104 | +프로젝트 주소: [http://khuhub.khu.ac.kr/Crypto/Crypto_trade_bot](http://khuhub.khu.ac.kr/Crypto/Crypto_trade_bot) | ||
105 | +## Acknowledgments | ||
106 | + | ||
107 | +* [upbit-reference](https://docs.upbit.com/reference) | ||
108 | +* [line-reference](https://developers.line.biz/en/reference/messaging-api/) | ... | ... |
images/about01.jpg
0 → 100644

141 KB
images/first.PNG
0 → 100644
11.6 KB
images/logo.jpg
0 → 100644

11.6 KB
images/second.PNG
0 → 100644
28.2 KB
... | @@ -12,8 +12,7 @@ const HTTPS = require('https'); | ... | @@ -12,8 +12,7 @@ const HTTPS = require('https'); |
12 | const domain = "2019102152.osschatbot.ga" | 12 | const domain = "2019102152.osschatbot.ga" |
13 | const sslport = 23023; | 13 | const sslport = 23023; |
14 | const TARGET_URL = 'https://api.line.me/v2/bot/message/reply' | 14 | const TARGET_URL = 'https://api.line.me/v2/bot/message/reply' |
15 | -const TOKEN = process.env.token; | 15 | + |
16 | -var userid = process.env.userid; | ||
17 | 16 | ||
18 | const bodyParser = require('body-parser'); | 17 | const bodyParser = require('body-parser'); |
19 | const crypto = require('crypto'); | 18 | const crypto = require('crypto'); |
... | @@ -24,6 +23,8 @@ const sign = require('jsonwebtoken').sign | ... | @@ -24,6 +23,8 @@ const sign = require('jsonwebtoken').sign |
24 | const access_key = process.env.access_key; | 23 | const access_key = process.env.access_key; |
25 | const secret_key = process.env.secret_key; | 24 | const secret_key = process.env.secret_key; |
26 | const server_url = "https://api.upbit.com" | 25 | const server_url = "https://api.upbit.com" |
26 | +const TOKEN = process.env.token; | ||
27 | +const userid = process.env.userid; | ||
27 | 28 | ||
28 | var delay_count=1; | 29 | var delay_count=1; |
29 | 30 | ||
... | @@ -76,7 +77,7 @@ function get_asset(market) { | ... | @@ -76,7 +77,7 @@ function get_asset(market) { |
76 | // hold_coin[data[i].currency]={avg_buy_price:data[i].avg_buy_price,volume:data[i].balance,current_price:candle[0].trade_price}; | 77 | // hold_coin[data[i].currency]={avg_buy_price:data[i].avg_buy_price,volume:data[i].balance,current_price:candle[0].trade_price}; |
77 | } | 78 | } |
78 | } | 79 | } |
79 | - resolve({ pre_asset: pre_asset.toFixed(0), current_asset: current_asset.toFixed(0), profit_data: (current_asset - pre_asset).toFixed(0), yield_data: ((current_asset - pre_asset) / pre_asset * 100).toFixed(2) }); | 80 | + resolve({ pre_asset: pre_asset.toFixed(0).replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","), current_asset: current_asset.toFixed(0).replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","), profit_data: (current_asset - pre_asset).toFixed(0).replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",") , yield_data: Number((current_asset - pre_asset) / pre_asset * 100).toFixed(2).replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",") }); |
80 | } else if (market == "coin") { | 81 | } else if (market == "coin") { |
81 | var hold_coin = new Object(); | 82 | var hold_coin = new Object(); |
82 | 83 | ||
... | @@ -132,9 +133,14 @@ async function get_marketName() { | ... | @@ -132,9 +133,14 @@ async function get_marketName() { |
132 | } | 133 | } |
133 | async function transaction_coin(coin_name, side, volume, price, ord_type, first = true) { | 134 | async function transaction_coin(coin_name, side, volume, price, ord_type, first = true) { |
134 | var volume = volume; | 135 | var volume = volume; |
135 | - if (side == "ask" && volume == "") { | 136 | + var avg_buy_price=0,market=""; |
137 | + if (side == "ask") { | ||
136 | await User.findOne({ market: coin_name }).then((result) => { | 138 | await User.findOne({ market: coin_name }).then((result) => { |
137 | - volume = String(result.volume); | 139 | + if(volume == ""){ |
140 | + volume = String(result.volume); | ||
141 | + } | ||
142 | + avg_buy_price=result.avg_buy_price; | ||
143 | + market=result.market.split('-')[1]; | ||
138 | }).catch(err => { console.log(err.error) }); | 144 | }).catch(err => { console.log(err.error) }); |
139 | } | 145 | } |
140 | const body = { | 146 | const body = { |
... | @@ -144,7 +150,6 @@ async function transaction_coin(coin_name, side, volume, price, ord_type, first | ... | @@ -144,7 +150,6 @@ async function transaction_coin(coin_name, side, volume, price, ord_type, first |
144 | price: price, | 150 | price: price, |
145 | ord_type: ord_type, | 151 | ord_type: ord_type, |
146 | } | 152 | } |
147 | - console.log(body); | ||
148 | //시장가 매수인 경우 price를 얼마치 살건지 입력 | 153 | //시장가 매수인 경우 price를 얼마치 살건지 입력 |
149 | //시장가 매도인경우 volume에 몇개를 팔건지 입력 | 154 | //시장가 매도인경우 volume에 몇개를 팔건지 입력 |
150 | const query = queryEncode(body) | 155 | const query = queryEncode(body) |
... | @@ -170,8 +175,10 @@ async function transaction_coin(coin_name, side, volume, price, ord_type, first | ... | @@ -170,8 +175,10 @@ async function transaction_coin(coin_name, side, volume, price, ord_type, first |
170 | }, delay) | 175 | }, delay) |
171 | }) | 176 | }) |
172 | var my_asset = await asset(1000); | 177 | var my_asset = await asset(1000); |
173 | - if(my_asset.market&&my_asset.market.avg_buy_price){ | 178 | + |
174 | - lookup_order(result.uuid,my_asset.market.avg_buy_price); | 179 | + if(avg_buy_price!=0){ |
180 | + console.log(avg_buy_price); | ||
181 | + lookup_order(result.uuid,avg_buy_price,market); | ||
175 | }else{ | 182 | }else{ |
176 | lookup_order(result.uuid); | 183 | lookup_order(result.uuid); |
177 | } | 184 | } |
... | @@ -332,7 +339,6 @@ async function price_comparison(candle, user_data = null, isbuying = false) { | ... | @@ -332,7 +339,6 @@ async function price_comparison(candle, user_data = null, isbuying = false) { |
332 | }).catch(err => { console.log(err); }) | 339 | }).catch(err => { console.log(err); }) |
333 | } | 340 | } |
334 | }else{ | 341 | }else{ |
335 | - console.log(촛불); | ||
336 | console.log(result); | 342 | console.log(result); |
337 | } | 343 | } |
338 | }) | 344 | }) |
... | @@ -371,7 +377,7 @@ async function latest_repeat(t1) { | ... | @@ -371,7 +377,7 @@ async function latest_repeat(t1) { |
371 | let today = new Date(); | 377 | let today = new Date(); |
372 | let minutes = today.getMinutes(); | 378 | let minutes = today.getMinutes(); |
373 | let seconds = today.getSeconds(); | 379 | let seconds = today.getSeconds(); |
374 | - if (seconds == 0) { | 380 | + if (seconds ==0) { |
375 | // if (minutes == 0 && seconds == 0) { | 381 | // if (minutes == 0 && seconds == 0) { |
376 | clearInterval(check_time); | 382 | clearInterval(check_time); |
377 | sort_info = (await sort_data()); | 383 | sort_info = (await sort_data()); |
... | @@ -401,7 +407,7 @@ async function latest_repeat(t1) { | ... | @@ -401,7 +407,7 @@ async function latest_repeat(t1) { |
401 | } | 407 | } |
402 | }, 1000); | 408 | }, 1000); |
403 | } | 409 | } |
404 | -function lookup_order(uuid,avg_buy_price=0) { | 410 | +function lookup_order(uuid,avg_buy_price=0,market="") { |
405 | const body = { | 411 | const body = { |
406 | uuid: uuid | 412 | uuid: uuid |
407 | } | 413 | } |
... | @@ -441,21 +447,21 @@ function lookup_order(uuid,avg_buy_price=0) { | ... | @@ -441,21 +447,21 @@ function lookup_order(uuid,avg_buy_price=0) { |
441 | volume = volume.toFixed(8).toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","); | 447 | volume = volume.toFixed(8).toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","); |
442 | funds = funds.toFixed(0).toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","); | 448 | funds = funds.toFixed(0).toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","); |
443 | if(avg_buy_price!=0){ | 449 | if(avg_buy_price!=0){ |
444 | - trading_notice(body.side, price, funds, volume,yield_data); | 450 | + trading_notice(body.side, price, funds, volume,yield_data,market); |
445 | }else{ | 451 | }else{ |
446 | trading_notice(body.side, price, funds, volume); | 452 | trading_notice(body.side, price, funds, volume); |
447 | } | 453 | } |
448 | }) | 454 | }) |
449 | return; | 455 | return; |
450 | } | 456 | } |
451 | -function trading_notice(side, price, funds, volume,yield_data=0) { | 457 | +function trading_notice(side, price, funds, volume,yield_data=0,market="") { |
452 | var messages = new Array(); | 458 | var messages = new Array(); |
453 | if (side == "bid") { | 459 | if (side == "bid") { |
454 | messages.push({ "type": "text", "text": "매수 주문이 체결되었습니다." }); | 460 | messages.push({ "type": "text", "text": "매수 주문이 체결되었습니다." }); |
455 | - messages.push({ "type": "text", "text": `체결 금액 : ${funds}원\n체결 수량 : ${volume}개\n체결 평균가 : ${price}원\n` }); | 461 | + messages.push({ "type": "text", "text": `${market}\n체결 금액 : ${funds}원\n체결 수량 : ${volume}개\n체결 평균가 : ${price}원\n` }); |
456 | } else { | 462 | } else { |
457 | messages.push({ "type": "text", "text": "매도 주문이 체결되었습니다." }); | 463 | messages.push({ "type": "text", "text": "매도 주문이 체결되었습니다." }); |
458 | - messages.push({ "type": "text", "text": `체결 금액 : ${funds}원\n체결 수량 : ${volume}개\n체결 평균가 : ${price}원\n수익률 : ${yield_data}%` }); | 464 | + messages.push({ "type": "text", "text": `${market}\n체결 금액 : ${funds}원\n체결 수량 : ${volume}개\n체결 평균가 : ${price}원\n수익률 : ${yield_data}%` }); |
459 | } | 465 | } |
460 | 466 | ||
461 | var TARGET_URL = 'https://api.line.me/v2/bot/message/push'; | 467 | var TARGET_URL = 'https://api.line.me/v2/bot/message/push'; | ... | ... |
1 | const mongoose=require('mongoose'); | 1 | const mongoose=require('mongoose'); |
2 | - | ||
3 | const coinSchema=mongoose.Schema({ | 2 | const coinSchema=mongoose.Schema({ |
4 | tid:{ | 3 | tid:{ |
5 | type:Number, | 4 | type:Number, |
... | @@ -20,11 +19,14 @@ const coinSchema=mongoose.Schema({ | ... | @@ -20,11 +19,14 @@ const coinSchema=mongoose.Schema({ |
20 | type:Number, | 19 | type:Number, |
21 | required:true | 20 | required:true |
22 | }, | 21 | }, |
23 | - count:{ | 22 | + up_count:{ |
23 | + type:Number, | ||
24 | + default:0 | ||
25 | + }, | ||
26 | + down_count:{ | ||
24 | type:Number, | 27 | type:Number, |
25 | default:0 | 28 | default:0 |
26 | } | 29 | } |
27 | }) | 30 | }) |
28 | - | ||
29 | const Coin=mongoose.model("Coin",coinSchema); | 31 | const Coin=mongoose.model("Coin",coinSchema); |
30 | module.exports={Coin}; | 32 | module.exports={Coin}; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
1 | const mongoose=require('mongoose'); | 1 | const mongoose=require('mongoose'); |
2 | - | ||
3 | const userSchema=mongoose.Schema({ | 2 | const userSchema=mongoose.Schema({ |
4 | uid:{ | 3 | uid:{ |
5 | type:Number | 4 | type:Number |
... | @@ -11,7 +10,7 @@ const userSchema=mongoose.Schema({ | ... | @@ -11,7 +10,7 @@ const userSchema=mongoose.Schema({ |
11 | type:String, | 10 | type:String, |
12 | }, | 11 | }, |
13 | count:{ | 12 | count:{ |
14 | - type:Number, | 13 | + type:Number |
15 | }, | 14 | }, |
16 | avg_buy_price:{ | 15 | avg_buy_price:{ |
17 | type:Number | 16 | type:Number |
... | @@ -20,6 +19,5 @@ const userSchema=mongoose.Schema({ | ... | @@ -20,6 +19,5 @@ const userSchema=mongoose.Schema({ |
20 | type:Number | 19 | type:Number |
21 | } | 20 | } |
22 | }) | 21 | }) |
23 | - | ||
24 | const User=mongoose.model("User",userSchema); | 22 | const User=mongoose.model("User",userSchema); |
25 | -module.exports={User}; | 23 | +module.exports={User}; |
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
-
Please register or login to post a comment