2016년 12월 21일 수요일

마크다운 편집기로 MS VisualStudio Code가 짱이닷!




마크다운 편집기로서
M$ visual studio code가 최고닷!

확장자 md로 설정하고 미리보기를 클릭 한 다음 좌우로 화면을 분할 하면
입력과 동시에 미리 보기가 보여주기 때문에 최고의 툴 이라 할 수 있겠다.


2016년 12월 10일 토요일

XCode 8.x "No such module error"

Xcode 8.x의 버그로 생각 된다.

Xcode 8 로 버젼 업이 되면서 swift 2.3 / 3.0으로 컨버팅 개고생을 당하고
이용하는 pod 라이브러리 업데이트 까지 아주 개고생을 시키고

하루에 20번 정도에 크래쉬를 경험 하여 짜증이 많이 나는데

어느날 갑자기  No such module error가 난다.

계속 참조 하고 있던 모듈인데....

이런 경우 아래의 스텝 으로 처리 시도를 하였으나 소용이 없다.

xcode 종료

pod cache clean

pod deintegrate || rm -rf Pods

rm -rf ~/Library/Developer/Xcode/DerivedData

xcode 재시작

clean
build

이렇게 하여도 계속 되는 이유 없는 에러가 속출 된다면

다음에 방법을 써보는 것도 좋다.

왼쪽 상단에  New scheme 클릭 또는 Product -> Scheme -> New schem 메뉴

이렇게 새로운 컴파일 스킴을 추가 한다.

그리고 새로운 컴파일 스킴으로 build 한다.

나의 경우는 마법처럼 모든 에러가 사라 졌다.






2016년 10월 26일 수요일

trobleshooting installation issue with CocoaPods

CocoaPods

http://mtsparrow.blogspot.kr/2016/03/cocoapod.html

github에서 좋은 오픈소스를 찾아 iOS프로젝트에 추가 하려고 하면
CocoaPods을 주로 사용 했다.

 프로젝트에 프레임워크를 추가 하기 좋긴 한대 가끔 이게 문제가 생기고
이유 없는 빌드 에러를 만들어 내면 남감 하다.

Xcode에서 이유없는 빌드 에러를 만나거나 오동작 할때는 가끔씩
rm -rf ~/Library/Developer/Xcode/DerivedData

여기를 정리 하곤 한다.

CocoaPods 도 무언가 오동작을 하거나 계속되는 이유없는 빌드 에러를 만날때 
해볼 만한 방법은

pod cache clean frameworkName1
pod cache clean frameworkName2
pod cache clean frameworkName3
pod cache clean frameworkName4
pod deintegrate || rm -rf Pods
pod install --verbose
rm -rf ~/Library/Developer/Xcode/DerivedData


요즘은 Carthage 를 많이 사용하는 것 같다.

하지만 아직 사용방법을 모른다.

나중에 새로운 프로젝트를 시작 할때는 참조 프레임워크를 Carthage로 추가 해봐야 겠다.

2016년 10월 23일 일요일

2007 mid mac mini ubuntu install




요약 :
우분투 32비트 설치를 위한 USB부팅 이미지를 만든다.
efi/boot/boot.iso

우분트 32비트 설치 이미지를 boot.iso로 이름을 변경해서 mac mini에 USB를 꽂은 다음 alt (option) 키로 부팅 하면
부팅 디스크 설정에서 usb선택시 32비트 우분투를 설치 할 수 있다.

설치 중간에 boot-repair를 이용해서 부트를 수정 하여 이제 옵션 키를 누르지 않고 바로 부팅이 가능하게 만든다.
다시 usb로 부팅해서 파티션을 나눈다. 32비트 우분투는 20기가 정도 설정한다.

UNetbootin 을 32비트 우분트에 설치해서 64비트 우분트를 내려 받고 설치 한다.


아래에 링크를 따라 하면 설치 할 수 있다.

http://digiecologist.blogspot.kr/2015/12/installing-ubuntu-on-2007-mac-mini-a1176.html


아래의 내용은 따라 하면 에러가 난다.
sudo add-apt-repository ppa:yannubuntu/boot-repair && sudo apt-get update
sudo apt-get install -y boot-repair
boot-repair

이과정에서 key 에러가 나면
http://askubuntu.com/questions/308760/w-gpg-error-http-ppa-launchpad-net-precise-release-the-following-signatures

The link above provides detailed explanation with screenshots. It also has some other simple methods that could be tried before doing this manually. Since it is not accessible from some regions let me describe the method that worked for me.

Go to the OpenPGP Public Key Server. There you can enter your key into the Search String field, leaving everything else as is, then click "Search!".
When searching for a key, always prepend 0x to it! This means that, for example, you have to search for 0x6AF0E1940624A220 instead of 6AF0E1940624A220.
Click on the link provided in the pub section. This should take you to page containing the key. The page's heading should be similar to Public Key Server -- Get "0x6AF0E1940624A220"
Copy everything from underneath the title (starting from -----BEGIN PGP PUBLIC KEY BLOCK-----) and save it in a file (e.g. key1).
Once you have the file, run the following command
sudo apt-key add key1

http://keyserver.ubuntu.com
위 내용으로 처리


몇시간 삽질을 하니 이 사이트에 도움되로 잘 설치 된다.
이제 맥미니 2007은 우분트 14.04로 다시 태어 났다.

2016년 10월 1일 토요일

C# FCM 푸시 전송 어플리케이션 만들기

FCM firebase Cloud Message
https://firebase.google.com/docs/cloud-messaging/?hl=ko

GCM에서 FCM으로 변경 되고 있다.

푸시를 전송 하는 것은 구글 사이트에 요청 하면 된다.
결국 http url request 를 하면 되는 것이고 그것을 어떠한 언어로든 구현 하면 된다.

전송되는 메세지는 json 포멧으로 전달 하면 된다.

이런 것을 지원 해주는 앱으로 구글 포스트 맨도 있다.
https://www.getpostman.com

FCM으로 변경되면서 직접 테스트로 push를 발송 해보는 페이지도 있으니 사실 테스트 어플리케이션이나 포스트맨 어플은 필요 없을수도 있다.

하지만 이번에 개발을 하면서 이미 구현된 서버가 C#으로 구현되어 있다.
모바일 앱과 동작 하는 앱서버가 C#으로 구현 되어 있고
여기에 푸시 기능을 추가 하려 하니 당연히 C# 코드가 필요 했다.

https://github.com/UniverseBryan/FCMWinServer

여기 코드를 참고 해서 간단한 윈폼 어플을 개발 해 보았다.

C#언어로 개발 해본 경험이 없지만
아주 생산성이 좋은듯 하다. 그동안 M$ 개발툴을 너무 멀리 하고 있었는데.
최근 파이썬을 비줠스튜디오로 만지면서 비줠스튜디오 커뮤니티 에디션을 조금 만저 봣는데
나름 C#도 생산성이 좋은 언어 인듯 하다.

아래의 코드가 푸시 전송 코드이다.
폼에 텍스트 입력 두개와 버튼 하나를 언지고
git에서 어든 dll파일을 추가해서 참조에 추가 하고 아래의 코드를 폼 코드에 추가 하면 된다.


using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string res = SendNotification("app registration token key string 152 bytes here", textBox2.Text);
        }


        public string SendNotification(string deviceId, string message)
        {
            string SERVER_API_KEY = "API KEY STRING";
           
            var value = message;
            string resultStr = "";

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://fcm.googleapis.com/fcm/send");
            request.Method = "POST";
            request.ContentType = "application/json;charset=utf-8;";
            request.Headers.Add(string.Format("Authorization: key={0}", SERVER_API_KEY));

            var postData =
            new
            {
                data = new
                {
                    title = textBox1.Text,
                    body = message,
                },

                // FCM allows 1000 connections in parallel.
                to = deviceId
            };

            //Linq to json
            string contentMsg = JsonConvert.SerializeObject(postData);
            Debug.WriteLine("contentMsg = " + contentMsg);

            Byte[] byteArray = Encoding.UTF8.GetBytes(contentMsg);
            request.ContentLength = byteArray.Length;

            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            try
            {
                WebResponse response = request.GetResponse();
                Stream responseStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(responseStream);
                resultStr = reader.ReadToEnd();
                Debug.WriteLine("response: " + resultStr);
                reader.Close();
                responseStream.Close();
                response.Close();
            }
            catch (Exception e)
            {
                resultStr = "";
                Debug.WriteLine(e.Message);
            }

            return resultStr;

        }

    }

   

}






2016년 9월 26일 월요일

UINavigationbar 1px bottom line 제거 하기

http://stackoverflow.com/questions/19226965/how-to-hide-ios7-uinavigationbar-1px-bottom-line

네비게이션 바를 추가 하면 위와 같이 네비게이션바의 색상과 view의 색상이 일치 할때도 
1px  라인이 하나 있다.

이것을 제거 하려면 어떻게 해야 하는가?


stackoverflow에 아름다운 방법이 있었다.

저 밑줄의 view를 찾아 내서 hidden 시켜 버리는 것이다.

private var shadowImageView: UIImageView?
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        if shadowImageView == nil {
            shadowImageView = findShadowImage(under: navigationController!.navigationBar)
        }
        shadowImageView?.hidden = true
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        
        shadowImageView?.hidden = false
    }
    
    private func findShadowImage(under view: UIView) -> UIImageView? {
        if view is UIImageView && view.bounds.size.height <= 1 {
            return (view as! UIImageView)
        }
        
        for subview in view.subviews {
            if let imageView = findShadowImage(under: subview) {
                return imageView
            }
        }
        return nil
    }


UIImage 이면서 height 가 1인 view를 찾아서 그것을  viewWillAppear() 메서드에서 hidden을 시키는... UIImage height가 1인 디자인을 추가 하지 않는한 정상적으로 동작 한다.


2016년 9월 19일 월요일

XCode 이전 버젼 다운 받기

https://developer.apple.com/download/more/



다행이도 7.3.1 을 받을 수 있다.


Xcode 7.3.1.dmg

XCode 8로 업그레이드 했다가 다른 라이브러리들이 지원을 하지 못하는 경우 어쩌나!

돌아 갈 수 있다.

2016년 9월 17일 토요일

XCode 8 , swift 2.3, 3.0 코드 컨버트

9월 14일 XCode 8이 출시 하였다.

업그레이드를 해야 하나 ....

이전에 swift2.0 2.1, 2.2등에 마이너 업그레이드 시에 자동으로 소스 코드를 변환 시켜줘서 문제 없이 동작 하기에 감행한 적은 있었다.

XCode 8은 swift 2.3과 3을 동시에 지원 하며 변환도 선택적으로 가능 한 듯 하다.
swift는 아직도 진화 중이다.



Convert 버튼을 누르면 다음과 같은 화면이 나타난다.



일단 디폴트는 3.0이지만 2.3을 선택 해보았다.



프로젝트에서 이용하는 다른 프레임워크도 변환 하는 체크가 있다.


생각 보다 적은 수의 파일이 변환해야 한다는 프리뷰 화면이 뜬다.
대부분 옵셔널 (!) 을 추가 해주는 변환이 많더라

swift 2.2 상태로 그대로 빌드 하면 어떻게 되나 해보았다.
XCode 8로 업그레이드 했지만 이전 코드를 그대로 빌드 할 수 있다면
구지 변환 하지 않고 그냥 그대로 고고 하고 싶다.



역시나 swiftJSON, Alamofire 등 다른 프레임워크에 문제가 있다.


swift 3.0으로 변환 하니 와우! 엄청나게 많은 파일을 수정 해야 한다는 preview...


신규 프로젝트는 3.0으로 가야 겟지만...
나머지는 그냥 2.3으로 가자 ㅠ.ㅠ








android - retrofit + gson 삽질기

아이폰에서 RESTfull API를 통한 서버 통신 앱을 만든다고 한다면
Alamofire + swiftyJSON 을 이용하면 간단하게 REST API 통신을 하고 JSON 파싱을 통해 데이터를 이용 할 수 있다.

안드로이드에서 같은 기능을 수행하는 라이브러리는 retrofit과 gson 조합이다.
retrofit + gson


https://github.com/square/retrofit
공식 github 사이트

여기에 보면 github 서비스 예제가 있다.
테스트해보면 잘된다.

하지만 나 처럼 안드로이드 개발 초보 입장에서 삽질은 여럿 도사리고 있었다.

삽질 항목을 나열 한다.

retrofit 2 버젼 기준으로. 이전 버젼은 사용해보지 못했으니 모른다.

1. interface에서 부분 URL이 되는 곳에서 백슬레쉬 /으로 시작 되면 안된다.

public interface Login {
    @GET("Login")
....


여기서 /Login 안된다

baseURL에서 포트 번호가 문제인지. 한참을 고생 했다. 결국 baseURL 쪽에 /로 끝내고
인터페이스 쪽에서는 /로 시작 하지 않게 하니 되더라

다른 인터넷 예제를 보면 그렇게 하지 않아도 되는 코드 들이 잇는 것으로 보아 정확한 문제를 모르겠다. 하지만 2.1.0에서 분명 문제가 발생 한다.


2. github 예제 처럼 @Path 만 있는게 아니다.
https://futurestud.io/tutorials/retrofit-multiple-query-parameters-of-same-name
여기를 참고 하면 파라메터 전달에 대해 @Query()를 쓰는게 참고가 되더라


이렇게 두어가지 문제로 삽질좀 하고 나니 나름 편하게 사용할 수있는 라이브러리 같다.



2016년 8월 4일 목요일

AWS free tier 사용기 - 예상하지 못했던 과금

AWS 에 가입하고 무료로 제공 하는  EC는 tmicro 이다.
기본 저장 볼륨은 30GB 이지만

M$ Windows SERVER가 설치 되어 있는 이 서버는  가용 용량이 턱없이 부족하다.

여기서 첫번째 실수를 했다.

어디선가 웹에서 AWS에 볼륨 사이즈를 늘리는 것을 보고 볼륨 사이즈를 늘렸다.

무료 사용자라는걸 잠시 잊었다.

거기에 지금까지 설정한 세팅이 아쉬워서 EBS 볼륨 스냅샷까지 만들었다.

추가 볼륨을 만들고 스냅샷으로 볼륩을 복원해서 큰 용량의 서버를 구축 했다.

뒤늦게 과금 문제가 생각 나서 무료에 대한 정보를 읽어 보았다.
30GB만 무료인것이다.

다시 30GB로 용량을 돌려 놓고 꾸역 꾸역 적은 공간에 디스크 압축 설정 까지 해가면서
무료로 설정 하였다.

아뿔싸

잊은게 잇었다.
스냅샷!

결국 새벽4시에 문자를 받았다.
US$ 2.63 미국 Amazon w 승인

그렇다 스냅샷 조차 허용되지 않는다. 1GB가 무료?
스냅샷은 1GB만 무료인것이다.

스냅샷도 시원하게 delete 시켰다.

2016년 8월 3일 수요일

mac용 chm 앱 ReadCHM

갑자기 CHM 파일이 Windows 10 / Windows Server 2012 R2에서 내용을 볼수가 없다.


갑자기 가 아니라 이미 오래전 부 터 열리지 않고 있었는지 모른다.
좀 낡은 chm 파일을 봐야 하는데 정말 답답하게 되었다.


파일은 열리지만 내용이 하나도 안보 이는 현상이다.


아마존에 Windows SERVER나 페러럴에 Windows 10이나 모두 같은 현상인 걸로 봐서
최신 MS 윈도우에 HTML처리 방식에서 더 이상 지원하지 않는것인지 M$ windows 컴맹인 나로서는 어떻게 해결할 방법이 없다.

그래서 맥용 CHM 파일을 볼수 잇는 앱을 찾아 나섰다.

여러 앱을 설치 했지만 한글 인코딩을 지원하지 않는다.
모두 그러한 기능이 없다.




단 하나의 앱을 제외 하고 무료로 한글 인코딩이 지원되는 앱은 ReadCHM 오직 하나 였다.







이 훌륭한 무료 앱은 PDF컨버팅 까지 지원하지만 유료 버젼으로 업그레이드 하지 않으면 PDF로 변환은 되지만 워터마크가 새겨진 PDF파일이 생성된다.

하지만 뭐 이 정도만이라도 허접한 윈도우 도움말 보기보단 훨 좋다.




2016년 7월 31일 일요일

TMap API로 예상 소요시간을 구하려면?

애플지도, 구글지도 등등 여러 다른  지도에서도 경로 탐색이 가능 하고
자동차, 자전거, 도보에 따른 예상 시간을 구할 수 있을 것이다.

하지만 꼭집어서 TMap API로 해주세요. 이런 경우 삽질이 불가피 했다.

TMapOpenAPI PDF 문서를 보면 다음과 같이 되어 있다.

4.9.15. (NSDictionary*) findTimeMachineCarPathWithStartPoint: (TMapPoint*)startPoint
타임머신 자동차 길안내로 출발 혹은 도착시간을 예측한 자동차 길 안내정보를 제공한다. 반환된 정보는 NSDictionary 안에 포함이 되어 있고, 자세한 내용은 skpPlanetx.com 의 경로안내를 참조하면 된다. 주소는 https://developers.skplanetx.com/apidoc/kor/t-map/course-guide/geojson/ 이다.


Parameters
- (TMapPoint*) startPoint : 출발지점
Return
- https://developers.skplanetx.com/apidoc/kor/t-map/course-guide/geojson/ 참조
Example


당연히 네비게이션을 만들수 있는 TMap API를가지고 경로 안내는 물론 예상 도착 시간을 구할 수 있어야 하는데
설명이 너무 부족 하다.

https://developers.skplanetx.com/apidoc/kor/t-map/course-guide/geojson/
여기 사이트를 참조 하라?
응답 값으로 geoJSON이란 형태로 주어진 위치에 대한 경로 정보가 리턴 된다고 한다.

type
features --- type
geometry
properties -- totalTime

findTimeMachineCarPathWithStartPoint 메서드에 응답 NSDictionary도 이와 같다는 의미로 해석 하면 될 듯 하다.

결국 내가 구하고자 하는 것은 예상 소요시간이므로

let path = TMapPathData()

let startPoint = TMapPoint(lon: start_lon, lat: start_lat)
let endPoint = TMapPoint(lon: end_lon, lat: end_lat)

let date = NSDate()

var dict = path.findTimeMachineCarPathWithStartPoint(startPoint , endPoint: endPoint, isStartTime: true, time: date, wayPoints: nil)

let totalTime = (dict["features"]! as! NSArray)[0]["properties"]!!["totalTime"] as! Double


features의 NSArray중 첫번째에서 properties,그 하위 totalTime을 Double로 변환 시키면 totalTime을 구할 수 있다.

2016년 7월 16일 토요일

zipline 국내 종목 처리시 문제점 해결

    self.simulation_dt,
  File "c:\zipline-0.9.0\zipline\utils\events.py", line 209, in handle_data
    context.trading_environment,
  File "c:\zipline-0.9.0\zipline\utils\events.py", line 228, in handle_data
    self.callback(context, data)
  File "c:\zipline-0.9.0\zipline\algorithm.py", line 368, in handle_data
    self._handle_data(self, data)
  File "C:\Users\Administrator\OneDrive\Developement\SystemT\systemt\backtesting
.py", line 62, in handle_data
    ma5 = history(5, '1d', 'price').mean()
  File "c:\zipline-0.9.0\zipline\utils\api_support.py", line 51, in wrapped
    return getattr(get_algo_instance(), f.__name__)(*args, **kwargs)
  File "c:\zipline-0.9.0\zipline\utils\api_support.py", line 98, in wrapped_meth
od
    return method(self, *args, **kwargs)
  File "c:\zipline-0.9.0\zipline\algorithm.py", line 1270, in history
    return self.history_container.get_history(history_spec, self.datetime)
  File "c:\zipline-0.9.0\zipline\history\history_container.py", line 904, in get
_history
    raw=True
  File "c:\zipline-0.9.0\zipline\history\history_container.py", line 56, in ffil
l_buffer_from_prior_values
    nan_sids = pd.isnull(buffer_values[0])
IndexError: index 0 is out of bounds for axis 0 with size 0
Press any key to continue . . .


zipline 0.8.4
zipline 0.9.0

pandas_datareader를 이용해서 yahoo에서 AAPL 애플 주가를 얻은 다음
zipline 백테스팅 예제를 처리 하면 잘 동작 한다.

dataFrame에서 날짜 인덱스 부분을 UTC 시간으로 변경 하는 작업을 해주면 문제 없이 동작 한다.

하지만 국내 증시
나의 경우 직접 대신 증권 API를 통해서 일자별 시가, 고가, 저가, 종가를 얻어다가 DB를 구성 하고
이를 다시 로드 하여 dataframe을 구성해서

zipline에서 이용할 때 위와 같은 에러를 만났다.

대충 원인을 분석 하니 날짜 인덱스가 문제가 되는것으로 보인다.

그래서 pandas를 통해서 해당 종목을 yahoo에서 얻어서 처리를 해도 마찬가지 에러가 발생 한다.

우리나라 주식 거래일과 미쿡의 주식 거래일이 달라서 생기는 문제로 잠정 원인을 파악 했다.

그럼 해결방법은?

분석 시작 날짜로 부터 오늘의 데이터 까지를 대략 AAPL 즉 애플의 데이터 (미쿡 증시) 를 가져 온다음
종가 Adj Close 컬럼의 값을 실제로 내가 분석 하고 싶은 우리나라 종목에 종가로 교체 해 버리고

zipline을 처리 하니 동작 하더라

여기서 중요 포인트는 데이터 프레임의 개수를 맞추는 작업

    import pandas_datareader.data as web
    import datetime
    start = datetime.datetime(2010, 1, 1)
    end = datetime.datetime(2016,7,15)
    data  = web.DataReader("AAPL", "yahoo", start, end)
    data = data[['Adj Close']]
    data.columns = [code]
    data.head()
    data = data.tz_localize("UTC")

    df = makeDataFrame(code)
   
   
    df = df[['CLOSE']]
    data = data[len(data) - len(df):]  #데이터 프레임의 row 수를 맞추는 작업
    data[code] = np.where(1, df['CLOSE'], df['CLOSE'])    #DB에서 가져온 df의 CLOSE 컬럼을 AAPL의 종가 컬럼으로 교체
   
   
   
   
zipline이 아직 개발 단계에 문제점이 많지만 간편하게 백테스팅을 처리 할 수 있는 좋은 라이브러리 같다.

2016년 7월 14일 목요일

주가 분석 , 이평선 골든 크로스, 볼린져 밴드 하단 터치 이후 상승, MACD signal Cross 종목 찾기

https://github.com/sparrowapps/systemt

2016년 7월 14일 골든 크로스 결과만 보자면

sqlite> select * from MAGC;
0|A004560|현대비앤지스틸
1|A175330|JB금융지주
2|A004960|한신공영
3|A900110|이스트아시아홀딩스
4|A085310|엔케이
5|A000155|두산우
6|A016880|웅진
7|A059090|미코
8|A200780|비씨월드제약
9|A082740|두산엔진
10|A110570|넥솔론
11|A100030|모바일리더
12|A160550|NEW
13|A027050|코리아나
14|A083450|GST
15|A089600|나스미디어
16|A041440|에버다임
17|A200230|텔콘
18|A090990|코리아03호
19|A065440|이루온
20|A079650|서산
21|A008700|아남전자
22|A024120|KB오토시스
23|A050760|에스폴리텍
24|A008970|동양철관

sqlite> 

네이버님의 골든크로스 결과보다 몇몇 종목이 더 나 왔다.


#-*- coding: utf-8 -*-

import pandas as pd
from pandas import Series, DataFrame
import sqlite3

import numpy as np

from logger import get_logger
from stockcode import get_code_list
from settings import START_DATE
from cp_constant import *
import win32com.client

def checkData( cursor, table_name ):
    row = cursor.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name='{}'".format(table_name)).fetchone()
    if row is None: return False
       
    row = cursor.execute("SELECT COUNT(*) FROM '{}'".format(table_name)).fetchone()
    if row is None : return False

    return True

def makeDataFrame( code ):
    with sqlite3.connect("price.db") as con:
        cursor = con.cursor()
        table_name = code
       
        if checkData( cursor, table_name ) == False: return False
       
        df = pd.read_sql("SELECT * FROM '{}'".format(table_name), con, index_col=None)
        return df

def isMAGoldCross( df, MA1 = 20, MA2 = 60 ):
    df['short_ma'] = pd.rolling_mean(df['CLOSE'],MA1)
    df['long_ma'] = pd.rolling_mean(df['CLOSE'],MA2)
    df['signal'] = 0.0
       
    if len(df) < MA2 * 4 : return False
       
    df['signal'][MA1:] = np.where(df['short_ma'][MA1:] > df['long_ma'][MA1:], 1.0, 0.0)
    df['positions'] = 0.0
    df['positions'] = df['signal'].diff()
       
    if df['signal'][len(df)-1] == 1 and df['positions'][len(df)-1] == 1:
        return True
    else:
        return False

def isBBandSignal( df, period = 20):
    df['Bol_upper'] = pd.rolling_mean(df['CLOSE'], window=period) + 2* pd.rolling_std(df['CLOSE'], period, min_periods=period)
    df['Bol_lower'] = pd.rolling_mean(df['CLOSE'], window=period) - 2* pd.rolling_std(df['CLOSE'], period, min_periods=period)
    df['signal'] = 0.0
    df['signal'] = np.where(df['Bol_lower'] > df['CLOSE'], 1.0, 0.0)
    df['positions'] = 0.0
    df['positions'] = df['signal'].diff()

    if len(df) < 2: return False

    if df['signal'][len(df)-2] == 1 and df['positions'][len(df)-2] == 1:
        if df['signal'][len(df)-1] == 0 and df['positions'][len(df)-1] == -1.0:
            return True
        else:
            return False
    else:
        return False

def isMACDSignal( df, n1 = 12, n2= 26, c= 9):
    df['MACD'] = pd.ewma(df['CLOSE'], span=n1) - pd.ewma(df['CLOSE'], span=n2)
    df['MACD_Signal'] = pd.ewma(df['MACD'], span=c)
    df['signal'] = 0.0
    df['signal'] = np.where(df['MACD'] < df['MACD_Signal'], 1.0, 0.0)
    df['positions'] = 0.0
    df['positions'] = df['signal'].diff()

    if df['signal'][len(df)-1] == 1 and df['positions'][len(df)-1] == 1:
        return True
    else:
        return False

def run():
    with sqlite3.connect("analyze.db") as con:
        cursor = con.cursor()

        code_magc = {'CODE':[],
                     'NAME':[]}
        code_bb   = {'CODE':[],
                     'NAME':[]}
        code_macd = {'CODE':[],
                     'NAME':[]}

        for code, name in get_code_list():
            df = makeDataFrame( code )
            res = isMAGoldCross( df, 20, 60 )
            if res == True:
                code_magc['CODE'].append(code)
                code_magc['NAME'].append(name)
                get_logger().debug("MA20, MA60 Golden Cross {}{}".format(code,name))

            res = isBBandSignal( df, 20 )
            if res == True:
                code_bb['CODE'].append(code)
                code_bb['NAME'].append(name)
                get_logger().debug("BBnad lower after up {}{}".format(code,name))

            res = isMACDSignal( df, 12, 26, 9)
            if res == True:
                code_macd['CODE'].append(code)
                code_macd['NAME'].append(name)
                get_logger().debug("MACD sig {}{}".format(code,name))

        magc = DataFrame(code_magc)
        bb   = DataFrame(code_bb)
        macd = DataFrame(code_magc)
        magc.to_sql("MAGC", con, if_exists='replace', chunksize=1000)
        get_logger().debug("MAGC {} saved.".format(len(magc)))
        bb.to_sql("BB", con, if_exists='replace', chunksize=1000)
        get_logger().debug("BB {} saved.".format(len(bb)))
        macd.to_sql("MACD", con, if_exists='replace', chunksize=1000)
        get_logger().debug("MACD {} saved.".format(len(macd)))

if __name__ == '__main__':
    run()

python pandas_datareader yahoo 파이낸스 삽질기


코스콤에서 주식 코드를 얻어다가 yahoo 파이넨스에 pandas를 이용해서 주가 데이터를
가져 와서 sqlite  데이터를 구축 하려고 했다.

이렇게 가져온 데이터를 다시 pandas 데이터 프레임으로  sqlite로 부터 읽어 들여 분석을
하면 이동평균선 골든 크로스 등을 찾아 낼 수 있을 거라 생각 했다.

그런데 이상하게 동원이란 종목이 분석에 문제를 일으킨다.


>>> import pandas_datareader.data as web
>>> df = web.DataReader("003580.KS", "yahoo", "2016/06/01", "2016/07/13")
>>> print (df)
            Open  High   Low  Close  Volume  Adj Close
Date                                                
2016-06-01  1854  1854  1802   1814   45100       9070
2016-06-02  1866  1866  1804   1830   35200       9150
2016-06-03  1850  1874  1792   1854   31100       9270
2016-06-06  1854  1854  1854   1854       0       9270
2016-06-07  1840  1878  1822   1846   22900       9230
2016-06-08  1846  1846  1784   1814   26700       9070
2016-06-09  1780  1868  1780   1856   46500       9280
2016-06-10  1834  1870  1800   1832   20900       9160
2016-06-13  1800  1828  1772   1812   17700       9060
2016-06-14  1780  1800  1680   1758   25400       8790
2016-06-15  1740  1812  1724   1732   16600       8660
2016-06-16  1720  1788  1694   1730   16700       8650
2016-06-17  1730  1738  1602   1644   34900       8220
2016-06-20  1630  1670  1620   1638   29200       8190
2016-06-21  1650  1668  1638   1656   11300       8280
2016-06-22  1626  1696  1626   1660    7400       8300
2016-06-23  1632  1666  1630   1660   14000       8300
2016-06-24  1666  1676  1450   1550   77000       7750
2016-06-27  1486  1626  1482   1626   37600       8130
2016-06-28  1580  1694  1564   1690   32400       8450
2016-06-29  1688  1738  1622   1690   24000       8450
2016-06-30  1660  1762  1660   1762   35200       8810
2016-07-01  1762  1780  1726   1780   19600       8900
2016-07-04  1768  1790  1742   1760    6300       8800
2016-07-05  1734  1778  1732   1758   14100       8790
2016-07-06  1740  1740  1684   1720   14300       8600
2016-07-07  1780  1780  1702   1740   12900       8700
2016-07-08  1720  1784  1698   1740   24700       8700
2016-07-11  8400  8660  7630   8030  268800      40150
2016-07-12  7850  8400  7850   8350  106900      41750
2016-07-13  8500  8500  8250   8300   40000      41500




위에서 데이터를 보면 7월 8일 까지 1700원 때이던 데이터가 갑자기 8400원대로 변경 된다.
문제가 있다.

거래량도 갑자기 증가 한다.

야후 파이낸스에 데이터를 믿을수가 없다.

문제는 또한 KOSDAQ종목은 데이터가 없다.

이는 구글 파이낸스도 마찬가지다.

하여 결국 대신 증권 API를 이용해서 코드 부터 주식 과거 데이터를 구축 하는 코드로 변경 하였다.


야후 파이낸스도 믿을게 못된다.


2016년 6월 9일 목요일

XCode swift "Command failed due to signal: Segmentation fault: 11 해결 방법

XCode swfit 컴파일러가 컴파일중에 세그먼테이션 폴트가 발생하면 어떻게 해결 해야 하는가?



어떤 라인이 문제가 있는지 어떤 코드가 문제인지 찾아 주지도 못한다.

심지어 이런 상황에서 버그를 리포트 하라고 에디터 상단에 아래와 같은 화면이 표시 되기도 한다.




에디터는 신텍스 하이라이트가 실행 되지 못하고 모든 텍스트가 흑백으로 표시 될때도 있다.





XCode의 버젼은 7.3.1이다.


문제의 코드는 위에 주석처리 한 코드 아래의 코드가 문제를 일으키는 코드이다.

recentlAddr은 아래와 같이 선언 되어 있다.

var recentlAddr = [[String:AnyObject]]()

if 문으로 바로 비교를 들어가면 Xcode swift 컴파일러가 문제를 일으키는 것으로 보인다.

더많은 케이스가 있겟지만 내가 찾은 케이스는 이런 경우 이다.

위에 올바른 코드와 같이 

func bookmarkButtonClick(sender : UIButton) {
        let bookmark = self.recentlAddr[sender.tag]["bookmark"] as! String
        
        if bookmark == "1" {
            return
        }
        recnetTobookmark(sender.tag, bookMarkType: 3)
    }

이렇게 수정하면 아무런 문제 없이 컴파일이 잘되고 문제가 없다.

결론
 XCode 컴파일러의 Segmentation fault 11이 발생하면 그래도 코드를 유심히 찾아 봐라!

2016년 6월 2일 목요일

iOS app에서 GCM (Google Cloud Messaging 사용하기

APNS를 위한 애플 개발자 페이지에서 앱 설정을 해야 한다.

구글 클라우드 메세징 서비스 페이지
https://developers.google.com/cloud-messaging/

구글 페이지에서
좌측 iOS를 선택하고 언어를 swift로 선택을 하여 안내를 받을 수 있다.

Cocoapod 을 이용해서 Google GCM 라이브러리를 프로젝트에 추가 해야 한다.

https://developers.google.com/cloud-messaging/ios/start?ver=swift


아래에 훌륭한 문서를 참조 하면 쉽게 따라 할 수 있다.
http://devlecture.tistory.com/entry/팁02-APNS-쉽게-만들고-테스트하기


Appdelegate 에 구글에서 제공하는 코드 뼈대를 잘 넣어 줘서 푸시를 수신 받을 수 있다.


앱이 포그라운드 상태에서 푸시를 받으면 아래에 메서드가 호출 된다.
 func application( application: UIApplication,
    didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
      print("Notification received: \(userInfo)")
      // This works only if the app started the GCM service
      GCMService.sharedInstance().appDidReceiveMessage(userInfo);
      // Handle the received message
      // ...
  }
 
앱이 백그라운드 상태에서 푸시를 받으면 아래에 메서드가 호출 된다.  
  func application( application: UIApplication,
    didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
    fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) {
      print("Notification received: \(userInfo)")
      // This works only if the app started the GCM service
      GCMService.sharedInstance().appDidReceiveMessage(userInfo);
      // Handle the received message
      // Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
      // ...
  }
 
아래에 메서드는 GCM 서버로 부터 registrationToken을 받는다. 
  func registrationHandler(registrationToken: String!, error: NSError!) {
  }
 
테스트는 구글  크롬 어플리케이션 중에 포스트 맨을 이용하여 테스트가 가능 하다.

  registrationToken과 구글 GCM 사이트에서 앱을 등록 하고 p12 키등을 입력 받아서 얻은 Server API Key가 있어야 테스트가 가능 하다.



중요
디버깅 모드에서 푸시가 잘되는데 adhoc (릴리즈 버젼에서 푸시가 오지 않는경우)

개발이 완료되어 배포 시점이 되면 다음 코드를 수정 해야 한다.
  func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken
      deviceToken: NSData ) {
  // [END receive_apns_token]
        // [START get_gcm_reg_token]
        // Create a config and set a delegate that implements the GGLInstaceIDDelegate protocol.
        let instanceIDConfig = GGLInstanceIDConfig.defaultConfig()
        instanceIDConfig.delegate = self
        // Start the GGLInstanceID shared instance with that config and request a registration
        // token to enable reception of notifications
        GGLInstanceID.sharedInstance().startWithConfig(instanceIDConfig)
        registrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken,
          kGGLInstanceIDAPNSServerTypeSandboxOption:true]
        GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID,
          scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler)
        // [END get_gcm_reg_token]
  }

kGGLInstanceIDAPNSServerTypeSandboxOption:true --> kGGLInstanceIDAPNSServerTypeSandboxOption:false

2016년 5월 21일 토요일

기아 uvo 2.0 사용기



현대에는 블루링크, 기아에는 uvo라는 이통사와 연계된 텔레메틱스 시스템이 있다.

2년간 무료 서비스이기에 사용중이다.

2년후에는 유료로 전환되는데 사용결정은 일단 그때로 미루고 최근 사용해본 소감을 나열 한다.

아이뻐 6s + 화면에서는 그냥 UI가 커진 화면으로 나와서 좀 어색 하지만 딱 필요한 기능만 넣은 간단한 UVO앱으로 uvo 서비스를 제공 한다.

가장 좋은 기능은 네비게이션에게 목적지를 전송 하는 기능
출발전 아이폰으로 목적지를 검색하고 전송한 후 차에서 네비게이션을 확인 하여 전송 받은 목적지를 선택 하여 목적지로 설정 할 수 있다.

그리고 점점 더워지는 요즘 날씨에 꼭 필요한 원격시동
어릴적 아버지 자동차에 사제 원격시동 장치는 기어가 들어가 있음에도 시동이 걸려 주차한 상태에서 다른 차를 받아 버리는 어처구니 없는 사고가 일어 났었지만..... 쩝

설마 그런 버그는 없겠지...?  그리고 기어를 넣고 시동을 끄면 머 경고 해주니.. 설마 그럴일은 없을 거야? 나름 안심 하고 사용 한다.

원격시동은 미리 설정한 공조 장치 (에어컨 온도) 등이 작동 하면서 시동이 걸려 요즘 처럼 날씨가 더워지는 시기에 아주 좋은 기능 같다. 겨울에도 디젤차의 경우 예열이 필요 하니 유용 할 듯 하다.

그밖에 항상 내차의 문은 잠겨있는지? 주차는 어디에 했는지? 요정도의 기능이 있어 좋다.
그리고 어떤 식으로 동작 하는지는 모르지만 기아 레드 멤버스 웹페이지에 로그인 해보면
내 차량 정보와 현재 주행 거리 ( 하루 정도 전) 의 거리가 표시 된다. 차량 내부에 uvo 단말기가 기아 서버에 현재 주행 거리를 주기적으로 sms로 전송 하는 것으로 보인다.

좀 아쉬운 점이라면 앱의 GUI가 좀 예쁘지 않다는거? 아이폰 6s+에서 그냥 UI가 커진 이미지는 정말 좀 아쉽다.

uvo 자체가 자동차에 있는 단말기에 전화를 걸어서 처리 하는 것으로 보이는데 동작 속도가 좀 오래 걸린다는거? 문잠금 정도 확인 하는데도 20여초가 걸리니 좀 성질 급한 나는 좀 아쉽다.

그래도 2년 동안 이러한 기능들을 무료로 사용 할 수 있어서 좋다.
이밖에도 불의의 사고로 에어백이 전개 되거나 차량 추돌이 감지 되었을때 긴급 SOS기능으로 보험사 등에 연락을 한다는 것도 좋은 기능 같다.



2016년 5월 11일 수요일

블랙박스 CR-500HD 베터리 교체 하기

2009 투싼ix에서 사용하던 블랙박스 파인뷰의 CR-500HD 상당이 고가의 블랙박이다.
화질 좋고 야간 촬영도 어둡지 않고 시인성이 좋다.

학교 선배 형님이 장착 해주신건데 너무 고가 이지만 장착 후  사고 하나를 잡아내서 몸값은 했다.
그러나 이 블랙박스의 치명적인 결함이 있으니 내장 베터리가 2년이면 고장이 난다.

내장 베터리가 고장이나면 메번 부팅시마다 시간설정을 확인 하라는 메세지가 나오고
저장된 동영상은 모두 날짜가 뒤죽 박죽 되어 있다.

http://ttlshome.tistory.com/335

http://blog.daum.net/_blog/BlogTypeView.do?blogid=0Zv3l&articleno=86&categoryId=0&regdt=20140914145225

위 링크는 모두 이런 문제 때문에 자가 교체 하는 내용이다.

고장이 난지 벌써 2년이 흘렀지만 그냥 그냥 내비 둿다.
베터리까지 사서 교체 하기는 너무 힘들고 어려울듯 해서였다.

투싼ix를 처분하면서 CR-500HD를 차량에서 떼어 냈다.
그리고 알바로 작업했던 하드웨어에 비슷한 모양에 베터리가 있길래 위 링크를 참조해서 직접 분해하고 베터리를 교체 해 보았다.

결론
잘된다.

아파트 베란다에 지상 주차장 감시용 CCTV나 맨드러야 겠당!

2016년 4월 21일 목요일

swift 에서 URLRequest, JSON 파싱 (REST API 클라이언트 개발)

2013년 앱개발 알바를 할때는 다음과 같은 기록을 남겼다.

http://mtsparrow.blogspot.kr/2013/04/ios-json.html

2016년 swift 에서는 어떻게 해야 하는가?

http://mtsparrow.blogspot.kr/2016/03/cocoapod.html

코코아 팟을 이용해서 오픈 소스 중에 JSON과  URLRequest를 편리 하게 해주는
오픈 소스 두개를 추가 한다.

SwiftyJSON
https://github.com/swiftjson/SwiftJson


Alamofire

https://github.com/Alamofire/Alamofire


다음은 간단한 예제 코드이다.

    var arrRes = [[String:AnyObject]]() // Array of dictionary
    var reqParam = [ String:AnyObject]()


func RequestRestAPI() {
        // 요청 키 밸류
        reqParam = [
            "--":"--",
            "--":"--",
            "--":"--",
            "--":"--",
            "--":"--",
            "--":"--",
            "--":"--",
            "--":"--",
            "--":"--"
        ]
        
        print(reqParam)
        

        ActIndicator.startAnimating() 
        Alamofire.request(.POST, "https://URL~~~blahblah/api/", parameters: reqParam).responseJSON { (responseData) -> Void in
            self.ActIndicator.stopAnimating()
            if responseData.result.value != nil {
                let swiftyJsonVar = JSON(responseData.result.value!)
                
                print(swiftyJsonVar["result"])
                print(swiftyJsonVar["message"])
                if swiftyJsonVar["result"].stringValue == "true" {
                    if let resData = swiftyJsonVar["data"].arrayObject {
                        self.arrRes = resData as! [[String:AnyObject]]
                    }
                    print(self.arrRes)
                    
                    // 응답 성공 arrRes에  딕셔너리에서 결과 키 밸류를 이용해서 처리 한다.
                    
                } else {
                    // 에러 응답 처리
                }
            } else {
               // 네트워크 에러 처리

            }
        }

    }


간단하게 설명을 하자면

ActIndicator.startAnimation()은 엑티비티 인디케이터를 네트워크 시작시 에니메이션을 시작 한다.

Alamofire.request 로 REST API에 URL을 주고 reqParam으로 정의된 키벨류를 전달 한다.

네트워크 요청이 완료되면 Alamofire.request의 블럭 코딩 부분이 수행되며 액티비티 인디케이터 에니 메이션을 종료 하고 SwiftyJSON을 이용한 결과 데이터를 얻어내면 된다.

위 예제는 응답 결과를 JSON을 받고 약속된 프로코톨로 result, message, data 키로 받게 되어 있다.
REST API마다 JSON의 형태는 다를 것이고 이경우 result, message, data는 동일 하며
data의 형태는 다시 키벨류 쌍으로 REST API tag의 따라 다양하게 정의된 경우 이다.

// 응답 성공의 경우 data의 arrayObject를 self.arrRes에 할당 했으며 데이터 타입은 String:AnyObject이다.

정의된 String Key로 값을 접근하면 된다.