index.js 14.1 KB
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
const options = { method: 'GET', headers: { Accept: 'application/json' } };
const express = require('express');
const app = express();
const { Coin } = require("./models/Coin");
const { User } = require('./models/User');
require("dotenv").config();
const fs = require('fs');
const path = require('path');
const HTTPS = require('https');
const domain = "2019102152.osschatbot.ga"
const sslport = 23023;
const bodyParser = require('body-parser');
const crypto = require('crypto');
const queryEncode = require('querystring').encode;
const request = require('request-promise-native');
const { v4 } = require("uuid")
const sign = require('jsonwebtoken').sign
const access_key = process.env.access_key;
const secret_key = process.env.secret_key;
const server_url = "https://api.upbit.com"


var krw_balance;
var divided_money;

var sort_info = new Array();
const mongoose = require('mongoose');
const config = require('./config/key');
app.use(bodyParser.json());
const connect = mongoose.connect(config.mongoURI, {
    useNewUrlParser: true, useUnifiedTopology: true
})
    .then(() => console.log('디비연결 성공'))
    .catch((err) => console.log(err));


var korean_name = new Object();

function get_asset(market) {
    const payload = {
        access_key: access_key,
        nonce: v4(),
    }
    const token = sign(payload, secret_key)
    const options = {
        method: "GET",
        url: server_url + "/v1/accounts",
        headers: { Authorization: `Bearer ${token}` },
    }
    const result = (market) => new Promise((resolve) => {
        request(options, function (err, res, body) {
            if (err) throw new Error(err)
            var empty = new Object();
            data = JSON.parse(body);
            if (market) {
                data.filter(function (item) {
                    if (item.currency == market.split('-')[1]) {
                        // resolve(item);
                        empty.market = item;
                    } else if (item.currency == "KRW") {
                        empty.KRW = item
                    }
                    resolve(empty);
                })
            } else {
                resolve(data);
            }
        })
    })
    return result(market);
}
async function get_marketName() {
    var data = new Array();
    //전체 암호화폐 리스트 불러오기
    let response = await fetch(`${server_url}/v1/market/all`, options)
        .then(res => res.json())
        .then(json => {
            for (i in json) {
                data.push(json[i].market);
                korean_name[json[i].market] = json[i].korean_name;
            }
        })
    return data;
}
async function transaction_coin(coin_name, side, volume, price, ord_type, first = true) {
    var volume = volume;
    if (side == "ask" && volume == "") {
        await User.findOne({ market:coin_name }).then((result) => {
            volume = String(result.volume);
        }).catch(err => { console.log(err.error) });
    }
    const body = {
        market: coin_name,
        side: side,
        volume: volume,
        price: price,
        ord_type: ord_type,
    }
    //시장가 매수인 경우 price를 얼마치 살건지 입력
    //시장가 매도인경우 volume에 몇개를 팔건지 입력
    const query = queryEncode(body)
    const hash = crypto.createHash('sha512')
    const queryHash = hash.update(query, 'utf-8').digest('hex')
    const payload = {
        access_key: access_key,
        nonce: v4(),
        query_hash: queryHash,
        query_hash_alg: 'SHA512',
    }
    const token = sign(payload, secret_key)
    const options = {
        method: "POST",
        url: server_url + "/v1/orders",
        headers: { Authorization: `Bearer ${token}` },
        json: body
    }
    await request(options).then(async (result) => {
        const asset = (delay) => new Promise((resolve) => {
            setTimeout(async () => {
                resolve(await get_asset(result.market));
            }, delay)
        })
        var my_asset = await asset(1000);
        if (side == "bid") {
            //처음 매수일 때
            if (first) {
                const user = new User({ uid: 1, krw_balance: my_asset.KRW.balance, market: coin_name, count: 1, avg_buy_price: my_asset.market.avg_buy_price, volume: my_asset.market.balance });
                await user.save().then(() => { isuser = true })
                //2회 이상 매수일 때
            } else {
                User.findOneAndUpdate({ uid: 1 }, { krw_balance: my_asset.KRW.balance, avg_buy_price: my_asset.market.avg_buy_price, volume: my_asset.market.balance, $inc: { count: 1 } }, { new: true }, (err, result) => {
                    if (err) {
                        // console.log(err);
                    } else {
                        // console.log(result);
                    }
                })
            }
        } else if (side == "ask") {
            //1회 매수 후 매도일 때
            console.log("매도 쪽으로 진입");
            if (first) {
                console.log("1회 매수 후 매도")
                User.deleteOne({ market:coin_name }, (err, res) => {
                    if (err) {
                        // console.log(err);
                    }
                    krw_balance = my_asset.KRW.balance;
                    console.log(krw_balance);
                    divided_money = krw_balance / 10;
                });
                //분할 매수 분할 매도 중일 때
            } else {
                User.findOneAndUpdate({ uid: 1 }, { krw_balance: my_asset.KRW.balance, avg_buy_price: my_asset.market.avg_buy_price, volume: my_asset.market.balance, count: 1 }, { new: true }, (err, result) => {
                    if (err) {
                        // console.log(err);
                    } else {
                        // console.log(result);
                    }
                })
            }
        }
    }).catch((err) => { console.log(err.error) })
}
async function get_marketInfo() {
    //각 암호화폐 정보 조회
    var name_list = await get_marketName();
    const url2 = `${server_url}/v1/ticker/?markets=${name_list}`;
    var arr = new Array();
    let response2 = await fetch(url2, options)
        .then(res => res.json())
        .then(json => {
            for (i in json) {
                if (json[i].acc_trade_price_24h > 100000000000) {
                    arr.push([json[i].market, json[i].acc_trade_price_24h, json[i].trade_price]);
                }
            }
        })
    return arr
}
async function sort_data() {
    arr = await get_marketInfo();
    arr.sort((a, b) => {
        return b[1] - a[1];
    })
    return arr;
}
async function save_coin(arr) {
    for (var i = 0; i < 10; i++) {
        if (arr[i]) {
            const coin = new Coin({
                tid: i + 1,
                name: arr[i][0],
                korean_name: korean_name[arr[i][0]],
                acc_trade_price_24h: arr[i][1],
                current_price: arr[i][2]
            });
            await coin.save((err) => {
                if (err) {
                    // console.log(err)
                }
            })
        }
    }
    return true;
}
async function refresh_db() {
    Coin.find()
        .then(result => {
            if (result.length !== 0) {
                Coin.deleteMany({ tid: { $gt: 0 } }, (err, result) => {
                    if (err) {
                        // console.log(err);
                    } else {
                        // console.log(result);
                    }
                })
            }
            save_coin(sort_info);
        })
}
async function get_candle(minute, market) {
    const url = `https://api.upbit.com/v1/candles/minutes/${minute}?market=${market}&count=1`;
    var candle = new Array();
    let response = await fetch(url, options)
        .then(res => res.json())
        .then(json => candle = json)
    return candle;
}
async function price_comparison(candle, user_data = null,isbuying=false) {
    //매수한 코인이 있을 때
    if(user_data!=null) {
        console.log("매수 평균가 : " + user_data.avg_buy_price + ", 현재 가격 : " + candle[0].trade_price);
        //매수평균가가 현재 시장 가격보다 더 비쌀 경우
        var yield_data = (candle[0].trade_price - user_data.avg_buy_price) / user_data.avg_buy_price * 100;
        if (user_data.avg_buy_price > candle[0].trade_price) {
            if (user_data.count < 10) {
                transaction_coin(user_data.market, "bid", null, divided_money, "price", false)
            } else {
                transaction_coin(user_data.market, "bid", null, user_data.krw_balance, "price", false)
            }
            //매수평균가가 현재 시장 가격보다 더 쌀 경우
            //수수료 포함 0.5% 이상 수익중일 때    
        } else if (yield_data>= 0.5) {
            //매수 count가 1회이면서 수익률이 1.5프로 이상일때 시장가 전액 매도
            if (user_data.count == 1 && yield_data >= 1.5) {
                transaction_coin(user_data.market, "ask", user_data.volume, null, "market");
                //전체 계좌 제산의 10프로를 제외한 나머지 코인을 매도
            } else {
                transaction_coin(user_data.market, "ask", (user_data.volume / user_data.count) * (user_data.count - 1), null, "market", false);
            }
        }
    //매수한 코인이 없을 때
    }else{
        await Coin.findOne({ name: candle[0].market }).then(async (result) => {
            //가격이 떨어졌을때
            //해당 코인의 count를 +1 해준다
            //해당 코인의 count가 3이상이면 upbit api로 매수 주문을 보낸다.
            var yield_data = (candle[0].trade_price - result.current_price) / result.current_price * 100;
            if (result.current_price > candle[0].trade_price) {
                console.log("***" + result.korean_name + " " + candle[0].unit + "분 동안 "+yield_data.toFixed(2)+"%");
                await Coin.findOneAndUpdate({ name: candle[0].market }, { current_price: candle[0].trade_price,up_count:0,$inc: { down_count: 1 }}, { new: true }).then(async (result) => {
                        if (result.down_count >= 3) {
                            transaction_coin(result.name, "bid", null, divided_money, "price");
                        }
                }).catch(err=>{console.log(err)})
                //가격이 상승하거나 변동이 없을 때
                //해당 코인의 count를 초기화한다.
            } else{
                console.log(result.korean_name + " " + candle[0].unit + "분 동안"+yield_data.toFixed(2)+"%");
                await Coin.findOneAndUpdate({ name: candle[0].market }, { current_price: candle[0].trade_price, down_count: 0,$inc:{up_count:1} }, { new: true }).then(async (result) => {
                }).catch(err=>{console.log(err);})
            }
        }) 
    }
}
async function check_coin(t1) {
    User.find().then(async (user_data) => {
        //매수한 코인이 있을 때
        if(user_data.length!=0) {
            //3회 이상 매수 했을시 15분봉으로 교체해서 가격 확인
            //15분봉 처리해야함
            if (user_data[0].count > 3) {
                setTimeout(async () => {
                    candle = await get_candle(15, user_data[0].market);
                }, 60000 * 10);
            }else{
                var candle = await get_candle(5, user_data[0].market);
            }
            await price_comparison(candle, user_data[0]);
            
        }
        var isbuying=user_data.length==0;
        //코인 count and 매수 매도 작업
        for(var i=0;i<t1.length;i++){
            if(user_data[0]&&user_data[0].market!=t1[i]){
                break;
            }
            var candle=await get_candle(5,t1[i]);
            await price_comparison(candle,null,isbuying);
        }
    }).catch((err) => {
        console.log(err);
    })
}
async function latest_repeat(t1) {
    let check_time = setInterval(async () => {
        let today = new Date();
        let minutes = today.getMinutes();
        let seconds = today.getSeconds();
        if (seconds == 0) {
            // if (minutes == 0 && seconds == 0) {
            clearInterval(check_time);
            sort_info = (await sort_data());
            (await refresh_db());
            Coin.find().sort({ tid: 1 }).then(result => {
                for (var key in result) {
                    t1.push(result[key].name)
                }
            })
            console.log("현재 시간은 " + today.toLocaleTimeString());
            var count = 0;
            let coin = setInterval(async () => {
                let today = new Date();
                let minutes = today.getMinutes();
                let seconds = today.getSeconds();
                console.log("현재 시간은 " + today.toLocaleTimeString());
                //1시간마다 db 최신화...
                if (count == 12) {
                    count = 0;
                    sort_info = (await sort_data());
                    (await refresh_db());
                    console.log("db최신화");
                } else {
                    await (check_coin(t1).then(count++));
                }
            }, 60000 * 5);
        }
    }, 1000);
}
try {
    const option = {
        ca: fs.readFileSync('/etc/letsencrypt/live/' + domain + '/fullchain.pem'),
        key: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/privkey.pem'), 'utf8').toString(),
        cert: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/cert.pem'), 'utf8').toString(),
    };
    HTTPS.createServer(option, app).listen(sslport, async () => {
        console.log(`[HTTPS] Server is started on port ${sslport}`);
        // console.log(await get_asset());
        await get_asset().then((result) => {
            krw_balance = result[0].balance;
            divided_money = krw_balance / 10
        });
        var t1 = new Array();
        await (latest_repeat(t1));
    });
} catch (error) {
    console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.');
    console.log(error);
}