주식차트를 위한 ohlc 데이터 만들기

주식차트는 분,초,시,일 등의 가격 데이터를 O(open), H(high), L(low), C(close)를 가지고 봉 형태로 표시해 줍니다. 이를 candlesticks chart라고 합니다.

그러면, 주식의 거래데이터가 아래와 가티 주어질 때 ohlc를 만듭니다.

### 
실제 상황과 비슷하게 하기 위해서 아래 예제를 조금 바꾸어보았습니다. 조건이 바뀌기 전에 답을 주신 '상파'님의 양해를 먼저 구합니다.
###

예) 1분마다 open, high, low, close를 만듭니다.
분:초, 거래가격
1:02, 1100
1:20, 1170
1:59, 1090
2:30, 1030
2:31, 1110
2:42, 1150
2:55, 1210
2:56, 1190
3:02, 1120
3:09, 1100
4:15, 1090
4:20, 1080
4:55, 1050
4:56, 1020
4:57, 1000

[조건] 실제 주식 거래의 경우,, 분당 거래 회수가 10번, 100번, 1000번 등일 수 있으므로, high, low를 찾을 때 되도록 전체 데이터에서 찾지 않고,, 앞뒤 데이터를 비교하여 찾아주십시오.. 

답: 
open = [1100, 1030, 1120, 1090]
high = [1170, 1210, 1120, 1090]
low = [1090, 1030, 1100, 1000]
close = [1090, 1190, 1100, 1000]
ohlc candlestick finance chart
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

11개의 풀이가 있습니다. 1 / 2 Page

요즘 들어 Queue를 자주 쓰네요 샘플 데이터를 만든 후에 차례로 처리했습니다. 처리 부분보다 초기화, 출력 코딩이 대부분이네요

그런데 비교 함수를 쓴다고 메모리 문제가 발생한다는 게 조금 의아합니다
메모리에 올라간 데이터들은 어차피 참조 방식으로 비교할 테고, 몇몇 Sort 알고리즘처럼 추가적인 메모리가 필요하지 않거든요

어쨋든, 500개의 데이타 샘플을 출력해봤습니다. 랜덤으로 초를 증가시키고, 가격도 1000을 기준으로 +- 250을 랜덤으로 주었습니다

struct Data
{
    public DateTime Time;
    public int Price;
}
static void Main()
{
    const int SAMPLE = 500;
    int time = 0, price = 0, minute = -1;
    Random rnd = new Random();

    Queue<Data> dataList = new Queue<Data>();
    List<int> open = new List<int>();
    List<int> high = new List<int>();
    List<int> low = new List<int>();
    List<int> close = new List<int>();
    List<int> count = new List<int>();

    for (int i = 0; i < SAMPLE; i++)
    {
        time += rnd.Next(0, 6);
        price = 1000 + rnd.Next(-250, 250);
        dataList.Enqueue(new Data() { Time = new DateTime().AddSeconds(time), Price = price });
    }

    while (dataList.Count > 0)
    {
        var data = dataList.Dequeue();
        if (minute < data.Time.Minute)
        {
            minute = data.Time.Minute;
            open.Add(data.Price);
            high.Add(data.Price);
            low.Add(data.Price);
            close.Add(data.Price);
            count.Add(1);
        }
        else
        {
            if (high[minute] < data.Price) high[minute] = data.Price;
            if (low[minute] > data.Price) low[minute] = data.Price;
            close[minute] = data.Price;
            count[minute]++;
        }
    }

    Console.Write("{0,15}{1,6}{2,6}{3,6}{4,6}{5,6}", "minute", "open", "high", "low", "close", "nData");
    Console.WriteLine();

    for (int i = 0; i < minute + 1; i++)
    {
        Console.Write("{0,15}", string.Format("{0:00}:00 ~ {1:00}:00", i, i + 1));
        Console.Write("{0,6}", open[i]);
        Console.Write("{0,6}", high[i]);
        Console.Write("{0,6}", low[i]);
        Console.Write("{0,6}", close[i]);
        Console.Write("{0,6}", count[i]);
        Console.WriteLine();
    }
}

결과

         minute  open  high   low close nData
  00:00 ~ 01:00   850  1183   750  1183    23
  01:00 ~ 02:00  1125  1222   798   911    24
  02:00 ~ 03:00   992  1237   768   884    25
  03:00 ~ 04:00   862  1166   777  1161    23
  04:00 ~ 05:00  1137  1216   755   755    28
  05:00 ~ 06:00   961  1238   766   970    31
  06:00 ~ 07:00  1010  1245   763  1147    24
  07:00 ~ 08:00   995  1169   769   819    21
  08:00 ~ 09:00   853  1246   754   774    22
  09:00 ~ 10:00  1228  1232   788   798    26
  10:00 ~ 11:00   791  1222   771   912    23
  11:00 ~ 12:00  1027  1232   774   815    25
  12:00 ~ 13:00   886  1239   758   910    23
  13:00 ~ 14:00   837  1182   756  1095    21
  14:00 ~ 15:00   811  1206   769  1048    26
  15:00 ~ 16:00  1201  1235   752  1112    21
  16:00 ~ 17:00  1188  1236   777   938    19
  17:00 ~ 18:00  1148  1234   767  1078    19
  18:00 ~ 19:00  1025  1247   854  1048    24
  19:00 ~ 20:00  1150  1225   784  1199    23
  20:00 ~ 21:00   854  1246   754   885    27
  21:00 ~ 22:00   932  1147   932  1147     2
계속하려면 아무 키나 누르십시오 . . .
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

Python 3.4.2

  1. 단위시간의 거래는 log 파일로 떨어지며 'trade.log' 라는 파일에 쌓이는 것은 전제로 함. 따라서 문제에 주어진 기록을 trade.log 라는 텍스트파일에 저장하여 line 단위로 읽어들임.

  2. 봉차트는 단위시간당 open, high, low, close 를 나타내므로 결과는 주어진 답변과 달리 분단위 표시하는 것이 좋음

  3. 거래시간당 시가, 고가, 저가, 종가를 바로 찾을 수 있게 dictionary 로 구현

  4. 단위시간이 늘어나면 KeyError exception 처리를 하여 log 가 늘어나더라도 처리할 수 있도록 함. 따라서 분단위가 올라가는 첫시점에는 시가가 고가, 저가, 종가를 모두 기록하게 됨

import re, os

log = open(os.path.join(os.getcwd(), 'trade.log'))
lines = log.readlines()
ohlc = {}

for line in lines:
    line_list = re.split("[:,]", line)
    line_time = int(line_list[0])
    line_price = int(line_list[2])
    try:
        ohlc[line_time]['close'] = line_price
        if line_price > ohlc[line_time]['high']:
            ohlc[line_time]['high'] = line_price
        elif line_price < ohlc[line_time]['low']:
            ohlc[line_time]['low'] = line_price
    except KeyError: # 분단위 첫 거래의 log 등록
        ohlc[line_time] = {'open': line_price, 'high': line_price, 'low': line_price, 'close': line_price}

print(ohlc)

결과

>>> print(ohlc)
{1: {'close': 1090, 'high': 1170, 'low': 1090, 'open': 1100}, 2: {'close': 1190, 'high': 1210, 'low': 1030, 'open': 1030}, 3: {'close': 1100, 'high': 1120, 'low': 1100, 'open': 1120}, 4: {'close': 1000, 'high': 1090, 'low': 1000, 'open': 1090}}
>>> print(ohlc[2]['high']) # 2분대거래의 고가?
1210
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

파이썬 2.7.6입니다.

data='''\
1, 1100
2, 1170
3, 1090
4, 1030
5, 1110

6, 1150
7, 1210
8, 1190
9, 1120
10, 1100

11, 1090
12, 1080
13, 1050
14, 1020
15, 1000'''

d = [int(x.split()[1]) for x in data.split('\n') if x] #if x 는 빈줄때문에 삽입
open=[];high=[];low=[];close=[]
while d:
    i=0
    tmp=[]
    while d and i<5:tmp.append(d.pop(0));i+=1#5개씩 가져와서 tmp에 넣음
    open.append(tmp[0])
    high.append(max(tmp))
    low.append(min(tmp))
    close.append(tmp[-1])

print 'open=',open
print 'high=',high
print 'low=',low
print 'close=',close

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

제가 출제하신분의 의도를 알아채지못하고 엉뚱한 코드를 작성하는 바람에
문제를 수정하는 수고로움을 끼쳐드렸네요. 죄송합니다.^^;;
일단 자료를 화일로 저장해서 한줄씩 읽어서 처리했구요,
지정한 4개의 리스트외에는 리스트를 사용하지 않았습니다.
메모리는 최소한도로 사용했습니다.

이번 풀이는 hana11님의 의도에 맞는거죠? ㅎㅎ

open=[];high=[];low=[];close=[]
prev_t=''
for line in file('s498.txt'):
    t,p=line.split(', ');p=int(p)
    if not prev_t or prev_t[:-3]!=t[:-3]:
            open.append(p)
            high.append(p)
            low.append(p)
            close.append(p)
    if p>high[-1] : high[-1] = p
    if p<low[-1] : low[-1] = p
    close[-1]=p
    prev_t = t

print open
print high
print low
print close

문제 출제자가 잘 못낸 것이지요.. <감사> - hana11, 2016/01/27 19:37 M D
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

var open : Dictionary = Dictionary() var close : Dictionary = Dictionary() var high : Dictionary = Dictionary() var low : Dictionary = Dictionary()

    let componentSepa = input.componentsSeparatedByString("\n")

    for line in componentSepa {
        let componentHour = line.componentsSeparatedByString(":").first
        let componentValue = line.componentsSeparatedByString(", ").last

        if(componentHour == "") {
            continue
        }

        //open.
        if nil == open[componentHour!] {
            open[componentHour!] = componentValue

            low[componentHour!] = componentValue
            high[componentHour!] = componentValue
        }

        //close
        close[componentHour!] = componentValue

        //low
        if low[componentHour!] > componentValue {
            low[componentHour!] = componentValue
        }

        //high
        if high[componentHour!] < componentValue {
            high[componentHour!] = componentValue
        }
    }


    let sortOpen = open.sort { (obj1, obj2) -> Bool in
        return obj1.0 < obj2.0
    }

    let sortClose = close.sort { (obj1, obj2) -> Bool in
        return obj1.0 < obj2.0
    }

    let sortHigh = high.sort { (obj1, obj2) -> Bool in
        return obj1.0 < obj2.0
    }

    let sortLow = low.sort { (obj1, obj2) -> Bool in
        return obj1.0 < obj2.0
    }

    //output
    for seq in sortOpen.enumerate() {
        print("open hour(\(seq.element.0)) : \(seq.element.1)")
    }
    for seq in sortClose.enumerate() {
        print("close hour(\(seq.element.0)) : \(seq.element.1)")
    }
    for seq in sortHigh.enumerate() {
        print("high hour(\(seq.element.0)) : \(seq.element.1)")
    }
    for seq in sortLow.enumerate() {
        print("low hour(\(seq.element.0)) : \(seq.element.1)")
    }

2016/02/27 22:24

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

Ruby

거래값 1개를 받으면 {분=>[o,l,h,c]} 해쉬를 생성. 분에 해당하는 해쉬가 있으면 lhc값 업데이트. 가장 윗줄 enq[해쉬,분,가격]이 로직. 나머지는 입출력.

enq = ->q,m,v { q[m]? q[m][1,3]=[*[*q[m][1,2],v].minmax,v] : q[m]=[*[v]*4]; q }
olhc = ->s { s.reduce({}) {|q,e| min,_,price=e.scan(/\w+/); enq[q,min,price.to_i] } }
agg_olhc = ->stocks { %w(open low high close).zip(olhc[stocks].values.transpose) }
out_olhc = ->str { agg_olhc[str].each {|tag,prices| puts "#{tag} = #{prices}" } }

Test

stocks_str = ["1:02, 1100", "1:20, 1170", "1:59, 1090", "2:30, 1030", "2:31, 1110", "2:42, 1150", "2:55, 1210", "2:56, 1190", "3:02, 1120", "3:09, 1100", "4:15, 1090", "4:20, 1080", "4:55, 1050", "4:56, 1020", "4:57, 1000"]
tagged_olhc = [["open", [1100, 1030, 1120, 1090]], ["low", [1090, 1030, 1100, 1000]], ["high", [1170, 1210, 1120, 1090]], ["close", [1090, 1190, 1100, 1000]]]
expect(agg_olhc[stocks_str]).to eq tagged_olhc

Output

#=> out_olhc[stocks_str]
open = [1100, 1030, 1120, 1090]
low = [1090, 1030, 1100, 1000]
high = [1170, 1210, 1120, 1090]
close = [1090, 1190, 1100, 1000]
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

open=[];high=[];low=[];close=[] prev_t='' for line in file('s498.txt'): t,p=line.split(', ');p=int(p) if not prev_t or prev_t[:-3]!=t[:-3]: open.append(p) high.append(p) low.append(p) close.append(p) if p>high[-1] : high[-1] = p if p<low[-1] : low[-1] = p close[-1]=p prev_t = t

print open print high print low print close

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

입력을 파싱하는 부분,

파싱된 정보를 각 분 단위로 비교/업데이트하는 부분

분단위로 생성된 결과를 수집하는 부분

최종 결과를 출력하는 부분을 각각 코루틴으로 만들어서

쓸데없이 길게 구현해보았습니다.

파이썬 3.5 입니다.

import re


def coroutine(fn):
    def inner(*a, **b):
        r = fn(*a, **b)
        next(r)
        return r
    return inner


@coroutine
def printer():
    while True:
        data = (yield)
        if data:
            for k, v in data.items():
                print("{} = {}".format(k, v[1:]))


@coroutine
def acc(target=None):
    data = dict(open=[], close=[], high=[], low=[])
    while True:
        input_ = (yield)
        if not input_:
            break
        vopen, vclose, vhigh, vlow = input_
        data['open'].append(vopen)
        data['close'].append(vclose)
        data['high'].append(vhigh)
        data['low'].append(vlow)
    if target:
        target.send(data)


@coroutine
def updater(target=None):
    current_min = -1
    vhigh, vlow, vopen, vclose = 0, 0, 0, 0
    continue_ = True
    while continue_:
        input_ = (yield)
        if input_ is None:
            continue_ = False
        else:
            minute, _, value = input_
            print(minute, current_min, continue_)
        if continue_ is False or minute != current_min:
            if target:
                target.send((vopen, vclose, vhigh, vlow))
            vopen, vclose, vhigh, vlow = value, value, value, value
            current_min = minute
        else:
            vhigh = value if value > vhigh else vhigh
            vlow = value if value < vlow else vlow
            vclose = value
    if target:
        target.send(None)


@coroutine
def parser(target=None):
    pat = re.compile(r'(\d{1,2}):(\d{1,2}), (\d+)')
    while True:
        data = (yield)
        m = pat.match(data)
        result = [int(x) for x in m.groups()] if m is not None else None
        if target:
            target.send(result)
        if not result:
            break


def main():
    process = parser(updater(acc(printer())))
    try:
        while True:
            line = input()
            process.send(line)
            if not line:
                break
    except:
        pass

main()
※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.
data=[
'1:02, 1100',
'1:20, 1170',
'1:59, 1090',
'2:30, 1030',
'2:31, 1110',
'2:42, 1150',
'2:55, 1210',
'2:56, 1190',
'3:02, 1120',
'3:09, 1100',
'4:15, 1090',
'4:20, 1080',
'4:55, 1050',
'4:56, 1020',
'4:57, 1000',
]
o=[]
h=[]
l=[]
c=[]
wm=-1;
for d in data:
    dl = d.split(',')
    v = int(dl[1].strip())
    m = int(dl[0].split(":")[0].strip())
    if wm != m:
        o.append(v)
        h.append(v)
        l.append(v)
        c.append(v)
        wm = m
    if h[-1] < v:
        h[-1] = v
    if l[-1] > v:
        l[-1] = v
    c[-1] = v
print("open = {}".format(o))
print("higt = {}".format(h))
print("low = {}".format(l))
print("close = {}".format(c))

Python 3.5.2에서 작성하였습니다.

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.
tmp = '''1:02, 1100
1:20, 1170
1:59, 1090
2:30, 1030
2:31, 1110
2:42, 1150
2:55, 1210
2:56, 1190
3:02, 1120
3:09, 1100
4:15, 1090
4:20, 1080
4:55, 1050
4:56, 1020
4:57, 1000'''.split('\n')
o,h,l,c = list(),list(),list(),list()
high_1, low_1, step = 0,0,0
value = [x.split(', ')[1] for x in tmp]
m = [x.split(':')[0] for x in tmp]
for x in range(len(m)):
    if step != int(m[x]):
        step = int(m[x])
        high_1 = value[x]
        low_1 = value[x]
        o.append(value[x])
    elif high_1 < value[x]:
        high_1 = value[x]
    elif low_1 >= value[x]:
        low_1 = value[x]
    if len(m)-1 == x or step != int(m[x+1]):
        h.append(high_1)
        l.append(low_1)
        c.append(value[x])
print('open =',o)
print('high =',h)
print('low =',l)
print('close =',c)

#### 2017.01.24 D-394 ####

앞뒤 비교해서 풀으라는게 무슨말인지 이해못해서 그냥 풀었습니다 허허헣...

※ 상대에게 상처를 주기보다 서로에게 도움이 될 수 있는 댓글을 달아 주세요.

풀이 작성

※ 풀이작성 안내
  • 본문에 코드를 삽입할 경우 에디터 우측 상단의 "코드삽입" 버튼을 이용 해 주세요.
  • 마크다운 문법으로 본문을 작성 해 주세요.
  • 풀이를 읽는 사람들을 위하여 풀이에 대한 설명도 부탁드려요. (아이디어나 사용한 알고리즘 또는 참고한 자료등)
  • 작성한 풀이는 다른 사람(빨간띠 이상)에 의해서 내용이 개선될 수 있습니다.
목록으로
코딩도장

코딩도장은 프로그래밍 문제풀이를 통해서 코딩 실력을 수련(Practice)하는 곳입니다.

ohlc x 1
candlestick x 1
finance chart x 1

언어별 풀이 현황
전 체 x 11
cs x 1
python x 5
기 타 x 3
ruby x 1
java x 1